Unverified Commit 2f4d4503 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Christian Brauner
Browse files

statmount: add flag to retrieve unescaped options



Filesystem options can be retrieved with STATMOUNT_MNT_OPTS, which
returns a string of comma separated options, where some characters are
escaped using the \OOO notation.

Add a new flag, STATMOUNT_OPT_ARRAY, which instead returns the raw
option values separated with '\0' charaters.

Since escaped charaters are rare, this inteface is preferable for
non-libmount users which likley don't want to deal with option
de-escaping.

Example code:

	if (st->mask & STATMOUNT_OPT_ARRAY) {
		const char *opt = st->str + st->opt_array;

		for (unsigned int i = 0; i < st->opt_num; i++) {
			printf("opt_array[%i]: <%s>\n", i, opt);
			opt += strlen(opt) + 1;
		}
	}

Example ouput:

(1) mnt_opts: <lowerdir+=/l\054w\054r,lowerdir+=/l\054w\054r1,upperdir=/upp\054r,workdir=/w\054rk,redirect_dir=nofollow,uuid=null>

(2) opt_array[0]: <lowerdir+=/l,w,r>
    opt_array[1]: <lowerdir+=/l,w,r1>
    opt_array[2]: <upperdir=/upp,r>
    opt_array[3]: <workdir=/w,rk>
    opt_array[4]: <redirect_dir=nofollow>
    opt_array[5]: <uuid=null>

Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Link: https://lore.kernel.org/r/20241112101006.30715-1-mszeredi@redhat.com


Acked-by: default avatarJeff Layton <jlayton@kernel.org>
[brauner: tweak variable naming and parsing add example output]
Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 39bb1bf0
Loading
Loading
Loading
Loading
+46 −1
Original line number Diff line number Diff line
@@ -5072,6 +5072,43 @@ static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq)
	return 0;
}

static int statmount_opt_array(struct kstatmount *s, struct seq_file *seq)
{
	struct vfsmount *mnt = s->mnt;
	struct super_block *sb = mnt->mnt_sb;
	size_t start = seq->count;
	char *buf_start, *buf_end, *opt_start, *opt_end;
	u32 count = 0;
	int err;

	if (!sb->s_op->show_options)
		return 0;

	buf_start = seq->buf + start;
	err = sb->s_op->show_options(seq, mnt->mnt_root);
	if (err)
		return err;

	if (unlikely(seq_has_overflowed(seq)))
		return -EAGAIN;

	if (seq->count == start)
		return 0;

	buf_end = seq->buf + seq->count;
	*buf_end = '\0';
	for (opt_start = buf_start + 1; opt_start < buf_end; opt_start = opt_end + 1) {
		opt_end = strchrnul(opt_start, ',');
		*opt_end = '\0';
		buf_start += string_unescape(opt_start, buf_start, 0, UNESCAPE_OCTAL) + 1;
		if (WARN_ON_ONCE(++count == 0))
			return -EOVERFLOW;
	}
	seq->count = buf_start - 1 - seq->buf;
	s->sm.opt_num = count;
	return 0;
}

static int statmount_string(struct kstatmount *s, u64 flag)
{
	int ret = 0;
@@ -5097,6 +5134,10 @@ static int statmount_string(struct kstatmount *s, u64 flag)
		sm->mnt_opts = start;
		ret = statmount_mnt_opts(s, seq);
		break;
	case STATMOUNT_OPT_ARRAY:
		sm->opt_array = start;
		ret = statmount_opt_array(s, seq);
		break;
	case STATMOUNT_FS_SUBTYPE:
		sm->fs_subtype = start;
		statmount_fs_subtype(s, seq);
@@ -5250,6 +5291,9 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
	if (!err && s->mask & STATMOUNT_MNT_OPTS)
		err = statmount_string(s, STATMOUNT_MNT_OPTS);

	if (!err && s->mask & STATMOUNT_OPT_ARRAY)
		err = statmount_string(s, STATMOUNT_OPT_ARRAY);

	if (!err && s->mask & STATMOUNT_FS_SUBTYPE)
		err = statmount_string(s, STATMOUNT_FS_SUBTYPE);

@@ -5278,7 +5322,8 @@ static inline bool retry_statmount(const long ret, size_t *seq_size)

#define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | \
			      STATMOUNT_FS_SUBTYPE | STATMOUNT_SB_SOURCE)
			      STATMOUNT_FS_SUBTYPE | STATMOUNT_SB_SOURCE | \
			      STATMOUNT_OPT_ARRAY)

static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
			      struct statmount __user *buf, size_t bufsize,
+5 −2
Original line number Diff line number Diff line
@@ -154,7 +154,7 @@ struct mount_attr {
 */
struct statmount {
	__u32 size;		/* Total size, including strings */
	__u32 mnt_opts;		/* [str] Mount options of the mount */
	__u32 mnt_opts;		/* [str] Options (comma separated, escaped) */
	__u64 mask;		/* What results were written */
	__u32 sb_dev_major;	/* Device ID */
	__u32 sb_dev_minor;
@@ -175,7 +175,9 @@ struct statmount {
	__u64 mnt_ns_id;	/* ID of the mount namespace */
	__u32 fs_subtype;	/* [str] Subtype of fs_type (if any) */
	__u32 sb_source;	/* [str] Source string of the mount */
	__u64 __spare2[48];
	__u32 opt_num;		/* Number of fs options */
	__u32 opt_array;	/* [str] Array of nul terminated fs options */
	__u64 __spare2[47];
	char str[];		/* Variable size part containing strings */
};

@@ -211,6 +213,7 @@ struct mnt_id_req {
#define STATMOUNT_MNT_OPTS		0x00000080U	/* Want/got mnt_opts */
#define STATMOUNT_FS_SUBTYPE		0x00000100U	/* Want/got fs_subtype */
#define STATMOUNT_SB_SOURCE		0x00000200U	/* Want/got sb_source */
#define STATMOUNT_OPT_ARRAY		0x00000400U	/* Want/got opt_... */

/*
 * Special @mnt_id values that can be passed to listmount