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