| .. | .. |
|---|
| 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; |
|---|