diff options
| author | Eric W. Biederman <ebiederm@xmission.com> | 2009-11-08 02:27:01 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 14:24:53 -0500 |
| commit | e8f077c8831528e2ec1ea6c8ba090e405fdcd0b7 (patch) | |
| tree | 707b25f1368d59d7d945ae46d2139f6592120a8e /fs/sysfs | |
| parent | 28a027cfc0d527fcc31bfeac1d94d572c68847d1 (diff) | |
sysfs: Use dentry_ops instead of directly playing with the dcache
Calling d_drop unconditionally when a sysfs_dirent is deleted has
the potential to leak mounts, so instead implement dentry delete
and revalidate operations that cause sysfs dentries to be removed
at the appropriate time.
Acked-by: Tejun Heo <tj@kernel.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/sysfs')
| -rw-r--r-- | fs/sysfs/dir.c | 73 |
1 files changed, 46 insertions, 27 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 01a9a64ecd4e..9ee130be03be 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
| @@ -298,6 +298,46 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) | |||
| 298 | goto repeat; | 298 | goto repeat; |
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | static int sysfs_dentry_delete(struct dentry *dentry) | ||
| 302 | { | ||
| 303 | struct sysfs_dirent *sd = dentry->d_fsdata; | ||
| 304 | return !!(sd->s_flags & SYSFS_FLAG_REMOVED); | ||
| 305 | } | ||
| 306 | |||
| 307 | static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) | ||
| 308 | { | ||
| 309 | struct sysfs_dirent *sd = dentry->d_fsdata; | ||
| 310 | int is_dir; | ||
| 311 | |||
| 312 | mutex_lock(&sysfs_mutex); | ||
| 313 | |||
| 314 | /* The sysfs dirent has been deleted */ | ||
| 315 | if (sd->s_flags & SYSFS_FLAG_REMOVED) | ||
| 316 | goto out_bad; | ||
| 317 | |||
| 318 | mutex_unlock(&sysfs_mutex); | ||
| 319 | out_valid: | ||
| 320 | return 1; | ||
| 321 | out_bad: | ||
| 322 | /* Remove the dentry from the dcache hashes. | ||
| 323 | * If this is a deleted dentry we use d_drop instead of d_delete | ||
| 324 | * so sysfs doesn't need to cope with negative dentries. | ||
| 325 | */ | ||
| 326 | is_dir = (sysfs_type(sd) == SYSFS_DIR); | ||
| 327 | mutex_unlock(&sysfs_mutex); | ||
| 328 | if (is_dir) { | ||
| 329 | /* If we have submounts we must allow the vfs caches | ||
| 330 | * to lie about the state of the filesystem to prevent | ||
| 331 | * leaks and other nasty things. | ||
| 332 | */ | ||
| 333 | if (have_submounts(dentry)) | ||
| 334 | goto out_valid; | ||
| 335 | shrink_dcache_parent(dentry); | ||
| 336 | } | ||
| 337 | d_drop(dentry); | ||
| 338 | return 0; | ||
| 339 | } | ||
| 340 | |||
| 301 | static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode) | 341 | static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode) |
| 302 | { | 342 | { |
| 303 | struct sysfs_dirent * sd = dentry->d_fsdata; | 343 | struct sysfs_dirent * sd = dentry->d_fsdata; |
| @@ -307,6 +347,8 @@ static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode) | |||
| 307 | } | 347 | } |
| 308 | 348 | ||
| 309 | static const struct dentry_operations sysfs_dentry_ops = { | 349 | static const struct dentry_operations sysfs_dentry_ops = { |
| 350 | .d_revalidate = sysfs_dentry_revalidate, | ||
| 351 | .d_delete = sysfs_dentry_delete, | ||
| 310 | .d_iput = sysfs_dentry_iput, | 352 | .d_iput = sysfs_dentry_iput, |
| 311 | }; | 353 | }; |
| 312 | 354 | ||
| @@ -527,44 +569,21 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) | |||
| 527 | } | 569 | } |
| 528 | 570 | ||
| 529 | /** | 571 | /** |
| 530 | * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent | 572 | * sysfs_dec_nlink - Decrement link count for the specified sysfs_dirent |
| 531 | * @sd: target sysfs_dirent | 573 | * @sd: target sysfs_dirent |
| 532 | * | 574 | * |
| 533 | * Drop dentry for @sd. @sd must have been unlinked from its | 575 | * Decrement nlink for @sd. @sd must have been unlinked from its |
| 534 | * parent on entry to this function such that it can't be looked | 576 | * parent on entry to this function such that it can't be looked |
| 535 | * up anymore. | 577 | * up anymore. |
| 536 | */ | 578 | */ |
| 537 | static void sysfs_drop_dentry(struct sysfs_dirent *sd) | 579 | static void sysfs_dec_nlink(struct sysfs_dirent *sd) |
| 538 | { | 580 | { |
| 539 | struct inode *inode; | 581 | struct inode *inode; |
| 540 | struct dentry *dentry; | ||
| 541 | 582 | ||
| 542 | inode = ilookup(sysfs_sb, sd->s_ino); | 583 | inode = ilookup(sysfs_sb, sd->s_ino); |
| 543 | if (!inode) | 584 | if (!inode) |
| 544 | return; | 585 | return; |
| 545 | 586 | ||
| 546 | /* Drop any existing dentries associated with sd. | ||
| 547 | * | ||
| 548 | * For the dentry to be properly freed we need to grab a | ||
| 549 | * reference to the dentry under the dcache lock, unhash it, | ||
| 550 | * and then put it. The playing with the dentry count allows | ||
| 551 | * dput to immediately free the dentry if it is not in use. | ||
| 552 | */ | ||
| 553 | repeat: | ||
| 554 | spin_lock(&dcache_lock); | ||
| 555 | list_for_each_entry(dentry, &inode->i_dentry, d_alias) { | ||
| 556 | if (d_unhashed(dentry)) | ||
| 557 | continue; | ||
| 558 | dget_locked(dentry); | ||
| 559 | spin_lock(&dentry->d_lock); | ||
| 560 | __d_drop(dentry); | ||
| 561 | spin_unlock(&dentry->d_lock); | ||
| 562 | spin_unlock(&dcache_lock); | ||
| 563 | dput(dentry); | ||
| 564 | goto repeat; | ||
| 565 | } | ||
| 566 | spin_unlock(&dcache_lock); | ||
| 567 | |||
| 568 | /* adjust nlink and update timestamp */ | 587 | /* adjust nlink and update timestamp */ |
| 569 | mutex_lock(&inode->i_mutex); | 588 | mutex_lock(&inode->i_mutex); |
| 570 | 589 | ||
| @@ -611,7 +630,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) | |||
| 611 | acxt->removed = sd->s_sibling; | 630 | acxt->removed = sd->s_sibling; |
| 612 | sd->s_sibling = NULL; | 631 | sd->s_sibling = NULL; |
| 613 | 632 | ||
| 614 | sysfs_drop_dentry(sd); | 633 | sysfs_dec_nlink(sd); |
| 615 | sysfs_deactivate(sd); | 634 | sysfs_deactivate(sd); |
| 616 | unmap_bin_file(sd); | 635 | unmap_bin_file(sd); |
| 617 | sysfs_put(sd); | 636 | sysfs_put(sd); |
