Which take an user, group, and mode, respectively, and set them on the target after mounting This is vaguely similar to tmpfs(5)'s [ug]id= and mode= options, but we POSIX-parse the user- and group names Oft requested in systemd/zram-generator, since a common use-case is to use it to create /tmp or an equivalent directory that needs to be a=rwx,o+t (or a user's private temp that needs to be owned by them) ‒ this is impossible without terrible hacks, cf. https://github.com/systemd/zram-generator/issues/150, https://github.com/systemd/zram-generator/issues/146, &c. This started off as a Set{User,Group,Mode}= systemd mount unit, but was poetterung into libmount options: https://github.com/systemd/systemd/pull/22889 --- As a PoC: $ truncate -s40G /tmp/ext4; /sbin/mkfs.ext4 /tmp/ext4 # ./mount -o \ X-mount.owner=nabijaczleweli,X-mount.group=1212,X-mount.mode=1234 \ /tmp/ext4 /tmp/a\ +\ b/ $ l -d /tmp/a\ +\ b/ d-w--wxr-T 3 nabijaczleweli 1212 4.0K Mar 30 20:03 '/tmp/a + b/' I've marked this RFC because the failures are, well, not pretty (for example, given a mis-spelled or unset username): mount: /tmp/a + b: filesystem was mounted, but any subsequent operation failed: Invalid argument. But I'm not sure how to proceed. I've stuffed the parsing and chowning stage into mnt_context_finalize_mount() for ease-of-PoC, but should: (a) the post-syscall error handling in mnt_context_get_mount_excode() be extended to recognise MNT_ERR_MOUNTOPT? (b) the parsing/chowning stages be split (parsing in pre-mount prep, chowning in post-mount)? with a new MNT_ERR_ flag potentially? (c) something else? Best, наб Please keep me in CC, as I'm not subscribed. libmount/src/context_mount.c | 136 +++++++++++++++++++++++++++++++++++ sys-utils/mount.8.adoc | 6 ++ 2 files changed, 142 insertions(+) diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c index 1fc3ff2cc..290f55ea7 100644 --- a/libmount/src/context_mount.c +++ b/libmount/src/context_mount.c @@ -1229,6 +1229,138 @@ static int is_source_already_rdonly(struct libmnt_context *cxt) return opts && mnt_optstr_get_option(opts, "ro", NULL, NULL) == 0; } +/* Extracted from mnt_optstr_get_uid() */ +static int parse_ugid(const char *value, size_t valsz, unsigned *uid) +{ + char buf[sizeof(stringify_value(UINT64_MAX))]; + int rc; + uint64_t num; + + assert(value); + assert(uid); + + if (valsz > sizeof(buf) - 1) { + rc = -ERANGE; + goto fail; + } + mem2strcpy(buf, value, valsz, sizeof(buf)); + + rc = ul_strtou64(buf, &num, 10); + if (rc != 0) + goto fail; + if (num > ULONG_MAX || (unsigned) num != num) { + rc = -ERANGE; + goto fail; + } + *uid = (unsigned) num; + + return 0; +fail: + DBG(UTILS, ul_debug("failed to convert '%.*s' to number [rc=%d]", (int) valsz, value, rc)); + return rc; +} + +static int parse_mode(const char *value, size_t valsz, mode_t *uid) +{ + char buf[sizeof(stringify_value(UINT64_MAX))]; + int rc; + uint64_t num; + + assert(value); + assert(uid); + + if (valsz > sizeof(buf) - 1) { + rc = -ERANGE; + goto fail; + } + mem2strcpy(buf, value, valsz, sizeof(buf)); + + rc = ul_strtou64(buf, &num, 8); + if (rc != 0) + goto fail; + if (num > 07777) { + rc = -ERANGE; + goto fail; + } + *uid = (mode_t) num; + + return 0; +fail: + DBG(UTILS, ul_debug("failed to convert '%.*s' to mode [rc=%d]", (int) valsz, value, rc)); + return rc; +} + +/* + * Process X-mount.owner=, X-mount.group=, X-mount.mode=. + */ +static int set_ownership_mode(struct libmnt_context *cxt) +{ + int rc; + + uid_t new_owner = (uid_t) -1; + uid_t new_group = (uid_t) -1; + mode_t new_mode = (mode_t) -1; + + const char *o = mnt_fs_get_user_options(cxt->fs); + + char *owner; + size_t owner_len; + if ((rc = mnt_optstr_get_option(o, "X-mount.owner", &owner, &owner_len)) < 0) + return rc; + if (!rc) { + if (!owner_len) + return -EINVAL; + + char *owner_tofree = NULL; + rc = mnt_get_uid(owner[owner_len] ? (owner_tofree = strndup(owner, owner_len)) : owner, &new_owner); + free(owner_tofree); + if (new_owner == (uid_t) -1 && isdigit(*owner)) + rc = parse_ugid(owner, owner_len, &new_owner); + if (rc) + return rc; + } + + char *group; + size_t group_len; + if ((rc = mnt_optstr_get_option(o, "X-mount.group", &group, &group_len)) < 0) + return rc; + if (!rc) { + if (!group_len) + return -EINVAL; + + char *group_tofree = NULL; + rc = mnt_get_uid(group[group_len] ? (group_tofree = strndup(group, group_len)) : group, &new_group); + free(group_tofree); + if (new_group == (uid_t) -1 && isdigit(*group)) + rc = parse_ugid(group, group_len, &new_group); + if (rc) + return rc; + } + + char *mode; + size_t mode_len; + if ((rc = mnt_optstr_get_option(o, "X-mount.mode", &mode, &mode_len)) < 0) + return rc; + if (!rc) { + if (!group_len) + return -EINVAL; + if ((rc = parse_mode(mode, mode_len, &new_mode))) + return rc; + } + + const char *target = mnt_fs_get_target(cxt->fs); + + if (new_owner != (uid_t) -1 || new_group != (uid_t) -1) + if (lchown(target, new_owner, new_group) == -1) + return -errno; + + if (new_mode != (mode_t) -1) + if (chmod(target, new_mode) == -1) + return -errno; + + return 0; +} + /** * mnt_context_finalize_mount: * @cxt: context @@ -1250,6 +1382,8 @@ int mnt_context_finalize_mount(struct libmnt_context *cxt) rc = mnt_context_prepare_update(cxt); if (!rc) rc = mnt_context_update_tabs(cxt); + if (!rc) + rc = set_ownership_mode(cxt); return rc; } @@ -1328,6 +1462,8 @@ again: rc = mnt_context_do_mount(cxt); if (!rc) rc = mnt_context_update_tabs(cxt); + if (!rc) + rc = set_ownership_mode(cxt); /* * Read-only device or already read-only mounted FS. diff --git a/sys-utils/mount.8.adoc b/sys-utils/mount.8.adoc index 343d7e297..53a0bbb34 100644 --- a/sys-utils/mount.8.adoc +++ b/sys-utils/mount.8.adoc @@ -633,6 +633,12 @@ Allow to make a target directory (mountpoint) if it does not exist yet. The opti **X-mount.subdir=**__directory__:: Allow mounting sub-directory from a filesystem instead of the root directory. For now, this feature is implemented by temporary filesystem root directory mount in unshared namespace and then bind the sub-directory to the final mount point and umount the root of the filesystem. The sub-directory mount shows up atomically for the rest of the system although it is implemented by multiple *mount*(2) syscalls. This feature is EXPERIMENTAL. +*X-mount.owner*=_username_|_UID_, *X-mount.group*=_group_|_GID_:: +Set _mountpoint_'s ownership after mounting. + +*X-mount.mode*=_mode_:: +Set _mountpoint_'s mode after mounting. + *nosymfollow*:: Do not follow symlinks when resolving paths. Symlinks can still be created, and *readlink*(1), *readlink*(2), *realpath*(1), and *realpath*(3) all still work properly. -- 2.30.2
Attachment:
signature.asc
Description: PGP signature