commit 652093892939c996df5fb4fb9707482cc6a88b3b
Author: Yiannis Pericleous <[EMAIL PROTECTED]>
Date:   Fri Mar 2 18:44:53 2007 -0500

    rename and link fixes

diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index d47d023..260065d 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -89,22 +89,21 @@ static int unionfs_create(struct inode *parent, struct 
dentry *dentry,
                }
        }
 
-       if (hidden_dentry->d_inode)
-               do_force_rm(dentry, hidden_dentry, bstart);
-       /* FIXME: Now this case should never happen, but for some
-        * reason rmdir above does not give a negative dentry
-        */
-
+       if (hidden_dentry->d_inode) {
+               err = do_force_rm(dentry, hidden_dentry, bstart);
+               if (err)
+                       goto out;
+       }
+       /* refresh */
        if (hidden_dentry->d_inode) {
                hidden_dentry = lookup_one_len(dentry->d_name.name,
-                               hidden_dentry->d_parent, dentry->d_name.len);
+                       hidden_dentry->d_parent, dentry->d_name.len);
                if (!hidden_dentry || IS_ERR(hidden_dentry)) {
                        if (IS_ERR(hidden_dentry))
                                err = PTR_ERR(hidden_dentry);
                        goto out;
                }
        }
-
        hidden_parent_dentry = lock_parent(hidden_dentry);
        if (IS_ERR(hidden_parent_dentry)) {
                err = PTR_ERR(hidden_parent_dentry);
@@ -171,7 +170,7 @@ static struct dentry *unionfs_lookup(struct inode *parent,
 static int unionfs_link(struct dentry *old_dentry, struct inode *dir,
                        struct dentry *new_dentry)
 {
-       int err = 0;
+       int create_p = 0, err = 0;
        struct dentry *hidden_old_dentry = NULL;
        struct dentry *hidden_new_dentry = NULL;
        struct dentry *hidden_dir_dentry = NULL;
@@ -183,7 +182,24 @@ static int unionfs_link(struct dentry *old_dentry, struct 
inode *dir,
 
        hidden_new_dentry = unionfs_lower_dentry(new_dentry);
 
-       if (dbstart(old_dentry) != dbstart(new_dentry)) {
+       if (dbstart(old_dentry) > dbstart(new_dentry)) {
+
+               /* dont copyup if new_dentry doesn't exist in union */
+               if (dbstart(new_dentry) == 0 && 
+                       dbstart(new_dentry) == dbend(new_dentry) &&
+                       (!unionfs_lower_dentry_idx(new_dentry, 0) ||
+                       !unionfs_lower_dentry_idx(new_dentry, 0)->d_inode)) {
+                       set_dbstart(new_dentry, dbstart(old_dentry));
+                       set_dbend(new_dentry, dbstart(old_dentry));
+                       create_p = 1;
+
+               }
+               else {
+                       err = -EROFS;
+                       goto docopyup;
+               }
+       }
+       if (dbstart(old_dentry) != dbstart(new_dentry) || create_p) {
                hidden_new_dentry =
                    create_parents(dir, new_dentry, dbstart(old_dentry));
                err = PTR_ERR(hidden_new_dentry);
@@ -195,6 +211,21 @@ static int unionfs_link(struct dentry *old_dentry, struct 
inode *dir,
        hidden_new_dentry = unionfs_lower_dentry(new_dentry);
        hidden_old_dentry = unionfs_lower_dentry(old_dentry);
 
+       if (hidden_new_dentry->d_inode) {
+               err = do_force_rm(new_dentry, hidden_new_dentry, 
dbstart(old_dentry));
+               if (err)
+                       goto out;
+       }
+       if (hidden_new_dentry->d_inode) {
+               hidden_new_dentry = lookup_one_len(new_dentry->d_name.name,
+                       hidden_new_dentry->d_parent, new_dentry->d_name.len);
+               if (!hidden_new_dentry || IS_ERR(hidden_new_dentry)) {
+                       if (IS_ERR(hidden_new_dentry))
+                               err = PTR_ERR(hidden_new_dentry);
+                       goto out;
+               }
+       }
+       
        BUG_ON(dbstart(old_dentry) != dbstart(new_dentry));
        hidden_dir_dentry = lock_parent(hidden_new_dentry);
        if (!(err = is_robranch(old_dentry)))
@@ -205,27 +236,41 @@ static int unionfs_link(struct dentry *old_dentry, struct 
inode *dir,
 docopyup:
        if (IS_COPYUP_ERR(err)) {
                int old_bstart = dbstart(old_dentry);
-               int bindex;
-
-               for (bindex = old_bstart - 1; bindex >= 0; bindex--) {
-                       err =
-                           copyup_dentry(old_dentry->d_parent->
-                                         d_inode, old_dentry,
-                                         old_bstart, bindex, NULL,
-                                         old_dentry->d_inode->i_size);
-                       if (!err) {
-                               hidden_new_dentry =
-                                   create_parents(dir, new_dentry, bindex);
-                               hidden_old_dentry = 
unionfs_lower_dentry(old_dentry);
-                               hidden_dir_dentry =
-                                   lock_parent(hidden_new_dentry);
-                               /* do vfs_link */
-                               err = vfs_link(hidden_old_dentry,
-                                            hidden_dir_dentry->d_inode,
-                                            hidden_new_dentry);
-                               unlock_dir(hidden_dir_dentry);
-                               goto check_link;
+               int bindex = 0;
+
+               err = copyup_dentry(old_dentry->d_parent->
+                                 d_inode, old_dentry,
+                                 old_bstart, bindex, NULL,
+                                 old_dentry->d_inode->i_size);
+               if (!err) {
+                       hidden_new_dentry =
+                               create_parents(dir, new_dentry, bindex);
+                       hidden_old_dentry = unionfs_lower_dentry(old_dentry);
+                       if (hidden_new_dentry->d_inode)
+                               err = do_force_rm(new_dentry, 
hidden_new_dentry, dbstart(old_dentry));
+                       if (err)
+                               goto out;
+                       /* refresh */
+                       if (hidden_new_dentry->d_inode) {
+                               hidden_new_dentry = lookup_one_len(
+                                       new_dentry->d_name.name,
+                                       hidden_new_dentry->d_parent,
+                                       new_dentry->d_name.len);
+                               if (!hidden_new_dentry || 
IS_ERR(hidden_new_dentry)) {
+                                       if (IS_ERR(hidden_new_dentry))
+                                               err = 
PTR_ERR(hidden_new_dentry);
+                                       goto out;
+                               }
                        }
+                       
+                       hidden_dir_dentry =
+                               lock_parent(hidden_new_dentry);
+                       /* do vfs_link */
+                       err = vfs_link(hidden_old_dentry,
+                                    hidden_dir_dentry->d_inode,
+                                    hidden_new_dentry);
+                       unlock_dir(hidden_dir_dentry);
+                       goto check_link;
                }
                goto out;
        }
@@ -240,6 +285,9 @@ check_link:
        fsstack_copy_attr_all(dir, hidden_new_dentry->d_parent->d_inode,
                                        unionfs_get_nlinks);
        fsstack_copy_inode_size(dir, hidden_new_dentry->d_parent->d_inode);
+       /* update odf, FIXME: what to do in case of err?  */
+       err = odf_link(old_dentry, new_dentry);
+       err = odf_purge_dir_cache(new_dentry->d_parent); 
 
        /* propagate number of hard-links */
        old_dentry->d_inode->i_nlink = unionfs_get_nlinks(old_dentry->d_inode);
@@ -290,14 +338,16 @@ static int unionfs_symlink(struct inode *dir, struct 
dentry *dentry,
                }
        }
 
-       if (hidden_dentry->d_inode)
-               do_force_rm(dentry, hidden_dentry, bstart);
-       /* FIXME: Now this case should never happen, but for some
-        * reason rmdir above does not give a negative dentry
-        */
+       if (hidden_dentry->d_inode) {
+               err = do_force_rm(dentry, hidden_dentry, bstart);
+               if (err)
+                       goto out;
+       }
+       /* refresh hidden */
        if (hidden_dentry->d_inode) {
                hidden_dentry = lookup_one_len(dentry->d_name.name,
-                               hidden_dentry->d_parent, dentry->d_name.len);
+                               hidden_dentry->d_parent, 
+                               dentry->d_name.len);
                if (!hidden_dentry || IS_ERR(hidden_dentry)) {
                        if (IS_ERR(hidden_dentry))
                                err = PTR_ERR(hidden_dentry);
@@ -370,14 +420,16 @@ static int unionfs_mkdir(struct inode *parent, struct 
dentry *dentry, int mode)
                }
        }
 
-       if (hidden_dentry->d_inode)
-               do_force_rm(dentry, hidden_dentry, bindex);
-       /* FIXME: Now this case should never happen, but for some
-        * reason rmdir above does not give a negative dentry
-        */
+       if (hidden_dentry->d_inode) {
+               err = do_force_rm(dentry, hidden_dentry, bindex);
+               if (err)
+                       goto out;
+       }
+       /* refresh dentry */
        if (hidden_dentry->d_inode) {
                hidden_dentry = lookup_one_len(dentry->d_name.name,
-                               hidden_dentry->d_parent, dentry->d_name.len);
+                               hidden_dentry->d_parent,
+                               dentry->d_name.len);
                if (!hidden_dentry || IS_ERR(hidden_dentry)) {
                        if (IS_ERR(hidden_dentry))
                                err = PTR_ERR(hidden_dentry);
@@ -468,14 +520,16 @@ static int unionfs_mknod(struct inode *dir, struct dentry 
*dentry, int mode,
                }
        }
        
-       if (hidden_dentry->d_inode)
-               do_force_rm(dentry, hidden_dentry, bstart);
-       /* FIXME: Now this case should never happen, but for some
-        * reason rmdir above does not give a negative dentry
-        */
+       if (hidden_dentry->d_inode) {
+               err = do_force_rm(dentry, hidden_dentry, bstart);
+               if (err)
+                       goto out;
+       }
+       /* refresh hidden dentry */
        if (hidden_dentry->d_inode) {
                hidden_dentry = lookup_one_len(dentry->d_name.name,
-                               hidden_dentry->d_parent, dentry->d_name.len);
+                               hidden_dentry->d_parent,
+                               dentry->d_name.len);
                if (!hidden_dentry || IS_ERR(hidden_dentry)) {
                        if (IS_ERR(hidden_dentry))
                                err = PTR_ERR(hidden_dentry);
diff --git a/fs/unionfs/odf.c b/fs/unionfs/odf.c
index cad8d86..7a3a5ee 100644
--- a/fs/unionfs/odf.c
+++ b/fs/unionfs/odf.c
@@ -241,6 +241,12 @@ int odf_rename(struct dentry *old_dentry, struct dentry 
*new_dentry)
        if (err)
                goto out;
 
+       if (UNIONFS_D(old_dentry)->odf_info == NULL) {
+               printk("rename attempt %s to %s not in odf\n", 
+                       old_dentry->d_name.name,
+                       new_dentry->d_name.name);
+               goto out;
+       }
        err = odf_lookup(new_dentry->d_parent, new_dentry, 0);
        if (err)
                goto out;
@@ -277,6 +283,56 @@ int odf_rename(struct dentry *old_dentry, struct dentry 
*new_dentry)
 out_unlock:
        odf_unlock(UNIONFS_D(old_dentry)->odf_info);
        odf_unlock(UNIONFS_D(new_dentry->d_parent)->odf_info);
+       err = odf_lookup(old_dentry->d_parent, old_dentry, 0);
+out:
+       return err;
+}
+
+/* Links two entries in the odf        */
+int odf_link(struct dentry *old_dentry, struct dentry *new_dentry)
+{
+       struct dentry *old_dir, *new_dir;
+       struct dentry *old_odfdentry, *new_odfdentry;
+       int err = 0;
+
+       err = odf_lookup(old_dentry->d_parent, old_dentry, 0);
+       if (err)
+               goto out;
+
+       err = odf_lookup(new_dentry->d_parent, new_dentry, 0);
+       if (err)
+               goto out;
+
+       /* if new exists, remove it */
+       if (UNIONFS_D(new_dentry)->odf_info){
+               err = odf_remove(new_dentry, ODF_RMV_ANY);
+       }
+       
+       odf_lock(UNIONFS_D(old_dentry)->odf_info);
+       odf_lock(UNIONFS_D(new_dentry->d_parent)->odf_info);
+       old_odfdentry = UNIONFS_D(old_dentry)->odf_info->dentry;
+       old_dir = old_odfdentry->d_parent;
+       new_dir = (UNIONFS_D(new_dentry->d_parent))->odf_info->dentry;
+       new_odfdentry = lookup_one_len(
+                               new_dentry->d_name.name,
+                               new_dir,
+                               new_dentry->d_name.len);
+       if (IS_ERR(new_odfdentry)) { 
+               err = PTR_ERR(new_odfdentry);
+               goto out_unlock;
+       }
+       
+       /* this should never happen */
+       BUG_ON(new_odfdentry->d_inode);
+       
+       err = vfs_link(old_odfdentry, old_dir->d_inode, new_odfdentry);
+       
+       dput(new_odfdentry);
+out_unlock:
+       odf_unlock(UNIONFS_D(old_dentry)->odf_info);
+       odf_unlock(UNIONFS_D(new_dentry->d_parent)->odf_info);
+       //err = odf_lookup(old_dentry->d_parent, old_dentry, 0);
+       //err = odf_lookup(new_dentry->d_parent, new_dentry, 0);
 out:
        return err;
 }
@@ -363,6 +419,8 @@ struct odf_dentry_info *__odf_lookup(struct odf_dentry_info 
*parent, const char
                else {
                        dput(odf_dentry);
                        odf_unlock(parent);
+                       odf_put_info(old_odi);
+                       odi = NULL;
                        goto out;
                }
                odf_unlock(parent);
@@ -622,26 +680,24 @@ int odf_create_wh(struct dentry *dentry)
 {
        int err = 0;
        struct dentry *odf_dentry;
-
-       BUG_ON(!UNIONFS_D(dentry)->odf_info);
-       odf_dentry = UNIONFS_D(dentry)->odf_info->dentry;
-       if (__odf_is_wh(odf_dentry->d_inode))
-               goto out;       /* nothing to be done */        
+       if (UNIONFS_D(dentry)->odf_info) {
+               odf_dentry = UNIONFS_D(dentry)->odf_info->dentry;
+               if (__odf_is_wh(odf_dentry->d_inode))
+                       goto out;       /* nothing to be done */        
        
-       /* remove entry if existed */
-       err = odf_remove(dentry, ODF_RMV_ANY);
-       if (err)
-               goto out;
-
-       odf_put_info(UNIONFS_D(dentry)->odf_info);
-       UNIONFS_D(dentry)->odf_info = NULL;
-
+               /* remove entry if existed */
+               err = odf_remove(dentry, ODF_RMV_ANY);
+               if (err)
+                       goto out;
+       }
        /* now create a new file and make it a whiteout */
        err = odf_lookup(dentry->d_parent, dentry, ODF_LOOKUP_FILE);
        if (err)
-               goto out;       
+               goto out;
        
        err = odf_set_wh(dentry,1);
+       odf_put_info(UNIONFS_D(dentry)->odf_info);
+       UNIONFS_D(dentry)->odf_info = NULL;
 out:
        return err;
 }
@@ -659,8 +715,6 @@ int odf_remove(struct dentry *dentry, int flags)
        struct odf_dentry_info *odi = NULL;
        int err = 0, rmv = 0;
 
-       BUG_ON(!UNIONFS_D(dentry)->odf_info);
-       
        /* refresh odi */
        err = odf_lookup(dentry->d_parent, dentry, 0);
        if (IS_ERR(odi)) {
@@ -668,6 +722,8 @@ int odf_remove(struct dentry *dentry, int flags)
                odi = NULL;
                goto out;
        }
+
+       BUG_ON(!UNIONFS_D(dentry)->odf_info);
        odi = UNIONFS_D(dentry)->odf_info;
        if (!odi)
                goto out;
diff --git a/fs/unionfs/odf.h b/fs/unionfs/odf.h
index e586418..658c627 100644
--- a/fs/unionfs/odf.h
+++ b/fs/unionfs/odf.h
@@ -64,6 +64,7 @@ int odf_purge_dir_cache(struct dentry *dentry);
 int odf_cache_dir(struct dentry *d_upper, struct dentry *d_odf, struct 
timespec *mtime);
 
 int odf_rename(struct dentry *old_dentry, struct dentry *new_dentry);
+int odf_link(struct dentry *old_dentry, struct dentry *new_dentry);
 
 /* unlink */
 int odf_remove(struct dentry *dentry, int flags);
diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index 41a02ae..5b3062c 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -18,6 +18,54 @@
 
 #include "union.h"
 
+int copyup_empty_dir(struct inode *parent, struct dentry *dentry, int mode)
+{
+       int err = 0;
+       struct dentry *hidden_dentry = NULL;
+       struct dentry *hidden_parent_dentry = NULL;
+       int i, bend, bindex;
+
+       bindex = 0;
+       bend = dbend(dentry);
+
+       hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+       if (!hidden_dentry) {
+               hidden_dentry = create_parents(parent, dentry, bindex);
+               if (!hidden_dentry || IS_ERR(hidden_dentry)) { 
+                       printk(KERN_DEBUG "hidden dentry NULL for "
+                                       "bindex = %d\n", bindex);
+                       err = PTR_ERR(hidden_dentry);
+                       goto out;
+               }
+       }
+
+       hidden_parent_dentry = lock_parent(hidden_dentry);
+
+       if (IS_ERR(hidden_parent_dentry)) {
+               err = PTR_ERR(hidden_parent_dentry);
+               goto out;
+       }
+
+       err = vfs_mkdir(hidden_parent_dentry->d_inode, hidden_dentry, mode);
+
+       unlock_dir(hidden_parent_dentry);
+
+       /* did the mkdir suceed? */
+       if (err)
+               goto out;
+
+       for (i = bindex + 1; i < bend; i++) {
+               if (unionfs_lower_dentry_idx(dentry, i)) {
+                       dput(unionfs_lower_dentry_idx(dentry, i));
+                       unionfs_set_lower_dentry_idx(dentry, i, NULL);
+               }
+       }
+       set_dbend(dentry, bindex);
+
+out:
+       return err;
+}
+
 static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
                     struct inode *new_dir, struct dentry *new_dentry,
                     int bindex)
@@ -30,6 +78,8 @@ static int do_rename(struct inode *old_dir, struct dentry 
*old_dentry,
 
        hidden_new_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);
        hidden_old_dentry = unionfs_lower_dentry_idx(old_dentry, bindex);
+       if (!hidden_old_dentry || !hidden_old_dentry->d_inode)
+               goto out_nofix;
 
        if (!hidden_new_dentry) {
                hidden_new_dentry =
@@ -72,6 +122,7 @@ out:
                        set_dbend(new_dentry, bindex);
        }
 
+out_nofix:
        return err;
 }
 
@@ -97,42 +148,71 @@ static int do_unionfs_rename(struct inode *old_dir,
        new_bstart = dbstart(new_dentry);
        new_bend = dbend(new_dentry);
 
+       if (new_bstart == new_bend &&  new_bstart == 0) {
+               if (!unionfs_lower_dentry_idx(new_dentry, 0) ||
+                   !unionfs_lower_dentry_idx(new_dentry, 0)->d_inode) {
+                       set_dbstart(new_dentry, old_bstart);
+                       set_dbend(new_dentry, old_bend);
+                       new_bstart = old_bstart;
+                       new_bend = old_bend;
+               }
+       }
+       
        /* Rename source to destination. */
-       err = do_rename(old_dir, old_dentry, new_dir, new_dentry, old_bstart);
-
+       for (bindex = old_bstart; bindex <= old_bend; bindex++) {
+               err = do_rename(old_dir, old_dentry, new_dir, new_dentry, 
bindex);
+               if (err)
+                       break;
+       }
        if (err) {
                if (!IS_COPYUP_ERR(err))
                        goto out;
                do_copyup = 1;
-       } else
+       } 
+       else if (new_bstart < old_bstart){
+               do_copyup = 1;
+       }
+       else
                revert = 1;
 
-       if (do_copyup) {
-       
-               /* copyup branch 0 and try rename again */
+       /* only need to copyup if bstart is not 0 */
+       if (do_copyup && old_bstart > 0) {      
                bindex = 0;
-               err = copyup_dentry(old_dentry->d_parent->d_inode,
+               
+               if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+                       /* at this point we can safely assume the directory
+                        * is logically empty, so mkdir at branch 0 */
+
+                       err = copyup_empty_dir(old_dir, 
+                               old_dentry, 
+                               old_dentry->d_inode->i_mode);
+                       if (err)
+                               goto revert;
+                       err = do_rename(old_dir, old_dentry, new_dir,
+                                       new_dentry, bindex);
+               }
+               else {
+                       err = copyup_dentry(old_dentry->d_parent->d_inode,
                                old_dentry, old_bstart, bindex, NULL,
                                old_dentry->d_inode->i_size);
-               if (!err)
-                       err = do_rename(old_dir, old_dentry, new_dir,
+                       if (!err)
+                               err = do_rename(old_dir, old_dentry, new_dir,
                                        new_dentry, bindex);
-               if (err)
-                       goto revert;
+                       if (err)
+                               goto revert;
+               }
        }
 
        /* if all ok, rename in odf */
        err = odf_rename(old_dentry, new_dentry);
        if (err) 
                goto revert; 
-
        /* FIXME: when should we whiteout? */
-       if ((old_bstart != old_bend) || (do_copyup != -1)) {
+       if (do_copyup != 0) {
                err = odf_create_wh(old_dentry);
                if (err)
                        goto revert;
        }
-
 out:
        return err;
 
@@ -223,6 +303,7 @@ int unionfs_rename(struct inode *old_dir, struct dentry 
*old_dentry,
                   struct inode *new_dir, struct dentry *new_dentry)
 {
        int err = 0;
+       struct dentry *hidden;
 
        BUG_ON(!is_valid_dentry(old_dentry));
        BUG_ON(!is_valid_dentry(new_dentry));
@@ -241,6 +322,23 @@ int unionfs_rename(struct inode *old_dir, struct dentry 
*old_dentry,
        if (err)
                goto out;
 
+       /* if the new dentry exists at branch 0 and is a whiteout
+        * vfs_rename will still succeed if it is not a directory
+        * FIXME: if it is a directory do we want to remove this
+        * directory or dow we want to fail? */
+       hidden = unionfs_lower_dentry_idx(new_dentry, 0);
+       if (hidden) {
+               if (hidden->d_inode && 
+                       S_ISDIR(hidden->d_inode->i_mode) &&
+                       UNIONFS_D(new_dentry)->odf_info->whiteout) {
+
+                        err = 
unionfs_force_rmdir(unionfs_lower_mnt_idx(new_dentry, 0), 
+                               hidden, 0);
+                       if (err)
+                               goto out;
+               }
+       }
+
        if (new_dentry->d_inode) {
                if (S_ISDIR(old_dentry->d_inode->i_mode) !=
                    S_ISDIR(new_dentry->d_inode->i_mode)) {
@@ -248,7 +346,6 @@ int unionfs_rename(struct inode *old_dir, struct dentry 
*old_dentry,
                                        -ENOTDIR : -EISDIR;
                        goto out;
                }
-
                if (S_ISDIR(new_dentry->d_inode->i_mode)) {
                        struct unionfs_dir_state *namelist;
                        /* check if this unionfs directory is empty or not */
_______________________________________________
unionfs-cvs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs-cvs

Reply via email to