commit 37cc67a0c06910637feec2e34f4132640271f94f
Author: Yiannis Pericleous <[EMAIL PROTECTED]>
Date: Wed Apr 4 16:06:07 2007 -0400
hardlinks lazy detection
diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 7127cb9..b70dc55 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -168,7 +168,7 @@ int unionfs_silly_rename(struct dentry *dentry, struct
dentry *hidden_dentry)
goto out;
/* create a whiteout */
- odi = __odf_lookup(UNIONFS_SB(dentry->d_sb)->odf,
+ odi = odf_lookup_name(UNIONFS_SB(dentry->d_sb)->odf,
UNIONFS_D(dentry->d_parent)->odf_info,
name, strlen(name), ODF_LOOKUP_WH, odi);
BUG_ON(IS_ERR(odi) || odi == NULL);
diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c
index 0eb0538..7f20de9 100644
--- a/fs/unionfs/dirfops.c
+++ b/fs/unionfs/dirfops.c
@@ -71,6 +71,8 @@ static int unionfs_readdir(struct file *file, void *dirent,
filldir_t filldir)
O_RDONLY);
if (IS_ERR(odf_file)){
err = PTR_ERR(odf_file);
+ mntput(UNIONFS_SB(file->f_dentry->d_sb)->odf->mnt);
+ dput(odi->dentry);
odf_file = NULL;
goto out;
}
diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
index 7fc8271..077dfb3 100644
--- a/fs/unionfs/dirhelper.c
+++ b/fs/unionfs/dirhelper.c
@@ -160,9 +160,9 @@ static int readdir_util_callback(void *dirent, const char
*name, int namelen,
}
/* check odf */
- odi = __odf_lookup(UNIONFS_SB(buf->dir->d_sb)->odf,
+ odi = odf_lookup_name(UNIONFS_SB(buf->dir->d_sb)->odf,
UNIONFS_D(buf->dir)->odf_info,
- name, namelen, ODF_LOOKUP_LOCKED, 0);
+ name, namelen, ODF_LOOKUP_LOCKED, NULL);
if (IS_ERR(odi)){
odi = NULL;
goto out;
@@ -336,6 +336,8 @@ retry:
O_TRUNC|O_CREAT|O_WRONLY);
if (IS_ERR(odf_file)){
err = PTR_ERR(odf_file);
+ dput(d_odf);
+ mntput(UNIONFS_SB(sb)->odf->mnt);
odf_file = NULL;
goto out;
}
diff --git a/fs/unionfs/odf.c b/fs/unionfs/odf.c
index 191ae17..fb81d03 100644
--- a/fs/unionfs/odf.c
+++ b/fs/unionfs/odf.c
@@ -394,36 +394,59 @@ out:
*/
int odf_lookup(struct dentry *parent, struct dentry *dentry, int flags)
{
+ struct odf_sb_info *osi = UNIONFS_SB(dentry->d_sb)->odf;
struct odf_dentry_info *links = NULL;
struct dentry *lower_dentry = NULL;
+ struct file *link_file = NULL;
char *name, *uuid;
int bstart, err = 0;
/* this might be called before interpose */
- for (bstart = 0; bstart <= dbend(dentry); bstart++) {
- lower_dentry = unionfs_lower_dentry_idx(dentry, bstart);
- if (lower_dentry)
- break;
+ if (dbstart(dentry) == dbend(dentry) && dbstart(dentry) == 0) {
+ for (bstart = 0; bstart <= dbend(dentry); bstart++) {
+ lower_dentry = unionfs_lower_dentry_idx(dentry, bstart);
+ if (lower_dentry)
+ break;
+ }
+ }
+ else {
+ lower_dentry = unionfs_lower_dentry(dentry);
+ bstart = dbstart(dentry);
}
if (lower_dentry && lower_dentry->d_inode &&
lower_dentry->d_inode->i_nlink > 1 &&
!S_ISDIR(lower_dentry->d_inode->i_mode)) {
+ /* get/create the link info file for lower fs */
name = kmalloc(UUID_LEN * 2 + 1,GFP_KERNEL);
- uuid = odf_get_branch_uuid(UNIONFS_SB(dentry->d_sb)->odf,
bstart);
+ uuid = odf_get_branch_uuid(osi, bstart);
sprintf(name,"%x%x%x%x", *(unsigned int *) uuid,
*(unsigned int *) uuid + 4,
*(unsigned int *) uuid + 8,
*(unsigned int *) uuid + 12);
- links = odf_ic_dentry(UNIONFS_SB(dentry->d_sb)->odf,
- lower_dentry->d_inode->i_ino, name,
strlen(name));
+ links = odf_ic_dentry(osi, lower_dentry->d_inode->i_ino,
+ name, strlen(name));
kfree(name);
if (IS_ERR(links)) {
err = PTR_ERR(links);
+ links = NULL;
+ goto out;
+ }
+
+ /* open the file */
+ dget(links->dentry);
+ mntget(osi->mnt);
+ link_file = dentry_open(links->dentry, osi->mnt, O_RDWR);
+ if (IS_ERR(link_file)) {
+ err = PTR_ERR(link_file);
+ link_file = NULL;
+ dput(links->dentry);
+ mntput(osi->mnt);
goto out;
}
- odf_put_info(links);
+
+ /* check if link was copied up and act approprietely */
}
UNIONFS_D(dentry)->odf_info = __odf_lookup(
@@ -432,28 +455,58 @@ int odf_lookup(struct dentry *parent, struct dentry
*dentry, int flags)
dentry->d_name.name,
dentry->d_name.len,
flags,
- UNIONFS_D(dentry)->odf_info);
+ UNIONFS_D(dentry)->odf_info,
+ link_file);
if (IS_ERR(UNIONFS_D(dentry)->odf_info)){
err = PTR_ERR(UNIONFS_D(dentry)->odf_info);
UNIONFS_D(dentry)->odf_info = NULL;
}
out:
+ odf_put_info(links);
+ if (link_file)
+ filp_close(link_file, NULL);
return err;
}
-struct odf_dentry_info *__odf_lookup(struct odf_sb_info *osi, struct
odf_dentry_info *parent, const char *name,
- int len, int flags, struct
odf_dentry_info *old_odi)
+struct odf_dentry_info *__odf_lookup(struct odf_sb_info *osi,
+ struct odf_dentry_info *parent,
+ const char *name, int len, int flags,
+ struct odf_dentry_info *old_odi,
+ struct file *link_file)
{
- struct dentry *odf_dentry;
+ struct inode *i;
+ struct dentry *odf_dentry, *d;
struct odf_dentry_info *odi = old_odi;
struct inode *odf_i_dir = parent->dentry->d_inode;
int opaque, whiteout, err = 0, cleaned = 0;
+ __le64 le64;
+ u64 ino = 0;
+ mm_segment_t oldfs;
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));
+ /* read the odf ino of the link */
+ if (link_file) {
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ link_file->f_pos = 0;
+ err = link_file->f_op->read(link_file, (char*)&le64,
+ sizeof(__le64), &link_file->f_pos);
+ set_fs(oldfs);
+ if (err<0) {
+ odf_put_info(old_odi);
+ odi = ERR_PTR(err);
+ goto out;
+ }
+ if(err != sizeof(__le64))
+ ino = 0;
+ else
+ ino = le64_to_cpu(le64);
+ err = 0;
+ }
odf_dentry = lookup_one_len(name, parent->dentry, len);
if (IS_ERR(odf_dentry)) {
@@ -494,7 +547,16 @@ retry:
else if (cleaned)
odf_lock(osi->odi_ic);
- if (flags & ODF_LOOKUP_FILE || flags & ODF_LOOKUP_WH) {
+ BUG_ON(link_file && (flags & ODF_LOOKUP_DIR));
+ if (link_file && ino && (flags & ODF_LOOKUP_FILE)) {
+ /* if link ino was set in the file, link to that */
+ i = iget(odf_dentry->d_sb, ino);
+ d = d_splice_alias(i, odf_dentry);
+ iput(i);
+ if (IS_ERR(d))
+ err = PTR_ERR(d);
+ }
+ else 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 );
@@ -549,7 +611,37 @@ retry:
__odf_set_wh(odf_dentry->d_inode,whiteout);
}
else {
- /* FIXME check for hardlinks */
+ /* handle hardlinks, 3 cases:
+ * 1) ino in linkfile and odf_dentry are the same: all ok
+ * 2) ino in linkfile is not set: set it to odf_dentry's ino
+ * 3) ino in linkfile and odf_dentry are different: bad
+ */
+
+ /* case 3: unlink odf file and link to given ino */
+ if (link_file && ino && ino != odf_dentry->d_inode->i_ino) {
+ vfs_unlink(odf_dentry->d_parent->d_inode, odf_dentry);
+ i = iget(odf_dentry->d_sb, ino);
+ d = d_splice_alias(i, odf_dentry);
+ iput(i);
+ if (IS_ERR(d)) {
+ odi = (struct odf_dentry_info *)d;
+ odf_put_info(old_odi);
+ goto out;
+ }
+ }
+ }
+
+ /* if link file did not have the ino, write it */
+ if (link_file && !ino) {
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ link_file->f_pos = 0;
+ le64 = cpu_to_le64(odf_dentry->d_inode->i_ino);
+ err = link_file->f_op->write(link_file, (char*)&le64,
+ sizeof(__le64), &link_file->f_pos);
+ set_fs(oldfs); /* XXX check errors, and for -ENOSPC as above */
+ BUG_ON(err != sizeof(__le64));
+ err = 0;
}
odi = odf_fill_info(old_odi, osi, odf_dentry);
@@ -598,7 +690,7 @@ struct odf_dentry_info *odf_ic_dentry(struct odf_sb_info
*osi, u64 ino, char *na
memset(tmp_name,0,6);
sprintf(tmp_name, "%x", breakdown[0]);
- odis[0] = __odf_lookup(osi, odi_ic, tmp_name, strlen(tmp_name),
ODF_LOOKUP_DIR, NULL);
+ odis[0] = odf_lookup_name(osi, odi_ic, tmp_name, strlen(tmp_name),
ODF_LOOKUP_DIR, NULL);
if (IS_ERR(odis[0])) {
err = PTR_ERR(odis[0]);
odis[0] = NULL;
@@ -607,7 +699,7 @@ struct odf_dentry_info *odf_ic_dentry(struct odf_sb_info
*osi, u64 ino, char *na
for (i = 1; i < 4; i++) {
memset(tmp_name,0,6);
sprintf(tmp_name, "%x", breakdown[i]);
- odis[i] = __odf_lookup(osi, odis[i-1], tmp_name,
strlen(tmp_name), ODF_LOOKUP_DIR, NULL);
+ odis[i] = odf_lookup_name(osi, odis[i-1], tmp_name,
strlen(tmp_name), ODF_LOOKUP_DIR, NULL);
if (IS_ERR(odis[i])) {
err = PTR_ERR(odis[i]);
odis[i] = NULL;
@@ -615,7 +707,7 @@ struct odf_dentry_info *odf_ic_dentry(struct odf_sb_info
*osi, u64 ino, char *na
}
}
- odi_ret = __odf_lookup(osi, odis[3], name, namelen, ODF_LOOKUP_FILE,
NULL);
+ odi_ret = odf_lookup_name(osi, odis[3], name, namelen, ODF_LOOKUP_FILE,
NULL);
if (IS_ERR(odi_ret)) {
err = PTR_ERR(odi_ret);
odi_ret = NULL;
diff --git a/fs/unionfs/odf.h b/fs/unionfs/odf.h
index 5f5044b..3c2d918 100644
--- a/fs/unionfs/odf.h
+++ b/fs/unionfs/odf.h
@@ -67,7 +67,14 @@ static inline char *odf_get_branch_uuid(struct odf_sb_info
*odf, int branch)
/* lookup */
int odf_lookup(struct dentry *parent, struct dentry *dentry, int flags);
struct odf_dentry_info *__odf_lookup(struct odf_sb_info *osi, struct
odf_dentry_info *parent,
- const char *name, int len, int flags, struct odf_dentry_info
*old_odi);
+ const char *name, int len, int flags, struct odf_dentry_info
*old_odi,
+ struct file *link_file);
+static inline struct odf_dentry_info *odf_lookup_name(struct odf_sb_info *osi,
+ struct odf_dentry_info *parent, const char *name, int len,
+ int flags, struct odf_dentry_info *old_odi)
+{
+ return __odf_lookup(osi, parent, name, len, flags, old_odi, NULL);
+}
struct odf_dentry_info *odf_getpath(struct dentry *d_odf, struct odf_sb_info
*osi, const char *name);
struct odf_dentry_info *odf_fill_info(struct odf_dentry_info *odi,
struct odf_sb_info *osi, struct dentry *odf_dentry);
_______________________________________________
unionfs-cvs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs-cvs