commit 982f080c99a4fc3a11a9dc7adf03a895e5dd97a6
Author: Yiannis Pericleous <[EMAIL PROTECTED]>
Date:   Sun Feb 25 15:59:06 2007 -0500

    inode: removed loops when creating & force remove if it already exists
    
    at branch 0 (and is a whiteout)

diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
index 76df000..4c2b21f 100644
--- a/fs/unionfs/dirhelper.c
+++ b/fs/unionfs/dirhelper.c
@@ -30,11 +30,51 @@ struct unionfs_rdutil_callback {
        struct unionfs_dir_state *rdstate;
        int mode;
 };
+struct unionfs_rmdir_callback {
+       int err;
+       int bindex;
+       int filldir_called;
+       struct dentry *dir;
+       struct vfsmount *mnt;
+};
+static int rmdir_util_callback(void *dirent, const char *name, int namelen,
+                                loff_t offset, u64 ino, unsigned int d_type)
+{
+       int err = 0;
+       struct unionfs_rmdir_callback *buf = dirent;
+       struct dentry *dentry;
+       
+       buf->filldir_called++;
+
+       if (name[0] == '.' &&
+               (namelen == 1 || (name[1] == '.' && namelen == 2)))
+                       goto out;
 
+       dentry = lookup_one_len(name, buf->dir, namelen);
+       if (IS_ERR(dentry)) {
+               err = PTR_ERR(dentry);
+               goto out;
+       }       
+       else if (!dentry->d_inode) 
+               BUG_ON(1); /*can this ever happen?*/
+       
+       if (d_type == DT_DIR)
+               err = unionfs_force_rmdir(buf->mnt, dentry, buf->bindex);
+       else
+               err = vfs_unlink(buf->dir->d_inode, dentry);
+       dput(dentry);
+out:
+       buf->err = err;
+       return err;
+}
 /* This filldir function makes sure only whiteouts exist within a directory. */
 static int readdir_util_callback(void *dirent, const char *name, int namelen,
                                 loff_t offset, u64 ino, unsigned int d_type)
 {
+       /* FIXME: Fix this so it doesn't store whiteouts and thus is more
+        * efficient
+        */
+
        int err = 0;
        struct unionfs_rdutil_callback *buf = dirent;
        int whiteout = 0;
@@ -327,3 +367,60 @@ out:
 
        return err;
 }
+
+/* This function recursively rermoves all the entries of a lower directory
+ * at a specific branch and then the directory itself. It does not
+ * cross branches, and does nothing visible to the union. It should
+ * only be used when both a whiteout and a dir at top branch exists
+ * and we need to create a new dentry
+ */
+int unionfs_force_rmdir(struct vfsmount *mnt, struct dentry *hidden_dentry, 
int bindex)
+{
+       int err = 0;
+       struct file *hidden_file;
+       struct unionfs_rmdir_callback *buf = NULL;
+
+       buf = kmalloc(sizeof(struct unionfs_rmdir_callback), GFP_KERNEL);
+       if (!buf) {
+               err = -ENOMEM;
+               goto out;
+       }
+       buf->err = 0;
+       buf->bindex = bindex;
+       buf->dir = hidden_dentry;
+       buf->mnt = mnt;
+
+       if (!hidden_dentry->d_inode)
+               goto out;
+       if (!S_ISDIR(hidden_dentry->d_inode->i_mode))
+               goto out;
+
+       dget(hidden_dentry);
+       mntget(mnt);
+       hidden_file = dentry_open(hidden_dentry, mnt, O_RDONLY);
+       if (IS_ERR(hidden_file)) {
+               err = PTR_ERR(hidden_file);
+               dput(hidden_dentry);
+               goto out;
+       }
+
+       do {
+               buf->filldir_called = 0;
+               err = vfs_readdir(hidden_file, rmdir_util_callback, buf);
+               if (buf->err)
+                       err = buf->err;
+       } while ((err >= 0) && buf->filldir_called);
+
+       /* fput calls dput for hidden_dentry */
+       fput(hidden_file);
+
+       if (err < 0)
+               goto out;
+
+       err = vfs_rmdir(hidden_dentry->d_parent->d_inode, hidden_dentry);
+
+out:
+       kfree(buf);
+
+       return err;
+}
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index c8da6d1..d47d023 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -18,15 +18,31 @@
 
 #include "union.h"
 
+static int do_force_rm(struct dentry *dentry, struct dentry *hidden_dentry, 
int bindex)
+{
+       int err;
+
+       /* this should only happen if its a wh */
+       BUG_ON(!UNIONFS_D(dentry)->odf_info->whiteout);
+               
+       /* try to remove it! */
+       if (S_ISDIR(hidden_dentry->d_inode->i_mode))
+               err = unionfs_force_rmdir(unionfs_lower_mnt_idx(dentry, 
bindex), 
+                                       hidden_dentry, bindex);
+
+       else
+               err = vfs_unlink(hidden_dentry->d_parent->d_inode,
+                                               hidden_dentry);
+       return err;
+}
+
 static int unionfs_create(struct inode *parent, struct dentry *dentry,
                          int mode, struct nameidata *nd)
 {
        int err = 0;
        struct dentry *hidden_dentry = NULL;
        struct dentry *hidden_parent_dentry = NULL;
-       int bindex = 0, bstart;
-       char *name = NULL;
-       int valid = 0;
+       int bstart;
 
        /*
         * We have to read-lock the superblock rwsem, and we have to
@@ -56,61 +72,71 @@ static int unionfs_create(struct inode *parent, struct 
dentry *dentry,
        BUG_ON(!valid && dentry->d_inode);
 
        /* We start out in the leftmost branch. */
-       bstart = dbstart(dentry);
-       hidden_dentry = unionfs_lower_dentry(dentry);
+       bstart = 0;
 
-       for (bindex = bstart; bindex >= 0; bindex--) {
-               hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
-               if (!hidden_dentry) {
-                       /* if hidden_dentry is NULL, create the entire
-                        * dentry directory structure in branch 'bindex'.
-                        * hidden_dentry will NOT be null when bindex == bstart
-                        * because lookup passed as a negative unionfs dentry
-                        * pointing to a lone negative underlying dentry */
-                       hidden_dentry = create_parents(parent, dentry, bindex);
-                       if (!hidden_dentry || IS_ERR(hidden_dentry)) {
-                               if (IS_ERR(hidden_dentry))
-                                       err = PTR_ERR(hidden_dentry);
-                               continue;
-                       }
+       hidden_dentry = unionfs_lower_dentry_idx(dentry, bstart);
+       if (!hidden_dentry) {
+               /* if hidden_dentry is NULL, create the entire
+                * dentry directory structure in branch 'bindex'.
+                * hidden_dentry will NOT be null when bindex == bstart
+                * because lookup passed as a negative unionfs dentry
+                * pointing to a lone negative underlying dentry */
+               hidden_dentry = create_parents(parent, dentry, bstart);
+               if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+                       if (IS_ERR(hidden_dentry))
+                               err = PTR_ERR(hidden_dentry);
+                       goto out;
                }
+       }
+
+       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
+        */
 
-               hidden_parent_dentry = lock_parent(hidden_dentry);
-               if (IS_ERR(hidden_parent_dentry)) {
-                       err = PTR_ERR(hidden_parent_dentry);
+       if (hidden_dentry->d_inode) {
+               hidden_dentry = lookup_one_len(dentry->d_name.name,
+                               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;
                }
-               /* We shouldn't create things in a read-only branch. */
-               if (!(err = is_robranch_super(dentry->d_sb, bindex)))
-                       err = vfs_create(hidden_parent_dentry->d_inode,
-                                        hidden_dentry, mode, nd);
-
-               if (err || !hidden_dentry->d_inode) {
-                       unlock_dir(hidden_parent_dentry);
-
-                       /* break out of for loop if the error wasn't  -EROFS */
-                       if (!IS_COPYUP_ERR(err))
-                               break;
-               } else {
-                       err = odf_lookup(dentry->d_parent, dentry, 
-                               ODF_LOOKUP_FILE|ODF_LOOKUP_RMV_WH);
-                       if (!err)
-                               err = odf_purge_dir_cache(dentry->d_parent); 
-                       if (!err)
-                               err = unionfs_interpose(dentry, parent->i_sb, 
0);
-                       if (!err) {
-                               fsstack_copy_attr_times(parent,
-                                               hidden_parent_dentry->d_inode);
-                               fsstack_copy_inode_size(parent,
-                                               hidden_parent_dentry->d_inode);
-                               /* update number of links on parent directory */
-                               parent->i_nlink = unionfs_get_nlinks(parent);
-                       }
-                       unlock_dir(hidden_parent_dentry);
-                       break;
+       }
+
+       hidden_parent_dentry = lock_parent(hidden_dentry);
+       if (IS_ERR(hidden_parent_dentry)) {
+               err = PTR_ERR(hidden_parent_dentry);
+               goto out;
+       }
+       /* We shouldn't create things in a read-only branch. */
+       if (!(err = is_robranch_super(dentry->d_sb, bstart)))
+               err = vfs_create(hidden_parent_dentry->d_inode,
+                                hidden_dentry, mode, nd);
+
+       if (err || !hidden_dentry->d_inode)
+               unlock_dir(hidden_parent_dentry);
+
+       else {
+               err = odf_lookup(dentry->d_parent, dentry, 
+                       ODF_LOOKUP_FILE|ODF_LOOKUP_RMV_WH);
+               if (!err)
+                       err = odf_purge_dir_cache(dentry->d_parent); 
+               if (!err)
+                       err = unionfs_interpose(dentry, parent->i_sb, 0);
+               if (!err) {
+                       fsstack_copy_attr_times(parent,
+                                       hidden_parent_dentry->d_inode);
+                       fsstack_copy_inode_size(parent,
+                                       hidden_parent_dentry->d_inode);
+                       /* update number of links on parent directory */
+                       parent->i_nlink = unionfs_get_nlinks(parent);
                }
+               unlock_dir(hidden_parent_dentry);
        }
 
+
 out:
        unionfs_unlock_dentry(dentry);
        unionfs_read_unlock(dentry->d_sb);
@@ -235,78 +261,82 @@ static int unionfs_symlink(struct inode *dir, struct 
dentry *dentry,
        struct dentry *hidden_dentry = NULL;
        struct dentry *hidden_dir_dentry = NULL;
        umode_t mode;
-       int bindex = 0, bstart;
+       int bstart;
 
        BUG_ON(!is_valid_dentry(dentry));
 
        unionfs_lock_dentry(dentry);
 
-       /* We start out in the leftmost branch. */
-       bstart = dbstart(dentry);
-
-       hidden_dentry = unionfs_lower_dentry(dentry);
+       /* We create in the leftmost branch. */
+       bstart = 0;
 
-       /* do a normal vfs_symlink()
-        * with possible recursive directory creation
-        */
-       for (bindex = bstart; bindex >= 0; bindex--) {
-               hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
-               if (!hidden_dentry) {
-                       /* if hidden_dentry is NULL, create the entire
-                        * dentry directory structure in branch 'bindex'.
-                        * hidden_dentry will NOT be null when bindex ==
-                        * bstart because lookup passed as a negative
-                        * unionfs dentry pointing to a lone negative
-                        * underlying dentry
-                        */
-                       hidden_dentry = create_parents(dir, dentry, bindex);
-                       if (!hidden_dentry || IS_ERR(hidden_dentry)) {
-                               if (IS_ERR(hidden_dentry))
-                                       err = PTR_ERR(hidden_dentry);
+       hidden_dentry = unionfs_lower_dentry_idx(dentry, bstart);
+       if (!hidden_dentry) {
+               /* if hidden_dentry is NULL, create the entire
+                * dentry directory structure in branch 'bindex'.
+                * hidden_dentry will NOT be null when bindex ==
+                * bstart because lookup passed as a negative
+                * unionfs dentry pointing to a lone negative
+                * underlying dentry
+                */
+               hidden_dentry = create_parents(dir, dentry, bstart);
+               if (!hidden_dentry || IS_ERR(hidden_dentry)) {
+                       if (IS_ERR(hidden_dentry))
+                               err = PTR_ERR(hidden_dentry);
 
                                printk(KERN_DEBUG "hidden dentry NULL (or 
error)"
-                                       "for bindex = %d\n", bindex);
-                               continue;
-                       }
+                                       "for bindex = %d\n", bstart);
+                               goto out;
                }
+       }
 
-               hidden_dir_dentry = lock_parent(hidden_dentry);
-
-               if (!(err = is_robranch_super(dentry->d_sb, bindex))) {
-                       mode = S_IALLUGO;
-                       err =
-                           vfs_symlink(hidden_dir_dentry->d_inode,
-                                       hidden_dentry, symname, 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) {
+               hidden_dentry = lookup_one_len(dentry->d_name.name,
+                               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;
                }
-               unlock_dir(hidden_dir_dentry);
-
-               if (err || !hidden_dentry->d_inode) {
-                       /* break out of for loop if error returned was NOT 
-EROFS */
-                       if (!IS_COPYUP_ERR(err))
-                               break;
-               } else {
-                       /* update odf and remove any wh before interpose*/
-                       err = odf_lookup(dentry->d_parent, dentry, 
-                               ODF_LOOKUP_FILE|ODF_LOOKUP_RMV_WH);
-                       if (!err)
-                               err = unionfs_interpose(dentry, dir->i_sb, 0);
-                       if (!err)
-                               err = odf_purge_dir_cache(dentry->d_parent); 
-                       if (!err) {
-                               fsstack_copy_attr_times(dir,
-                                               hidden_dir_dentry->d_inode);
-                               fsstack_copy_inode_size(dir,
-                                               hidden_dir_dentry->d_inode);
-                               /* update number of links on parent directory */
-                               dir->i_nlink = unionfs_get_nlinks(dir);
-                       }
-                       break;
+       }
+       
+       hidden_dir_dentry = lock_parent(hidden_dentry);
+
+       if (!(err = is_robranch_super(dentry->d_sb, bstart))) {
+               mode = S_IALLUGO;
+               err =
+                   vfs_symlink(hidden_dir_dentry->d_inode,
+                               hidden_dentry, symname, mode);
+       }
+       unlock_dir(hidden_dir_dentry);
+
+       if (!(err || !hidden_dentry->d_inode)) {
+               /* update odf and remove any wh before interpose*/
+               err = odf_lookup(dentry->d_parent, dentry, 
+                       ODF_LOOKUP_FILE|ODF_LOOKUP_RMV_WH);
+               if (!err)
+                       err = unionfs_interpose(dentry, dir->i_sb, 0);
+               if (!err)
+                       err = odf_purge_dir_cache(dentry->d_parent); 
+               if (!err) {
+                       fsstack_copy_attr_times(dir,
+                                       hidden_dir_dentry->d_inode);
+                       fsstack_copy_inode_size(dir,
+                                       hidden_dir_dentry->d_inode);
+                       /* update number of links on parent directory */
+                       dir->i_nlink = unionfs_get_nlinks(dir);
                }
        }
 
        if (!dentry->d_inode)
                d_drop(dentry);
 
+out:
        unionfs_unlock_dentry(dentry);
        return err;
 }
@@ -336,10 +366,25 @@ static int unionfs_mkdir(struct inode *parent, struct 
dentry *dentry, int mode)
                        printk(KERN_DEBUG "hidden dentry NULL for "
                                "bindex = %d\n", bindex);
                        err = PTR_ERR(hidden_dentry);
-                       goto out;;
+                       goto out;
                }
        }
 
+       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) {
+               hidden_dentry = lookup_one_len(dentry->d_name.name,
+                               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)) {
@@ -402,62 +447,72 @@ static int unionfs_mknod(struct inode *dir, struct dentry 
*dentry, int mode,
        int err = 0;
        struct dentry *hidden_dentry = NULL;
        struct dentry *hidden_parent_dentry = NULL;
-       int bindex = 0, bstart;
+       int bstart;
 
        BUG_ON(!is_valid_dentry(dentry));
 
        unionfs_lock_dentry(dentry);
-       bstart = dbstart(dentry);
+       bstart = 0;
 
-       hidden_dentry = unionfs_lower_dentry(dentry);
-
-       for (bindex = bstart; bindex >= 0; bindex--) {
-               if (is_robranch_super(dentry->d_sb, bindex))
-                       continue;
+       if (is_robranch_super(dentry->d_sb, bstart))
+                       goto out;
 
-               hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
-               if (!hidden_dentry) {
-                       hidden_dentry = create_parents(dir, dentry, bindex);
-                       if (IS_ERR(hidden_dentry)) {
-                               printk(KERN_DEBUG
-                                      "failed to create parents on %d, err = 
%ld\n",
-                                      bindex, PTR_ERR(hidden_dentry));
-                               continue;
-                       }
+       hidden_dentry = unionfs_lower_dentry_idx(dentry, bstart);
+       if (!hidden_dentry) {
+               hidden_dentry = create_parents(dir, dentry, bstart);
+               if (IS_ERR(hidden_dentry)) {
+                       printk(KERN_DEBUG
+                              "failed to create parents on %d, err = %ld\n",
+                              bstart, 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);
+       }
+       
+       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) {
+               hidden_dentry = lookup_one_len(dentry->d_name.name,
+                               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;
                }
+       }
 
-               err = vfs_mknod(hidden_parent_dentry->d_inode,
-                               hidden_dentry, mode, dev);
+       hidden_parent_dentry = lock_parent(hidden_dentry);
+       if (IS_ERR(hidden_parent_dentry)) {
+               err = PTR_ERR(hidden_parent_dentry);
+               goto out;
+       }
 
-               if (err) {
-                       unlock_dir(hidden_parent_dentry);
-                       break;
-               }
-               /* update odf and remove any wh before interpose*/
-               err = odf_lookup(dentry->d_parent, dentry, 
-                       ODF_LOOKUP_FILE|ODF_LOOKUP_RMV_WH);
-               if (!err)
-                       err = odf_purge_dir_cache(dentry->d_parent); 
-               if (!err)
-                       err = unionfs_interpose(dentry, dir->i_sb, 0);
-               if (!err) {
-                       fsstack_copy_attr_times(dir,
-                                       hidden_parent_dentry->d_inode);
-                       fsstack_copy_inode_size(dir,
-                                       hidden_parent_dentry->d_inode);
-                       /* update number of links on parent directory */
-                       dir->i_nlink = unionfs_get_nlinks(dir);
-               }
+       err = vfs_mknod(hidden_parent_dentry->d_inode,
+                       hidden_dentry, mode, dev);
+
+       if (err) {
                unlock_dir(hidden_parent_dentry);
+               goto out;
+       }
 
-               break;
+       /* update odf and remove any wh before interpose*/
+       err = odf_lookup(dentry->d_parent, dentry, 
+               ODF_LOOKUP_FILE|ODF_LOOKUP_RMV_WH);
+       if (!err)
+               err = odf_purge_dir_cache(dentry->d_parent); 
+       if (!err)
+               err = unionfs_interpose(dentry, dir->i_sb, 0);
+       if (!err) {
+               fsstack_copy_attr_times(dir,
+                               hidden_parent_dentry->d_inode);
+               fsstack_copy_inode_size(dir,
+                               hidden_parent_dentry->d_inode);
+               /* update number of links on parent directory */
+               dir->i_nlink = unionfs_get_nlinks(dir);
        }
+       unlock_dir(hidden_parent_dentry);
 
 out:
        if (!dentry->d_inode)
diff --git a/fs/unionfs/odf.c b/fs/unionfs/odf.c
index 5442c78..cad8d86 100644
--- a/fs/unionfs/odf.c
+++ b/fs/unionfs/odf.c
@@ -589,7 +589,7 @@ out:
 int odf_purge_dir_cache(struct dentry *dentry)
 {
        int err = 0;
-       struct iattr attr;
+       //struct iattr attr;
        struct odf_dentry_info *odi = NULL;
 
        odi = odf_ic_dir(dentry);
@@ -809,13 +809,6 @@ int __odf_set_opaque(struct inode *i, int branch)
        return err;
 }
 
-void print_uuid(unsigned char *uuid)
-{
-       int i;
-       for (i = 0; i < 4; i++){
-               printk("%x-", *((unsigned int *) ((uuid+i*4))));                
}
-       printk("\n");
-}
 /* 
  * Requires an odf_sb_info, returns the dirs= part of the mount options 
  */
@@ -887,7 +880,6 @@ char *odf_get_options(struct odf_sb_info *odf_sb)
                file->f_op->read(file, (char*)&le32, sizeof(__le32), 
&file->f_pos);
                perms = le32_to_cpu(le32);
                file->f_op->read(file, (char*)uuid, UUID_LEN, &file->f_pos);
-               print_uuid(uuid);
                ptr+=len;
                if (perms == MAY_READ)
                        sprintf(ptr,"=ro:");
@@ -1025,7 +1017,6 @@ int odf_put_options(struct super_block* sb_union, struct 
unionfs_dentry_info *hi
                        generate_random_uuid(uuid + count * UUID_LEN);
                else
                        memcpy(uuid + count * UUID_LEN, uuid + i * UUID_LEN, 
UUID_LEN);
-               print_uuid(uuid + count * UUID_LEN);
                bid[count] = (u32)m;
 
                /* write */
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 20318fa..31e3ca2 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -311,6 +311,7 @@ int unionfs_unlink(struct inode *dir, struct dentry 
*dentry);
 int unionfs_rmdir(struct inode *dir, struct dentry *dentry);
 
 int __unionfs_d_revalidate_chain(struct dentry *dentry, struct nameidata *nd);
+int unionfs_force_rmdir(struct vfsmount *mnt, struct dentry *hidden_dentry, 
int bindex);
 
 /* The values for unionfs_interpose's flag. */
 #define INTERPOSE_DEFAULT      0
_______________________________________________
unionfs-cvs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs-cvs

Reply via email to