commit 752243a083168fca1106c4b3041a645cf167145e
Author: Yiannis Pericleous <[EMAIL PROTECTED]>
Date:   Tue Feb 6 18:59:05 2007 -0500

    Fixed unlink and rmdir to go L->R removing and create wh only on failure
    
    Fixed inode create to remove wh
    + some other whiteout related fixes

diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index c9df99d..9e6341d 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -296,7 +296,7 @@ int unionfs_file_revalidate(struct file *file, int 
willwrite)
                err = -ESTALE;
                goto out_nofree;
        }
-
+       
        sbgen = atomic_read(&UNIONFS_SB(sb)->generation);
        dgen = atomic_read(&UNIONFS_D(dentry)->generation);
        fgen = atomic_read(&UNIONFS_F(file)->generation);
diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c
index e6dc488..89387de 100644
--- a/fs/unionfs/dirfops.c
+++ b/fs/unionfs/dirfops.c
@@ -97,7 +97,7 @@ static int unionfs_filldir(void *dirent, const char *name, 
int namelen,
 
 out:
        if (odi)
-               odf_clear_info(odi);
+               odf_clear_info(&odi);
        return err;
 }
 
diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
index 327e9bc..3b43b79 100644
--- a/fs/unionfs/dirhelper.c
+++ b/fs/unionfs/dirhelper.c
@@ -159,7 +159,7 @@ static int readdir_util_callback(void *dirent, const char 
*name, int namelen,
                odi = NULL;
                goto out;
        }
-       if (odi && !odi->whiteout)
+       if ((odi && !odi->whiteout) || !odi)
                err = -ENOTEMPTY;
        goto out; /* FIXME: bypass old code */
 
@@ -185,7 +185,7 @@ static int readdir_util_callback(void *dirent, const char 
*name, int namelen,
 
 out:
        if (odi)
-               odf_clear_info(odi);
+               odf_clear_info(&odi);
        buf->err = err;
        return err;
 }
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 97dad8c..9a9687c 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -191,6 +191,8 @@ static int unionfs_create(struct inode *parent, struct 
dentry *dentry,
                        if (!IS_COPYUP_ERR(err))
                                break;
                } else {
+                       UNIONFS_D(dentry)->odf_info =
+                               odf_lookup(dentry->d_parent, dentry, 
ODF_LOOKUP_FILE|ODF_LOOKUP_RMV_WH);
                        err = unionfs_interpose(dentry, parent->i_sb, 0);
                        if (!err) {
                                fsstack_copy_attr_times(parent,
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index aec8f73..91213bf 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -296,7 +296,6 @@ out_negative:
                nd->dentry = dentry;
                /* FIXME: fix following line for mount point crossing */
                nd->mnt = unionfs_lower_mnt_idx(parent_dentry, bindex);
-
                first_hidden_dentry = lookup_one_len_nd(name, hidden_dir_dentry,
                                                     namelen, nd);
                first_dentry_offset = bindex;
@@ -325,6 +324,15 @@ out_negative:
 /* This part of the code is for positive dentries. */
 out_positive:
        BUG_ON(dentry_count <= 0);
+       
+       /* if what we found is a whiteout then this is really a negative dentry 
*/
+       UNIONFS_D(dentry)->odf_info = odf_lookup(dentry->d_parent, dentry, 0);
+       if (UNIONFS_D(dentry)->odf_info && 
UNIONFS_D(dentry)->odf_info->whiteout){
+               first_dentry_offset = -1;
+               bindex = 0; /* FIXME: Always create at branch 0? can we 
guarantee that */
+                           /* if there is a wh, then branch 0 has negative 
dentry?    */
+               goto out;
+       }
 
        /* If we're holding onto the first negative dentry & corresponding
         * vfsmount - throw it out.
@@ -334,6 +342,13 @@ out_positive:
 
        /* Partial lookups need to reinterpose, or throw away older negs. */
        if (lookupmode == INTERPOSE_PARTIAL) {
+               UNIONFS_D(dentry)->odf_info = odf_lookup(
+                                       parent_dentry, dentry, 0);
+               if (UNIONFS_D(dentry)->odf_info &&
+                               UNIONFS_D(dentry)->odf_info->whiteout){
+                       err = -ENOENT;
+                       goto out_drop;
+               }
                if (dentry->d_inode) {
                        unionfs_reinterpose(dentry);
                        goto out;
@@ -433,8 +448,7 @@ void free_dentry_private_data(struct unionfs_dentry_info 
*udi)
 {
        if (!udi)
                return;
-       odf_clear_info(udi->odf_info);
-       udi->odf_info = NULL;
+       odf_clear_info(&udi->odf_info);
        kmem_cache_free(unionfs_dentry_cachep, udi);
 }
 
diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index 17029d0..415a744 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -71,14 +71,18 @@ int unionfs_interpose(struct dentry *dentry, struct 
super_block *sb, int flag)
        } else {
                ino_t ino;
                /* get unique inode number for unionfs */
-               if ( !UNIONFS_D(dentry)->odf_info )
+               if (!UNIONFS_D(dentry)->odf_info)
                        UNIONFS_D(dentry)->odf_info = 
-                               odf_lookup(dentry->d_parent, dentry, odf_flag | 
ODF_LOOKUP_RMV_WH);
+                               odf_lookup(dentry->d_parent, dentry, odf_flag);
                if (IS_ERR(UNIONFS_D(dentry)->odf_info)){
                        printk("unionfs_interpose: odf failed to create 
dentry\n");
                        err = PTR_ERR(UNIONFS_D(dentry)->odf_info);
                        goto out;
                }
+               if (UNIONFS_D(dentry)->odf_info->whiteout){
+                       err = -ENOENT;
+                       goto out;
+               }
                
                ino = UNIONFS_D(dentry)->odf_info->inum;
 
diff --git a/fs/unionfs/odf.c b/fs/unionfs/odf.c
index ecb1a4d..c7603ef 100644
--- a/fs/unionfs/odf.c
+++ b/fs/unionfs/odf.c
@@ -85,7 +85,10 @@ out:
        return odi;
 }
 
-int odf_reclaim_dir(struct dentry *dir)
+/*
+ * Moves the dentry to /odf/reclaim with the name of its inode no
+ */
+int odf_reclaim(struct dentry *dentry)
 {
        struct inode *old_dir, *new_dir;
        struct dentry *old_dentry, *new_dentry;
@@ -107,26 +110,26 @@ int odf_reclaim_dir(struct dentry *dir)
                err = -ENOMEM;
                goto out;
        }
-       sprintf(new_name,"%lu", dir->d_inode->i_ino);   
+       sprintf(new_name,"%lu", dentry->d_inode->i_ino);        
 
-       old_dentry = dir;
-       old_dir = dir->d_parent->d_inode;
+       old_dentry = dentry;
+       old_dir = dentry->d_parent->d_inode;
        new_dir = odi->dentry->d_inode;
        new_dentry = lookup_one_len(new_name, odi->dentry, strlen(new_name));
        if(IS_ERR(new_dentry)){
-               err = PTR_ERR(odi);
+               err = PTR_ERR(new_dentry);
                goto out;
        }
-       if(new_dentry->d_inode) {
-               /* FIXME: this should not happen */
-               printk(KERN_WARNING "odf_reclaim_dir: %s already exists in 
/odf/reclaim\n", new_name);
-       }
+       
+       /* this should never happen */
+       BUG_ON(new_dentry->d_inode);
+       
        err = vfs_rename(old_dir, old_dentry, new_dir, new_dentry);
        dput(new_dentry);
 out:
        kfree(new_name);
        if (odi)
-               odf_clear_info(odi);
+               odf_clear_info(&odi);
        return err;
 }
 
@@ -137,10 +140,8 @@ out:
  */
 struct odf_dentry_info *odf_lookup(struct dentry *parent, struct dentry 
*dentry, int flags)
 {      
-       if (UNIONFS_D(dentry)->odf_info) {
-               odf_clear_info(UNIONFS_D(dentry)->odf_info);
-               UNIONFS_D(dentry)->odf_info = NULL;
-       }
+       if (UNIONFS_D(dentry)->odf_info)
+               odf_clear_info(&UNIONFS_D(dentry)->odf_info);
 
        return odf_lookup_name(parent, dentry->d_name.name, dentry->d_name.len, 
flags);
 }
@@ -150,7 +151,7 @@ struct odf_dentry_info *odf_lookup_name(struct dentry 
*parent, const char *name,
        struct odf_dentry_info *odi = NULL;
        struct inode *odf_i_dir = UNIONFS_D(parent)->odf_info->dentry->d_inode;
        int err = 0;
-
+       
        odf_dentry = lookup_one_len(name, UNIONFS_D(parent)->odf_info->dentry, 
len);
        
        /* if create flags are set, remove existing whiteout */
@@ -197,7 +198,6 @@ struct odf_dentry_info *odf_lookup_name(struct dentry 
*parent, const char *name,
        
        odi = odf_fill_info(odf_dentry);
        dput(odf_dentry); /* since we dget in fill_info */
-
 out:
        return odi;
 }
@@ -214,15 +214,17 @@ int odf_create_wh(struct dentry *dentry)
        BUG_ON(!UNIONFS_D(dentry)->odf_info);
 
        odf_dentry = UNIONFS_D(dentry)->odf_info->dentry;
-       
-       if (S_ISDIR(odf_dentry->d_inode->i_mode))
-               err = odf_reclaim_dir(odf_dentry);
-       else
-               err = vfs_unlink(odf_dentry->d_parent->d_inode, odf_dentry);
+       if (odf_is_wh_i(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_clear_info(UNIONFS_D(dentry)->odf_info);
-       UNIONFS_D(dentry)->odf_info = NULL;
+
+       odf_clear_info(&UNIONFS_D(dentry)->odf_info);
+
+       /* now create a new file and make it a whiteout */
        UNIONFS_D(dentry)->odf_info = odf_lookup(dentry->d_parent, dentry, 
ODF_LOOKUP_FILE);
        if (!UNIONFS_D(dentry)->odf_info){
                err = PTR_ERR(UNIONFS_D(dentry)->odf_info); /* odf out of 
space? */
@@ -234,6 +236,57 @@ out:
 }
 
 /*
+ * Removes an entry from the odf
+ * File entries will be unlinked, directory entries will be reclaimed
+ * Flags : ODF_RMV_WH - remove entry only if whiteout
+ *         ODF_RMV_NOTWH - remove entry only if not whiteout
+ *         ODF_RMV_ANY - remove entry regardless of state
+ */
+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 */
+       UNIONFS_D(dentry)->odf_info = odf_lookup(dentry->d_parent, dentry, 0);
+       odi =  UNIONFS_D(dentry)->odf_info;
+       if (IS_ERR(odi) || !odi) {
+               odi = NULL;
+               err = -ENOENT;
+               goto out;
+       }
+       
+       /* should we remove? */
+       if (odf_is_wh(dentry)) {
+               if (flags & ODF_RMV_WH)
+                       rmv = 1;
+       }
+       else {
+               if (flags & ODF_RMV_NOTWH)
+                       rmv = 1;
+       }
+
+       if (!rmv)
+               goto out;
+
+       /* remove */
+       if (S_ISDIR(odi->dentry->d_inode->i_mode))
+               err = odf_reclaim(odi->dentry);
+       else
+               err = vfs_unlink(odi->dentry->d_parent->d_inode, odi->dentry);
+       if (err)
+               goto out;
+
+       /* clean up */
+       odf_clear_info(&UNIONFS_D(dentry)->odf_info);
+
+out:
+       return err;
+}
+
+/*
  * Sets the whiteout flag
  * The dentry version takes a UnionFS dentry, the inode one an ODF inode
  */
@@ -416,7 +469,7 @@ int odf_is_new(int unset)
        
        /* check the new bit, for now check if file newbit exists */
        err = path_lookup(ODF_NEWBIT, LOOKUP_FOLLOW, &nd);      
-       if (err) 
+       if (likely(err)) 
                return 0; /* is not new */
 
        newbit = nd.dentry;
@@ -546,13 +599,13 @@ struct odf_dentry_info *odf_fill_info(struct dentry 
*odf_dentry)
 /* 
  * Clears the odf_info data 
  */
-void odf_clear_info(struct odf_dentry_info *odi)
+void odf_clear_info(struct odf_dentry_info **odi)
 {
-       if (!odi)
+       if (!(*odi))
                return;
-       if (odi->dentry)
-               dput(odi->dentry);
-       kfree(odi);
+       dput((*odi)->dentry);
+       kfree(*odi);
+       *odi = NULL;
 }
 
 
diff --git a/fs/unionfs/odf.h b/fs/unionfs/odf.h
index bcd6b39..2fc01fd 100644
--- a/fs/unionfs/odf.h
+++ b/fs/unionfs/odf.h
@@ -20,9 +20,14 @@
 #define ODF_LOOKUP_DIR 2
 #define ODF_LOOKUP_RMV_WH 4
 
+/* Unlink/Remove flags */
+#define ODF_RMV_WH 1
+#define ODF_RMV_NOTWH 2
+#define ODF_RMV_ANY (ODF_RMV_WH|ODF_RMV_NOTWH)
+
 /* Inode flags */ 
-#define ODF_WHITEOUT 0x01000000
-#define ODF_OPAQUE 0x02000000
+#define ODF_WHITEOUT FS_SECRM_FL //0x01000000
+#define ODF_OPAQUE FS_COMPR_FL // 0x02000000
 #define ODF_OPQ_BASE 26 /* 0x04000000, bits 26 - 30 */
 #define ODF_OPQ_BITS 5
 
@@ -38,7 +43,11 @@ struct odf_dentry_info *odf_lookup(struct dentry *parent, 
struct dentry *dentry,
 struct odf_dentry_info *odf_lookup_name(struct dentry *parent, const char 
*name, int len, int flags);
 struct odf_dentry_info *odf_getns(void);
 struct odf_dentry_info *odf_fill_info(struct dentry *odf_dentry);
-void odf_clear_info(struct odf_dentry_info *odi);
+void odf_clear_info(struct odf_dentry_info **odi);
+
+/* unlink */
+int odf_remove(struct dentry *dentry, int flags);
+int odf_reclaim(struct dentry *dentry);
 
 /* whiteouts */
 int odf_set_wh(struct dentry *dentry, int boolean);
diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
index 21a8e4a..e563bbc 100644
--- a/fs/unionfs/subr.c
+++ b/fs/unionfs/subr.c
@@ -30,7 +30,6 @@ int create_whiteout(struct dentry *dentry, int start)
        struct dentry *hidden_wh_dentry;
        char *name = NULL;
        int err = -EINVAL;
-
        verify_locked(dentry);
 
        bstart = dbstart(dentry);
diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c
index dd1dd9c..dc846db 100644
--- a/fs/unionfs/unlink.c
+++ b/fs/unionfs/unlink.c
@@ -69,6 +69,56 @@ out:
 
        return err;
 }
+static int unionfs_do_unlink(struct inode *dir, struct dentry *dentry)
+{
+       struct dentry *hidden_dentry;
+       struct dentry *hidden_dir_dentry;
+       int bstart, bend, bindex;
+       int err = 0;
+
+       if ((err = unionfs_partial_lookup(dentry)))
+               goto out;
+
+       bstart = dbstart(dentry);
+       bend = dbend(dentry);
+
+       for (bindex = bstart; bindex <= bend; bindex++) {
+               hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+               if (!hidden_dentry)
+                       continue;
+                       
+               hidden_dir_dentry = lock_parent(hidden_dentry);
+
+               /* avoid destroying the hidden inode if the file is in use */
+               dget(hidden_dentry);
+               if (!(err = is_robranch_super(dentry->d_sb, bindex)))
+                       err = vfs_unlink(hidden_dir_dentry->d_inode, 
hidden_dentry);
+               else
+                       err = -EROFS;
+               dput(hidden_dentry);
+               fsstack_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+               unlock_dir(hidden_dir_dentry);
+               
+               if (err)
+                       break;
+       }
+
+       if (err) {
+               if (err == -EIO)
+                       printk(KERN_WARNING "unionfs_unlink: IO error unlinking 
from branch %d\n", bindex);
+               err = odf_create_wh(dentry);
+       }
+
+out:
+       if (!err)
+               dentry->d_inode->i_nlink--;
+
+       /* We don't want to leave negative leftover dentries for revalidate. */
+       if (!err && (dbopaque(dentry) != -1))
+               update_bstart(dentry);
+
+       return err;
+}
 
 int unionfs_unlink(struct inode *dir, struct dentry *dentry)
 {
@@ -78,11 +128,19 @@ int unionfs_unlink(struct inode *dir, struct dentry 
*dentry)
 
        unionfs_lock_dentry(dentry);
 
-       err = unionfs_unlink_whiteout(dir, dentry);
+       //err = unionfs_unlink_whiteout(dir, dentry);
+       err = unionfs_do_unlink(dir, dentry);
        /* call d_drop so the system "forgets" about us */
-       if (!err)
+       if (!err) {
+               /* If there was a whiteout created then the original
+                * ODF entry was removed and replaced by a previous call
+                * to create_whiteout. If not, the original entry is still
+                * here, so we just remove it only if its not a whiteout
+                */ 
+               /* FIXME: what if this fails? */
+               err = odf_remove(dentry, ODF_RMV_NOTWH);
                d_drop(dentry);
-
+       }
        unionfs_unlock_dentry(dentry);
        return err;
 }
@@ -119,10 +177,58 @@ out:
        return err;
 }
 
+static int unionfs_do_rmdir(struct inode *dir, struct dentry *dentry)
+{
+       struct dentry *hidden_dentry;
+       struct dentry *hidden_dir_dentry = NULL;
+       int bstart, bend, bindex;
+       int err = 0;
+
+       if ((err = unionfs_partial_lookup(dentry)))
+               goto out;
+
+       bstart = dbstart(dentry);
+       bend = dbend(dentry);
+
+       for (bindex = bstart; bindex <= bend; bindex++) {
+               hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+               if (!hidden_dentry)
+                       continue;
+
+               hidden_dir_dentry = lock_parent(hidden_dentry);
+
+               /* avoid destroying the hidden inode if the file is in use */
+               dget(hidden_dentry);
+               if (!(err = is_robranch_super(dentry->d_sb, bindex)))
+                       err = vfs_rmdir(hidden_dir_dentry->d_inode, 
hidden_dentry);
+               else
+                       err = -EROFS;
+               dput(hidden_dentry);
+               fsstack_copy_attr_times(dir, hidden_dir_dentry->d_inode);
+
+               /* propagate number of hard-links */
+               /* FIXME: does this belong in or out of the loop? */
+               dentry->d_inode->i_nlink = unionfs_get_nlinks(dentry->d_inode);
+               unlock_dir(hidden_dir_dentry);
+
+               if (err)
+                       break;
+       }
+       if (err) {
+               if (err == -EIO)
+                       printk(KERN_WARNING "unionfs_unlink: IO error unlinking 
from branch %d\n", bindex);
+               err = odf_create_wh(dentry);
+       }
+
+out:
+       return err;
+}
+
+
 int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
        int err = 0;
-       struct unionfs_dir_state *namelist = NULL;
+       struct unionfs_dir_state *namelist = NULL; /* FIXME: dont need this now 
*/
 
        BUG_ON(!is_valid_dentry(dentry));
 
@@ -133,7 +239,14 @@ int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
        if (err)
                goto out;
 
-       err = unionfs_rmdir_first(dir, dentry, namelist);
+//     err = unionfs_rmdir_first(dir, dentry, namelist);
+       err = unionfs_do_rmdir(dir, dentry);
+       if (err)
+               goto out;
+       /* FIXME: what if this fails? */
+       err = odf_remove(dentry, ODF_RMV_NOTWH);
+       goto out; /* FIXME: bypass old code */
+       
        /* create whiteout */
        if (!err)
                err = create_whiteout(dentry, dbstart(dentry));
_______________________________________________
unionfs-cvs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs-cvs

Reply via email to