| .. | .. |
|---|
| 2 | 2 | /* |
|---|
| 3 | 3 | * inode.c - part of debugfs, a tiny little debug file system |
|---|
| 4 | 4 | * |
|---|
| 5 | | - * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com> |
|---|
| 5 | + * Copyright (C) 2004,2019 Greg Kroah-Hartman <greg@kroah.com> |
|---|
| 6 | 6 | * Copyright (C) 2004 IBM Inc. |
|---|
| 7 | + * Copyright (C) 2019 Linux Foundation <gregkh@linuxfoundation.org> |
|---|
| 7 | 8 | * |
|---|
| 8 | 9 | * debugfs is for people to use instead of /proc or /sys. |
|---|
| 9 | 10 | * See ./Documentation/core-api/kernel-api.rst for more details. |
|---|
| 10 | 11 | */ |
|---|
| 12 | + |
|---|
| 13 | +#define pr_fmt(fmt) "debugfs: " fmt |
|---|
| 11 | 14 | |
|---|
| 12 | 15 | #include <linux/module.h> |
|---|
| 13 | 16 | #include <linux/fs.h> |
|---|
| .. | .. |
|---|
| 23 | 26 | #include <linux/parser.h> |
|---|
| 24 | 27 | #include <linux/magic.h> |
|---|
| 25 | 28 | #include <linux/slab.h> |
|---|
| 29 | +#include <linux/security.h> |
|---|
| 26 | 30 | |
|---|
| 27 | 31 | #include "internal.h" |
|---|
| 28 | 32 | |
|---|
| .. | .. |
|---|
| 31 | 35 | static struct vfsmount *debugfs_mount; |
|---|
| 32 | 36 | static int debugfs_mount_count; |
|---|
| 33 | 37 | static bool debugfs_registered; |
|---|
| 38 | +static unsigned int debugfs_allow __ro_after_init = DEFAULT_DEBUGFS_ALLOW_BITS; |
|---|
| 39 | + |
|---|
| 40 | +/* |
|---|
| 41 | + * Don't allow access attributes to be changed whilst the kernel is locked down |
|---|
| 42 | + * so that we can use the file mode as part of a heuristic to determine whether |
|---|
| 43 | + * to lock down individual files. |
|---|
| 44 | + */ |
|---|
| 45 | +static int debugfs_setattr(struct dentry *dentry, struct iattr *ia) |
|---|
| 46 | +{ |
|---|
| 47 | + int ret = security_locked_down(LOCKDOWN_DEBUGFS); |
|---|
| 48 | + |
|---|
| 49 | + if (ret && (ia->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))) |
|---|
| 50 | + return ret; |
|---|
| 51 | + return simple_setattr(dentry, ia); |
|---|
| 52 | +} |
|---|
| 53 | + |
|---|
| 54 | +static const struct inode_operations debugfs_file_inode_operations = { |
|---|
| 55 | + .setattr = debugfs_setattr, |
|---|
| 56 | +}; |
|---|
| 57 | +static const struct inode_operations debugfs_dir_inode_operations = { |
|---|
| 58 | + .lookup = simple_lookup, |
|---|
| 59 | + .setattr = debugfs_setattr, |
|---|
| 60 | +}; |
|---|
| 61 | +static const struct inode_operations debugfs_symlink_inode_operations = { |
|---|
| 62 | + .get_link = simple_get_link, |
|---|
| 63 | + .setattr = debugfs_setattr, |
|---|
| 64 | +}; |
|---|
| 34 | 65 | |
|---|
| 35 | 66 | static struct inode *debugfs_get_inode(struct super_block *sb) |
|---|
| 36 | 67 | { |
|---|
| .. | .. |
|---|
| 163 | 194 | return 0; |
|---|
| 164 | 195 | } |
|---|
| 165 | 196 | |
|---|
| 166 | | -static void debugfs_i_callback(struct rcu_head *head) |
|---|
| 197 | +static void debugfs_free_inode(struct inode *inode) |
|---|
| 167 | 198 | { |
|---|
| 168 | | - struct inode *inode = container_of(head, struct inode, i_rcu); |
|---|
| 169 | 199 | if (S_ISLNK(inode->i_mode)) |
|---|
| 170 | 200 | kfree(inode->i_link); |
|---|
| 171 | 201 | free_inode_nonrcu(inode); |
|---|
| 172 | | -} |
|---|
| 173 | | - |
|---|
| 174 | | -static void debugfs_destroy_inode(struct inode *inode) |
|---|
| 175 | | -{ |
|---|
| 176 | | - call_rcu(&inode->i_rcu, debugfs_i_callback); |
|---|
| 177 | 202 | } |
|---|
| 178 | 203 | |
|---|
| 179 | 204 | static const struct super_operations debugfs_super_operations = { |
|---|
| 180 | 205 | .statfs = simple_statfs, |
|---|
| 181 | 206 | .remount_fs = debugfs_remount, |
|---|
| 182 | 207 | .show_options = debugfs_show_options, |
|---|
| 183 | | - .destroy_inode = debugfs_destroy_inode, |
|---|
| 208 | + .free_inode = debugfs_free_inode, |
|---|
| 184 | 209 | }; |
|---|
| 185 | 210 | |
|---|
| 186 | 211 | static void debugfs_release_dentry(struct dentry *dentry) |
|---|
| .. | .. |
|---|
| 242 | 267 | int flags, const char *dev_name, |
|---|
| 243 | 268 | void *data) |
|---|
| 244 | 269 | { |
|---|
| 270 | + if (!(debugfs_allow & DEBUGFS_ALLOW_API)) |
|---|
| 271 | + return ERR_PTR(-EPERM); |
|---|
| 272 | + |
|---|
| 245 | 273 | return mount_single(fs_type, flags, data, debug_fill_super); |
|---|
| 246 | 274 | } |
|---|
| 247 | 275 | |
|---|
| .. | .. |
|---|
| 269 | 297 | { |
|---|
| 270 | 298 | struct dentry *dentry; |
|---|
| 271 | 299 | |
|---|
| 272 | | - if (IS_ERR(parent)) |
|---|
| 300 | + if (!debugfs_initialized() || IS_ERR_OR_NULL(name) || IS_ERR(parent)) |
|---|
| 273 | 301 | return NULL; |
|---|
| 274 | 302 | |
|---|
| 275 | 303 | if (!parent) |
|---|
| 276 | 304 | parent = debugfs_mount->mnt_root; |
|---|
| 277 | 305 | |
|---|
| 278 | | - dentry = lookup_one_len_unlocked(name, parent, strlen(name)); |
|---|
| 306 | + dentry = lookup_positive_unlocked(name, parent, strlen(name)); |
|---|
| 279 | 307 | if (IS_ERR(dentry)) |
|---|
| 280 | 308 | return NULL; |
|---|
| 281 | | - if (!d_really_is_positive(dentry)) { |
|---|
| 282 | | - dput(dentry); |
|---|
| 283 | | - return NULL; |
|---|
| 284 | | - } |
|---|
| 285 | 309 | return dentry; |
|---|
| 286 | 310 | } |
|---|
| 287 | 311 | EXPORT_SYMBOL_GPL(debugfs_lookup); |
|---|
| .. | .. |
|---|
| 291 | 315 | struct dentry *dentry; |
|---|
| 292 | 316 | int error; |
|---|
| 293 | 317 | |
|---|
| 294 | | - pr_debug("debugfs: creating file '%s'\n",name); |
|---|
| 318 | + if (!(debugfs_allow & DEBUGFS_ALLOW_API)) |
|---|
| 319 | + return ERR_PTR(-EPERM); |
|---|
| 320 | + |
|---|
| 321 | + if (!debugfs_initialized()) |
|---|
| 322 | + return ERR_PTR(-ENOENT); |
|---|
| 323 | + |
|---|
| 324 | + pr_debug("creating file '%s'\n", name); |
|---|
| 295 | 325 | |
|---|
| 296 | 326 | if (IS_ERR(parent)) |
|---|
| 297 | 327 | return parent; |
|---|
| 298 | 328 | |
|---|
| 299 | 329 | error = simple_pin_fs(&debug_fs_type, &debugfs_mount, |
|---|
| 300 | 330 | &debugfs_mount_count); |
|---|
| 301 | | - if (error) |
|---|
| 331 | + if (error) { |
|---|
| 332 | + pr_err("Unable to pin filesystem for file '%s'\n", name); |
|---|
| 302 | 333 | return ERR_PTR(error); |
|---|
| 334 | + } |
|---|
| 303 | 335 | |
|---|
| 304 | 336 | /* If the parent is not specified, we create it in the root. |
|---|
| 305 | 337 | * We need the root dentry to do this, which is in the super |
|---|
| .. | .. |
|---|
| 310 | 342 | parent = debugfs_mount->mnt_root; |
|---|
| 311 | 343 | |
|---|
| 312 | 344 | inode_lock(d_inode(parent)); |
|---|
| 313 | | - dentry = lookup_one_len(name, parent, strlen(name)); |
|---|
| 345 | + if (unlikely(IS_DEADDIR(d_inode(parent)))) |
|---|
| 346 | + dentry = ERR_PTR(-ENOENT); |
|---|
| 347 | + else |
|---|
| 348 | + dentry = lookup_one_len(name, parent, strlen(name)); |
|---|
| 314 | 349 | if (!IS_ERR(dentry) && d_really_is_positive(dentry)) { |
|---|
| 350 | + if (d_is_dir(dentry)) |
|---|
| 351 | + pr_err("Directory '%s' with parent '%s' already present!\n", |
|---|
| 352 | + name, parent->d_name.name); |
|---|
| 353 | + else |
|---|
| 354 | + pr_err("File '%s' in directory '%s' already present!\n", |
|---|
| 355 | + name, parent->d_name.name); |
|---|
| 315 | 356 | dput(dentry); |
|---|
| 316 | 357 | dentry = ERR_PTR(-EEXIST); |
|---|
| 317 | 358 | } |
|---|
| .. | .. |
|---|
| 329 | 370 | inode_unlock(d_inode(dentry->d_parent)); |
|---|
| 330 | 371 | dput(dentry); |
|---|
| 331 | 372 | simple_release_fs(&debugfs_mount, &debugfs_mount_count); |
|---|
| 332 | | - return NULL; |
|---|
| 373 | + return ERR_PTR(-ENOMEM); |
|---|
| 333 | 374 | } |
|---|
| 334 | 375 | |
|---|
| 335 | 376 | static struct dentry *end_creating(struct dentry *dentry) |
|---|
| .. | .. |
|---|
| 352 | 393 | dentry = start_creating(name, parent); |
|---|
| 353 | 394 | |
|---|
| 354 | 395 | if (IS_ERR(dentry)) |
|---|
| 355 | | - return NULL; |
|---|
| 396 | + return dentry; |
|---|
| 397 | + |
|---|
| 398 | + if (!(debugfs_allow & DEBUGFS_ALLOW_API)) { |
|---|
| 399 | + failed_creating(dentry); |
|---|
| 400 | + return ERR_PTR(-EPERM); |
|---|
| 401 | + } |
|---|
| 356 | 402 | |
|---|
| 357 | 403 | inode = debugfs_get_inode(dentry->d_sb); |
|---|
| 358 | | - if (unlikely(!inode)) |
|---|
| 404 | + if (unlikely(!inode)) { |
|---|
| 405 | + pr_err("out of free dentries, can not create file '%s'\n", |
|---|
| 406 | + name); |
|---|
| 359 | 407 | return failed_creating(dentry); |
|---|
| 408 | + } |
|---|
| 360 | 409 | |
|---|
| 361 | 410 | inode->i_mode = mode; |
|---|
| 362 | 411 | inode->i_private = data; |
|---|
| 363 | 412 | |
|---|
| 413 | + inode->i_op = &debugfs_file_inode_operations; |
|---|
| 364 | 414 | inode->i_fop = proxy_fops; |
|---|
| 365 | 415 | dentry->d_fsdata = (void *)((unsigned long)real_fops | |
|---|
| 366 | 416 | DEBUGFS_FSDATA_IS_REAL_FOPS_BIT); |
|---|
| .. | .. |
|---|
| 391 | 441 | * This function will return a pointer to a dentry if it succeeds. This |
|---|
| 392 | 442 | * pointer must be passed to the debugfs_remove() function when the file is |
|---|
| 393 | 443 | * to be removed (no automatic cleanup happens if your module is unloaded, |
|---|
| 394 | | - * you are responsible here.) If an error occurs, %NULL will be returned. |
|---|
| 444 | + * you are responsible here.) If an error occurs, ERR_PTR(-ERROR) will be |
|---|
| 445 | + * returned. |
|---|
| 395 | 446 | * |
|---|
| 396 | 447 | * If debugfs is not enabled in the kernel, the value -%ENODEV will be |
|---|
| 397 | 448 | * returned. |
|---|
| .. | .. |
|---|
| 427 | 478 | * debugfs core. |
|---|
| 428 | 479 | * |
|---|
| 429 | 480 | * It is your responsibility to protect your struct file_operation |
|---|
| 430 | | - * methods against file removals by means of debugfs_use_file_start() |
|---|
| 431 | | - * and debugfs_use_file_finish(). ->open() is still protected by |
|---|
| 481 | + * methods against file removals by means of debugfs_file_get() |
|---|
| 482 | + * and debugfs_file_put(). ->open() is still protected by |
|---|
| 432 | 483 | * debugfs though. |
|---|
| 433 | 484 | * |
|---|
| 434 | 485 | * Any struct file_operations defined by means of |
|---|
| .. | .. |
|---|
| 465 | 516 | * wide range of flexibility in creating a file, or a directory (if you want |
|---|
| 466 | 517 | * to create a directory, the debugfs_create_dir() function is |
|---|
| 467 | 518 | * recommended to be used instead.) |
|---|
| 468 | | - * |
|---|
| 469 | | - * This function will return a pointer to a dentry if it succeeds. This |
|---|
| 470 | | - * pointer must be passed to the debugfs_remove() function when the file is |
|---|
| 471 | | - * to be removed (no automatic cleanup happens if your module is unloaded, |
|---|
| 472 | | - * you are responsible here.) If an error occurs, %NULL will be returned. |
|---|
| 473 | | - * |
|---|
| 474 | | - * If debugfs is not enabled in the kernel, the value -%ENODEV will be |
|---|
| 475 | | - * returned. |
|---|
| 476 | 519 | */ |
|---|
| 477 | | -struct dentry *debugfs_create_file_size(const char *name, umode_t mode, |
|---|
| 478 | | - struct dentry *parent, void *data, |
|---|
| 479 | | - const struct file_operations *fops, |
|---|
| 480 | | - loff_t file_size) |
|---|
| 520 | +void debugfs_create_file_size(const char *name, umode_t mode, |
|---|
| 521 | + struct dentry *parent, void *data, |
|---|
| 522 | + const struct file_operations *fops, |
|---|
| 523 | + loff_t file_size) |
|---|
| 481 | 524 | { |
|---|
| 482 | 525 | struct dentry *de = debugfs_create_file(name, mode, parent, data, fops); |
|---|
| 483 | 526 | |
|---|
| 484 | | - if (de) |
|---|
| 527 | + if (!IS_ERR(de)) |
|---|
| 485 | 528 | d_inode(de)->i_size = file_size; |
|---|
| 486 | | - return de; |
|---|
| 487 | 529 | } |
|---|
| 488 | 530 | EXPORT_SYMBOL_GPL(debugfs_create_file_size); |
|---|
| 489 | 531 | |
|---|
| .. | .. |
|---|
| 500 | 542 | * This function will return a pointer to a dentry if it succeeds. This |
|---|
| 501 | 543 | * pointer must be passed to the debugfs_remove() function when the file is |
|---|
| 502 | 544 | * to be removed (no automatic cleanup happens if your module is unloaded, |
|---|
| 503 | | - * you are responsible here.) If an error occurs, %NULL will be returned. |
|---|
| 545 | + * you are responsible here.) If an error occurs, ERR_PTR(-ERROR) will be |
|---|
| 546 | + * returned. |
|---|
| 504 | 547 | * |
|---|
| 505 | 548 | * If debugfs is not enabled in the kernel, the value -%ENODEV will be |
|---|
| 506 | 549 | * returned. |
|---|
| .. | .. |
|---|
| 511 | 554 | struct inode *inode; |
|---|
| 512 | 555 | |
|---|
| 513 | 556 | if (IS_ERR(dentry)) |
|---|
| 514 | | - return NULL; |
|---|
| 557 | + return dentry; |
|---|
| 558 | + |
|---|
| 559 | + if (!(debugfs_allow & DEBUGFS_ALLOW_API)) { |
|---|
| 560 | + failed_creating(dentry); |
|---|
| 561 | + return ERR_PTR(-EPERM); |
|---|
| 562 | + } |
|---|
| 515 | 563 | |
|---|
| 516 | 564 | inode = debugfs_get_inode(dentry->d_sb); |
|---|
| 517 | | - if (unlikely(!inode)) |
|---|
| 565 | + if (unlikely(!inode)) { |
|---|
| 566 | + pr_err("out of free dentries, can not create directory '%s'\n", |
|---|
| 567 | + name); |
|---|
| 518 | 568 | return failed_creating(dentry); |
|---|
| 569 | + } |
|---|
| 519 | 570 | |
|---|
| 520 | 571 | inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; |
|---|
| 521 | | - inode->i_op = &simple_dir_inode_operations; |
|---|
| 572 | + inode->i_op = &debugfs_dir_inode_operations; |
|---|
| 522 | 573 | inode->i_fop = &simple_dir_operations; |
|---|
| 523 | 574 | |
|---|
| 524 | 575 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ |
|---|
| .. | .. |
|---|
| 550 | 601 | struct inode *inode; |
|---|
| 551 | 602 | |
|---|
| 552 | 603 | if (IS_ERR(dentry)) |
|---|
| 553 | | - return NULL; |
|---|
| 604 | + return dentry; |
|---|
| 605 | + |
|---|
| 606 | + if (!(debugfs_allow & DEBUGFS_ALLOW_API)) { |
|---|
| 607 | + failed_creating(dentry); |
|---|
| 608 | + return ERR_PTR(-EPERM); |
|---|
| 609 | + } |
|---|
| 554 | 610 | |
|---|
| 555 | 611 | inode = debugfs_get_inode(dentry->d_sb); |
|---|
| 556 | | - if (unlikely(!inode)) |
|---|
| 612 | + if (unlikely(!inode)) { |
|---|
| 613 | + pr_err("out of free dentries, can not create automount '%s'\n", |
|---|
| 614 | + name); |
|---|
| 557 | 615 | return failed_creating(dentry); |
|---|
| 616 | + } |
|---|
| 558 | 617 | |
|---|
| 559 | 618 | make_empty_dir_inode(inode); |
|---|
| 560 | 619 | inode->i_flags |= S_AUTOMOUNT; |
|---|
| .. | .. |
|---|
| 586 | 645 | * This function will return a pointer to a dentry if it succeeds. This |
|---|
| 587 | 646 | * pointer must be passed to the debugfs_remove() function when the symbolic |
|---|
| 588 | 647 | * link is to be removed (no automatic cleanup happens if your module is |
|---|
| 589 | | - * unloaded, you are responsible here.) If an error occurs, %NULL will be |
|---|
| 590 | | - * returned. |
|---|
| 648 | + * unloaded, you are responsible here.) If an error occurs, ERR_PTR(-ERROR) |
|---|
| 649 | + * will be returned. |
|---|
| 591 | 650 | * |
|---|
| 592 | 651 | * If debugfs is not enabled in the kernel, the value -%ENODEV will be |
|---|
| 593 | 652 | * returned. |
|---|
| .. | .. |
|---|
| 599 | 658 | struct inode *inode; |
|---|
| 600 | 659 | char *link = kstrdup(target, GFP_KERNEL); |
|---|
| 601 | 660 | if (!link) |
|---|
| 602 | | - return NULL; |
|---|
| 661 | + return ERR_PTR(-ENOMEM); |
|---|
| 603 | 662 | |
|---|
| 604 | 663 | dentry = start_creating(name, parent); |
|---|
| 605 | 664 | if (IS_ERR(dentry)) { |
|---|
| 606 | 665 | kfree(link); |
|---|
| 607 | | - return NULL; |
|---|
| 666 | + return dentry; |
|---|
| 608 | 667 | } |
|---|
| 609 | 668 | |
|---|
| 610 | 669 | inode = debugfs_get_inode(dentry->d_sb); |
|---|
| 611 | 670 | if (unlikely(!inode)) { |
|---|
| 671 | + pr_err("out of free dentries, can not create symlink '%s'\n", |
|---|
| 672 | + name); |
|---|
| 612 | 673 | kfree(link); |
|---|
| 613 | 674 | return failed_creating(dentry); |
|---|
| 614 | 675 | } |
|---|
| 615 | 676 | inode->i_mode = S_IFLNK | S_IRWXUGO; |
|---|
| 616 | | - inode->i_op = &simple_symlink_inode_operations; |
|---|
| 677 | + inode->i_op = &debugfs_symlink_inode_operations; |
|---|
| 617 | 678 | inode->i_link = link; |
|---|
| 618 | 679 | d_instantiate(dentry, inode); |
|---|
| 619 | 680 | return end_creating(dentry); |
|---|
| 620 | 681 | } |
|---|
| 621 | 682 | EXPORT_SYMBOL_GPL(debugfs_create_symlink); |
|---|
| 622 | 683 | |
|---|
| 623 | | -static void __debugfs_remove_file(struct dentry *dentry, struct dentry *parent) |
|---|
| 684 | +static void __debugfs_file_removed(struct dentry *dentry) |
|---|
| 624 | 685 | { |
|---|
| 625 | 686 | struct debugfs_fsdata *fsd; |
|---|
| 626 | | - |
|---|
| 627 | | - simple_unlink(d_inode(parent), dentry); |
|---|
| 628 | | - d_delete(dentry); |
|---|
| 629 | 687 | |
|---|
| 630 | 688 | /* |
|---|
| 631 | 689 | * Paired with the closing smp_mb() implied by a successful |
|---|
| .. | .. |
|---|
| 641 | 699 | wait_for_completion(&fsd->active_users_drained); |
|---|
| 642 | 700 | } |
|---|
| 643 | 701 | |
|---|
| 644 | | -static int __debugfs_remove(struct dentry *dentry, struct dentry *parent) |
|---|
| 702 | +static void remove_one(struct dentry *victim) |
|---|
| 645 | 703 | { |
|---|
| 646 | | - int ret = 0; |
|---|
| 647 | | - |
|---|
| 648 | | - if (simple_positive(dentry)) { |
|---|
| 649 | | - dget(dentry); |
|---|
| 650 | | - if (!d_is_reg(dentry)) { |
|---|
| 651 | | - if (d_is_dir(dentry)) |
|---|
| 652 | | - ret = simple_rmdir(d_inode(parent), dentry); |
|---|
| 653 | | - else |
|---|
| 654 | | - simple_unlink(d_inode(parent), dentry); |
|---|
| 655 | | - if (!ret) |
|---|
| 656 | | - d_delete(dentry); |
|---|
| 657 | | - } else { |
|---|
| 658 | | - __debugfs_remove_file(dentry, parent); |
|---|
| 659 | | - } |
|---|
| 660 | | - dput(dentry); |
|---|
| 661 | | - } |
|---|
| 662 | | - return ret; |
|---|
| 704 | + if (d_is_reg(victim)) |
|---|
| 705 | + __debugfs_file_removed(victim); |
|---|
| 706 | + simple_release_fs(&debugfs_mount, &debugfs_mount_count); |
|---|
| 663 | 707 | } |
|---|
| 664 | 708 | |
|---|
| 665 | 709 | /** |
|---|
| 666 | | - * debugfs_remove - removes a file or directory from the debugfs filesystem |
|---|
| 667 | | - * @dentry: a pointer to a the dentry of the file or directory to be |
|---|
| 668 | | - * removed. If this parameter is NULL or an error value, nothing |
|---|
| 669 | | - * will be done. |
|---|
| 670 | | - * |
|---|
| 671 | | - * This function removes a file or directory in debugfs that was previously |
|---|
| 672 | | - * created with a call to another debugfs function (like |
|---|
| 673 | | - * debugfs_create_file() or variants thereof.) |
|---|
| 674 | | - * |
|---|
| 675 | | - * This function is required to be called in order for the file to be |
|---|
| 676 | | - * removed, no automatic cleanup of files will happen when a module is |
|---|
| 677 | | - * removed, you are responsible here. |
|---|
| 678 | | - */ |
|---|
| 679 | | -void debugfs_remove(struct dentry *dentry) |
|---|
| 680 | | -{ |
|---|
| 681 | | - struct dentry *parent; |
|---|
| 682 | | - int ret; |
|---|
| 683 | | - |
|---|
| 684 | | - if (IS_ERR_OR_NULL(dentry)) |
|---|
| 685 | | - return; |
|---|
| 686 | | - |
|---|
| 687 | | - parent = dentry->d_parent; |
|---|
| 688 | | - inode_lock(d_inode(parent)); |
|---|
| 689 | | - ret = __debugfs_remove(dentry, parent); |
|---|
| 690 | | - inode_unlock(d_inode(parent)); |
|---|
| 691 | | - if (!ret) |
|---|
| 692 | | - simple_release_fs(&debugfs_mount, &debugfs_mount_count); |
|---|
| 693 | | -} |
|---|
| 694 | | -EXPORT_SYMBOL_GPL(debugfs_remove); |
|---|
| 695 | | - |
|---|
| 696 | | -/** |
|---|
| 697 | | - * debugfs_remove_recursive - recursively removes a directory |
|---|
| 710 | + * debugfs_remove - recursively removes a directory |
|---|
| 698 | 711 | * @dentry: a pointer to a the dentry of the directory to be removed. If this |
|---|
| 699 | 712 | * parameter is NULL or an error value, nothing will be done. |
|---|
| 700 | 713 | * |
|---|
| .. | .. |
|---|
| 706 | 719 | * removed, no automatic cleanup of files will happen when a module is |
|---|
| 707 | 720 | * removed, you are responsible here. |
|---|
| 708 | 721 | */ |
|---|
| 709 | | -void debugfs_remove_recursive(struct dentry *dentry) |
|---|
| 722 | +void debugfs_remove(struct dentry *dentry) |
|---|
| 710 | 723 | { |
|---|
| 711 | | - struct dentry *child, *parent; |
|---|
| 712 | | - |
|---|
| 713 | 724 | if (IS_ERR_OR_NULL(dentry)) |
|---|
| 714 | 725 | return; |
|---|
| 715 | 726 | |
|---|
| 716 | | - parent = dentry; |
|---|
| 717 | | - down: |
|---|
| 718 | | - inode_lock(d_inode(parent)); |
|---|
| 719 | | - loop: |
|---|
| 720 | | - /* |
|---|
| 721 | | - * The parent->d_subdirs is protected by the d_lock. Outside that |
|---|
| 722 | | - * lock, the child can be unlinked and set to be freed which can |
|---|
| 723 | | - * use the d_u.d_child as the rcu head and corrupt this list. |
|---|
| 724 | | - */ |
|---|
| 725 | | - spin_lock(&parent->d_lock); |
|---|
| 726 | | - list_for_each_entry(child, &parent->d_subdirs, d_child) { |
|---|
| 727 | | - if (!simple_positive(child)) |
|---|
| 728 | | - continue; |
|---|
| 729 | | - |
|---|
| 730 | | - /* perhaps simple_empty(child) makes more sense */ |
|---|
| 731 | | - if (!list_empty(&child->d_subdirs)) { |
|---|
| 732 | | - spin_unlock(&parent->d_lock); |
|---|
| 733 | | - inode_unlock(d_inode(parent)); |
|---|
| 734 | | - parent = child; |
|---|
| 735 | | - goto down; |
|---|
| 736 | | - } |
|---|
| 737 | | - |
|---|
| 738 | | - spin_unlock(&parent->d_lock); |
|---|
| 739 | | - |
|---|
| 740 | | - if (!__debugfs_remove(child, parent)) |
|---|
| 741 | | - simple_release_fs(&debugfs_mount, &debugfs_mount_count); |
|---|
| 742 | | - |
|---|
| 743 | | - /* |
|---|
| 744 | | - * The parent->d_lock protects agaist child from unlinking |
|---|
| 745 | | - * from d_subdirs. When releasing the parent->d_lock we can |
|---|
| 746 | | - * no longer trust that the next pointer is valid. |
|---|
| 747 | | - * Restart the loop. We'll skip this one with the |
|---|
| 748 | | - * simple_positive() check. |
|---|
| 749 | | - */ |
|---|
| 750 | | - goto loop; |
|---|
| 751 | | - } |
|---|
| 752 | | - spin_unlock(&parent->d_lock); |
|---|
| 753 | | - |
|---|
| 754 | | - inode_unlock(d_inode(parent)); |
|---|
| 755 | | - child = parent; |
|---|
| 756 | | - parent = parent->d_parent; |
|---|
| 757 | | - inode_lock(d_inode(parent)); |
|---|
| 758 | | - |
|---|
| 759 | | - if (child != dentry) |
|---|
| 760 | | - /* go up */ |
|---|
| 761 | | - goto loop; |
|---|
| 762 | | - |
|---|
| 763 | | - if (!__debugfs_remove(child, parent)) |
|---|
| 764 | | - simple_release_fs(&debugfs_mount, &debugfs_mount_count); |
|---|
| 765 | | - inode_unlock(d_inode(parent)); |
|---|
| 727 | + simple_pin_fs(&debug_fs_type, &debugfs_mount, &debugfs_mount_count); |
|---|
| 728 | + simple_recursive_removal(dentry, remove_one); |
|---|
| 729 | + simple_release_fs(&debugfs_mount, &debugfs_mount_count); |
|---|
| 766 | 730 | } |
|---|
| 767 | | -EXPORT_SYMBOL_GPL(debugfs_remove_recursive); |
|---|
| 731 | +EXPORT_SYMBOL_GPL(debugfs_remove); |
|---|
| 732 | + |
|---|
| 733 | +/** |
|---|
| 734 | + * debugfs_lookup_and_remove - lookup a directory or file and recursively remove it |
|---|
| 735 | + * @name: a pointer to a string containing the name of the item to look up. |
|---|
| 736 | + * @parent: a pointer to the parent dentry of the item. |
|---|
| 737 | + * |
|---|
| 738 | + * This is the equlivant of doing something like |
|---|
| 739 | + * debugfs_remove(debugfs_lookup(..)) but with the proper reference counting |
|---|
| 740 | + * handled for the directory being looked up. |
|---|
| 741 | + */ |
|---|
| 742 | +void debugfs_lookup_and_remove(const char *name, struct dentry *parent) |
|---|
| 743 | +{ |
|---|
| 744 | + struct dentry *dentry; |
|---|
| 745 | + |
|---|
| 746 | + dentry = debugfs_lookup(name, parent); |
|---|
| 747 | + if (!dentry) |
|---|
| 748 | + return; |
|---|
| 749 | + |
|---|
| 750 | + debugfs_remove(dentry); |
|---|
| 751 | + dput(dentry); |
|---|
| 752 | +} |
|---|
| 753 | +EXPORT_SYMBOL_GPL(debugfs_lookup_and_remove); |
|---|
| 768 | 754 | |
|---|
| 769 | 755 | /** |
|---|
| 770 | 756 | * debugfs_rename - rename a file/directory in the debugfs filesystem |
|---|
| .. | .. |
|---|
| 821 | 807 | goto exit; |
|---|
| 822 | 808 | } |
|---|
| 823 | 809 | d_move(old_dentry, dentry); |
|---|
| 824 | | - fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name, |
|---|
| 810 | + fsnotify_move(d_inode(old_dir), d_inode(new_dir), &old_name.name, |
|---|
| 825 | 811 | d_is_dir(old_dentry), |
|---|
| 826 | 812 | NULL, old_dentry); |
|---|
| 827 | 813 | release_dentry_name_snapshot(&old_name); |
|---|
| .. | .. |
|---|
| 832 | 818 | if (dentry && !IS_ERR(dentry)) |
|---|
| 833 | 819 | dput(dentry); |
|---|
| 834 | 820 | unlock_rename(new_dir, old_dir); |
|---|
| 835 | | - return NULL; |
|---|
| 821 | + if (IS_ERR(dentry)) |
|---|
| 822 | + return dentry; |
|---|
| 823 | + return ERR_PTR(-EINVAL); |
|---|
| 836 | 824 | } |
|---|
| 837 | 825 | EXPORT_SYMBOL_GPL(debugfs_rename); |
|---|
| 838 | 826 | |
|---|
| .. | .. |
|---|
| 845 | 833 | } |
|---|
| 846 | 834 | EXPORT_SYMBOL_GPL(debugfs_initialized); |
|---|
| 847 | 835 | |
|---|
| 836 | +static int __init debugfs_kernel(char *str) |
|---|
| 837 | +{ |
|---|
| 838 | + if (str) { |
|---|
| 839 | + if (!strcmp(str, "on")) |
|---|
| 840 | + debugfs_allow = DEBUGFS_ALLOW_API | DEBUGFS_ALLOW_MOUNT; |
|---|
| 841 | + else if (!strcmp(str, "no-mount")) |
|---|
| 842 | + debugfs_allow = DEBUGFS_ALLOW_API; |
|---|
| 843 | + else if (!strcmp(str, "off")) |
|---|
| 844 | + debugfs_allow = 0; |
|---|
| 845 | + } |
|---|
| 846 | + |
|---|
| 847 | + return 0; |
|---|
| 848 | +} |
|---|
| 849 | +early_param("debugfs", debugfs_kernel); |
|---|
| 848 | 850 | static int __init debugfs_init(void) |
|---|
| 849 | 851 | { |
|---|
| 850 | 852 | int retval; |
|---|
| 853 | + |
|---|
| 854 | + if (!(debugfs_allow & DEBUGFS_ALLOW_MOUNT)) |
|---|
| 855 | + return -EPERM; |
|---|
| 851 | 856 | |
|---|
| 852 | 857 | retval = sysfs_create_mount_point(kernel_kobj, "debug"); |
|---|
| 853 | 858 | if (retval) |
|---|
| .. | .. |
|---|
| 862 | 867 | return retval; |
|---|
| 863 | 868 | } |
|---|
| 864 | 869 | core_initcall(debugfs_init); |
|---|
| 865 | | - |
|---|