Skip to content

Commit

Permalink
Merge patch series "Add the ability to query mount options in statmount"
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
brauner committed Jun 28, 2024
2 parents d842379 + e2f718e commit 682d121
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 6 deletions.
37 changes: 36 additions & 1 deletion fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -4980,6 +4980,34 @@ static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)
s->sm.mnt_ns_id = ns->seq;
}

static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq)
{
struct vfsmount *mnt = s->mnt;
struct super_block *sb = mnt->mnt_sb;
int err;

if (sb->s_op->show_options) {
size_t start = seq->count;

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;

/* skip leading comma */
memmove(seq->buf + start, seq->buf + start + 1,
seq->count - start - 1);
seq->count--;
}

return 0;
}

static int statmount_string(struct kstatmount *s, u64 flag)
{
int ret;
Expand All @@ -5000,6 +5028,10 @@ static int statmount_string(struct kstatmount *s, u64 flag)
sm->mnt_point = seq->count;
ret = statmount_mnt_point(s, seq);
break;
case STATMOUNT_MNT_OPTS:
sm->mnt_opts = seq->count;
ret = statmount_mnt_opts(s, seq);
break;
default:
WARN_ON_ONCE(true);
return -EINVAL;
Expand Down Expand Up @@ -5130,6 +5162,9 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
if (!err && s->mask & STATMOUNT_MNT_POINT)
err = statmount_string(s, STATMOUNT_MNT_POINT);

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

if (!err && s->mask & STATMOUNT_MNT_NS_ID)
statmount_mnt_ns_id(s, ns);

Expand All @@ -5151,7 +5186,7 @@ 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_FS_TYPE | STATMOUNT_MNT_OPTS)

static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
struct statmount __user *buf, size_t bufsize,
Expand Down
6 changes: 3 additions & 3 deletions fs/proc_namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ static int show_sb_opts(struct seq_file *m, struct super_block *sb)
return security_sb_show_options(m, sb);
}

static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
static void show_vfsmnt_opts(struct seq_file *m, struct vfsmount *mnt)
{
static const struct proc_fs_opts mnt_opts[] = {
{ MNT_NOSUID, ",nosuid" },
Expand Down Expand Up @@ -124,7 +124,7 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
err = show_sb_opts(m, sb);
if (err)
goto out;
show_mnt_opts(m, mnt);
show_vfsmnt_opts(m, mnt);
if (sb->s_op->show_options)
err = sb->s_op->show_options(m, mnt_path.dentry);
seq_puts(m, " 0 0\n");
Expand Down Expand Up @@ -153,7 +153,7 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
goto out;

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

/* Tagged fields ("foo:X" or "bar") */
if (IS_MNT_SHARED(r))
Expand Down
3 changes: 2 additions & 1 deletion include/uapi/linux/mount.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ struct mount_attr {
*/
struct statmount {
__u32 size; /* Total size, including strings */
__u32 __spare1;
__u32 mnt_opts; /* [str] Mount options of the mount */
__u64 mask; /* What results were written */
__u32 sb_dev_major; /* Device ID */
__u32 sb_dev_minor;
Expand Down Expand Up @@ -206,6 +206,7 @@ struct mnt_id_req {
#define STATMOUNT_MNT_POINT 0x00000010U /* Want/got mnt_point */
#define STATMOUNT_FS_TYPE 0x00000020U /* Want/got fs_type */
#define STATMOUNT_MNT_NS_ID 0x00000040U /* Want/got mnt_ns_id */
#define STATMOUNT_MNT_OPTS 0x00000080U /* Want/got mnt_opts */

/*
* Special @mnt_id values that can be passed to listmount
Expand Down
91 changes: 90 additions & 1 deletion tools/testing/selftests/filesystems/statmount/statmount_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ static char root_mntpoint[] = "/tmp/statmount_test_root.XXXXXX";
static int orig_root;
static uint64_t root_id, parent_id;
static uint32_t old_root_id, old_parent_id;
static FILE *f_mountinfo;

static void cleanup_namespace(void)
{
Expand Down Expand Up @@ -134,6 +135,11 @@ static void setup_namespace(void)
sprintf(buf, "0 %d 1", gid);
write_file("/proc/self/gid_map", buf);

f_mountinfo = fopen("/proc/self/mountinfo", "re");
if (!f_mountinfo)
ksft_exit_fail_msg("failed to open mountinfo: %s\n",
strerror(errno));

ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
if (ret == -1)
ksft_exit_fail_msg("making mount tree private: %s\n",
Expand Down Expand Up @@ -435,6 +441,88 @@ static void test_statmount_fs_type(void)
free(sm);
}

static void test_statmount_mnt_opts(void)
{
struct statmount *sm;
const char *statmount_opts;
char *line = NULL;
size_t len = 0;

sm = statmount_alloc(root_id, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS,
0);
if (!sm) {
ksft_test_result_fail("statmount mnt opts: %s\n",
strerror(errno));
return;
}

while (getline(&line, &len, f_mountinfo) != -1) {
int i;
char *p, *p2;
unsigned int old_mnt_id;

old_mnt_id = atoi(line);
if (old_mnt_id != sm->mnt_id_old)
continue;

for (p = line, i = 0; p && i < 5; i++)
p = strchr(p + 1, ' ');
if (!p)
continue;

p2 = strchr(p + 1, ' ');
if (!p2)
continue;
*p2 = '\0';
p = strchr(p2 + 1, '-');
if (!p)
continue;
for (p++, i = 0; p && i < 2; i++)
p = strchr(p + 1, ' ');
if (!p)
continue;
p++;

/* skip generic superblock options */
if (strncmp(p, "ro", 2) == 0)
p += 2;
else if (strncmp(p, "rw", 2) == 0)
p += 2;
if (*p == ',')
p++;
if (strncmp(p, "sync", 4) == 0)
p += 4;
if (*p == ',')
p++;
if (strncmp(p, "dirsync", 7) == 0)
p += 7;
if (*p == ',')
p++;
if (strncmp(p, "lazytime", 8) == 0)
p += 8;
if (*p == ',')
p++;
p2 = strrchr(p, '\n');
if (p2)
*p2 = '\0';

statmount_opts = sm->str + sm->mnt_opts;
if (strcmp(statmount_opts, p) != 0)
ksft_test_result_fail(
"unexpected mount options: '%s' != '%s'\n",
statmount_opts, p);
else
ksft_test_result_pass("statmount mount options\n");
free(sm);
free(line);
return;
}

ksft_test_result_fail("didnt't find mount entry\n");
free(sm);
free(line);
}

static void test_statmount_string(uint64_t mask, size_t off, const char *name)
{
struct statmount *sm;
Expand Down Expand Up @@ -561,14 +649,15 @@ int main(void)

setup_namespace();

ksft_set_plan(14);
ksft_set_plan(15);
test_listmount_empty_root();
test_statmount_zero_mask();
test_statmount_mnt_basic();
test_statmount_sb_basic();
test_statmount_mnt_root();
test_statmount_mnt_point();
test_statmount_fs_type();
test_statmount_mnt_opts();
test_statmount_string(STATMOUNT_MNT_ROOT, str_off(mnt_root), "mount root");
test_statmount_string(STATMOUNT_MNT_POINT, str_off(mnt_point), "mount point");
test_statmount_string(STATMOUNT_FS_TYPE, str_off(fs_type), "fs type");
Expand Down

0 comments on commit 682d121

Please sign in to comment.