]> git.neil.brown.name Git - LaFS.git/commitdiff
roll: handle directory updates.
authorNeilBrown <neilb@suse.de>
Fri, 4 Mar 2011 01:47:30 +0000 (12:47 +1100)
committerNeilBrown <neilb@suse.de>
Fri, 4 Mar 2011 01:47:30 +0000 (12:47 +1100)
Handle directory updates found during roll-forward by making the
relevant change to the directory and inode.

This has not been tested yet!!

Signed-off-by: NeilBrown <neilb@suse.de>
dir.c
lafs.h
roll.c
state.h

diff --git a/dir.c b/dir.c
index ae1d083a906a1d78d9fe7f4193428db51d3bb34f..e795aaf33730c5a537044ceaf40bd16adcc49d07 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -618,6 +618,210 @@ static void dir_log_commit(struct update_handle *uh,
                                       name->len, name->name);
 }
 
+int
+lafs_dir_roll_mini(struct inode *dir, int handle, int dirop,
+                  u32 inum, char *name, int len)
+{
+       int err = 0;
+       struct dirop_handle doh, old_doh;
+       struct datablock *inodb = NULL, *olddb = NULL;
+       struct inode *inode = NULL;
+       struct rename_roll *rr = NULL, **rrp;
+       struct fs *fs = fs_from_inode(dir);
+       int last;
+
+       if (inum)
+               inode = lafs_iget(dir->i_sb, inum, SYNC);
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
+       if (!inode && dirop != DIROP_REN_TARGET)
+               return -EINVAL;
+
+       switch (dirop) {
+       default:
+               err = -EINVAL;
+               break;
+       case DIROP_LINK:
+               /* name doesn't exist - we create it. */
+               err = dir_create_prepare(fs, dir, name, len,
+                                        inum, mode_to_dt(inode->i_mode), &doh);
+               inodb = lafs_inode_dblock(dir, SYNC, MKREF(roll_dir));
+               if (IS_ERR(inodb))
+                       err = PTR_ERR(inodb);
+
+               err = err ?: dir_create_pin(&doh);
+               err = err ?: lafs_pin_dblock(inodb, ReleaseSpace);
+               if (err < 0) {
+                       dir_create_abort(&doh);
+                       break;
+               }
+               inode_inc_link_count(inode);
+               lafs_inode_checkpin(inode);
+               lafs_dirty_dblock(inodb);
+               clear_bit(B_PinPending, &inodb->b.flags);
+               dir_create_commit(&doh, fs, dir, name, len,
+                                 inum, mode_to_dt(inode->i_mode));
+               err = 0;
+               break;
+
+       case DIROP_UNLINK:
+               /* Name exists, we need to remove it */
+               last = (inode->i_nlink == 1);
+               err = dir_delete_prepare(fs, dir, name, len, &doh);
+               inodb = lafs_inode_dblock(inode, SYNC, MKREF(roll_dir));
+               if (IS_ERR(inode))
+                       err = PTR_ERR(inodb);
+               if (last && !err)
+                       err = lafs_make_orphan(fs, inodb);
+               if (err) {
+                       dir_delete_abort(&doh);
+                       break;
+               }
+               lafs_iolock_block(&inodb->b);
+               set_bit(B_PinPending, &inodb->b.flags);
+               lafs_iounlock_block(&inodb->b);
+               err = dir_delete_pin(&doh);
+               err = err ?: lafs_pin_dblock(inodb, ReleaseSpace);
+               if (err < 0) {
+                       dir_delete_abort(&doh);
+                       break;
+               }
+               inode_dec_link_count(inode);
+               dir_delete_commit(&doh, fs, dir, name, len);
+               lafs_inode_checkpin(inode);
+               lafs_dirty_dblock(inodb);
+               clear_bit(B_PinPending, &inodb->b.flags);
+               err = 0;
+               break;
+
+       case DIROP_REN_SOURCE:
+               rr = kmalloc(sizeof(*rr) + len, GFP_KERNEL);
+               if (!rr) {
+                       err = -ENOMEM;
+                       break;
+               }
+               rr->next = fs->pending_renames;
+               rr->key = handle;
+               rr->dir = dir; igrab(dir);
+               rr->inode = inode; igrab(inode);
+               rr->nlen = len;
+               strncpy(rr->name, name, len);
+               rr->name[len] = 0;
+               fs->pending_renames = rr;
+               rr = NULL;
+               break;
+
+       case DIROP_REN_TARGET:
+               rrp = &fs->pending_renames;
+               while (*rrp) {
+                       rr = *rrp;
+                       if (rr->key == handle)
+                               break;
+                       rrp = &rr->next;
+               }
+               if (!*rrp) {
+                       rr = NULL;
+                       err = -EINVAL;
+                       break;
+               }
+               *rrp = rr->next;
+               rr->next = NULL;
+
+               last = (inode && inode->i_nlink == 1);
+
+               /* FIXME check both are dirs or non-dirs, and that a
+                * target directory is empty */
+               err = dir_delete_prepare(fs, rr->dir,
+                                        rr->name, rr->nlen,
+                                        &old_doh);
+               olddb = lafs_inode_dblock(rr->inode, SYNC, MKREF(roll_dir));
+               if (IS_ERR(olddb))
+                       err = PTR_ERR(olddb);
+               if (inode) {
+                       /*unlink inode, update name */
+                       err = dir_update_prepare(fs, dir, name, len, &doh)
+                               ?: err;
+                       inodb = lafs_inode_dblock(inode, SYNC, MKREF(roll_dir));
+                       if (IS_ERR(inodb))
+                               err = PTR_ERR(inodb);
+                       if (last && !err)
+                               err = lafs_make_orphan(fs, inodb);
+                       lafs_iolock_block(&inodb->b);
+                       set_bit(B_PinPending, &inodb->b.flags);
+                       lafs_iounlock_block(&inodb->b);
+               } else
+                       /* create new link */
+                       err = dir_create_prepare(fs, dir, name, len,
+                                                rr->inode->i_ino,
+                                                mode_to_dt(rr->inode->i_mode),
+                                                &doh) ?: err;
+
+               if (!err) {
+                       lafs_iolock_block(&olddb->b);
+                       set_bit(B_PinPending, &olddb->b.flags);
+                       lafs_iounlock_block(&olddb->b);
+               }
+
+               err = err ?: dir_delete_pin(&old_doh);
+               err = err ?: lafs_pin_dblock(olddb, ReleaseSpace);
+               if (inode) {
+                       err = err ?: lafs_pin_dblock(inodb, ReleaseSpace);
+                       err = err ?: dir_update_pin(&doh);
+               } else
+                       err = err ?: dir_create_pin(&doh);
+               if (err < 0) {
+                       dir_delete_abort(&old_doh);
+                       if (inode)
+                               dir_update_abort(&doh);
+                       else
+                               dir_create_abort(&doh);
+                       break;
+               }
+               dir_delete_commit(&old_doh, fs, rr->dir, rr->name, rr->nlen);
+               if (S_ISDIR(rr->inode->i_mode)) {
+                       inode_dec_link_count(rr->dir);
+                       if (!inode)
+                               inode_inc_link_count(dir);
+               }
+               if (inode)
+                       dir_update_commit(fs, rr->inode->i_ino,
+                                         mode_to_dt(rr->inode->i_mode),
+                                         &doh);
+               else
+                       dir_create_commit(&doh, fs, dir, name, len,
+                                         rr->inode->i_ino,
+                                         mode_to_dt(rr->inode->i_mode));
+               LAFSI(rr->inode)->md.file.parent = dir->i_ino;
+               if (inode) {
+                       if (S_ISDIR(inode->i_mode))
+                               inode_dec_link_count(inode);
+                       inode_dec_link_count(inode);
+                       lafs_inode_checkpin(inode);
+               }
+               lafs_dirty_inode(rr->inode);
+               lafs_inode_checkpin(rr->dir);
+               lafs_inode_checkpin(dir);
+               clear_bit(B_PinPending, &olddb->b.flags);
+               if (inode) {
+                       clear_bit(B_PinPending, &inodb->b.flags);
+                       putdref(inodb, MKREF(dir_roll));
+               }
+               err = 0;
+               break;
+       }
+       if (inode && !IS_ERR(inode))
+               iput(inode);
+       if (inodb && !IS_ERR(inodb))
+               putdref(inodb, MKREF(roll_dir));
+       if (olddb && !IS_ERR(olddb))
+               putdref(olddb, MKREF(roll_dir));
+       if (rr) {
+               iput(rr->dir);
+               iput(rr->inode);
+               kfree(rr);
+       }
+       return err;
+}
 /*------------------------------------------------------------
  * Now we have the lowlevel operations in place, we
  * can implement the VFS interface.
@@ -726,7 +930,7 @@ retry:
        lafs_inode_checkpin(inode);
        lafs_dirty_dblock(inodb);
        clear_bit(B_PinPending, &inodb->b.flags);
-       putdref(inodb, MKREF(inode_update));
+       putdref(inodb, MKREF(link));
 
        dir_log_commit(&uh, fs, dir, &to->d_name, inode->i_ino,
                       DIROP_LINK, NULL);
@@ -742,7 +946,7 @@ abort_unlock:
        clear_bit(B_PinPending, &inodb->b.flags);
 abort:
        if (!IS_ERR(inodb))
-               putdref(inodb, MKREF(inode_update));
+               putdref(inodb, MKREF(link));
        dir_create_abort(&doh);
        lafs_cluster_update_abort(&uh);
        return err;
diff --git a/lafs.h b/lafs.h
index f7f0b7c79bfadd8aa7cc832556bdf2207eb84af8..616b3c0c1e9b0cdedfe1a5d440d3ef3e00699401 100644 (file)
--- a/lafs.h
+++ b/lafs.h
@@ -634,6 +634,8 @@ int lafs_dir_empty(char *block);
 void lafs_dir_make_index(char *orig, char *new, int psz, u32 target);
 
 int lafs_dir_handle_orphan(struct datablock *db);
+int lafs_dir_roll_mini(struct inode *dir, int handle, int dirop,
+                      u32 inum, char *name, int len);
 
 int lafs_release_page(struct page *page, gfp_t gfp_flags);
 void lafs_invalidate_page(struct page *page, unsigned long offset);
diff --git a/roll.c b/roll.c
index d4207984b3984507988c3b38b0ba37f8e38fd091..937983a28e9f3e9c63d2e7ac7a48a87592468877 100644 (file)
--- a/roll.c
+++ b/roll.c
@@ -240,6 +240,7 @@ roll_mini(struct fs *fs, int fsnum, int inum, int trunc,
        struct datablock *db = NULL;
        int err = 0;
        void *buf;
+       char *name;
 
        dprintk("Roll Mini  %d/%d/%lu/%d,%d\n",
                fsnum, inum, (unsigned long) bnum,
@@ -335,8 +336,18 @@ roll_mini(struct fs *fs, int fsnum, int inum, int trunc,
                break;
 
        case TypeDir:
-               /* Haven't written this yet FIXME */
-               BUG();
+               /* 'bnum' is the handle for match 'rename' parts.
+                * 'offset' is the DIROP type
+                * 'len' is 4 plus length of name.
+                * data contains 4-byte inode number, then name
+                */
+               if (len <= 4) { 
+                       err = -EIO;
+                       break;
+               }
+               inum = le32_to_cpu(*(u32*)data);
+               name = data + 4;
+               err = lafs_dir_roll_mini(inode, bnum, offset, inum, name, len-4);
                break;
        }
        /* We borrow the orphan list to keep a reference on
@@ -715,6 +726,19 @@ static int roll_forward(struct fs *fs)
 
        lafs_add_active(fs, next);
 
+       /* pending_renames will normally be empty, but it is not
+        * impossible that we crashed and an awkward time.  So just
+        * clean up whatever is there 
+        */
+       while (fs->pending_renames != NULL) {
+               struct rename_roll *rr = fs->pending_renames;
+               fs->pending_renames = rr->next;
+               iput(rr->dir);
+               iput(rr->inode);
+               kfree(rr);
+       }
+
+
        /* Now we release all the nlink==0 inodes that we found */
        while (!list_empty(&fs->pending_orphans)) {
                struct datablock *db = list_entry(fs->pending_orphans.next,
diff --git a/state.h b/state.h
index a8ae6a4017a33a12db6dd82837010fefbca092bd..0808018a0b7a03c852e80a08e154fb5578e0d014 100644 (file)
--- a/state.h
+++ b/state.h
@@ -333,6 +333,15 @@ struct fs {
 
        struct hlist_head stable[SHASHSIZE];
        spinlock_t stable_lock;
+
+       struct rename_roll {
+               struct rename_roll *next;
+               u32 key;
+               struct inode *dir, *inode;
+               int nlen;
+               char name[1];
+       } *pending_renames;
+
 };
 
 static inline int test_phase_locked(struct fs *fs)