From bc69fdde0ae1aff595590d802b6ef39114f2b260 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 22 Sep 2023 12:12:08 +0800 Subject: [PATCH 01/10] autofs: refactor autofs_prepare_pipe() Refactor autofs_prepare_pipe() by seperating out a check function to be used later. Signed-off-by: Ian Kent Reviewed-by: Bill O'Donnell Message-Id: <20230922041215.13675-2-raven@themaw.net> Signed-off-by: Christian Brauner --- fs/autofs/autofs_i.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index d5a44fa88acf..c24d32be7937 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -209,12 +209,20 @@ int autofs_fill_super(struct super_block *, void *, int); struct autofs_info *autofs_new_ino(struct autofs_sb_info *); void autofs_clean_ino(struct autofs_info *); -static inline int autofs_prepare_pipe(struct file *pipe) +static inline int autofs_check_pipe(struct file *pipe) { if (!(pipe->f_mode & FMODE_CAN_WRITE)) return -EINVAL; if (!S_ISFIFO(file_inode(pipe)->i_mode)) return -EINVAL; + return 0; +} + +static inline int autofs_prepare_pipe(struct file *pipe) +{ + int ret = autofs_check_pipe(pipe); + if (ret < 0) + return ret; /* We want a packet pipe */ pipe->f_flags |= O_DIRECT; /* We don't expect -EAGAIN */ From 546694b8f65807427a0104154abd8cdc633b36e3 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 22 Sep 2023 12:12:09 +0800 Subject: [PATCH 02/10] autofs: add autofs_parse_fd() Factor out the fd mount option handling. Signed-off-by: Ian Kent Reviewed-by: Bill O'Donnell Message-Id: <20230922041215.13675-3-raven@themaw.net> Signed-off-by: Christian Brauner --- fs/autofs/inode.c | 48 ++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 2b49662ed237..e279e275b0a5 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -129,6 +129,33 @@ static const match_table_t tokens = { {Opt_err, NULL} }; +static int autofs_parse_fd(struct autofs_sb_info *sbi, int fd) +{ + struct file *pipe; + int ret; + + pipe = fget(fd); + if (!pipe) { + pr_err("could not open pipe file descriptor\n"); + return -EBADF; + } + + ret = autofs_check_pipe(pipe); + if (ret < 0) { + pr_err("Invalid/unusable pipe\n"); + fput(pipe); + return -EBADF; + } + + if (sbi->pipe) + fput(sbi->pipe); + + sbi->pipefd = fd; + sbi->pipe = pipe; + + return 0; +} + static int parse_options(char *options, struct inode *root, int *pgrp, bool *pgrp_set, struct autofs_sb_info *sbi) @@ -139,6 +166,7 @@ static int parse_options(char *options, int pipefd = -1; kuid_t uid; kgid_t gid; + int ret; root->i_uid = current_uid(); root->i_gid = current_gid(); @@ -162,7 +190,9 @@ static int parse_options(char *options, case Opt_fd: if (match_int(args, &pipefd)) return 1; - sbi->pipefd = pipefd; + ret = autofs_parse_fd(sbi, pipefd); + if (ret) + return 1; break; case Opt_uid: if (match_int(args, &option)) @@ -222,7 +252,6 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) { struct inode *root_inode; struct dentry *root; - struct file *pipe; struct autofs_sb_info *sbi; struct autofs_info *ino; int pgrp = 0; @@ -275,7 +304,6 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) ret = -ENOMEM; goto fail_ino; } - pipe = NULL; root->d_fsdata = ino; @@ -321,16 +349,7 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) pr_debug("pipe fd = %d, pgrp = %u\n", sbi->pipefd, pid_nr(sbi->oz_pgrp)); - pipe = fget(sbi->pipefd); - if (!pipe) { - pr_err("could not open pipe file descriptor\n"); - goto fail_put_pid; - } - ret = autofs_prepare_pipe(pipe); - if (ret < 0) - goto fail_fput; - sbi->pipe = pipe; sbi->flags &= ~AUTOFS_SBI_CATATONIC; /* @@ -342,11 +361,6 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) /* * Failure ... clean up. */ -fail_fput: - pr_err("pipe file descriptor does not contain proper ops\n"); - fput(pipe); -fail_put_pid: - put_pid(sbi->oz_pgrp); fail_dput: dput(root); goto fail_free; From a7467430b4de0985b7d1de8f1e50f8dd47eb6c4a Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 22 Sep 2023 12:12:10 +0800 Subject: [PATCH 03/10] autofs: refactor super block info init Move the allocation and initialisation of the super block info struct to its own function. Signed-off-by: Ian Kent Reviewed-by: Bill O'Donnell Message-Id: <20230922041215.13675-4-raven@themaw.net> Signed-off-by: Christian Brauner --- fs/autofs/inode.c | 53 +++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index e279e275b0a5..992d6cb29707 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -171,11 +171,6 @@ static int parse_options(char *options, root->i_uid = current_uid(); root->i_gid = current_gid(); - sbi->min_proto = AUTOFS_MIN_PROTO_VERSION; - sbi->max_proto = AUTOFS_MAX_PROTO_VERSION; - - sbi->pipefd = -1; - if (!options) return 1; @@ -248,6 +243,31 @@ static int parse_options(char *options, return (sbi->pipefd < 0); } +static struct autofs_sb_info *autofs_alloc_sbi(void) +{ + struct autofs_sb_info *sbi; + + sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); + if (!sbi) + return NULL; + + sbi->magic = AUTOFS_SBI_MAGIC; + sbi->flags = AUTOFS_SBI_CATATONIC; + sbi->min_proto = AUTOFS_MIN_PROTO_VERSION; + sbi->max_proto = AUTOFS_MAX_PROTO_VERSION; + sbi->pipefd = -1; + + set_autofs_type_indirect(&sbi->type); + mutex_init(&sbi->wq_mutex); + mutex_init(&sbi->pipe_mutex); + spin_lock_init(&sbi->fs_lock); + spin_lock_init(&sbi->lookup_lock); + INIT_LIST_HEAD(&sbi->active_list); + INIT_LIST_HEAD(&sbi->expiring_list); + + return sbi; +} + int autofs_fill_super(struct super_block *s, void *data, int silent) { struct inode *root_inode; @@ -258,31 +278,14 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) bool pgrp_set = false; int ret = -EINVAL; - sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); + sbi = autofs_alloc_sbi(); if (!sbi) return -ENOMEM; + pr_debug("starting up, sbi = %p\n", sbi); - s->s_fs_info = sbi; - sbi->magic = AUTOFS_SBI_MAGIC; - sbi->pipefd = -1; - sbi->pipe = NULL; - sbi->exp_timeout = 0; - sbi->oz_pgrp = NULL; sbi->sb = s; - sbi->version = 0; - sbi->sub_version = 0; - sbi->flags = AUTOFS_SBI_CATATONIC; - set_autofs_type_indirect(&sbi->type); - sbi->min_proto = 0; - sbi->max_proto = 0; - mutex_init(&sbi->wq_mutex); - mutex_init(&sbi->pipe_mutex); - spin_lock_init(&sbi->fs_lock); - sbi->queues = NULL; - spin_lock_init(&sbi->lookup_lock); - INIT_LIST_HEAD(&sbi->active_list); - INIT_LIST_HEAD(&sbi->expiring_list); + s->s_fs_info = sbi; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = AUTOFS_SUPER_MAGIC; From 7efd93ea790ec64221458bbb0705ccba54beab0e Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 22 Sep 2023 12:12:11 +0800 Subject: [PATCH 04/10] autofs: reformat 0pt enum declaration The enum of options is only reformated in the patch to convert autofs to use the mount API so do that now to simplify the conversion patch. Signed-off-by: Ian Kent Reviewed-by: Bill O'Donnell Message-Id: <20230922041215.13675-5-raven@themaw.net> Signed-off-by: Christian Brauner --- fs/autofs/inode.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 992d6cb29707..d2b333c0682a 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -110,9 +110,20 @@ static const struct super_operations autofs_sops = { .evict_inode = autofs_evict_inode, }; -enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, - Opt_indirect, Opt_direct, Opt_offset, Opt_strictexpire, - Opt_ignore}; +enum { + Opt_err, + Opt_direct, + Opt_fd, + Opt_gid, + Opt_ignore, + Opt_indirect, + Opt_maxproto, + Opt_minproto, + Opt_offset, + Opt_pgrp, + Opt_strictexpire, + Opt_uid, +}; static const match_table_t tokens = { {Opt_fd, "fd=%u"}, From 9b2731666d1d46bb476df6e4f2ec9a776ad7a507 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 22 Sep 2023 12:12:12 +0800 Subject: [PATCH 05/10] autofs: refactor parse_options() Seperate out parts of parse_options() that will match better the individual option processing used in the mount API to further simplify the upcoming conversion. Signed-off-by: Ian Kent Reviewed-by: Bill O'Donnell Message-Id: <20230922041215.13675-6-raven@themaw.net> Signed-off-by: Christian Brauner --- fs/autofs/inode.c | 136 ++++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index d2b333c0682a..5e061ce3ab8d 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -167,18 +167,84 @@ static int autofs_parse_fd(struct autofs_sb_info *sbi, int fd) return 0; } -static int parse_options(char *options, - struct inode *root, int *pgrp, bool *pgrp_set, - struct autofs_sb_info *sbi) +static int autofs_parse_param(char *optstr, struct inode *root, + int *pgrp, bool *pgrp_set, + struct autofs_sb_info *sbi) { - char *p; substring_t args[MAX_OPT_ARGS]; int option; int pipefd = -1; kuid_t uid; kgid_t gid; + int token; int ret; + token = match_token(optstr, tokens, args); + switch (token) { + case Opt_fd: + if (match_int(args, &pipefd)) + return 1; + ret = autofs_parse_fd(sbi, pipefd); + if (ret) + return 1; + break; + case Opt_uid: + if (match_int(args, &option)) + return 1; + uid = make_kuid(current_user_ns(), option); + if (!uid_valid(uid)) + return 1; + root->i_uid = uid; + break; + case Opt_gid: + if (match_int(args, &option)) + return 1; + gid = make_kgid(current_user_ns(), option); + if (!gid_valid(gid)) + return 1; + root->i_gid = gid; + break; + case Opt_pgrp: + if (match_int(args, &option)) + return 1; + *pgrp = option; + *pgrp_set = true; + break; + case Opt_minproto: + if (match_int(args, &option)) + return 1; + sbi->min_proto = option; + break; + case Opt_maxproto: + if (match_int(args, &option)) + return 1; + sbi->max_proto = option; + break; + case Opt_indirect: + set_autofs_type_indirect(&sbi->type); + break; + case Opt_direct: + set_autofs_type_direct(&sbi->type); + break; + case Opt_offset: + set_autofs_type_offset(&sbi->type); + break; + case Opt_strictexpire: + sbi->flags |= AUTOFS_SBI_STRICTEXPIRE; + break; + case Opt_ignore: + sbi->flags |= AUTOFS_SBI_IGNORE; + } + + return 0; +} + +static int parse_options(char *options, + struct inode *root, int *pgrp, bool *pgrp_set, + struct autofs_sb_info *sbi) +{ + char *p; + root->i_uid = current_uid(); root->i_gid = current_gid(); @@ -186,71 +252,13 @@ static int parse_options(char *options, return 1; while ((p = strsep(&options, ",")) != NULL) { - int token; - if (!*p) continue; - token = match_token(p, tokens, args); - switch (token) { - case Opt_fd: - if (match_int(args, &pipefd)) - return 1; - ret = autofs_parse_fd(sbi, pipefd); - if (ret) - return 1; - break; - case Opt_uid: - if (match_int(args, &option)) - return 1; - uid = make_kuid(current_user_ns(), option); - if (!uid_valid(uid)) - return 1; - root->i_uid = uid; - break; - case Opt_gid: - if (match_int(args, &option)) - return 1; - gid = make_kgid(current_user_ns(), option); - if (!gid_valid(gid)) - return 1; - root->i_gid = gid; - break; - case Opt_pgrp: - if (match_int(args, &option)) - return 1; - *pgrp = option; - *pgrp_set = true; - break; - case Opt_minproto: - if (match_int(args, &option)) - return 1; - sbi->min_proto = option; - break; - case Opt_maxproto: - if (match_int(args, &option)) - return 1; - sbi->max_proto = option; - break; - case Opt_indirect: - set_autofs_type_indirect(&sbi->type); - break; - case Opt_direct: - set_autofs_type_direct(&sbi->type); - break; - case Opt_offset: - set_autofs_type_offset(&sbi->type); - break; - case Opt_strictexpire: - sbi->flags |= AUTOFS_SBI_STRICTEXPIRE; - break; - case Opt_ignore: - sbi->flags |= AUTOFS_SBI_IGNORE; - break; - default: + if (autofs_parse_param(p, root, pgrp, pgrp_set, sbi)) return 1; - } } + return (sbi->pipefd < 0); } From 1f50012d9c63c690f25956239bd25d10236405f8 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 22 Sep 2023 12:12:13 +0800 Subject: [PATCH 06/10] autofs: validate protocol version Move the protocol parameter validation into a seperate function. Signed-off-by: Ian Kent Reviewed-by: Bill O'Donnell Message-Id: <20230922041215.13675-7-raven@themaw.net> Signed-off-by: Christian Brauner --- fs/autofs/inode.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 5e061ce3ab8d..e2026e063d8c 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -287,6 +287,28 @@ static struct autofs_sb_info *autofs_alloc_sbi(void) return sbi; } +static int autofs_validate_protocol(struct autofs_sb_info *sbi) +{ + /* Test versions first */ + if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || + sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { + pr_err("kernel does not match daemon version " + "daemon (%d, %d) kernel (%d, %d)\n", + sbi->min_proto, sbi->max_proto, + AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); + return -EINVAL; + } + + /* Establish highest kernel protocol version */ + if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) + sbi->version = AUTOFS_MAX_PROTO_VERSION; + else + sbi->version = sbi->max_proto; + sbi->sub_version = AUTOFS_PROTO_SUBVERSION; + + return 0; +} + int autofs_fill_super(struct super_block *s, void *data, int silent) { struct inode *root_inode; @@ -335,22 +357,8 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) goto fail_dput; } - /* Test versions first */ - if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || - sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - pr_err("kernel does not match daemon version " - "daemon (%d, %d) kernel (%d, %d)\n", - sbi->min_proto, sbi->max_proto, - AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); + if (autofs_validate_protocol(sbi)) goto fail_dput; - } - - /* Establish highest kernel protocol version */ - if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION) - sbi->version = AUTOFS_MAX_PROTO_VERSION; - else - sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; if (pgrp_set) { sbi->oz_pgrp = find_get_pid(pgrp); From e6ec453bd0f03a60a80f00f95ae2eaa260faa3c2 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 22 Sep 2023 12:12:14 +0800 Subject: [PATCH 07/10] autofs: convert autofs to use the new mount api Convert the autofs filesystem to use the mount API. The conversion patch was originally written by David Howells. I have taken that patch and broken it into several patches in an effort to make the change easier to review. Signed-off-by: Ian Kent Reviewed-by: Bill O'Donnell Message-Id: <20230922041215.13675-8-raven@themaw.net> Signed-off-by: Christian Brauner --- fs/autofs/autofs_i.h | 5 +- fs/autofs/init.c | 9 +- fs/autofs/inode.c | 265 ++++++++++++++++++++++++------------------- 3 files changed, 155 insertions(+), 124 deletions(-) diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index c24d32be7937..244f18cdf23c 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -25,6 +25,8 @@ #include #include #include +#include +#include /* This is the range of ioctl() numbers we claim as ours */ #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY @@ -205,7 +207,8 @@ static inline void managed_dentry_clear_managed(struct dentry *dentry) /* Initializing function */ -int autofs_fill_super(struct super_block *, void *, int); +extern const struct fs_parameter_spec autofs_param_specs[]; +int autofs_init_fs_context(struct fs_context *fc); struct autofs_info *autofs_new_ino(struct autofs_sb_info *); void autofs_clean_ino(struct autofs_info *); diff --git a/fs/autofs/init.c b/fs/autofs/init.c index d3f55e874338..b5e4dfa04ed0 100644 --- a/fs/autofs/init.c +++ b/fs/autofs/init.c @@ -7,16 +7,11 @@ #include #include "autofs_i.h" -static struct dentry *autofs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) -{ - return mount_nodev(fs_type, flags, data, autofs_fill_super); -} - struct file_system_type autofs_fs_type = { .owner = THIS_MODULE, .name = "autofs", - .mount = autofs_mount, + .init_fs_context = autofs_init_fs_context, + .parameters = autofs_param_specs, .kill_sb = autofs_kill_sb, }; MODULE_ALIAS_FS("autofs"); diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index e2026e063d8c..0477bce7d277 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -6,7 +6,6 @@ #include #include -#include #include "autofs_i.h" @@ -111,7 +110,6 @@ static const struct super_operations autofs_sops = { }; enum { - Opt_err, Opt_direct, Opt_fd, Opt_gid, @@ -125,100 +123,106 @@ enum { Opt_uid, }; -static const match_table_t tokens = { - {Opt_fd, "fd=%u"}, - {Opt_uid, "uid=%u"}, - {Opt_gid, "gid=%u"}, - {Opt_pgrp, "pgrp=%u"}, - {Opt_minproto, "minproto=%u"}, - {Opt_maxproto, "maxproto=%u"}, - {Opt_indirect, "indirect"}, - {Opt_direct, "direct"}, - {Opt_offset, "offset"}, - {Opt_strictexpire, "strictexpire"}, - {Opt_ignore, "ignore"}, - {Opt_err, NULL} +const struct fs_parameter_spec autofs_param_specs[] = { + fsparam_flag ("direct", Opt_direct), + fsparam_fd ("fd", Opt_fd), + fsparam_u32 ("gid", Opt_gid), + fsparam_flag ("ignore", Opt_ignore), + fsparam_flag ("indirect", Opt_indirect), + fsparam_u32 ("maxproto", Opt_maxproto), + fsparam_u32 ("minproto", Opt_minproto), + fsparam_flag ("offset", Opt_offset), + fsparam_u32 ("pgrp", Opt_pgrp), + fsparam_flag ("strictexpire", Opt_strictexpire), + fsparam_u32 ("uid", Opt_uid), + {} }; -static int autofs_parse_fd(struct autofs_sb_info *sbi, int fd) +struct autofs_fs_context { + kuid_t uid; + kgid_t gid; + int pgrp; + bool pgrp_set; +}; + +/* + * Open the fd. We do it here rather than in get_tree so that it's done in the + * context of the system call that passed the data and not the one that + * triggered the superblock creation, lest the fd gets reassigned. + */ +static int autofs_parse_fd(struct fs_context *fc, struct autofs_sb_info *sbi, + struct fs_parameter *param, + struct fs_parse_result *result) { struct file *pipe; int ret; - pipe = fget(fd); + if (param->type == fs_value_is_file) { + /* came through the new api */ + pipe = param->file; + param->file = NULL; + } else { + pipe = fget(result->uint_32); + } if (!pipe) { - pr_err("could not open pipe file descriptor\n"); + errorf(fc, "could not open pipe file descriptor"); return -EBADF; } ret = autofs_check_pipe(pipe); if (ret < 0) { - pr_err("Invalid/unusable pipe\n"); - fput(pipe); + errorf(fc, "Invalid/unusable pipe"); + if (param->type != fs_value_is_file) + fput(pipe); return -EBADF; } if (sbi->pipe) fput(sbi->pipe); - sbi->pipefd = fd; + sbi->pipefd = result->uint_32; sbi->pipe = pipe; return 0; } -static int autofs_parse_param(char *optstr, struct inode *root, - int *pgrp, bool *pgrp_set, - struct autofs_sb_info *sbi) +static int autofs_parse_param(struct fs_context *fc, struct fs_parameter *param) { - substring_t args[MAX_OPT_ARGS]; - int option; - int pipefd = -1; + struct autofs_fs_context *ctx = fc->fs_private; + struct autofs_sb_info *sbi = fc->s_fs_info; + struct fs_parse_result result; kuid_t uid; kgid_t gid; - int token; - int ret; + int opt; - token = match_token(optstr, tokens, args); - switch (token) { + opt = fs_parse(fc, autofs_param_specs, param, &result); + if (opt < 0) + return opt; + + switch (opt) { case Opt_fd: - if (match_int(args, &pipefd)) - return 1; - ret = autofs_parse_fd(sbi, pipefd); - if (ret) - return 1; - break; + return autofs_parse_fd(fc, sbi, param, &result); case Opt_uid: - if (match_int(args, &option)) - return 1; - uid = make_kuid(current_user_ns(), option); + uid = make_kuid(current_user_ns(), result.uint_32); if (!uid_valid(uid)) - return 1; - root->i_uid = uid; + return invalfc(fc, "Invalid uid"); + ctx->uid = uid; break; case Opt_gid: - if (match_int(args, &option)) - return 1; - gid = make_kgid(current_user_ns(), option); + gid = make_kgid(current_user_ns(), result.uint_32); if (!gid_valid(gid)) - return 1; - root->i_gid = gid; + return invalfc(fc, "Invalid gid"); + ctx->gid = gid; break; case Opt_pgrp: - if (match_int(args, &option)) - return 1; - *pgrp = option; - *pgrp_set = true; + ctx->pgrp = result.uint_32; + ctx->pgrp_set = true; break; case Opt_minproto: - if (match_int(args, &option)) - return 1; - sbi->min_proto = option; + sbi->min_proto = result.uint_32; break; case Opt_maxproto: - if (match_int(args, &option)) - return 1; - sbi->max_proto = option; + sbi->max_proto = result.uint_32; break; case Opt_indirect: set_autofs_type_indirect(&sbi->type); @@ -239,29 +243,6 @@ static int autofs_parse_param(char *optstr, struct inode *root, return 0; } -static int parse_options(char *options, - struct inode *root, int *pgrp, bool *pgrp_set, - struct autofs_sb_info *sbi) -{ - char *p; - - root->i_uid = current_uid(); - root->i_gid = current_gid(); - - if (!options) - return 1; - - while ((p = strsep(&options, ",")) != NULL) { - if (!*p) - continue; - - if (autofs_parse_param(p, root, pgrp, pgrp_set, sbi)) - return 1; - } - - return (sbi->pipefd < 0); -} - static struct autofs_sb_info *autofs_alloc_sbi(void) { struct autofs_sb_info *sbi; @@ -287,12 +268,14 @@ static struct autofs_sb_info *autofs_alloc_sbi(void) return sbi; } -static int autofs_validate_protocol(struct autofs_sb_info *sbi) +static int autofs_validate_protocol(struct fs_context *fc) { + struct autofs_sb_info *sbi = fc->s_fs_info; + /* Test versions first */ if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { - pr_err("kernel does not match daemon version " + errorf(fc, "kernel does not match daemon version " "daemon (%d, %d) kernel (%d, %d)\n", sbi->min_proto, sbi->max_proto, AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION); @@ -309,24 +292,18 @@ static int autofs_validate_protocol(struct autofs_sb_info *sbi) return 0; } -int autofs_fill_super(struct super_block *s, void *data, int silent) +static int autofs_fill_super(struct super_block *s, struct fs_context *fc) { + struct autofs_fs_context *ctx = fc->fs_private; + struct autofs_sb_info *sbi = s->s_fs_info; struct inode *root_inode; struct dentry *root; - struct autofs_sb_info *sbi; struct autofs_info *ino; - int pgrp = 0; - bool pgrp_set = false; - int ret = -EINVAL; - - sbi = autofs_alloc_sbi(); - if (!sbi) - return -ENOMEM; + int ret = -ENOMEM; pr_debug("starting up, sbi = %p\n", sbi); sbi->sb = s; - s->s_fs_info = sbi; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = AUTOFS_SUPER_MAGIC; @@ -338,33 +315,24 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) * Get the root inode and dentry, but defer checking for errors. */ ino = autofs_new_ino(sbi); - if (!ino) { - ret = -ENOMEM; - goto fail_free; - } + if (!ino) + goto fail; + root_inode = autofs_get_inode(s, S_IFDIR | 0755); + root_inode->i_uid = ctx->uid; + root_inode->i_gid = ctx->gid; + root = d_make_root(root_inode); - if (!root) { - ret = -ENOMEM; + if (!root) goto fail_ino; - } root->d_fsdata = ino; - /* Can this call block? */ - if (parse_options(data, root_inode, &pgrp, &pgrp_set, sbi)) { - pr_err("called with bogus options\n"); - goto fail_dput; - } - - if (autofs_validate_protocol(sbi)) - goto fail_dput; - - if (pgrp_set) { - sbi->oz_pgrp = find_get_pid(pgrp); + if (ctx->pgrp_set) { + sbi->oz_pgrp = find_get_pid(ctx->pgrp); if (!sbi->oz_pgrp) { - pr_err("could not find process group %d\n", - pgrp); + ret = invalf(fc, "Could not find process group %d", + ctx->pgrp); goto fail_dput; } } else { @@ -393,15 +361,80 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) */ fail_dput: dput(root); - goto fail_free; + goto fail; fail_ino: autofs_free_ino(ino); -fail_free: - kfree(sbi); - s->s_fs_info = NULL; +fail: return ret; } +/* + * Validate the parameters and then request a superblock. + */ +static int autofs_get_tree(struct fs_context *fc) +{ + struct autofs_sb_info *sbi = fc->s_fs_info; + int ret; + + ret = autofs_validate_protocol(fc); + if (ret) + return ret; + + if (sbi->pipefd < 0) + return invalf(fc, "No control pipe specified"); + + return get_tree_nodev(fc, autofs_fill_super); +} + +static void autofs_free_fc(struct fs_context *fc) +{ + struct autofs_fs_context *ctx = fc->fs_private; + struct autofs_sb_info *sbi = fc->s_fs_info; + + if (sbi) { + if (sbi->pipe) + fput(sbi->pipe); + kfree(sbi); + } + kfree(ctx); +} + +static const struct fs_context_operations autofs_context_ops = { + .free = autofs_free_fc, + .parse_param = autofs_parse_param, + .get_tree = autofs_get_tree, +}; + +/* + * Set up the filesystem mount context. + */ +int autofs_init_fs_context(struct fs_context *fc) +{ + struct autofs_fs_context *ctx; + struct autofs_sb_info *sbi; + + ctx = kzalloc(sizeof(struct autofs_fs_context), GFP_KERNEL); + if (!ctx) + goto nomem; + + ctx->uid = current_uid(); + ctx->gid = current_gid(); + + sbi = autofs_alloc_sbi(); + if (!sbi) + goto nomem_ctx; + + fc->fs_private = ctx; + fc->s_fs_info = sbi; + fc->ops = &autofs_context_ops; + return 0; + +nomem_ctx: + kfree(ctx); +nomem: + return -ENOMEM; +} + struct inode *autofs_get_inode(struct super_block *sb, umode_t mode) { struct inode *inode = new_inode(sb); From dede367149c48822c9f699291d71a3211c2a91bb Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Fri, 22 Sep 2023 12:12:15 +0800 Subject: [PATCH 08/10] autofs: fix protocol sub version setting There were a number of updates to protocol version 4, take account of that when setting the super block info sub version field. Signed-off-by: Ian Kent Reviewed-by: Bill O'Donnell Message-Id: <20230922041215.13675-9-raven@themaw.net> Signed-off-by: Christian Brauner --- fs/autofs/inode.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 0477bce7d277..6d2e01c9057d 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -287,7 +287,17 @@ static int autofs_validate_protocol(struct fs_context *fc) sbi->version = AUTOFS_MAX_PROTO_VERSION; else sbi->version = sbi->max_proto; - sbi->sub_version = AUTOFS_PROTO_SUBVERSION; + + switch (sbi->version) { + case 4: + sbi->sub_version = 7; + break; + case 5: + sbi->sub_version = AUTOFS_PROTO_SUBVERSION; + break; + default: + sbi->sub_version = 0; + } return 0; } From 9cf16b380af5bab7d0952b9aad0601ebf986de69 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 22 Sep 2023 13:49:05 +0200 Subject: [PATCH 09/10] fsconfig: ensure that dirfd is set to aux The code in fs_param_is_fd() expects param->dirfd to be set to the fd that was used to set param->file to initialize result->uint_32. So make sure it's set so users like autofs using FSCONFIG_SET_FD with the new mount api can rely on this to be set to the correct value. Link: https://lore.kernel.org/lkml/20230922-vorbringen-spaghetti-946729122076@brauner Signed-off-by: Christian Brauner --- fs/fsopen.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/fsopen.c b/fs/fsopen.c index ce03f6521c88..6593ae518115 100644 --- a/fs/fsopen.c +++ b/fs/fsopen.c @@ -465,6 +465,7 @@ SYSCALL_DEFINE5(fsconfig, param.file = fget(aux); if (!param.file) goto out_key; + param.dirfd = aux; break; default: break; From d3c50061765d4b5616dc97f5804fc18122598a9b Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Mon, 23 Oct 2023 17:33:59 +0800 Subject: [PATCH 10/10] autofs: fix add autofs_parse_fd() We are seeing systemd hang on its autofs direct mount at /proc/sys/fs/binfmt_misc. Historically this was due to a mismatch in the communication structure size between a 64 bit kernel and a 32 bit user space and was fixed by making the pipe communication record oriented. During autofs v5 development I decided to stay with the existing usage instead of changing to a packed structure for autofs <=> user space communications which turned out to be a mistake on my part. Problems arose and they were fixed by allowing for the 64 bit to 32 bit size difference in the automount(8) code. Along the way systemd started to use autofs and eventually encountered this problem too. systemd refused to compensate for the length difference insisting it be fixed in the kernel. Fortunately Linus implemented the packetized pipe which resolved the problem in a straight forward and simple way. In the autofs mount api conversion series I inadvertatly dropped the packet pipe flag settings when adding the autofs_parse_fd() function. This patch fixes that omission. Fixes: 546694b8f658 ("autofs: add autofs_parse_fd()") Signed-off-by: Ian Kent Link: https://lore.kernel.org/r/20231023093359.64265-1-raven@themaw.net Tested-by: Anders Roxell Cc: Bill O'Donnell Cc: Christian Brauner Cc: Arnd Bergmann Cc: Dan Carpenter Cc: Anders Roxell Cc: Naresh Kamboju Cc: Stephen Rothwell Reported-by: Linux Kernel Functional Testing Reported-by: Anders Roxell Signed-off-by: Christian Brauner --- fs/autofs/autofs_i.h | 13 +++++++++---- fs/autofs/inode.c | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index 244f18cdf23c..8c1d587b3eef 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -221,15 +221,20 @@ static inline int autofs_check_pipe(struct file *pipe) return 0; } +static inline void autofs_set_packet_pipe_flags(struct file *pipe) +{ + /* We want a packet pipe */ + pipe->f_flags |= O_DIRECT; + /* We don't expect -EAGAIN */ + pipe->f_flags &= ~O_NONBLOCK; +} + static inline int autofs_prepare_pipe(struct file *pipe) { int ret = autofs_check_pipe(pipe); if (ret < 0) return ret; - /* We want a packet pipe */ - pipe->f_flags |= O_DIRECT; - /* We don't expect -EAGAIN */ - pipe->f_flags &= ~O_NONBLOCK; + autofs_set_packet_pipe_flags(pipe); return 0; } diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 6d2e01c9057d..a3d62acc293a 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -177,6 +177,8 @@ static int autofs_parse_fd(struct fs_context *fc, struct autofs_sb_info *sbi, return -EBADF; } + autofs_set_packet_pipe_flags(pipe); + if (sbi->pipe) fput(sbi->pipe);