]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/libfs.c
Merge tag 'io_uring-5.6-2020-02-05' of git://git.kernel.dk/linux-block
[linux.git] / fs / libfs.c
index 1463b038ffc4e6f34dd5e213820df57a67106640..c686bd9caac677e653a682ef2ed00c23b381f084 100644 (file)
@@ -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,
 };