| 1 | |
| 2 | |
| 3 | *
|
| 4 | * linux/fs/devpts/inode.c
|
| 5 | *
|
| 6 | * Copyright 1998-2004 H. Peter Anvin -- All Rights Reserved
|
| 7 | *
|
| 8 | * -------------------------------------------------------------------------
|
| 9 |
|
| 10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 11 |
|
| 12 | #include <linux/module.h>
|
| 13 | #include <linux/init.h>
|
| 14 | #include <linux/fs.h>
|
| 15 | #include <linux/fs_context.h>
|
| 16 | #include <linux/fs_parser.h>
|
| 17 | #include <linux/sched.h>
|
| 18 | #include <linux/namei.h>
|
| 19 | #include <linux/slab.h>
|
| 20 | #include <linux/mount.h>
|
| 21 | #include <linux/tty.h>
|
| 22 | #include <linux/mutex.h>
|
| 23 | #include <linux/magic.h>
|
| 24 | #include <linux/idr.h>
|
| 25 | #include <linux/devpts_fs.h>
|
| 26 | #include <linux/fsnotify.h>
|
| 27 | #include <linux/seq_file.h>
|
| 28 |
|
| 29 | #define DEVPTS_DEFAULT_MODE 0600
|
| 30 | |
| 31 | * ptmx is a new node in /dev/pts and will be unused in legacy (single-
|
| 32 | * instance) mode. To prevent surprises in user space, set permissions of
|
| 33 | * ptmx to 0. Use 'chmod' or remount with '-o ptmxmode' to set meaningful
|
| 34 | * permissions.
|
| 35 |
|
| 36 | #define DEVPTS_DEFAULT_PTMX_MODE 0000
|
| 37 | #define PTMX_MINOR 2
|
| 38 |
|
| 39 | |
| 40 | * sysctl support for setting limits on the number of Unix98 ptys allocated.
|
| 41 | * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly.
|
| 42 |
|
| 43 | static int pty_limit = NR_UNIX98_PTY_DEFAULT;
|
| 44 | static int pty_reserve = NR_UNIX98_PTY_RESERVE;
|
| 45 | static int pty_limit_min;
|
| 46 | static int pty_limit_max = INT_MAX;
|
| 47 | static atomic_t pty_count = ATOMIC_INIT(0);
|
| 48 |
|
| 49 | static const struct ctl_table pty_table[] = {
|
| 50 | {
|
| 51 | .procname = "max",
|
| 52 | .maxlen = sizeof(int),
|
| 53 | .mode = 0644,
|
| 54 | .data = &pty_limit,
|
| 55 | .proc_handler = proc_dointvec_minmax,
|
| 56 | .extra1 = &pty_limit_min,
|
| 57 | .extra2 = &pty_limit_max,
|
| 58 | }, {
|
| 59 | .procname = "reserve",
|
| 60 | .maxlen = sizeof(int),
|
| 61 | .mode = 0644,
|
| 62 | .data = &pty_reserve,
|
| 63 | .proc_handler = proc_dointvec_minmax,
|
| 64 | .extra1 = &pty_limit_min,
|
| 65 | .extra2 = &pty_limit_max,
|
| 66 | }, {
|
| 67 | .procname = "nr",
|
| 68 | .maxlen = sizeof(int),
|
| 69 | .mode = 0444,
|
| 70 | .data = &pty_count,
|
| 71 | .proc_handler = proc_dointvec,
|
| 72 | },
|
| 73 | };
|
| 74 |
|
| 75 | struct pts_mount_opts {
|
| 76 | int setuid;
|
| 77 | int setgid;
|
| 78 | kuid_t uid;
|
| 79 | kgid_t gid;
|
| 80 | umode_t mode;
|
| 81 | umode_t ptmxmode;
|
| 82 | int reserve;
|
| 83 | int max;
|
| 84 | };
|
| 85 |
|
| 86 | enum {
|
| 87 | Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance, Opt_max,
|
| 88 | Opt_err
|
| 89 | };
|
| 90 |
|
| 91 | static const struct fs_parameter_spec devpts_param_specs[] = {
|
| 92 | fsparam_gid ("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_uid ("uid", Opt_uid),
|
| 98 | {}
|
| 99 | };
|
| 100 |
|
| 101 | struct pts_fs_info {
|
| 102 | struct ida allocated_ptys;
|
| 103 | struct pts_mount_opts mount_opts;
|
| 104 | struct super_block *sb;
|
| 105 | struct inode *ptmx_inode; |
| 106 | };
|
| 107 |
|
| 108 | static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb)
|
| 109 | {
|
| 110 | return sb->s_fs_info;
|
| 111 | }
|
| 112 |
|
| 113 | static int devpts_ptmx_path(struct path *path)
|
| 114 | {
|
| 115 | struct super_block *sb;
|
| 116 | int err;
|
| 117 |
|
| 118 |
|
| 119 | err = path_pts(path);
|
| 120 | if (err)
|
| 121 | return err;
|
| 122 |
|
| 123 |
|
| 124 | sb = path->mnt->mnt_sb;
|
| 125 | if ((sb->s_magic != DEVPTS_SUPER_MAGIC) ||
|
| 126 | (path->mnt->mnt_root != sb->s_root))
|
| 127 | return -ENODEV;
|
| 128 |
|
| 129 | return 0;
|
| 130 | }
|
| 131 |
|
| 132 | |
| 133 | * Try to find a suitable devpts filesystem. We support the following
|
| 134 | * scenarios:
|
| 135 | * - The ptmx device node is located in the same directory as the devpts
|
| 136 | * mount where the pts device nodes are located.
|
| 137 | * This is e.g. the case when calling open on the /dev/pts/ptmx device
|
| 138 | * node when the devpts filesystem is mounted at /dev/pts.
|
| 139 | * - The ptmx device node is located outside the devpts filesystem mount
|
| 140 | * where the pts device nodes are located. For example, the ptmx device
|
| 141 | * is a symlink, separate device node, or bind-mount.
|
| 142 | * A supported scenario is bind-mounting /dev/pts/ptmx to /dev/ptmx and
|
| 143 | * then calling open on /dev/ptmx. In this case a suitable pts
|
| 144 | * subdirectory can be found in the common parent directory /dev of the
|
| 145 | * devpts mount and the ptmx bind-mount, after resolving the /dev/ptmx
|
| 146 | * bind-mount.
|
| 147 | * If no suitable pts subdirectory can be found this function will fail.
|
| 148 | * This is e.g. the case when bind-mounting /dev/pts/ptmx to /ptmx.
|
| 149 |
|
| 150 | struct vfsmount *devpts_mntget(struct file *filp, struct pts_fs_info *fsi)
|
| 151 | {
|
| 152 | struct path path;
|
| 153 | int err = 0;
|
| 154 |
|
| 155 | path = filp->f_path;
|
| 156 | path_get(&path);
|
| 157 |
|
| 158 | |
| 159 | * a single file.
|
| 160 |
|
| 161 | while (path.mnt->mnt_root == path.dentry)
|
| 162 | if (follow_up(&path) == 0)
|
| 163 | break;
|
| 164 |
|
| 165 |
|
| 166 | if ((path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) ||
|
| 167 | (DEVPTS_SB(path.mnt->mnt_sb) != fsi))
|
| 168 | err = devpts_ptmx_path(&path);
|
| 169 | dput(path.dentry);
|
| 170 | if (!err) {
|
| 171 | if (DEVPTS_SB(path.mnt->mnt_sb) == fsi)
|
| 172 | return path.mnt;
|
| 173 |
|
| 174 | err = -ENODEV;
|
| 175 | }
|
| 176 |
|
| 177 | mntput(path.mnt);
|
| 178 | return ERR_PTR(err);
|
| 179 | }
|
| 180 |
|
| 181 | struct pts_fs_info *devpts_acquire(struct file *filp)
|
| 182 | {
|
| 183 | struct pts_fs_info *result;
|
| 184 | struct path path;
|
| 185 | struct super_block *sb;
|
| 186 |
|
| 187 | path = filp->f_path;
|
| 188 | path_get(&path);
|
| 189 |
|
| 190 |
|
| 191 | if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) {
|
| 192 | int err;
|
| 193 |
|
| 194 | err = devpts_ptmx_path(&path);
|
| 195 | if (err) {
|
| 196 | result = ERR_PTR(err);
|
| 197 | goto out;
|
| 198 | }
|
| 199 | }
|
| 200 |
|
| 201 | |
| 202 | * pty code needs to hold extra references in case of last /dev/tty close
|
| 203 |
|
| 204 | sb = path.mnt->mnt_sb;
|
| 205 | atomic_inc(&sb->s_active);
|
| 206 | result = DEVPTS_SB(sb);
|
| 207 |
|
| 208 | out:
|
| 209 | path_put(&path);
|
| 210 | return result;
|
| 211 | }
|
| 212 |
|
| 213 | void devpts_release(struct pts_fs_info *fsi)
|
| 214 | {
|
| 215 | deactivate_super(fsi->sb);
|
| 216 | }
|
| 217 |
|
| 218 | |
| 219 | * devpts_parse_param - Parse mount parameters
|
| 220 |
|
| 221 | static int devpts_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
| 222 | {
|
| 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 | if (result.uint_32 == 0)
|
| 253 | return invalf(fc, "max must be greater than 0");
|
| 254 | opts->max = result.uint_32;
|
| 255 | break;
|
| 256 | }
|
| 257 |
|
| 258 | return 0;
|
| 259 | }
|
| 260 |
|
| 261 | static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
|
| 262 | {
|
| 263 | int mode;
|
| 264 | struct dentry *dentry;
|
| 265 | struct inode *inode;
|
| 266 | struct dentry *root = sb->s_root;
|
| 267 | struct pts_fs_info *fsi = DEVPTS_SB(sb);
|
| 268 | struct pts_mount_opts *opts = &fsi->mount_opts;
|
| 269 | kuid_t ptmx_uid = current_fsuid();
|
| 270 | kgid_t ptmx_gid = current_fsgid();
|
| 271 |
|
| 272 | dentry = simple_start_creating(root, "ptmx");
|
| 273 | if (IS_ERR(dentry)) {
|
| 274 | pr_err("Unable to alloc dentry for ptmx node\n");
|
| 275 | return PTR_ERR(dentry);
|
| 276 | }
|
| 277 |
|
| 278 | |
| 279 | * Create a new 'ptmx' node in this mount of devpts.
|
| 280 |
|
| 281 | inode = new_inode(sb);
|
| 282 | if (!inode) {
|
| 283 | simple_done_creating(dentry);
|
| 284 | pr_err("Unable to alloc inode for ptmx node\n");
|
| 285 | return -ENOMEM;
|
| 286 | }
|
| 287 |
|
| 288 | inode->i_ino = 2;
|
| 289 | simple_inode_init_ts(inode);
|
| 290 |
|
| 291 | mode = S_IFCHR|opts->ptmxmode;
|
| 292 | init_special_inode(inode, mode, MKDEV(TTYAUX_MAJOR, 2));
|
| 293 | inode->i_uid = ptmx_uid;
|
| 294 | inode->i_gid = ptmx_gid;
|
| 295 | fsi->ptmx_inode = inode;
|
| 296 |
|
| 297 | d_make_persistent(dentry, inode);
|
| 298 |
|
| 299 | simple_done_creating(dentry);
|
| 300 |
|
| 301 | return 0;
|
| 302 | }
|
| 303 |
|
| 304 | static void update_ptmx_mode(struct pts_fs_info *fsi)
|
| 305 | {
|
| 306 | fsi->ptmx_inode->i_mode = S_IFCHR|fsi->mount_opts.ptmxmode;
|
| 307 | }
|
| 308 |
|
| 309 | static int devpts_reconfigure(struct fs_context *fc)
|
| 310 | {
|
| 311 | struct pts_fs_info *fsi = DEVPTS_SB(fc->root->d_sb);
|
| 312 | struct pts_fs_info *new = fc->s_fs_info;
|
| 313 |
|
| 314 | |
| 315 | * Ideally, we'd update each option conditionally on it having been
|
| 316 | * explicitly changed, but the default is to reset everything so that
|
| 317 | * would break UAPI...
|
| 318 |
|
| 319 | fsi->mount_opts.setuid = new->mount_opts.setuid;
|
| 320 | fsi->mount_opts.setgid = new->mount_opts.setgid;
|
| 321 | fsi->mount_opts.uid = new->mount_opts.uid;
|
| 322 | fsi->mount_opts.gid = new->mount_opts.gid;
|
| 323 | fsi->mount_opts.mode = new->mount_opts.mode;
|
| 324 | fsi->mount_opts.ptmxmode = new->mount_opts.ptmxmode;
|
| 325 | fsi->mount_opts.max = new->mount_opts.max;
|
| 326 |
|
| 327 | |
| 328 | * parse_mount_options() restores options to default values
|
| 329 | * before parsing and may have changed ptmxmode. So, update the
|
| 330 | * mode in the inode too. Bogus options don't fail the remount,
|
| 331 | * so do this even on error return.
|
| 332 |
|
| 333 | update_ptmx_mode(fsi);
|
| 334 |
|
| 335 | return 0;
|
| 336 | }
|
| 337 |
|
| 338 | static int devpts_show_options(struct seq_file *seq, struct dentry *root)
|
| 339 | {
|
| 340 | struct pts_fs_info *fsi = DEVPTS_SB(root->d_sb);
|
| 341 | struct pts_mount_opts *opts = &fsi->mount_opts;
|
| 342 |
|
| 343 | if (opts->setuid)
|
| 344 | seq_printf(seq, ",uid=%u",
|
| 345 | from_kuid_munged(&init_user_ns, opts->uid));
|
| 346 | if (opts->setgid)
|
| 347 | seq_printf(seq, ",gid=%u",
|
| 348 | from_kgid_munged(&init_user_ns, opts->gid));
|
| 349 | seq_printf(seq, ",mode=%03o", opts->mode);
|
| 350 | seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode);
|
| 351 | if (opts->max < NR_UNIX98_PTY_MAX)
|
| 352 | seq_printf(seq, ",max=%d", opts->max);
|
| 353 |
|
| 354 | return 0;
|
| 355 | }
|
| 356 |
|
| 357 | static const struct super_operations devpts_sops = {
|
| 358 | .statfs = simple_statfs,
|
| 359 | .show_options = devpts_show_options,
|
| 360 | };
|
| 361 |
|
| 362 | static int devpts_fill_super(struct super_block *s, struct fs_context *fc)
|
| 363 | {
|
| 364 | struct pts_fs_info *fsi = DEVPTS_SB(s);
|
| 365 | struct inode *inode;
|
| 366 |
|
| 367 | s->s_iflags &= ~SB_I_NODEV;
|
| 368 | s->s_blocksize = 1024;
|
| 369 | s->s_blocksize_bits = 10;
|
| 370 | s->s_magic = DEVPTS_SUPER_MAGIC;
|
| 371 | s->s_op = &devpts_sops;
|
| 372 | s->s_d_flags = DCACHE_DONTCACHE;
|
| 373 | s->s_time_gran = 1;
|
| 374 | fsi->sb = s;
|
| 375 |
|
| 376 | inode = new_inode(s);
|
| 377 | if (!inode)
|
| 378 | return -ENOMEM;
|
| 379 | inode->i_ino = 1;
|
| 380 | simple_inode_init_ts(inode);
|
| 381 | inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
|
| 382 | inode->i_op = &simple_dir_inode_operations;
|
| 383 | inode->i_fop = &simple_dir_operations;
|
| 384 | set_nlink(inode, 2);
|
| 385 |
|
| 386 | s->s_root = d_make_root(inode);
|
| 387 | if (!s->s_root) {
|
| 388 | pr_err("get root dentry failed\n");
|
| 389 | return -ENOMEM;
|
| 390 | }
|
| 391 |
|
| 392 | return mknod_ptmx(s, fc);
|
| 393 | }
|
| 394 |
|
| 395 | |
| 396 | * devpts_get_tree()
|
| 397 | *
|
| 398 | * Mount a new (private) instance of devpts. PTYs created in this
|
| 399 | * instance are independent of the PTYs in other devpts instances.
|
| 400 |
|
| 401 | static int devpts_get_tree(struct fs_context *fc)
|
| 402 | {
|
| 403 | return get_tree_nodev(fc, devpts_fill_super);
|
| 404 | }
|
| 405 |
|
| 406 | static void devpts_free_fc(struct fs_context *fc)
|
| 407 | {
|
| 408 | kfree(fc->s_fs_info);
|
| 409 | }
|
| 410 |
|
| 411 | static const struct fs_context_operations devpts_context_ops = {
|
| 412 | .free = devpts_free_fc,
|
| 413 | .parse_param = devpts_parse_param,
|
| 414 | .get_tree = devpts_get_tree,
|
| 415 | .reconfigure = devpts_reconfigure,
|
| 416 | };
|
| 417 |
|
| 418 | |
| 419 | * Set up the filesystem mount context.
|
| 420 |
|
| 421 | static int devpts_init_fs_context(struct fs_context *fc)
|
| 422 | {
|
| 423 | struct pts_fs_info *fsi;
|
| 424 |
|
| 425 | fsi = kzalloc_obj(struct pts_fs_info);
|
| 426 | if (!fsi)
|
| 427 | return -ENOMEM;
|
| 428 |
|
| 429 | ida_init(&fsi->allocated_ptys);
|
| 430 | fsi->mount_opts.uid = GLOBAL_ROOT_UID;
|
| 431 | fsi->mount_opts.gid = GLOBAL_ROOT_GID;
|
| 432 | fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE;
|
| 433 | fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
|
| 434 | fsi->mount_opts.max = NR_UNIX98_PTY_MAX;
|
| 435 |
|
| 436 | if (fc->purpose == FS_CONTEXT_FOR_MOUNT &&
|
| 437 | current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns)
|
| 438 | fsi->mount_opts.reserve = true;
|
| 439 |
|
| 440 | fc->s_fs_info = fsi;
|
| 441 | fc->ops = &devpts_context_ops;
|
| 442 | return 0;
|
| 443 | }
|
| 444 |
|
| 445 | static void devpts_kill_sb(struct super_block *sb)
|
| 446 | {
|
| 447 | struct pts_fs_info *fsi = DEVPTS_SB(sb);
|
| 448 |
|
| 449 | if (fsi)
|
| 450 | ida_destroy(&fsi->allocated_ptys);
|
| 451 | kfree(fsi);
|
| 452 | kill_anon_super(sb);
|
| 453 | }
|
| 454 |
|
| 455 | static struct file_system_type devpts_fs_type = {
|
| 456 | .name = "devpts",
|
| 457 | .init_fs_context = devpts_init_fs_context,
|
| 458 | .parameters = devpts_param_specs,
|
| 459 | .kill_sb = devpts_kill_sb,
|
| 460 | .fs_flags = FS_USERNS_MOUNT,
|
| 461 | };
|
| 462 |
|
| 463 | |
| 464 | * The normal naming convention is simply /dev/pts/<number>; this conforms
|
| 465 | * to the System V naming convention
|
| 466 |
|
| 467 |
|
| 468 | int devpts_new_index(struct pts_fs_info *fsi)
|
| 469 | {
|
| 470 | int index = -ENOSPC;
|
| 471 |
|
| 472 | if (atomic_inc_return(&pty_count) >= (pty_limit -
|
| 473 | (fsi->mount_opts.reserve ? 0 : pty_reserve)))
|
| 474 | goto out;
|
| 475 |
|
| 476 | index = ida_alloc_max(&fsi->allocated_ptys, fsi->mount_opts.max - 1,
|
| 477 | GFP_KERNEL);
|
| 478 |
|
| 479 | out:
|
| 480 | if (index < 0)
|
| 481 | atomic_dec(&pty_count);
|
| 482 | return index;
|
| 483 | }
|
| 484 |
|
| 485 | void devpts_kill_index(struct pts_fs_info *fsi, int idx)
|
| 486 | {
|
| 487 | ida_free(&fsi->allocated_ptys, idx);
|
| 488 | atomic_dec(&pty_count);
|
| 489 | }
|
| 490 |
|
| 491 | |
| 492 | * devpts_pty_new -- create a new inode in /dev/pts/
|
| 493 | * @fsi: Filesystem info for this instance.
|
| 494 | * @index: used as a name of the node
|
| 495 | * @priv: what's given back by devpts_get_priv
|
| 496 | *
|
| 497 | * The dentry for the created inode is returned.
|
| 498 | * Remove it from /dev/pts/ with devpts_pty_kill().
|
| 499 |
|
| 500 | struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv)
|
| 501 | {
|
| 502 | struct dentry *dentry;
|
| 503 | struct super_block *sb = fsi->sb;
|
| 504 | struct inode *inode;
|
| 505 | struct dentry *root;
|
| 506 | struct pts_mount_opts *opts;
|
| 507 | char s[12];
|
| 508 |
|
| 509 | root = sb->s_root;
|
| 510 | opts = &fsi->mount_opts;
|
| 511 |
|
| 512 | inode = new_inode(sb);
|
| 513 | if (!inode)
|
| 514 | return ERR_PTR(-ENOMEM);
|
| 515 |
|
| 516 | inode->i_ino = index + 3;
|
| 517 | inode->i_uid = opts->setuid ? opts->uid : current_fsuid();
|
| 518 | inode->i_gid = opts->setgid ? opts->gid : current_fsgid();
|
| 519 | simple_inode_init_ts(inode);
|
| 520 | init_special_inode(inode, S_IFCHR|opts->mode, MKDEV(UNIX98_PTY_SLAVE_MAJOR, index));
|
| 521 |
|
| 522 | sprintf(s, "%d", index);
|
| 523 |
|
| 524 | dentry = d_alloc_name(root, s);
|
| 525 | if (!dentry) {
|
| 526 | iput(inode);
|
| 527 | return ERR_PTR(-ENOMEM);
|
| 528 | }
|
| 529 | dentry->d_fsdata = priv;
|
| 530 | d_make_persistent(dentry, inode);
|
| 531 | fsnotify_create(d_inode(root), dentry);
|
| 532 | dput(dentry);
|
| 533 | return dentry; |
| 534 | }
|
| 535 |
|
| 536 | |
| 537 | * devpts_get_priv -- get private data for a slave
|
| 538 | * @dentry: dentry of the slave
|
| 539 | *
|
| 540 | * Returns whatever was passed as priv in devpts_pty_new for a given inode.
|
| 541 |
|
| 542 | void *devpts_get_priv(struct dentry *dentry)
|
| 543 | {
|
| 544 | if (dentry->d_sb->s_magic != DEVPTS_SUPER_MAGIC)
|
| 545 | return NULL;
|
| 546 | return dentry->d_fsdata;
|
| 547 | }
|
| 548 |
|
| 549 | |
| 550 | * devpts_pty_kill -- remove inode form /dev/pts/
|
| 551 | * @dentry: dentry of the slave to be removed
|
| 552 | *
|
| 553 | * This is an inverse operation of devpts_pty_new.
|
| 554 |
|
| 555 | void devpts_pty_kill(struct dentry *dentry)
|
| 556 | {
|
| 557 | WARN_ON_ONCE(dentry->d_sb->s_magic != DEVPTS_SUPER_MAGIC);
|
| 558 |
|
| 559 | dentry->d_fsdata = NULL;
|
| 560 | drop_nlink(dentry->d_inode);
|
| 561 | d_drop(dentry);
|
| 562 | fsnotify_unlink(d_inode(dentry->d_parent), dentry);
|
| 563 | d_make_discardable(dentry);
|
| 564 | }
|
| 565 |
|
| 566 | static int __init init_devpts_fs(void)
|
| 567 | {
|
| 568 | int err = register_filesystem(&devpts_fs_type);
|
| 569 | if (!err) {
|
| 570 | register_sysctl("kernel/pty", pty_table);
|
| 571 | }
|
| 572 | return err;
|
| 573 | }
|
| 574 | module_init(init_devpts_fs)
|