commit e42950c9a6ba5e4d8b4899913826137abca3ed92
Author: Yiannis Pericleous <[EMAIL PROTECTED]>
Date: Mon Feb 5 18:08:37 2007 -0500
Basic whiteout functionality, rmdir moves dir to reclaim if logically empty
diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c
index 6ff32a0..e6dc488 100644
--- a/fs/unionfs/dirfops.c
+++ b/fs/unionfs/dirfops.c
@@ -28,6 +28,7 @@ static void verify_rdstate_offset(struct unionfs_dir_state
*rdstate)
struct unionfs_getdents_callback {
struct unionfs_dir_state *rdstate;
void *dirent;
+ struct dentry *dir;
int entries_written;
int filldir_called;
int filldir_error;
@@ -41,10 +42,24 @@ static int unionfs_filldir(void *dirent, const char *name,
int namelen,
{
struct unionfs_getdents_callback *buf = dirent;
struct filldir_node *found = NULL;
+ struct odf_dentry_info *odi = NULL;
int err = 0;
int is_wh_entry = 0;
+ int odf_flag = ODF_LOOKUP_FILE;
- buf->filldir_called++;
+ buf->filldir_called++;
+
+ /* odf check whiteouts */
+ if (d_type == DT_DIR)
+ odf_flag = ODF_LOOKUP_DIR;
+ odi = odf_lookup_name(buf->dir, name, namelen, odf_flag);
+ if (IS_ERR(odi)){
+ err = PTR_ERR(odi);
+ odi = NULL;
+ goto out;
+ }
+ if (odi && odi->whiteout)
+ goto out;
if ((namelen > UNIONFS_WHLEN) &&
!strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) {
@@ -81,6 +96,8 @@ static int unionfs_filldir(void *dirent, const char *name,
int namelen,
buf->filldir_error = err;
out:
+ if (odi)
+ odf_clear_info(odi);
return err;
}
@@ -130,6 +147,7 @@ static int unionfs_readdir(struct file *file, void *dirent,
filldir_t filldir)
buf.filldir_called = 0;
buf.filldir_error = 0;
buf.entries_written = 0;
+ buf.dir = file->f_dentry;
buf.dirent = dirent;
buf.filldir = filldir;
buf.rdstate = uds;
diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
index bd15eb4..327e9bc 100644
--- a/fs/unionfs/dirhelper.c
+++ b/fs/unionfs/dirhelper.c
@@ -133,6 +133,7 @@ out:
struct unionfs_rdutil_callback {
int err;
int filldir_called;
+ struct dentry *dir;
struct unionfs_dir_state *rdstate;
int mode;
};
@@ -145,14 +146,25 @@ static int readdir_util_callback(void *dirent, const char
*name, int namelen,
struct unionfs_rdutil_callback *buf = dirent;
int whiteout = 0;
struct filldir_node *found;
+ struct odf_dentry_info *odi = NULL;
buf->filldir_called = 1;
if (name[0] == '.' && (namelen == 1 || (name[1] == '.' && namelen ==
2)))
goto out;
+ /* check odf odf */
+ odi = odf_lookup_name(buf->dir, name, namelen, 0);
+ if (IS_ERR(odi)){
+ odi = NULL;
+ goto out;
+ }
+ if (odi && !odi->whiteout)
+ err = -ENOTEMPTY;
+ goto out; /* FIXME: bypass old code */
+
if (namelen > UNIONFS_WHLEN &&
- !strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) {
+ !strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) {
namelen -= UNIONFS_WHLEN;
name += UNIONFS_WHLEN;
whiteout = 1;
@@ -172,6 +184,8 @@ static int readdir_util_callback(void *dirent, const char
*name, int namelen,
buf->rdstate->bindex, whiteout);
out:
+ if (odi)
+ odf_clear_info(odi);
buf->err = err;
return err;
}
@@ -243,6 +257,7 @@ int check_empty(struct dentry *dentry, struct
unionfs_dir_state **namelist)
do {
buf->filldir_called = 0;
+ buf->dir = dentry;
buf->rdstate->bindex = bindex;
err = vfs_readdir(hidden_file,
readdir_util_callback, buf);
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 830a4e5..aec8f73 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -332,9 +332,6 @@ out_positive:
dput(first_hidden_dentry);
unionfs_mntput(first_dentry, first_dentry_offset);
- /* check odf */
- UNIONFS_D(dentry)->odf_info = odf_lookup(parent_dentry, dentry, 0);
-
/* Partial lookups need to reinterpose, or throw away older negs. */
if (lookupmode == INTERPOSE_PARTIAL) {
if (dentry->d_inode) {
@@ -356,6 +353,14 @@ out_positive:
if (err)
goto out_drop;
+ /* check for whiteout */
+ if (UNIONFS_D(dentry)->odf_info) {
+ if (UNIONFS_D(dentry)->odf_info->whiteout){
+ err = -ENOENT;
+ goto out_drop;
+ }
+ }
+
goto out;
out_drop:
diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index 6db077e..17029d0 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -73,7 +73,13 @@ int unionfs_interpose(struct dentry *dentry, struct
super_block *sb, int flag)
/* get unique inode number for unionfs */
if ( !UNIONFS_D(dentry)->odf_info )
UNIONFS_D(dentry)->odf_info =
- odf_lookup(dentry->d_parent, dentry, odf_flag);
+ odf_lookup(dentry->d_parent, dentry, odf_flag |
ODF_LOOKUP_RMV_WH);
+ 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;
+ }
+
ino = UNIONFS_D(dentry)->odf_info->inum;
inode = iget(sb, ino);
diff --git a/fs/unionfs/odf.c b/fs/unionfs/odf.c
index 4d27aa1..ecb1a4d 100644
--- a/fs/unionfs/odf.c
+++ b/fs/unionfs/odf.c
@@ -1,6 +1,5 @@
#include "union.h"
-static int sss = 0;
/*
* Initialize any odf data we might need
*/
@@ -65,30 +64,113 @@ out:
}
/*
+ * Returns the dentry of /odf/reclaim
+ */
+struct odf_dentry_info* odf_getrc(void)
+{
+ struct nameidata nd;
+ struct odf_dentry_info* odi = NULL;
+ int err = 0;
+
+ err = path_lookup(ODF_RC, LOOKUP_FOLLOW, &nd);
+ if (err) {
+ printk(KERN_WARNING "unionfs_odf: Invalid odf\n");
+ odi = ERR_PTR(err);
+ goto out;
+ }
+ odi = odf_fill_info(nd.dentry);
+ path_release(&nd);
+
+out:
+ return odi;
+}
+
+int odf_reclaim_dir(struct dentry *dir)
+{
+ struct inode *old_dir, *new_dir;
+ struct dentry *old_dentry, *new_dentry;
+ struct odf_dentry_info *odi = odf_getrc();
+ char *new_name = NULL;
+ int err = 0;
+
+ if (IS_ERR(odi)){
+ err = PTR_ERR(odi);
+ odi = NULL;
+ goto out;
+ }
+ if (!odi){ /* Invalid ODF format(?) */
+ err = -EINVAL;
+ goto out;
+ }
+ new_name = kzalloc(ODF_INAME_LEN, GFP_KERNEL);
+ if (!new_name){
+ err = -ENOMEM;
+ goto out;
+ }
+ sprintf(new_name,"%lu", dir->d_inode->i_ino);
+
+ old_dentry = dir;
+ old_dir = dir->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);
+ 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);
+ }
+ err = vfs_rename(old_dir, old_dentry, new_dir, new_dentry);
+ dput(new_dentry);
+out:
+ kfree(new_name);
+ if (odi)
+ odf_clear_info(odi);
+ return err;
+}
+
+/*
* Lookup an entry in the odf, create it if not found,
* else check for whiteouts
* Handle hard links
*/
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;
+ }
+
+ return odf_lookup_name(parent, dentry->d_name.name, dentry->d_name.len,
flags);
+}
+struct odf_dentry_info *odf_lookup_name(struct dentry *parent, const char
*name, int len, int flags)
{
struct dentry *odf_dentry = NULL;
struct odf_dentry_info *odi = NULL;
struct inode *odf_i_dir = UNIONFS_D(parent)->odf_info->dentry->d_inode;
int err = 0;
- if (UNIONFS_D(dentry)->odf_info) {
- odf_clear_info(UNIONFS_D(dentry)->odf_info);
- UNIONFS_D(dentry)->odf_info = NULL;
+ odf_dentry = lookup_one_len(name, UNIONFS_D(parent)->odf_info->dentry,
len);
+
+ /* if create flags are set, remove existing whiteout */
+ if (!IS_ERR(odf_dentry) && odf_dentry->d_inode && (flags &
ODF_LOOKUP_RMV_WH))
+ {
+ if (odf_is_wh_i(odf_dentry->d_inode)){
+ vfs_unlink(odf_i_dir, odf_dentry);
+ dput(odf_dentry);
+ /* does this make sense? */
+ odf_dentry = lookup_one_len(name,
UNIONFS_D(parent)->odf_info->dentry, len);
+ }
}
- odf_dentry = lookup_one_len(dentry->d_name.name,
- UNIONFS_D(parent)->odf_info->dentry,
- dentry->d_name.len);
-
/* create inode in odf if dont exist */
/* XXX: should we fail if res is ERR? */
if (IS_ERR(odf_dentry) || !odf_dentry->d_inode) {
+ if (name[0]=='.')
+ BUG_ON(len==1 || (name[1]=='.'&&len==2));
+
/* FIXME need to check hardlinks before create */
if (flags & ODF_LOOKUP_FILE)
err = vfs_create(odf_i_dir, odf_dentry, S_IRWXUGO, 0 );
@@ -100,12 +182,14 @@ struct odf_dentry_info *odf_lookup(struct dentry *parent,
struct dentry *dentry,
dput(odf_dentry);
goto out;
}
+ if (err) {
+ printk(KERN_WARNING "could not create odf dentry" );
+ odi = ERR_PTR(err);
+ goto out;
+ }
odf_set_opaque_i(odf_dentry->d_inode, -1);
odf_set_wh_i(odf_dentry->d_inode,0);
- /* XXX */
- if (err)
- printk(KERN_WARNING "could not create odf dentry" );
}
else {
/* FIXME check for whiteouts & hardlinks */
@@ -118,6 +202,36 @@ out:
return odi;
}
+/*
+ * Unlinks the file from odf if exist, creates a new file with
+ * the same name and sets its whiteout flag
+ */
+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 (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 (err)
+ goto out;
+ odf_clear_info(UNIONFS_D(dentry)->odf_info);
+ UNIONFS_D(dentry)->odf_info = NULL;
+ 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? */
+ goto out;
+ }
+ err = odf_set_wh(dentry,1);
+out:
+ return err;
+}
/*
* Sets the whiteout flag
@@ -125,7 +239,10 @@ out:
*/
int odf_set_wh(struct dentry *dentry, int boolean)
{
- return odf_set_wh_i(UNIONFS_D(dentry)->odf_info->dentry->d_inode,
boolean);
+ int err = odf_set_wh_i(UNIONFS_D(dentry)->odf_info->dentry->d_inode,
boolean);
+ if (!err)
+ UNIONFS_D(dentry)->odf_info->whiteout = boolean;
+ return err;
}
int odf_set_wh_i(struct inode *i, int boolean)
{
@@ -148,7 +265,9 @@ int odf_is_wh(struct dentry *dentry)
}
int odf_is_wh_i(struct inode *i)
{
- return EXT2_I(i)->i_flags & ODF_WHITEOUT;
+ if (EXT2_I(i)->i_flags & ODF_WHITEOUT)
+ return 1;
+ return 0;
}
/*
@@ -163,7 +282,9 @@ int odf_is_opaque_i(struct inode *i)
{
if (!S_ISDIR(i->i_mode))
return 0;
- return EXT2_I(i)->i_flags & ODF_OPAQUE;
+ if (EXT2_I(i)->i_flags & ODF_OPAQUE)
+ return 1;
+ return 0;
}
/*
* Gets opaque branch of a dentry, -1 if not opaque
@@ -192,7 +313,10 @@ int odf_get_opaque_i(struct inode *i)
*/
int odf_set_opaque(struct dentry *dentry, int branch)
{
- return odf_set_opaque_i(UNIONFS_D(dentry)->odf_info->dentry->d_inode,
branch);
+ int err =
odf_set_opaque_i(UNIONFS_D(dentry)->odf_info->dentry->d_inode, branch);
+ if (!err)
+ UNIONFS_D(dentry)->odf_info->opaque = branch;
+ return err;
}
int odf_set_opaque_i(struct inode *i, int branch)
{
@@ -411,6 +535,8 @@ out:
struct odf_dentry_info *odf_fill_info(struct dentry *odf_dentry)
{
struct odf_dentry_info *odi = kzalloc(sizeof(struct
odf_dentry_info),GFP_KERNEL);
+ odi->whiteout = odf_is_wh_i(odf_dentry->d_inode);
+ odi->opaque = odf_get_opaque_i(odf_dentry->d_inode);
odi->dentry = odf_dentry;
odi->inum = odf_dentry->d_inode->i_ino;
dget(odf_dentry);
diff --git a/fs/unionfs/odf.h b/fs/unionfs/odf.h
index 6d15dc7..bcd6b39 100644
--- a/fs/unionfs/odf.h
+++ b/fs/unionfs/odf.h
@@ -13,10 +13,12 @@
/* Some string constants */
#define ODF_BRANCH_PATH 255
#define ODF_OPTIONS_LEN 255
+#define ODF_INAME_LEN 10
/* Lookup flags (what to create if lookup fails) */
#define ODF_LOOKUP_FILE 1
-#define ODF_LOOKUP_DIR 2
+#define ODF_LOOKUP_DIR 2
+#define ODF_LOOKUP_RMV_WH 4
/* Inode flags */
#define ODF_WHITEOUT 0x01000000
@@ -33,6 +35,7 @@ int odf_putoptions(struct super_block* sb_union, struct
unionfs_dentry_info *hid
/* lookup */
struct odf_dentry_info *odf_lookup(struct dentry *parent, struct dentry
*dentry, int flags);
+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);
@@ -42,6 +45,7 @@ int odf_set_wh(struct dentry *dentry, int boolean);
int odf_is_wh(struct dentry *dentry);
int odf_set_wh_i(struct inode *i, int boolean);
int odf_is_wh_i(struct inode *i);
+int odf_create_wh(struct dentry *dentry);
/* opaque */
int odf_is_opaque(struct dentry *dentry);
@@ -51,5 +55,4 @@ int odf_is_opaque_i(struct inode *i);
int odf_get_opaque_i(struct inode *i);
int odf_set_opaque_i(struct inode *i, int branch);
-
#endif /* _ODF_H_ */
diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
index f0869c2..21a8e4a 100644
--- a/fs/unionfs/subr.c
+++ b/fs/unionfs/subr.c
@@ -37,7 +37,11 @@ int create_whiteout(struct dentry *dentry, int start)
bend = dbend(dentry);
/* odf whiteout */
- odf_set_wh(dentry,1);
+ err = odf_create_wh(dentry);
+ if (err)
+ goto out;
+ else /*FIXME very creative and constructive if/else
*/
+ goto out; /* to bypass the original whiteout code
*/
/* create dentry's whiteout equivalent */
name = alloc_whname(dentry->d_name.name, dentry->d_name.len);
_______________________________________________
unionfs-cvs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs-cvs