]> git.neil.brown.name Git - LaFS.git/commitdiff
Flush orphans before failing rmdir due to not being empty.
authorNeilBrown <neilb@suse.de>
Wed, 2 Sep 2009 00:09:51 +0000 (10:09 +1000)
committerNeilBrown <neilb@suse.de>
Wed, 2 Sep 2009 00:09:51 +0000 (10:09 +1000)
Before we can be sure if a directory is empty, we need to
flush and orphans that might be holding space in the directory
in use.  Only then can a check on the file size be meaningful.

dir.c
lafs.h
orphan.c

diff --git a/dir.c b/dir.c
index ccaa33b1334b114f381c82e6fe5d7429e58f5227..7ee11b5dc2ce188bbefcf8fc5acdd85b721d1103 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -758,9 +758,28 @@ lafs_rmdir(struct inode *dir, struct dentry *de)
        struct datablock *inodb;
        int err;
 
-       /* FIXME run orphan changes */
-       if (inode->i_size || inode->i_nlink > 2)
+       if (inode->i_nlink > 2)
                return -ENOTEMPTY;
+       if (inode->i_size) {
+               /* Probably not empty, but it could be that we
+                * just need to wait for orphans the clear.
+                * They cannot clear while we hold i_mutex, so
+                * we have to run it ourselves.
+                */
+               struct datablock *db;
+               DEFINE_WAIT(wq);
+               while ((db = lafs_find_orphan(inode))) {
+                       prepare_to_wait(&fs->async_complete, &wq,
+                                       TASK_UNINTERRUPTIBLE);
+                       lafs_dir_handle_orphan(db);
+                       if (lafs_drop_orphan(fs, db) == 0)
+                               /* still an orphan, need to wait */
+                               schedule();
+               }
+               finish_wait(&fs->async_complete, &wq);
+               if (inode->i_size)
+                       return -ENOTEMPTY;
+       }
 
        dprintk("rmdir %s\n", de->d_name.name);
 
diff --git a/lafs.h b/lafs.h
index a82e394b5799c98ba6f461da4b5b505a550266a8..ed7a8fce2d2ef4c56f4d75031c5df2122e8eea77 100644 (file)
--- a/lafs.h
+++ b/lafs.h
@@ -585,6 +585,7 @@ int lafs_make_orphan_nb(struct fs *fs, struct datablock *db);
 void lafs_orphan_release(struct fs *fs, struct datablock *b);
 long lafs_run_orphans(struct fs *fs);
 int lafs_drop_orphan(struct fs *fs, struct datablock *db);
+struct datablock *lafs_find_orphan(struct inode *ino);
 
 /* Segment.c */
 int lafs_prealloc(struct block *b, int type);
index 3cbbf55a1f26fcf21fdc7e429e4bbd26b9a7b8f0..dd1c89764ed081093d9e4dadd3b13535f007a828 100644 (file)
--- a/orphan.c
+++ b/orphan.c
@@ -535,3 +535,25 @@ int lafs_drop_orphan(struct fs *fs, struct datablock *db)
                return 1;
        }
 }
+
+struct datablock *lafs_find_orphan(struct inode *ino)
+{
+       /* I could walk the child tree of the inode, or
+        * walk the pending_orphan list looking for an
+        * orphan for this inode.
+        * The latter seems easier.
+        * Performance will be quadratic in the size of the
+        * orphan list, so we could possibly consider
+        * improvements later.
+        */
+       struct fs *fs = fs_from_inode(ino);
+       struct datablock *db;
+
+       spin_lock(&fs->lock);
+       list_for_each_entry(db, &fs->pending_orphans, orphans)
+               if (db->b.inode == ino) {
+                       spin_unlock(&fs->lock);
+                       return db;
+               }
+       return NULL;
+}