aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/dir.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2009-11-08 02:27:01 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-11 14:24:53 -0500
commite8f077c8831528e2ec1ea6c8ba090e405fdcd0b7 (patch)
tree707b25f1368d59d7d945ae46d2139f6592120a8e /fs/sysfs/dir.c
parent28a027cfc0d527fcc31bfeac1d94d572c68847d1 (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/dir.c')
-rw-r--r--fs/sysfs/dir.c73
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
301static 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
307static 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);
319out_valid:
320 return 1;
321out_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
301static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode) 341static 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
309static const struct dentry_operations sysfs_dentry_ops = { 349static 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 */
537static void sysfs_drop_dentry(struct sysfs_dirent *sd) 579static 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 */
553repeat:
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);