const struct libmnt_optmap *ent = mnt_opt_get_mapent(opt);
if (ent && mnt_opt_get_map(opt) == cxt->map_linux &&
- ent->id == MS_RDONLY)
- ;
- else if (!name || mnt_opt_get_map(opt) || mnt_opt_is_external(opt))
+ ent->id == MS_RDONLY) {
+ value = NULL;
+ } else if (!name || mnt_opt_get_map(opt) || mnt_opt_is_external(opt))
continue;
DBG(HOOK, ul_debugobj(hs, " fsconfig(name=%s,value=%s)", name, value));
return rc;
}
-static int hook_set_vfsflags(struct libmnt_context *cxt,
+static int set_vfsflags(struct libmnt_context *cxt,
const struct libmnt_hookset *hs,
- void *data __attribute__((__unused__)))
+ uint64_t set, uint64_t clr, int recursive)
{
struct libmnt_sysapi *api;
- struct libmnt_optlist *ol;
struct mount_attr attr = { .attr_clr = 0 };
unsigned int callflags = AT_EMPTY_PATH;
- uint64_t set = 0, clr = 0;
- int rc = 0;
-
- DBG(HOOK, ul_debugobj(hs, "setting VFS flags"));
+ int rc;
api = get_sysapi(cxt);
assert(api);
if (api->fd_tree < 0 && mnt_fs_get_target(cxt->fs)) {
rc = api->fd_tree = open_mount_tree(cxt, NULL, (unsigned long) -1);
if (rc < 0)
- goto done;
+ return rc;
rc = 0;
}
- ol = mnt_context_get_optlist(cxt);
- if (!ol)
- return -ENOMEM;
-
- rc = mnt_optlist_get_attrs(ol, &set, &clr);
- if (rc)
- return rc;
-
- if (mnt_optlist_is_recursive(ol))
+ if (recursive)
callflags |= AT_RECURSIVE;
if (set & (MOUNT_ATTR_RELATIME | MOUNT_ATTR_NOATIME | MOUNT_ATTR_STRICTATIME))
attr.attr_set = set;
attr.attr_clr = clr;
+ errno = 0;
rc = mount_setattr(api->fd_tree, "", callflags, &attr, sizeof(attr));
set_syscall_status(cxt, "move_setattr", rc == 0);
if (rc && errno == EINVAL)
return -MNT_ERR_APPLYFLAGS;
-done:
+
return rc == 0 ? 0 : -errno;
}
+static int hook_set_vfsflags(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ struct libmnt_optlist *ol;
+ uint64_t set = 0, clr = 0;
+ int rc = 0;
+
+ DBG(HOOK, ul_debugobj(hs, "setting VFS flags"));
+
+ ol = mnt_context_get_optlist(cxt);
+ if (!ol)
+ return -ENOMEM;
+
+ /* normal flags */
+ rc = mnt_optlist_get_attrs(ol, &set, &clr, MNT_OL_NOREC);
+ if (!rc && (set || clr))
+ rc = set_vfsflags(cxt, hs, set, clr, 0);
+
+ /* recursive flags */
+ set = clr = 0;
+ if (!rc)
+ rc = mnt_optlist_get_attrs(ol, &set, &clr, MNT_OL_REC);
+ if (!rc && (set || clr))
+ rc = set_vfsflags(cxt, hs, set, clr, 1);
+
+ return rc;
+}
+
static int hook_set_propagation(struct libmnt_context *cxt,
const struct libmnt_hookset *hs,
void *data __attribute__((__unused__)))
/* MOUNT_ATTR_* flags for mount_setattr() */
if (!rc)
- rc = mnt_optlist_get_attrs(ol, &set, &clr);
+ rc = mnt_optlist_get_attrs(ol, &set, &clr, 0);
/* open_tree() or fsopen() */
if (!rc)
__MNT_OL_FLTR_COUNT /* keep it last */
};
+
extern int mnt_optlist_get_flags(struct libmnt_optlist *ls, unsigned long *flags,
const struct libmnt_optmap *map, unsigned int what);
-extern int mnt_optlist_get_attrs(struct libmnt_optlist *ls, uint64_t *set, uint64_t *clr);
+
+/* recursive status for mnt_optlist_get_attrs() */
+#define MNT_OL_REC 1
+#define MNT_OL_NOREC 2
+
+extern int mnt_optlist_get_attrs(struct libmnt_optlist *ls, uint64_t *set, uint64_t *clr, int rec);
+
extern int mnt_optlist_get_optstr(struct libmnt_optlist *ol, const char **optstr,
const struct libmnt_optmap *map, unsigned int what);
extern int mnt_optlist_strdup_optstr(struct libmnt_optlist *ls, char **optstr,
enum libmnt_optsrc src;
unsigned int external : 1, /* visible for external helpers only */
+ recursive : 1, /* recursive flag */
+ is_linux : 1, /* defined in ls->linux_map (VFS attr) */
quoted : 1; /* name="value" */
};
if (opt->ent->id & MS_REC)
ls->is_recursive = 0;
-
}
optlist_cleanup_cache(ls, opt->map);
return 0;
}
+#ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
+static inline uint64_t flag_to_attr(unsigned long flag)
+{
+ switch (flag) {
+ case MS_RDONLY:
+ return MOUNT_ATTR_RDONLY;
+ case MS_NOSUID:
+ return MOUNT_ATTR_NOSUID;
+ case MS_NOEXEC:
+ return MOUNT_ATTR_NOEXEC;
+ case MS_NODIRATIME:
+ return MOUNT_ATTR_NODIRATIME;
+ case MS_RELATIME:
+ return MOUNT_ATTR_RELATIME;
+ case MS_NOATIME:
+ return MOUNT_ATTR_NOATIME;
+ case MS_STRICTATIME:
+ return MOUNT_ATTR_STRICTATIME;
+ case MS_NOSYMFOLLOW:
+ return MOUNT_ATTR_NOSYMFOLLOW;
+ }
+ return 0;
+}
+
+/*
+ * Is the @opt relevant for mount_setattr() ?
+ */
+static inline int is_vfs_opt(struct libmnt_opt *opt)
+{
+ if (!opt->map || !opt->ent || !opt->ent->id || !opt->is_linux)
+ return 0;
+
+ return flag_to_attr(opt->ent->id) == 0 ? 0 : 1;
+}
+#endif
+
static struct libmnt_opt *optlist_new_opt(struct libmnt_optlist *ls,
const char *name, size_t namesz,
const char *value, size_t valsz,
/* shortcuts */
if (map && ent && map == ls->linux_map) {
+ opt->is_linux = 1;
+
if (ent->id & MS_PROPAGATION)
ls->propagation |= ent->id;
else if (opt->ent->id == MS_REMOUNT)
else if (opt->ent->id == MS_SILENT)
ls->is_silent = 1;
- if (opt->ent->id & MS_REC)
+ if (opt->ent->id & MS_REC) {
ls->is_recursive = 1;
+ opt->recursive = 1;
+ }
}
-
+#ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
+ if (!opt->recursive && opt->value
+ && is_vfs_opt(opt) && strcmp(opt->value, "recursive") == 0)
+ opt->recursive = 1;
+#endif
if (ent && map) {
DBG(OPTLIST, ul_debugobj(ls, " added %s [id=0x%08x map=%p]",
opt->name, ent->id, map));
return 0;
}
-#ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
-static inline uint64_t flag_to_attr(unsigned long flag)
-{
- switch (flag) {
- case MS_RDONLY:
- return MOUNT_ATTR_RDONLY;
- case MS_NOSUID:
- return MOUNT_ATTR_NOSUID;
- case MS_NOEXEC:
- return MOUNT_ATTR_NOEXEC;
- case MS_NODIRATIME:
- return MOUNT_ATTR_NODIRATIME;
- case MS_RELATIME:
- return MOUNT_ATTR_RELATIME;
- case MS_NOATIME:
- return MOUNT_ATTR_NOATIME;
- case MS_STRICTATIME:
- return MOUNT_ATTR_STRICTATIME;
- case MS_NOSYMFOLLOW:
- return MOUNT_ATTR_NOSYMFOLLOW;
- }
- return 0;
-}
-#endif
/*
* Like mnt_optlist_get_flags() for VFS flags, but converts classic MS_* flags to
* new MOUNT_ATTR_*
*/
#ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
-int mnt_optlist_get_attrs(struct libmnt_optlist *ls, uint64_t *set, uint64_t *clr)
+int mnt_optlist_get_attrs(struct libmnt_optlist *ls, uint64_t *set, uint64_t *clr, int rec)
{
struct libmnt_iter itr;
struct libmnt_opt *opt;
continue;
if (!opt->ent || !opt->ent->id)
continue;
+
+ if (rec == MNT_OL_REC && !opt->recursive)
+ continue;
+ if (rec == MNT_OL_NOREC && opt->recursive)
+ continue;
+
if (!is_wanted_opt(opt, ls->linux_map, MNT_OL_FLTR_DFLT))
continue;
x = flag_to_attr( opt->ent->id );
#else
int mnt_optlist_get_attrs(struct libmnt_optlist *ls __attribute__((__unused__)),
uint64_t *set __attribute__((__unused__)),
- uint64_t *clr __attribute__((__unused__)))
+ uint64_t *clr __attribute__((__unused__)),
+ int mask)
{
return 0;
}
int mnt_opt_set_value(struct libmnt_opt *opt, const char *str)
{
- return strdup_to_struct_member(opt, value, str);
+ int rc;
+
+ opt->recursive = 0;
+ rc = strdup_to_struct_member(opt, value, str);
+
+ if (rc == 0 && str && strcmp(str, "recursive") == 0)
+ opt->recursive = 1;
+ return rc;
}
int mnt_opt_set_u64value(struct libmnt_opt *opt, uint64_t num)
Note that a read-only bind will create a read-only mountpoint (VFS entry), but the original filesystem superblock will still be writable, meaning that the _olddir_ will be writable, but the _newdir_ will be read-only.
-It's also possible to change nosuid, nodev, noexec, noatime, nodiratime, relatime and nosymfollow VFS entry flags via a "remount,bind" operation. The other flags (for example filesystem-specific flags) are silently ignored. It's impossible to change mount options recursively (for example with *-o rbind,ro*).
+It's also possible to change nosuid, nodev, noexec, noatime, nodiratime, relatime and nosymfollow VFS entry flags via a "remount,bind" operation. The other flags (for example filesystem-specific flags) are silently ignored. The classic mount(2) system call does not allow to change mount options recursively (for example with *-o rbind,ro*). The recursive semantic is possible with a new mount_setattr(2) kernel system call and it's supported since libmount from util-linux v2.39 by a new experimental "recursive" option argument (e.g. *-o rbind,ro=recursive*). For more details see the *FILESYSTEM-INDEPENDENT MOUNT OPTIONS* section for more details.
Since util-linux 2.31, *mount* ignores the *bind* flag from _/etc/fstab_ on a *remount* operation (if *-o remount* is specified on command line). This is necessary to fully control mount options on remount by command line. In previous versions the bind flag has been always applied and it was impossible to re-define mount options without interaction with the bind semantic. This *mount* behavior does not affect situations when "remount,bind" is specified in the _/etc/fstab_ file.
Some of these options could be enabled or disabled by default in the system kernel. To check the current setting see the options in _/proc/mounts_. Note that filesystems also have per-filesystem specific default mount options (see for example *tune2fs -l* output for ext__N__ filesystems).
+The options *nosuid*, *noexec*, *nodiratime*, *relatime*, *noatime*, *strictatime*, and *nosymfollow* are interpreted only by the abstract VFS kernel layer and applied to the mountpoint node rather than to the filesystem itself. Try:
+____
+
+ findmnt -o TARGET,VFS-OPTIONS,FS-OPTIONS
+____
+
+to get a complete overview of filesystems and VFS options.
+
+The read-only setting (*ro* or *rw*) is interpreted by VFS and the filesystem
+and depends on how the option is specified on the mount(8) command line. The
+default is to interpret it on the filesystem level. The operation "-o bind,remount,ro"
+is applied only to the VFS mountpoint, and operation "-o remount,ro" is applied to
+VFS and filesystem superblock. This semantic allows create a read-only
+mountpoint but keeps the filesystem writable from another mountpoint.
+
+Since v2.39 libmount can use a new kernel mount interface to set the VFS
+options recursive. For backward compatibility, this feature is not enabled by
+default, although recursive operation (e.g. rbind) has been requested. The new
+option argument "recursive" could be specified, for example:
+____
+
+ mount -orbind,ro=recursive,noexec=recursive,nosuid /foo /bar
+____
+
+recursively binds filesystems from /foo to /bar, /bar, and all submounts will
+be read-only and noexec, but only /bar itself will be "nosuid". The "recursive"
+optional argument for VFS mount options is an EXPERIMENTAL feature.
+
The following options apply to any filesystem that is being mounted (but not every filesystem actually honors them - e.g., the *sync* option today has an effect only for ext2, ext3, ext4, fat, vfat, ufs and xfs):
*async*::