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

Reply via email to