commit 5a741b327f8d899c6b6e64fadf911345bf7f90ee
Author: Yiannis Pericleous <[EMAIL PROTECTED]>
Date: Sun Mar 4 20:03:40 2007 -0500
silly rename and other rename fixes
diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 3bb94fa..9754d8d 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -111,6 +111,71 @@ static int find_new_branch_index(struct file *file, int
bindex,
return -1;
}
+/* Same as copyup_deleted_file, but with rename instead of copyup
+ */
+int unionfs_silly_rename(struct dentry *dentry, struct dentry *hidden_dentry)
+{
+ static unsigned int counter;
+ const int i_inosize = sizeof(hidden_dentry->d_inode->i_ino) * 2;
+ const int countersize = sizeof(counter) * 2;
+ const int nlen = sizeof(".unionfs") + i_inosize + countersize - 1;
+ char name[nlen + 1];
+
+ int err;
+ struct dentry *tmp_dentry = NULL;
+ struct dentry *hidden_old_dir_dentry;
+ struct dentry *hidden_new_dir_dentry;
+ struct odf_dentry_info *odi = NULL;
+
+ sprintf(name, ".unionfs%*.*lx",
+ i_inosize, i_inosize, hidden_dentry->d_inode->i_ino);
+
+ tmp_dentry = NULL;
+ do {
+ char *suffix = name + nlen - countersize;
+
+ dput(tmp_dentry);
+ counter++;
+ sprintf(suffix, "%*.*x", countersize, countersize, counter);
+
+ printk(KERN_DEBUG "unionfs: trying to rename %s to %s\n",
+ hidden_dentry->d_name.name, name);
+
+ tmp_dentry = lookup_one_len(name, hidden_dentry->d_parent,
+ strlen(name));
+ if (IS_ERR(tmp_dentry)) {
+ err = PTR_ERR(tmp_dentry);
+ goto out;
+ }
+ } while (tmp_dentry->d_inode != NULL); /* need negative dentry */
+
+ dget(hidden_dentry);
+ hidden_old_dir_dentry = dget_parent(hidden_dentry);
+ hidden_new_dir_dentry = dget_parent(tmp_dentry);
+
+ lock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry);
+
+ err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_dentry,
+ hidden_new_dir_dentry->d_inode, tmp_dentry);
+
+ unlock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry);
+
+ dput(hidden_old_dir_dentry);
+ dput(hidden_new_dir_dentry);
+ dput(hidden_dentry);
+
+ if (err)
+ goto out;
+
+ /* create a whiteout */
+ odi = __odf_lookup(UNIONFS_D(dentry->d_parent)->odf_info,
+ name, strlen(name), ODF_LOOKUP_WH, odi);
+ BUG_ON(IS_ERR(odi) || odi == NULL);
+out:
+ odf_put_info(odi);
+ return err;
+}
+
/* put all references held by upper struct file and free lower file pointer
* array
*/
diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
index 4c2b21f..6ce34ce 100644
--- a/fs/unionfs/dirhelper.c
+++ b/fs/unionfs/dirhelper.c
@@ -21,6 +21,7 @@
#define RD_NONE 0
#define RD_CHECK_EMPTY 1
#define RD_CACHE_ODF 2
+#define RD_CHECK_HD_EMPTY 3
/* The callback structure for check_empty. */
struct unionfs_rdutil_callback {
int err;
@@ -87,19 +88,25 @@ static int readdir_util_callback(void *dirent, const char
*name, int namelen,
*/
int create_flag = d_type == DT_DIR ? ODF_LOOKUP_DIR : ODF_LOOKUP_FILE;
- if (buf->mode == RD_CHECK_EMPTY)
+ if (buf->mode == RD_CHECK_EMPTY || buf->mode == RD_CHECK_HD_EMPTY)
buf->filldir_called = 1;
else
buf->filldir_called++;
if (name[0] == '.' &&
(namelen == 1 || (name[1] == '.' && namelen == 2))){
- if (buf->mode == RD_CHECK_EMPTY)
+ if (buf->mode != RD_CACHE_ODF)
goto out;
else
create_flag = 0;
}
-
+
+ /* we dont care about odf when checking hidden dirs */
+ if (buf->mode == RD_CHECK_HD_EMPTY) {
+ err = -ENOTEMPTY;
+ goto out;
+ }
+
/* check odf */
/* XXX: lookup might require to lock it, so unlock for now */
odf_unlock(UNIONFS_D(buf->dir)->odf_info);
@@ -230,7 +237,6 @@ out:
}
unionfs_read_unlock(sb);
-
return err;
}
@@ -424,3 +430,91 @@ out:
return err;
}
+
+/* Remove the hidden dentry by:
+ * a) rmdir if its a physically empty dir
+ * b) silly rename if its not physically empty
+ * c) unlink if not a dir
+ */
+int unionfs_force_rm(struct dentry *dentry, struct dentry **hidden_dentry, int
bindex)
+{
+ struct file *hidden_file;
+ struct super_block *sb;
+ struct unionfs_rdutil_callback *buf = NULL;
+ int err;
+
+ sb = dentry->d_sb;
+ if (!S_ISDIR((*hidden_dentry)->d_inode->i_mode)) {
+ err = vfs_unlink((*hidden_dentry)->d_parent->d_inode,
+ (*hidden_dentry));
+ if (err)
+ goto out;
+ goto refresh;
+ }
+
+ /* check if dir is empty */
+ buf = kmalloc(sizeof(struct unionfs_rdutil_callback), GFP_KERNEL);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+ buf->err = 0;
+ buf->mode = RD_CHECK_HD_EMPTY;
+ buf->rdstate = NULL;
+ buf->dir = NULL;
+ buf->filp = NULL;
+
+ dget(*hidden_dentry);
+ mntget(unionfs_lower_mnt_idx(dentry, bindex));
+ branchget(sb, bindex);
+ hidden_file = dentry_open(*hidden_dentry,
+ unionfs_lower_mnt_idx(dentry, bindex),
+ O_RDONLY);
+ if (IS_ERR(hidden_file)) {
+ err = PTR_ERR(hidden_file);
+ dput(*hidden_dentry);
+ branchput(sb, bindex);
+ goto out;
+ }
+
+ do {
+ buf->filldir_called = 0;
+ err = vfs_readdir(hidden_file,
+ readdir_util_callback, buf);
+ if (buf->err)
+ err = buf->err;
+ } while ((err >= 0) && buf->filldir_called);
+
+ /* fput calls dput for hidden_dentry */
+ fput(hidden_file);
+ branchput(sb, bindex);
+
+ if (err == 0)
+ err = vfs_rmdir((*hidden_dentry)->d_parent->d_inode,
*hidden_dentry);
+
+ else if (err == -ENOTEMPTY)
+ err = unionfs_silly_rename(dentry, *hidden_dentry);
+
+ if (err)
+ goto out;
+
+refresh:
+ *hidden_dentry = lookup_one_len(dentry->d_name.name,
+ (*hidden_dentry)->d_parent,
+ dentry->d_name.len);
+ if (IS_ERR(*hidden_dentry)) {
+ err = PTR_ERR(*hidden_dentry);
+ goto out;
+ }
+ BUG_ON((*hidden_dentry)->d_inode);
+ dput(unionfs_lower_dentry_idx(dentry, bindex));
+ if (dentry->d_inode) {
+ iput(unionfs_lower_inode_idx(dentry->d_inode, bindex));
+ unionfs_set_lower_inode_idx(dentry->d_inode, bindex, NULL);
+ }
+ unionfs_set_lower_dentry_idx(dentry, bindex, *hidden_dentry);
+out:
+ if (buf)
+ kfree(buf);
+ return err;
+}
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 260065d..222dcab 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -18,24 +18,6 @@
#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)
{
@@ -89,21 +71,12 @@ static int unionfs_create(struct inode *parent, struct
dentry *dentry,
}
}
- if (hidden_dentry->d_inode) {
- err = do_force_rm(dentry, hidden_dentry, bstart);
+ if (hidden_dentry->d_inode && UNIONFS_D(dentry)->odf_info->whiteout) {
+ err = unionfs_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);
- 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);
@@ -211,20 +184,11 @@ 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 (hidden_new_dentry->d_inode &&
UNIONFS_D(new_dentry)->odf_info->whiteout) {
+ err = unionfs_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);
@@ -246,23 +210,15 @@ docopyup:
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);
+ if (hidden_new_dentry->d_inode &&
+
UNIONFS_D(new_dentry)->odf_info->whiteout) {
+ err = unionfs_force_rm(new_dentry,
+ &hidden_new_dentry,
+ dbstart(old_dentry));
+ if (err)
goto out;
- }
}
-
+
hidden_dir_dentry =
lock_parent(hidden_new_dentry);
/* do vfs_link */
@@ -338,22 +294,11 @@ static int unionfs_symlink(struct inode *dir, struct
dentry *dentry,
}
}
- if (hidden_dentry->d_inode) {
- err = do_force_rm(dentry, hidden_dentry, bstart);
+ if (hidden_dentry->d_inode && UNIONFS_D(dentry)->odf_info->whiteout) {
+ err = unionfs_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);
- if (!hidden_dentry || IS_ERR(hidden_dentry)) {
- if (IS_ERR(hidden_dentry))
- err = PTR_ERR(hidden_dentry);
- goto out;
- }
- }
hidden_dir_dentry = lock_parent(hidden_dentry);
@@ -420,22 +365,11 @@ static int unionfs_mkdir(struct inode *parent, struct
dentry *dentry, int mode)
}
}
- if (hidden_dentry->d_inode) {
- err = do_force_rm(dentry, hidden_dentry, bindex);
+ if (hidden_dentry->d_inode && UNIONFS_D(dentry)->odf_info->whiteout) {
+ err = unionfs_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);
- 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);
@@ -520,22 +454,11 @@ static int unionfs_mknod(struct inode *dir, struct dentry
*dentry, int mode,
}
}
- if (hidden_dentry->d_inode) {
- err = do_force_rm(dentry, hidden_dentry, bstart);
+ if (hidden_dentry->d_inode && UNIONFS_D(dentry)->odf_info->whiteout) {
+ err = unionfs_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);
- 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)) {
diff --git a/fs/unionfs/odf.c b/fs/unionfs/odf.c
index 7a3a5ee..19e26a3 100644
--- a/fs/unionfs/odf.c
+++ b/fs/unionfs/odf.c
@@ -331,8 +331,6 @@ int odf_link(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);
- //err = odf_lookup(new_dentry->d_parent, new_dentry, 0);
out:
return err;
}
@@ -349,6 +347,7 @@ out:
* - ODF_LOOKUP_RMV_WH: if lookup returns a wh, remove it
* (useful for create where we want to rmv wh's)
* - ODF_LOOKUP_OPQ: if a dir is created, set it opaque (branch 0)
+ * - ODF_LOOKUP_WH: create a whiteout
*/
int odf_lookup(struct dentry *parent, struct dentry *dentry, int flags)
{
@@ -372,10 +371,13 @@ struct odf_dentry_info *__odf_lookup(struct
odf_dentry_info *parent, const char
struct dentry *odf_dentry;
struct odf_dentry_info *odi = old_odi;
struct inode *odf_i_dir = parent->dentry->d_inode;
- int opaque, err = 0;
+ int opaque, whiteout, err = 0;
uid_t olduid;
gid_t oldgid;
+ BUG_ON((flags & ODF_LOOKUP_FILE) && (flags & ODF_LOOKUP_DIR));
+ BUG_ON((flags & ODF_LOOKUP_WH) && (flags != ODF_LOOKUP_WH));
+
odf_dentry = lookup_one_len(name, parent->dentry, len);
if (IS_ERR(odf_dentry)) {
@@ -393,6 +395,10 @@ struct odf_dentry_info *__odf_lookup(struct
odf_dentry_info *parent, const char
odf_dentry = lookup_one_len(name, parent->dentry, len);
}
}
+
+ /* if we should not lookup_wh if a non-wh entry exists */
+ if (flags & ODF_LOOKUP_WH)
+ BUG_ON(odf_dentry->d_inode &&
!__odf_is_wh(odf_dentry->d_inode));
/* create inode in odf if dont exist */
if (!odf_dentry->d_inode) {
@@ -406,7 +412,7 @@ struct odf_dentry_info *__odf_lookup(struct odf_dentry_info
*parent, const char
/* FIXME need to check hardlinks before create */
odf_lock(parent); /* lock parent */
- if (flags & ODF_LOOKUP_FILE) {
+ if (flags & ODF_LOOKUP_FILE || flags & ODF_LOOKUP_WH) {
current->fsuid = 0;
current->fsgid = 0;
err = vfs_create(odf_i_dir, odf_dentry, S_IRWXUGO, 0 );
@@ -436,11 +442,11 @@ struct odf_dentry_info *__odf_lookup(struct
odf_dentry_info *parent, const char
goto out;
}
-
/* set opaqueness to 0 or -1 */
opaque = (flags & ODF_LOOKUP_OPQ) ? 0 : -1;
+ whiteout = (flags & ODF_LOOKUP_WH) ? 1 : 0;
__odf_set_opaque(odf_dentry->d_inode, opaque);
- __odf_set_wh(odf_dentry->d_inode,0);
+ __odf_set_wh(odf_dentry->d_inode,whiteout);
}
else {
/* FIXME check for hardlinks */
diff --git a/fs/unionfs/odf.h b/fs/unionfs/odf.h
index 658c627..bc6b152 100644
--- a/fs/unionfs/odf.h
+++ b/fs/unionfs/odf.h
@@ -24,6 +24,7 @@
#define ODF_LOOKUP_DIR 2
#define ODF_LOOKUP_RMV_WH 4
#define ODF_LOOKUP_OPQ 8
+#define ODF_LOOKUP_WH 16
/* Unlink/Remove flags */
#define ODF_RMV_WH 1
diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index 5b3062c..5552f84 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -164,14 +164,10 @@ static int do_unionfs_rename(struct inode *old_dir,
if (err)
break;
}
- if (err) {
- if (!IS_COPYUP_ERR(err))
- goto out;
+ if (err)
do_copyup = 1;
- }
- else if (new_bstart < old_bstart){
+ else if (new_bstart < old_bstart)
do_copyup = 1;
- }
else
revert = 1;
@@ -183,15 +179,14 @@ static int do_unionfs_rename(struct inode *old_dir,
/* 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,
+ err = copyup_empty_dir(new_dir,
+ new_dentry,
old_dentry->d_inode->i_mode);
if (err)
goto revert;
- err = do_rename(old_dir, old_dentry, new_dir,
- new_dentry, bindex);
}
else {
+ /* otherwise do regular copyup & rename */
err = copyup_dentry(old_dentry->d_parent->d_inode,
old_dentry, old_bstart, bindex, NULL,
old_dentry->d_inode->i_size);
@@ -202,23 +197,32 @@ static int do_unionfs_rename(struct inode *old_dir,
goto revert;
}
}
+ else if (err && old_bstart == 0) {
+ if (bindex == 0)
+ /* no changes made, no need to revert, just fail */
+ goto out;
+ /* else, a whiteout will take care of everything */
+ }
/* if all ok, rename in odf */
err = odf_rename(old_dentry, new_dentry);
- if (err)
- goto revert;
- /* FIXME: when should we whiteout? */
+ BUG_ON(err); /* odf should not fail */
+
if (do_copyup != 0) {
err = odf_create_wh(old_dentry);
- if (err)
- goto revert;
+ BUG_ON(err);
}
+
+ /* update the odf dir cache */
+ err = odf_purge_dir_cache(new_dentry->d_parent);
+ if (!err && old_dentry->d_parent != new_dentry->d_parent);
+ err = odf_purge_dir_cache(old_dentry->d_parent);
+ BUG_ON(err);
out:
return err;
revert:
/* Do revert here. */
- /* FIXME: revert odf also? only if create_wh_fails? */
local_err = unionfs_refresh_hidden_dentry(new_dentry, old_bstart);
if (local_err) {
printk(KERN_WARNING "Revert failed in rename: the new refresh "
@@ -323,17 +327,20 @@ int unionfs_rename(struct inode *old_dir, struct dentry
*old_dentry,
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? */
+ * or if it is a logically empty directory remove it */
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 (hidden && hidden->d_inode ) {
+ if (UNIONFS_D(new_dentry)->odf_info->whiteout) {
+ err = unionfs_force_rm(new_dentry, &hidden, 0);
+ if (err)
+ goto out;
+ }
+ else if (S_ISDIR(new_dentry->d_inode->i_mode)) {
+ /* remove it only if logically empty */
+ err = check_empty(new_dentry, NULL);
+ if (err)
+ goto out;
+ err = unionfs_force_rm(new_dentry, &hidden, 0);
if (err)
goto out;
}
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 31e3ca2..71b7eb2 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -312,6 +312,8 @@ 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);
+int unionfs_force_rm(struct dentry *dentry, struct dentry **hidden_dentry, int
bindex);
+int unionfs_silly_rename(struct dentry *dentry, struct dentry *hidden_dentry);
/* 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