From 16b4f29387f85f482423a3e6f43b45ab7cf6627d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 4 Mar 2011 12:47:30 +1100 Subject: [PATCH] roll: handle directory updates. 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 --- dir.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- lafs.h | 2 + roll.c | 28 +++++++- state.h | 9 +++ 4 files changed, 243 insertions(+), 4 deletions(-) diff --git a/dir.c b/dir.c index ae1d083..e795aaf 100644 --- 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 f7f0b7c..616b3c0 100644 --- 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 d420798..937983a 100644 --- 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 a8ae6a4..0808018 100644 --- 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) -- 2.39.5