Skip to content

Commit 682d121

Browse files
committed
Merge patch series "Add the ability to query mount options in statmount"
Josef Bacik <[email protected]> says: Currently if you want to get mount options for a mount and you're using statmount(), you still have to open /proc/mounts to parse the mount options. statmount() does have the ability to store an arbitrary string however, additionally the way we do that is with a seq_file, which is also how we use ->show_options for the individual file systems. Extent statmount() to have a flag for fetching the mount options of a mount. This allows users to not have to parse /proc mount for anything related to a mount. I've extended the existing statmount() test to validate this feature works as expected. As you can tell from the ridiculous amount of silly string parsing, this is a huge win for users and climate change as we will no longer have to waste several cycles parsing strings anymore. Josef Bacik (4): fs: rename show_mnt_opts -> show_vfsmnt_opts fs: add a helper to show all the options for a mount fs: export mount options via statmount() sefltests: extend the statmount test for mount options fs/internal.h | 5 + fs/namespace.c | 7 + fs/proc_namespace.c | 29 ++-- include/uapi/linux/mount.h | 3 +- .../filesystems/statmount/statmount_test.c | 131 +++++++++++++++++- 5 files changed, 164 insertions(+), 11 deletions(-) Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
2 parents d842379 + e2f718e commit 682d121

File tree

4 files changed

+131
-6
lines changed

4 files changed

+131
-6
lines changed

fs/namespace.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4980,6 +4980,34 @@ static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)
49804980
s->sm.mnt_ns_id = ns->seq;
49814981
}
49824982

4983+
static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq)
4984+
{
4985+
struct vfsmount *mnt = s->mnt;
4986+
struct super_block *sb = mnt->mnt_sb;
4987+
int err;
4988+
4989+
if (sb->s_op->show_options) {
4990+
size_t start = seq->count;
4991+
4992+
err = sb->s_op->show_options(seq, mnt->mnt_root);
4993+
if (err)
4994+
return err;
4995+
4996+
if (unlikely(seq_has_overflowed(seq)))
4997+
return -EAGAIN;
4998+
4999+
if (seq->count == start)
5000+
return 0;
5001+
5002+
/* skip leading comma */
5003+
memmove(seq->buf + start, seq->buf + start + 1,
5004+
seq->count - start - 1);
5005+
seq->count--;
5006+
}
5007+
5008+
return 0;
5009+
}
5010+
49835011
static int statmount_string(struct kstatmount *s, u64 flag)
49845012
{
49855013
int ret;
@@ -5000,6 +5028,10 @@ static int statmount_string(struct kstatmount *s, u64 flag)
50005028
sm->mnt_point = seq->count;
50015029
ret = statmount_mnt_point(s, seq);
50025030
break;
5031+
case STATMOUNT_MNT_OPTS:
5032+
sm->mnt_opts = seq->count;
5033+
ret = statmount_mnt_opts(s, seq);
5034+
break;
50035035
default:
50045036
WARN_ON_ONCE(true);
50055037
return -EINVAL;
@@ -5130,6 +5162,9 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
51305162
if (!err && s->mask & STATMOUNT_MNT_POINT)
51315163
err = statmount_string(s, STATMOUNT_MNT_POINT);
51325164

5165+
if (!err && s->mask & STATMOUNT_MNT_OPTS)
5166+
err = statmount_string(s, STATMOUNT_MNT_OPTS);
5167+
51335168
if (!err && s->mask & STATMOUNT_MNT_NS_ID)
51345169
statmount_mnt_ns_id(s, ns);
51355170

@@ -5151,7 +5186,7 @@ static inline bool retry_statmount(const long ret, size_t *seq_size)
51515186
}
51525187

51535188
#define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
5154-
STATMOUNT_FS_TYPE)
5189+
STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS)
51555190

51565191
static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
51575192
struct statmount __user *buf, size_t bufsize,

fs/proc_namespace.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb)
6161
return security_sb_show_options(m, sb);
6262
}
6363

64-
static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
64+
static void show_vfsmnt_opts(struct seq_file *m, struct vfsmount *mnt)
6565
{
6666
static const struct proc_fs_opts mnt_opts[] = {
6767
{ MNT_NOSUID, ",nosuid" },
@@ -124,7 +124,7 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
124124
err = show_sb_opts(m, sb);
125125
if (err)
126126
goto out;
127-
show_mnt_opts(m, mnt);
127+
show_vfsmnt_opts(m, mnt);
128128
if (sb->s_op->show_options)
129129
err = sb->s_op->show_options(m, mnt_path.dentry);
130130
seq_puts(m, " 0 0\n");
@@ -153,7 +153,7 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
153153
goto out;
154154

155155
seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
156-
show_mnt_opts(m, mnt);
156+
show_vfsmnt_opts(m, mnt);
157157

158158
/* Tagged fields ("foo:X" or "bar") */
159159
if (IS_MNT_SHARED(r))

include/uapi/linux/mount.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ struct mount_attr {
154154
*/
155155
struct statmount {
156156
__u32 size; /* Total size, including strings */
157-
__u32 __spare1;
157+
__u32 mnt_opts; /* [str] Mount options of the mount */
158158
__u64 mask; /* What results were written */
159159
__u32 sb_dev_major; /* Device ID */
160160
__u32 sb_dev_minor;
@@ -206,6 +206,7 @@ struct mnt_id_req {
206206
#define STATMOUNT_MNT_POINT 0x00000010U /* Want/got mnt_point */
207207
#define STATMOUNT_FS_TYPE 0x00000020U /* Want/got fs_type */
208208
#define STATMOUNT_MNT_NS_ID 0x00000040U /* Want/got mnt_ns_id */
209+
#define STATMOUNT_MNT_OPTS 0x00000080U /* Want/got mnt_opts */
209210

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

tools/testing/selftests/filesystems/statmount/statmount_test.c

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ static char root_mntpoint[] = "/tmp/statmount_test_root.XXXXXX";
107107
static int orig_root;
108108
static uint64_t root_id, parent_id;
109109
static uint32_t old_root_id, old_parent_id;
110+
static FILE *f_mountinfo;
110111

111112
static void cleanup_namespace(void)
112113
{
@@ -134,6 +135,11 @@ static void setup_namespace(void)
134135
sprintf(buf, "0 %d 1", gid);
135136
write_file("/proc/self/gid_map", buf);
136137

138+
f_mountinfo = fopen("/proc/self/mountinfo", "re");
139+
if (!f_mountinfo)
140+
ksft_exit_fail_msg("failed to open mountinfo: %s\n",
141+
strerror(errno));
142+
137143
ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
138144
if (ret == -1)
139145
ksft_exit_fail_msg("making mount tree private: %s\n",
@@ -435,6 +441,88 @@ static void test_statmount_fs_type(void)
435441
free(sm);
436442
}
437443

444+
static void test_statmount_mnt_opts(void)
445+
{
446+
struct statmount *sm;
447+
const char *statmount_opts;
448+
char *line = NULL;
449+
size_t len = 0;
450+
451+
sm = statmount_alloc(root_id, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS,
452+
0);
453+
if (!sm) {
454+
ksft_test_result_fail("statmount mnt opts: %s\n",
455+
strerror(errno));
456+
return;
457+
}
458+
459+
while (getline(&line, &len, f_mountinfo) != -1) {
460+
int i;
461+
char *p, *p2;
462+
unsigned int old_mnt_id;
463+
464+
old_mnt_id = atoi(line);
465+
if (old_mnt_id != sm->mnt_id_old)
466+
continue;
467+
468+
for (p = line, i = 0; p && i < 5; i++)
469+
p = strchr(p + 1, ' ');
470+
if (!p)
471+
continue;
472+
473+
p2 = strchr(p + 1, ' ');
474+
if (!p2)
475+
continue;
476+
*p2 = '\0';
477+
p = strchr(p2 + 1, '-');
478+
if (!p)
479+
continue;
480+
for (p++, i = 0; p && i < 2; i++)
481+
p = strchr(p + 1, ' ');
482+
if (!p)
483+
continue;
484+
p++;
485+
486+
/* skip generic superblock options */
487+
if (strncmp(p, "ro", 2) == 0)
488+
p += 2;
489+
else if (strncmp(p, "rw", 2) == 0)
490+
p += 2;
491+
if (*p == ',')
492+
p++;
493+
if (strncmp(p, "sync", 4) == 0)
494+
p += 4;
495+
if (*p == ',')
496+
p++;
497+
if (strncmp(p, "dirsync", 7) == 0)
498+
p += 7;
499+
if (*p == ',')
500+
p++;
501+
if (strncmp(p, "lazytime", 8) == 0)
502+
p += 8;
503+
if (*p == ',')
504+
p++;
505+
p2 = strrchr(p, '\n');
506+
if (p2)
507+
*p2 = '\0';
508+
509+
statmount_opts = sm->str + sm->mnt_opts;
510+
if (strcmp(statmount_opts, p) != 0)
511+
ksft_test_result_fail(
512+
"unexpected mount options: '%s' != '%s'\n",
513+
statmount_opts, p);
514+
else
515+
ksft_test_result_pass("statmount mount options\n");
516+
free(sm);
517+
free(line);
518+
return;
519+
}
520+
521+
ksft_test_result_fail("didnt't find mount entry\n");
522+
free(sm);
523+
free(line);
524+
}
525+
438526
static void test_statmount_string(uint64_t mask, size_t off, const char *name)
439527
{
440528
struct statmount *sm;
@@ -561,14 +649,15 @@ int main(void)
561649

562650
setup_namespace();
563651

564-
ksft_set_plan(14);
652+
ksft_set_plan(15);
565653
test_listmount_empty_root();
566654
test_statmount_zero_mask();
567655
test_statmount_mnt_basic();
568656
test_statmount_sb_basic();
569657
test_statmount_mnt_root();
570658
test_statmount_mnt_point();
571659
test_statmount_fs_type();
660+
test_statmount_mnt_opts();
572661
test_statmount_string(STATMOUNT_MNT_ROOT, str_off(mnt_root), "mount root");
573662
test_statmount_string(STATMOUNT_MNT_POINT, str_off(mnt_point), "mount point");
574663
test_statmount_string(STATMOUNT_FS_TYPE, str_off(fs_type), "fs type");

0 commit comments

Comments
 (0)