vfs-6.7.autofs

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCZTppJgAKCRCRxhvAZXjc
 omYoAQD+g3BxYbxEdEuhrnbaZMljp2GEYn+L6I2txdvmp/TpSQEAsQipcEgMC1WI
 uc9IDiakYWWCSaN8F7BGR7zKsK5feAc=
 =V+jp
 -----END PGP SIGNATURE-----

Merge tag 'vfs-6.7.autofs' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs

Pull autofs mount api updates from Christian Brauner:
 "This ports autofs to the new mount api. The patchset has existed for
  quite a while but never made it upstream. Ian picked it back up.

  This also fixes a bug where fs_param_is_fd() was passed a garbage
  param->dirfd but it expected it to be set to the fd that was used to
  set param->file otherwise result->uint_32 contains nonsense. So make
  sure it's set.

  One less filesystem using the old mount api. We're getting there,
  albeit rather slow. The last remaining major filesystem that hasn't
  converted is btrfs. Patches exist - I even wrote them - but so far
  they haven't made it upstream"

* tag 'vfs-6.7.autofs' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs:
  autofs: fix add autofs_parse_fd()
  fsconfig: ensure that dirfd is set to aux
  autofs: fix protocol sub version setting
  autofs: convert autofs to use the new mount api
  autofs: validate protocol version
  autofs: refactor parse_options()
  autofs: reformat 0pt enum declaration
  autofs: refactor super block info init
  autofs: add autofs_parse_fd()
  autofs: refactor autofs_prepare_pipe()
This commit is contained in:
Linus Torvalds 2023-10-30 09:10:21 -10:00
commit 0d63d8b229
4 changed files with 286 additions and 185 deletions

View File

@ -25,6 +25,8 @@
#include <linux/completion.h>
#include <linux/file.h>
#include <linux/magic.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
/* This is the range of ioctl() numbers we claim as ours */
#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY
@ -205,20 +207,34 @@ 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 *);
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 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;
autofs_set_packet_pipe_flags(pipe);
return 0;
}

View File

@ -7,16 +7,11 @@
#include <linux/init.h>
#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");

View File

@ -6,7 +6,6 @@
#include <linux/seq_file.h>
#include <linux/pagemap.h>
#include <linux/parser.h>
#include "autofs_i.h"
@ -110,150 +109,213 @@ 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};
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}
enum {
Opt_direct,
Opt_fd,
Opt_gid,
Opt_ignore,
Opt_indirect,
Opt_maxproto,
Opt_minproto,
Opt_offset,
Opt_pgrp,
Opt_strictexpire,
Opt_uid,
};
static int parse_options(char *options,
struct inode *root, int *pgrp, bool *pgrp_set,
struct autofs_sb_info *sbi)
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),
{}
};
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)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
int pipefd = -1;
kuid_t uid;
kgid_t gid;
struct file *pipe;
int ret;
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;
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;
sbi->pipefd = pipefd;
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:
return 1;
}
if (param->type == fs_value_is_file) {
/* came through the new api */
pipe = param->file;
param->file = NULL;
} else {
pipe = fget(result->uint_32);
}
return (sbi->pipefd < 0);
if (!pipe) {
errorf(fc, "could not open pipe file descriptor");
return -EBADF;
}
ret = autofs_check_pipe(pipe);
if (ret < 0) {
errorf(fc, "Invalid/unusable pipe");
if (param->type != fs_value_is_file)
fput(pipe);
return -EBADF;
}
autofs_set_packet_pipe_flags(pipe);
if (sbi->pipe)
fput(sbi->pipe);
sbi->pipefd = result->uint_32;
sbi->pipe = pipe;
return 0;
}
int autofs_fill_super(struct super_block *s, void *data, int silent)
static int autofs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
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 opt;
opt = fs_parse(fc, autofs_param_specs, param, &result);
if (opt < 0)
return opt;
switch (opt) {
case Opt_fd:
return autofs_parse_fd(fc, sbi, param, &result);
case Opt_uid:
uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(uid))
return invalfc(fc, "Invalid uid");
ctx->uid = uid;
break;
case Opt_gid:
gid = make_kgid(current_user_ns(), result.uint_32);
if (!gid_valid(gid))
return invalfc(fc, "Invalid gid");
ctx->gid = gid;
break;
case Opt_pgrp:
ctx->pgrp = result.uint_32;
ctx->pgrp_set = true;
break;
case Opt_minproto:
sbi->min_proto = result.uint_32;
break;
case Opt_maxproto:
sbi->max_proto = result.uint_32;
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 struct autofs_sb_info *autofs_alloc_sbi(void)
{
struct inode *root_inode;
struct dentry *root;
struct file *pipe;
struct autofs_sb_info *sbi;
struct autofs_info *ino;
int pgrp = 0;
bool pgrp_set = false;
int ret = -EINVAL;
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
pr_debug("starting up, sbi = %p\n", sbi);
return NULL;
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;
sbi->min_proto = AUTOFS_MIN_PROTO_VERSION;
sbi->max_proto = AUTOFS_MAX_PROTO_VERSION;
sbi->pipefd = -1;
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);
return 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) {
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);
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;
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;
}
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_info *ino;
int ret = -ENOMEM;
pr_debug("starting up, sbi = %p\n", sbi);
sbi->sb = s;
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = AUTOFS_SUPER_MAGIC;
@ -265,48 +327,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;
}
pipe = NULL;
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;
}
/* 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);
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);
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 {
@ -321,16 +359,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,22 +371,82 @@ 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;
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);

View File

@ -465,6 +465,7 @@ SYSCALL_DEFINE5(fsconfig,
param.file = fget(aux);
if (!param.file)
goto out_key;
param.dirfd = aux;
break;
default:
break;