]> git.neil.brown.name Git - LaFS.git/commitdiff
Check if dirblock can be orphan before making it one.
authorNeilBrown <neilb@suse.de>
Sat, 14 Aug 2010 12:25:38 +0000 (22:25 +1000)
committerNeilBrown <neilb@suse.de>
Sat, 14 Aug 2010 12:25:38 +0000 (22:25 +1000)
Only certain sorts of deletions can make a directory block
into and orphan - check them out before committing resources.

Signed-off-by: NeilBrown <neilb@suse.de>
dir.c

diff --git a/dir.c b/dir.c
index b2df67aafb458cad3c5506baeb246e713abecfb3..50a7374c5eb979448505a09fade5aaa69ac0f458 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -185,7 +185,6 @@ struct dirop_handle {
  * This is split into pre_create and commit_create
  * We already know that the name doesn't exist so a lookup will fail,
  * but will find the right place in the tree.
- * FIXME cache the results of the prior lookup??
  * pre_create allocates blocks as needed and stores info in the dirop_handle.
  * commit_create finalises the create and cannot fail.
  */
@@ -387,7 +386,7 @@ dir_delete_prepare(struct fs *fs, struct inode *dir,
               const char *name, int nlen, struct dirop_handle *doh)
 {
        struct datablock *dirblk;
-       int err;
+       int orphan = 0;
 
        doh->dirent_block =
                dirblk = dir_lookup_blk(dir, name, nlen, &doh->index,
@@ -408,11 +407,33 @@ dir_delete_prepare(struct fs *fs, struct inode *dir,
        /* i_mutex protect us now, so don't need to maintain the lock */
        lafs_iounlock_block(&dirblk->b);
 
-       /* FIXME should I check if the orphanage is needed
-        * before committing this block to it?
+       /* Only make this block an orphan if there is a real
+        * possibilitiy.
+        * i.e. one of
+        *    We found the last possible entry
+        *    We found the first entry
+        *    We found the only entry (in which case we found the first)
+        *    First entry is deleted
         */
-       err = lafs_make_orphan(fs, doh->dirent_block, dir);
-       return err;
+       if (((doh->hash+1) & MaxDirHash) == doh->dirent_block->b.fileaddr)
+               /* Last possible entry is being remove */
+               orphan=1;
+       if (!orphan) {
+               u32 seed = LAFSI(dir)->md.file.seed;
+               u8 firstpiece = 0;
+               struct dir_ent de;
+               char bits = dir->i_blkbits - 8;
+               char *buf = map_dblock(dirblk);
+               lafs_dir_find(buf, bits, seed, 0, &firstpiece);
+               if (doh->index == firstpiece ||
+                   lafs_dir_extract(buf, bits, &de,
+                                    firstpiece, NULL)->target == 0)
+                       orphan = 1;
+               unmap_dblock(dirblk, buf);
+       }
+       if (orphan)
+               return lafs_make_orphan(fs, doh->dirent_block, dir);
+       return 0;
 }
 
 static void
@@ -1382,7 +1403,7 @@ int lafs_dir_handle_orphan(struct datablock *b)
         * and remove the unanchor.
         */
        lafs_dir_find(buf, bits, seed, 0, &firstpiece);
-       hash = LAFSI(dir)->md.file.seed;
+       hash = seed;
        if (firstpiece &&
            lafs_dir_extract(buf, bits, &de, firstpiece, &hash)->target == 0 &&
            lafs_dir_find(buf, bits, seed, hash+1, &piece) == 0) {