Skip to content

Commit cc0876f

Browse files
dhowellsbrauner
authored andcommitted
vfs: Convert devpts to use the new mount API
Convert the devpts filesystem to the new internal mount API as the old one will be obsoleted and removed. This allows greater flexibility in communication of mount parameters between userspace, the VFS and the filesystem. See Documentation/filesystems/mount_api.txt for more information. Cc: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: David Howells <dhowells@redhat.com> [sandeen: forward port, keep pr_err vs errorf] Co-developed-by: Eric Sandeen <sandeen@redhat.com> Signed-off-by: Eric Sandeen <sandeen@redhat.com> Link: https://lore.kernel.org/r/20250205213931.74614-3-sandeen@redhat.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent f584714 commit cc0876f

1 file changed

Lines changed: 110 additions & 141 deletions

File tree

fs/devpts/inode.c

Lines changed: 110 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <linux/module.h>
1313
#include <linux/init.h>
1414
#include <linux/fs.h>
15+
#include <linux/fs_context.h>
16+
#include <linux/fs_parser.h>
1517
#include <linux/sched.h>
1618
#include <linux/namei.h>
1719
#include <linux/slab.h>
@@ -21,7 +23,6 @@
2123
#include <linux/magic.h>
2224
#include <linux/idr.h>
2325
#include <linux/devpts_fs.h>
24-
#include <linux/parser.h>
2526
#include <linux/fsnotify.h>
2627
#include <linux/seq_file.h>
2728

@@ -87,14 +88,14 @@ enum {
8788
Opt_err
8889
};
8990

90-
static const match_table_t tokens = {
91-
{Opt_uid, "uid=%u"},
92-
{Opt_gid, "gid=%u"},
93-
{Opt_mode, "mode=%o"},
94-
{Opt_ptmxmode, "ptmxmode=%o"},
95-
{Opt_newinstance, "newinstance"},
96-
{Opt_max, "max=%d"},
97-
{Opt_err, NULL}
91+
static const struct fs_parameter_spec devpts_param_specs[] = {
92+
fsparam_u32 ("gid", Opt_gid),
93+
fsparam_s32 ("max", Opt_max),
94+
fsparam_u32oct ("mode", Opt_mode),
95+
fsparam_flag ("newinstance", Opt_newinstance),
96+
fsparam_u32oct ("ptmxmode", Opt_ptmxmode),
97+
fsparam_u32 ("uid", Opt_uid),
98+
{}
9899
};
99100

100101
struct pts_fs_info {
@@ -214,93 +215,48 @@ void devpts_release(struct pts_fs_info *fsi)
214215
deactivate_super(fsi->sb);
215216
}
216217

217-
#define PARSE_MOUNT 0
218-
#define PARSE_REMOUNT 1
219-
220218
/*
221-
* parse_mount_options():
222-
* Set @opts to mount options specified in @data. If an option is not
223-
* specified in @data, set it to its default value.
224-
*
225-
* Note: @data may be NULL (in which case all options are set to default).
219+
* devpts_parse_param - Parse mount parameters
226220
*/
227-
static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts)
221+
static int devpts_parse_param(struct fs_context *fc, struct fs_parameter *param)
228222
{
229-
char *p;
230-
kuid_t uid;
231-
kgid_t gid;
232-
233-
opts->setuid = 0;
234-
opts->setgid = 0;
235-
opts->uid = GLOBAL_ROOT_UID;
236-
opts->gid = GLOBAL_ROOT_GID;
237-
opts->mode = DEVPTS_DEFAULT_MODE;
238-
opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
239-
opts->max = NR_UNIX98_PTY_MAX;
240-
241-
/* Only allow instances mounted from the initial mount
242-
* namespace to tap the reserve pool of ptys.
243-
*/
244-
if (op == PARSE_MOUNT)
245-
opts->reserve =
246-
(current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns);
247-
248-
while ((p = strsep(&data, ",")) != NULL) {
249-
substring_t args[MAX_OPT_ARGS];
250-
int token;
251-
int option;
252-
253-
if (!*p)
254-
continue;
255-
256-
token = match_token(p, tokens, args);
257-
switch (token) {
258-
case Opt_uid:
259-
if (match_int(&args[0], &option))
260-
return -EINVAL;
261-
uid = make_kuid(current_user_ns(), option);
262-
if (!uid_valid(uid))
263-
return -EINVAL;
264-
opts->uid = uid;
265-
opts->setuid = 1;
266-
break;
267-
case Opt_gid:
268-
if (match_int(&args[0], &option))
269-
return -EINVAL;
270-
gid = make_kgid(current_user_ns(), option);
271-
if (!gid_valid(gid))
272-
return -EINVAL;
273-
opts->gid = gid;
274-
opts->setgid = 1;
275-
break;
276-
case Opt_mode:
277-
if (match_octal(&args[0], &option))
278-
return -EINVAL;
279-
opts->mode = option & S_IALLUGO;
280-
break;
281-
case Opt_ptmxmode:
282-
if (match_octal(&args[0], &option))
283-
return -EINVAL;
284-
opts->ptmxmode = option & S_IALLUGO;
285-
break;
286-
case Opt_newinstance:
287-
break;
288-
case Opt_max:
289-
if (match_int(&args[0], &option) ||
290-
option < 0 || option > NR_UNIX98_PTY_MAX)
291-
return -EINVAL;
292-
opts->max = option;
293-
break;
294-
default:
295-
pr_err("called with bogus options\n");
296-
return -EINVAL;
297-
}
223+
struct pts_fs_info *fsi = fc->s_fs_info;
224+
struct pts_mount_opts *opts = &fsi->mount_opts;
225+
struct fs_parse_result result;
226+
int opt;
227+
228+
opt = fs_parse(fc, devpts_param_specs, param, &result);
229+
if (opt < 0)
230+
return opt;
231+
232+
switch (opt) {
233+
case Opt_uid:
234+
opts->uid = result.uid;
235+
opts->setuid = 1;
236+
break;
237+
case Opt_gid:
238+
opts->gid = result.gid;
239+
opts->setgid = 1;
240+
break;
241+
case Opt_mode:
242+
opts->mode = result.uint_32 & S_IALLUGO;
243+
break;
244+
case Opt_ptmxmode:
245+
opts->ptmxmode = result.uint_32 & S_IALLUGO;
246+
break;
247+
case Opt_newinstance:
248+
break;
249+
case Opt_max:
250+
if (result.uint_32 > NR_UNIX98_PTY_MAX)
251+
return invalf(fc, "max out of range");
252+
opts->max = result.uint_32;
253+
break;
298254
}
299255

300256
return 0;
301257
}
302258

303-
static int mknod_ptmx(struct super_block *sb)
259+
static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
304260
{
305261
int mode;
306262
int rc = -ENOMEM;
@@ -362,13 +318,23 @@ static void update_ptmx_mode(struct pts_fs_info *fsi)
362318
}
363319
}
364320

365-
static int devpts_remount(struct super_block *sb, int *flags, char *data)
321+
static int devpts_reconfigure(struct fs_context *fc)
366322
{
367-
int err;
368-
struct pts_fs_info *fsi = DEVPTS_SB(sb);
369-
struct pts_mount_opts *opts = &fsi->mount_opts;
323+
struct pts_fs_info *fsi = DEVPTS_SB(fc->root->d_sb);
324+
struct pts_fs_info *new = fc->s_fs_info;
370325

371-
err = parse_mount_options(data, PARSE_REMOUNT, opts);
326+
/* Apply the revised options. We don't want to change ->reserve.
327+
* Ideally, we'd update each option conditionally on it having been
328+
* explicitly changed, but the default is to reset everything so that
329+
* would break UAPI...
330+
*/
331+
fsi->mount_opts.setuid = new->mount_opts.setuid;
332+
fsi->mount_opts.setgid = new->mount_opts.setgid;
333+
fsi->mount_opts.uid = new->mount_opts.uid;
334+
fsi->mount_opts.gid = new->mount_opts.gid;
335+
fsi->mount_opts.mode = new->mount_opts.mode;
336+
fsi->mount_opts.ptmxmode = new->mount_opts.ptmxmode;
337+
fsi->mount_opts.max = new->mount_opts.max;
372338

373339
/*
374340
* parse_mount_options() restores options to default values
@@ -378,7 +344,7 @@ static int devpts_remount(struct super_block *sb, int *flags, char *data)
378344
*/
379345
update_ptmx_mode(fsi);
380346

381-
return err;
347+
return 0;
382348
}
383349

384350
static int devpts_show_options(struct seq_file *seq, struct dentry *root)
@@ -402,31 +368,13 @@ static int devpts_show_options(struct seq_file *seq, struct dentry *root)
402368

403369
static const struct super_operations devpts_sops = {
404370
.statfs = simple_statfs,
405-
.remount_fs = devpts_remount,
406371
.show_options = devpts_show_options,
407372
};
408373

409-
static void *new_pts_fs_info(struct super_block *sb)
410-
{
411-
struct pts_fs_info *fsi;
412-
413-
fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL);
414-
if (!fsi)
415-
return NULL;
416-
417-
ida_init(&fsi->allocated_ptys);
418-
fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
419-
fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
420-
fsi->sb = sb;
421-
422-
return fsi;
423-
}
424-
425-
static int
426-
devpts_fill_super(struct super_block *s, void *data, int silent)
374+
static int devpts_fill_super(struct super_block *s, struct fs_context *fc)
427375
{
376+
struct pts_fs_info *fsi = DEVPTS_SB(s);
428377
struct inode *inode;
429-
int error;
430378

431379
s->s_iflags &= ~SB_I_NODEV;
432380
s->s_blocksize = 1024;
@@ -435,20 +383,11 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
435383
s->s_op = &devpts_sops;
436384
s->s_d_op = &simple_dentry_operations;
437385
s->s_time_gran = 1;
386+
fsi->sb = s;
438387

439-
error = -ENOMEM;
440-
s->s_fs_info = new_pts_fs_info(s);
441-
if (!s->s_fs_info)
442-
goto fail;
443-
444-
error = parse_mount_options(data, PARSE_MOUNT, &DEVPTS_SB(s)->mount_opts);
445-
if (error)
446-
goto fail;
447-
448-
error = -ENOMEM;
449388
inode = new_inode(s);
450389
if (!inode)
451-
goto fail;
390+
return -ENOMEM;
452391
inode->i_ino = 1;
453392
simple_inode_init_ts(inode);
454393
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
@@ -459,31 +398,60 @@ devpts_fill_super(struct super_block *s, void *data, int silent)
459398
s->s_root = d_make_root(inode);
460399
if (!s->s_root) {
461400
pr_err("get root dentry failed\n");
462-
goto fail;
401+
return -ENOMEM;
463402
}
464403

465-
error = mknod_ptmx(s);
466-
if (error)
467-
goto fail_dput;
468-
469-
return 0;
470-
fail_dput:
471-
dput(s->s_root);
472-
s->s_root = NULL;
473-
fail:
474-
return error;
404+
return mknod_ptmx(s, fc);
475405
}
476406

477407
/*
478-
* devpts_mount()
408+
* devpts_get_tree()
479409
*
480410
* Mount a new (private) instance of devpts. PTYs created in this
481411
* instance are independent of the PTYs in other devpts instances.
482412
*/
483-
static struct dentry *devpts_mount(struct file_system_type *fs_type,
484-
int flags, const char *dev_name, void *data)
413+
static int devpts_get_tree(struct fs_context *fc)
414+
{
415+
return get_tree_nodev(fc, devpts_fill_super);
416+
}
417+
418+
static void devpts_free_fc(struct fs_context *fc)
419+
{
420+
kfree(fc->s_fs_info);
421+
}
422+
423+
static const struct fs_context_operations devpts_context_ops = {
424+
.free = devpts_free_fc,
425+
.parse_param = devpts_parse_param,
426+
.get_tree = devpts_get_tree,
427+
.reconfigure = devpts_reconfigure,
428+
};
429+
430+
/*
431+
* Set up the filesystem mount context.
432+
*/
433+
static int devpts_init_fs_context(struct fs_context *fc)
485434
{
486-
return mount_nodev(fs_type, flags, data, devpts_fill_super);
435+
struct pts_fs_info *fsi;
436+
437+
fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL);
438+
if (!fsi)
439+
return -ENOMEM;
440+
441+
ida_init(&fsi->allocated_ptys);
442+
fsi->mount_opts.uid = GLOBAL_ROOT_UID;
443+
fsi->mount_opts.gid = GLOBAL_ROOT_GID;
444+
fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
445+
fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
446+
fsi->mount_opts.max = NR_UNIX98_PTY_MAX;
447+
448+
if (fc->purpose == FS_CONTEXT_FOR_MOUNT &&
449+
current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns)
450+
fsi->mount_opts.reserve = true;
451+
452+
fc->s_fs_info = fsi;
453+
fc->ops = &devpts_context_ops;
454+
return 0;
487455
}
488456

489457
static void devpts_kill_sb(struct super_block *sb)
@@ -498,7 +466,8 @@ static void devpts_kill_sb(struct super_block *sb)
498466

499467
static struct file_system_type devpts_fs_type = {
500468
.name = "devpts",
501-
.mount = devpts_mount,
469+
.init_fs_context = devpts_init_fs_context,
470+
.parameters = devpts_param_specs,
502471
.kill_sb = devpts_kill_sb,
503472
.fs_flags = FS_USERNS_MOUNT,
504473
};

0 commit comments

Comments
 (0)