.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * proc/fs/generic.c --- generic routines for the proc-fs |
---|
3 | 4 | * |
---|
.. | .. |
---|
162 | 163 | { |
---|
163 | 164 | const char *cp = name, *next; |
---|
164 | 165 | struct proc_dir_entry *de; |
---|
165 | | - unsigned int len; |
---|
166 | 166 | |
---|
167 | 167 | de = *ret; |
---|
168 | 168 | if (!de) |
---|
.. | .. |
---|
173 | 173 | if (!next) |
---|
174 | 174 | break; |
---|
175 | 175 | |
---|
176 | | - len = next - cp; |
---|
177 | | - de = pde_subdir_find(de, cp, len); |
---|
| 176 | + de = pde_subdir_find(de, cp, next - cp); |
---|
178 | 177 | if (!de) { |
---|
179 | 178 | WARN(1, "name '%s'\n", name); |
---|
180 | 179 | return -ENOENT; |
---|
181 | 180 | } |
---|
182 | | - cp += len + 1; |
---|
| 181 | + cp = next + 1; |
---|
183 | 182 | } |
---|
184 | 183 | *residual = cp; |
---|
185 | 184 | *ret = de; |
---|
.. | .. |
---|
270 | 269 | struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry, |
---|
271 | 270 | unsigned int flags) |
---|
272 | 271 | { |
---|
| 272 | + struct proc_fs_info *fs_info = proc_sb_info(dir->i_sb); |
---|
| 273 | + |
---|
| 274 | + if (fs_info->pidonly == PROC_PIDONLY_ON) |
---|
| 275 | + return ERR_PTR(-ENOENT); |
---|
| 276 | + |
---|
273 | 277 | return proc_lookup_de(dir, dentry, PDE(dir)); |
---|
274 | 278 | } |
---|
275 | 279 | |
---|
.. | .. |
---|
326 | 330 | int proc_readdir(struct file *file, struct dir_context *ctx) |
---|
327 | 331 | { |
---|
328 | 332 | struct inode *inode = file_inode(file); |
---|
| 333 | + struct proc_fs_info *fs_info = proc_sb_info(inode->i_sb); |
---|
| 334 | + |
---|
| 335 | + if (fs_info->pidonly == PROC_PIDONLY_ON) |
---|
| 336 | + return 1; |
---|
329 | 337 | |
---|
330 | 338 | return proc_readdir_de(file, ctx, PDE(inode)); |
---|
331 | 339 | } |
---|
.. | .. |
---|
445 | 453 | proc_set_user(ent, (*parent)->uid, (*parent)->gid); |
---|
446 | 454 | |
---|
447 | 455 | ent->proc_dops = &proc_misc_dentry_ops; |
---|
| 456 | + /* Revalidate everything under /proc/${pid}/net */ |
---|
| 457 | + if ((*parent)->proc_dops == &proc_net_dentry_ops) |
---|
| 458 | + pde_force_lookup(ent); |
---|
448 | 459 | |
---|
449 | 460 | out: |
---|
450 | 461 | return ent; |
---|
.. | .. |
---|
484 | 495 | ent = __proc_create(&parent, name, S_IFDIR | mode, 2); |
---|
485 | 496 | if (ent) { |
---|
486 | 497 | ent->data = data; |
---|
487 | | - ent->proc_fops = &proc_dir_operations; |
---|
| 498 | + ent->proc_dir_ops = &proc_dir_operations; |
---|
488 | 499 | ent->proc_iops = &proc_dir_inode_operations; |
---|
489 | 500 | if (force_lookup) { |
---|
490 | 501 | pde_force_lookup(ent); |
---|
.. | .. |
---|
524 | 535 | ent = __proc_create(&parent, name, mode, 2); |
---|
525 | 536 | if (ent) { |
---|
526 | 537 | ent->data = NULL; |
---|
527 | | - ent->proc_fops = NULL; |
---|
| 538 | + ent->proc_dir_ops = NULL; |
---|
528 | 539 | ent->proc_iops = NULL; |
---|
529 | 540 | ent = proc_register(parent, ent); |
---|
530 | 541 | } |
---|
.. | .. |
---|
552 | 563 | return p; |
---|
553 | 564 | } |
---|
554 | 565 | |
---|
| 566 | +static inline void pde_set_flags(struct proc_dir_entry *pde) |
---|
| 567 | +{ |
---|
| 568 | + if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT) |
---|
| 569 | + pde->flags |= PROC_ENTRY_PERMANENT; |
---|
| 570 | +} |
---|
| 571 | + |
---|
555 | 572 | struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, |
---|
556 | 573 | struct proc_dir_entry *parent, |
---|
557 | | - const struct file_operations *proc_fops, void *data) |
---|
| 574 | + const struct proc_ops *proc_ops, void *data) |
---|
558 | 575 | { |
---|
559 | 576 | struct proc_dir_entry *p; |
---|
560 | | - |
---|
561 | | - BUG_ON(proc_fops == NULL); |
---|
562 | 577 | |
---|
563 | 578 | p = proc_create_reg(name, mode, &parent, data); |
---|
564 | 579 | if (!p) |
---|
565 | 580 | return NULL; |
---|
566 | | - p->proc_fops = proc_fops; |
---|
| 581 | + p->proc_ops = proc_ops; |
---|
| 582 | + pde_set_flags(p); |
---|
567 | 583 | return proc_register(parent, p); |
---|
568 | 584 | } |
---|
569 | 585 | EXPORT_SYMBOL(proc_create_data); |
---|
570 | 586 | |
---|
571 | 587 | struct proc_dir_entry *proc_create(const char *name, umode_t mode, |
---|
572 | 588 | struct proc_dir_entry *parent, |
---|
573 | | - const struct file_operations *proc_fops) |
---|
| 589 | + const struct proc_ops *proc_ops) |
---|
574 | 590 | { |
---|
575 | | - return proc_create_data(name, mode, parent, proc_fops, NULL); |
---|
| 591 | + return proc_create_data(name, mode, parent, proc_ops, NULL); |
---|
576 | 592 | } |
---|
577 | 593 | EXPORT_SYMBOL(proc_create); |
---|
578 | 594 | |
---|
.. | .. |
---|
594 | 610 | return seq_release(inode, file); |
---|
595 | 611 | } |
---|
596 | 612 | |
---|
597 | | -static const struct file_operations proc_seq_fops = { |
---|
598 | | - .open = proc_seq_open, |
---|
599 | | - .read = seq_read, |
---|
600 | | - .llseek = seq_lseek, |
---|
601 | | - .release = proc_seq_release, |
---|
| 613 | +static const struct proc_ops proc_seq_ops = { |
---|
| 614 | + /* not permanent -- can call into arbitrary seq_operations */ |
---|
| 615 | + .proc_open = proc_seq_open, |
---|
| 616 | + .proc_read_iter = seq_read_iter, |
---|
| 617 | + .proc_lseek = seq_lseek, |
---|
| 618 | + .proc_release = proc_seq_release, |
---|
602 | 619 | }; |
---|
603 | 620 | |
---|
604 | 621 | struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode, |
---|
.. | .. |
---|
610 | 627 | p = proc_create_reg(name, mode, &parent, data); |
---|
611 | 628 | if (!p) |
---|
612 | 629 | return NULL; |
---|
613 | | - p->proc_fops = &proc_seq_fops; |
---|
| 630 | + p->proc_ops = &proc_seq_ops; |
---|
614 | 631 | p->seq_ops = ops; |
---|
615 | 632 | p->state_size = state_size; |
---|
616 | 633 | return proc_register(parent, p); |
---|
.. | .. |
---|
624 | 641 | return single_open(file, de->single_show, de->data); |
---|
625 | 642 | } |
---|
626 | 643 | |
---|
627 | | -static const struct file_operations proc_single_fops = { |
---|
628 | | - .open = proc_single_open, |
---|
629 | | - .read = seq_read, |
---|
630 | | - .llseek = seq_lseek, |
---|
631 | | - .release = single_release, |
---|
| 644 | +static const struct proc_ops proc_single_ops = { |
---|
| 645 | + /* not permanent -- can call into arbitrary ->single_show */ |
---|
| 646 | + .proc_open = proc_single_open, |
---|
| 647 | + .proc_read_iter = seq_read_iter, |
---|
| 648 | + .proc_lseek = seq_lseek, |
---|
| 649 | + .proc_release = single_release, |
---|
632 | 650 | }; |
---|
633 | 651 | |
---|
634 | 652 | struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode, |
---|
.. | .. |
---|
640 | 658 | p = proc_create_reg(name, mode, &parent, data); |
---|
641 | 659 | if (!p) |
---|
642 | 660 | return NULL; |
---|
643 | | - p->proc_fops = &proc_single_fops; |
---|
| 661 | + p->proc_ops = &proc_single_ops; |
---|
644 | 662 | p->single_show = show; |
---|
645 | 663 | return proc_register(parent, p); |
---|
646 | 664 | } |
---|
.. | .. |
---|
685 | 703 | |
---|
686 | 704 | de = pde_subdir_find(parent, fn, len); |
---|
687 | 705 | if (de) { |
---|
688 | | - rb_erase(&de->subdir_node, &parent->subdir); |
---|
689 | | - if (S_ISDIR(de->mode)) { |
---|
690 | | - parent->nlink--; |
---|
| 706 | + if (unlikely(pde_is_permanent(de))) { |
---|
| 707 | + WARN(1, "removing permanent /proc entry '%s'", de->name); |
---|
| 708 | + de = NULL; |
---|
| 709 | + } else { |
---|
| 710 | + rb_erase(&de->subdir_node, &parent->subdir); |
---|
| 711 | + if (S_ISDIR(de->mode)) |
---|
| 712 | + parent->nlink--; |
---|
691 | 713 | } |
---|
692 | 714 | } |
---|
693 | 715 | write_unlock(&proc_subdir_lock); |
---|
.. | .. |
---|
723 | 745 | write_unlock(&proc_subdir_lock); |
---|
724 | 746 | return -ENOENT; |
---|
725 | 747 | } |
---|
| 748 | + if (unlikely(pde_is_permanent(root))) { |
---|
| 749 | + write_unlock(&proc_subdir_lock); |
---|
| 750 | + WARN(1, "removing permanent /proc entry '%s/%s'", |
---|
| 751 | + root->parent->name, root->name); |
---|
| 752 | + return -EINVAL; |
---|
| 753 | + } |
---|
726 | 754 | rb_erase(&root->subdir_node, &parent->subdir); |
---|
727 | 755 | |
---|
728 | 756 | de = root; |
---|
729 | 757 | while (1) { |
---|
730 | 758 | next = pde_subdir_first(de); |
---|
731 | 759 | if (next) { |
---|
| 760 | + if (unlikely(pde_is_permanent(next))) { |
---|
| 761 | + write_unlock(&proc_subdir_lock); |
---|
| 762 | + WARN(1, "removing permanent /proc entry '%s/%s'", |
---|
| 763 | + next->parent->name, next->name); |
---|
| 764 | + return -EINVAL; |
---|
| 765 | + } |
---|
732 | 766 | rb_erase(&next->subdir_node, &de->subdir); |
---|
733 | 767 | de = next; |
---|
734 | 768 | continue; |
---|