diff options
Diffstat (limited to 'fs/libfs.c')
| -rw-r--r-- | fs/libfs.c | 70 | 
1 files changed, 70 insertions, 0 deletions
diff --git a/fs/libfs.c b/fs/libfs.c index 1463b038ffc4..c686bd9caac6 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -19,6 +19,7 @@  #include <linux/buffer_head.h> /* sync_mapping_buffers */  #include <linux/fs_context.h>  #include <linux/pseudo_fs.h> +#include <linux/fsnotify.h>  #include <linux/uaccess.h> @@ -239,6 +240,75 @@ const struct inode_operations simple_dir_inode_operations = {  };  EXPORT_SYMBOL(simple_dir_inode_operations); +static struct dentry *find_next_child(struct dentry *parent, struct dentry *prev) +{ +	struct dentry *child = NULL; +	struct list_head *p = prev ? &prev->d_child : &parent->d_subdirs; + +	spin_lock(&parent->d_lock); +	while ((p = p->next) != &parent->d_subdirs) { +		struct dentry *d = container_of(p, struct dentry, d_child); +		if (simple_positive(d)) { +			spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); +			if (simple_positive(d)) +				child = dget_dlock(d); +			spin_unlock(&d->d_lock); +			if (likely(child)) +				break; +		} +	} +	spin_unlock(&parent->d_lock); +	dput(prev); +	return child; +} + +void simple_recursive_removal(struct dentry *dentry, +                              void (*callback)(struct dentry *)) +{ +	struct dentry *this = dget(dentry); +	while (true) { +		struct dentry *victim = NULL, *child; +		struct inode *inode = this->d_inode; + +		inode_lock(inode); +		if (d_is_dir(this)) +			inode->i_flags |= S_DEAD; +		while ((child = find_next_child(this, victim)) == NULL) { +			// kill and ascend +			// update metadata while it's still locked +			inode->i_ctime = current_time(inode); +			clear_nlink(inode); +			inode_unlock(inode); +			victim = this; +			this = this->d_parent; +			inode = this->d_inode; +			inode_lock(inode); +			if (simple_positive(victim)) { +				d_invalidate(victim);	// avoid lost mounts +				if (d_is_dir(victim)) +					fsnotify_rmdir(inode, victim); +				else +					fsnotify_unlink(inode, victim); +				if (callback) +					callback(victim); +				dput(victim);		// unpin it +			} +			if (victim == dentry) { +				inode->i_ctime = inode->i_mtime = +					current_time(inode); +				if (d_is_dir(dentry)) +					drop_nlink(inode); +				inode_unlock(inode); +				dput(dentry); +				return; +			} +		} +		inode_unlock(inode); +		this = child; +	} +} +EXPORT_SYMBOL(simple_recursive_removal); +  static const struct super_operations simple_super_operations = {  	.statfs		= simple_statfs,  };  |