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