Unionfs ODF2: Fixes to make unionfs exportable
authorRachita Kothiyal <rachita@louie.fsl.cs.sunysb.edu>
Sun, 16 Dec 2007 08:08:41 +0000 (03:08 -0500)
committerRachita Kothiyal <rachita@dewey.fsl.cs.sunysb.edu>
Thu, 1 May 2008 23:03:27 +0000 (19:03 -0400)
Signed-off-by: Rachita Kothiyal <rachita@fsl.cs.sunysb.edu>
fs/unionfs/commonfops.c
fs/unionfs/dentry.c
fs/unionfs/export.c

index bd55ec611f1c2913a6bee72a85bb776706dac37b..516ea5256cfcfcde888faa07cb07bd9ed217f3d8 100644 (file)
@@ -630,11 +630,9 @@ int unionfs_open(struct inode *inode, struct file *file)
        dentry = file->f_path.dentry;
 
        /*
-        * FIXME: With nfs exporting we can get a disconnected dentry here.
-        * This can happen if the dcache is dropped on the server while the
-        * client is working, and export ops decodefh returns ESTALE to the
-        * user.  It would best to catch this by implementing decodefh or
-        * maybe we could connect the dentry ourselves using getparent?
+        * With nfs exporting we cannot get a disconnected dentry here,
+        * since we already connect it in unionfs_get_parent.  This check
+        * can probably be removed after sufficient testing.
         */
        if (dentry->d_flags & DCACHE_DISCONNECTED) {
                err = -ESTALE;
index 7dccd8bddffd38f7b16c44a43873b26f6c80410e..46a6a59d7a9515f728dbe7ba5873f022ccdd4fcc 100644 (file)
@@ -38,13 +38,10 @@ static bool __unionfs_d_revalidate_one(struct dentry *dentry,
        struct nameidata lowernd; /* TODO: be gentler to the stack */
 
        /*
-        * FIXME: with nfs exporting, if export ops fails to connect a
-        * dentry and returns ESTALE to the client, we can end up with an
-        * inode that has ibstart -1.  This fix only stops the oopsing but
-        * the dentry will become unusable on the client, it will keep
-        * getting ESTALE on any attempt to fix it.  The best way to fix
-        * this is by implementing decode_fh and making sure no inode gets
-        * left with -1 ibstart
+        * This will never be true with nfs exporting, since unionfs_interpose
+        * will now ensure that no inode is left with ibstart -1.  Retaining
+        * the check for now to be safe.  This should go away after sufficient
+        * testing.
         */
        if (dentry->d_inode && ibstart(dentry->d_inode) < 0) {
                valid = 0;
index f8bd66a3405e27cf397bb2403315674b7a5f991a..46d7653e2f0c6b2704e2976a785db3f245de2ec3 100644 (file)
@@ -93,27 +93,9 @@ static struct dentry *__get_parent(struct super_block *sb,
 
                        /* our odf root can never be disconnected */
                        BUG_ON(d == odf_root);
-                       if (odf_root->d_sb->s_export_op->get_name)
-                               res = odf_root->d_sb->s_export_op->get_name(
+                       res = exportfs_get_name(NULL,
                                        UNIONFS_D(parent)->odf.dentry,
                                        name, d);
-                       else {
-                               res = exportfs_get_name(
-                                       NULL, /* NULL mnt is wrong! */
-                                       UNIONFS_D(parent)->odf.dentry,
-                                       name, d);
-                               BUG();
-                               /*
-                                * FIXME: we're passing a null mnt above,
-                                * which is wrong.  We need to pass a real
-                                * mnt.  But first, we need to change struct
-                                * odf_dentry_info so that it'll record a
-                                * struct path instead of just a dentry.
-                                * That struct path will include both a
-                                * dentry and mnt, which have to be dget/put
-                                * and mntget/put properly. -ezk
-                                */
-                       }
                } else
                        strncpy(name, d->d_name.name, d->d_name.len);
 
@@ -185,98 +167,189 @@ out:
        return res;
 }
 
-/*
- * @dentry: the directory in which to find a name
- * @name:   a pointer to a %NAME_MAX+1 char buffer to store the name
- * @child:  the dentry for the child directory.
- *
- * searches the cached dir in odf to find a dirent with
- * the same inode number as the child, and returns that.
- */
 static int unionfs_get_name(struct dentry *dentry, char *name,
                            struct dentry *child)
 {
-       int err;
-       struct inode *dir = dentry->d_inode;
-       struct dentry *odf_cache = NULL;
-       struct file *file = NULL;
-       /* dirent */
-       u64 ino;
-       char *tmp_name = NULL;
-       int namelen;
-       unsigned int d_type;
-
-       err = -ENOTDIR;
-       if (!dir || !S_ISDIR(dir->i_mode))
-               goto out;
+       int err = 0;
+       struct odf_sb_info *odf;
+       struct super_block *odf_sb;
+       struct inode *odf_child_i, *odf_dentry_i;
+       struct dentry *odf_child, *odf_dentry;
 
-       err = -EINVAL;
-       if (!dir->i_fop)
-               goto out;
+       odf = &UNIONFS_SB(child->d_sb)->odf;
+       odf_sb = odf->sb->d_sb;
 
-       if (!UNIONFS_D(dentry) || !UNIONFS_D(dentry)->odf.dentry)
+       odf_dentry_i = iget(odf_sb, dentry->d_inode->i_ino);
+       if (!odf_dentry_i) {
+               err = -ENOMEM;
                goto out;
+       }
 
-       odf_cache = odf_ic_cache_dentry(dentry);
-       if (IS_ERR(odf_cache)) {
-               err = PTR_ERR(odf_cache);
-               odf_cache = NULL;
+       odf_dentry = d_alloc_anon(odf_dentry_i);
+       if (!odf_dentry) {
+               iput(odf_dentry_i);
+               err = -ENOMEM;
                goto out;
        }
 
-       /* reconstruct the cached dir if necessary */
-       if (!odf_cache->d_inode || !i_size_read(odf_cache->d_inode)) {
-               unionfs_lock_dentry(dentry);
-               err = odf_cache_dir(dentry, odf_cache,
-                                   &dentry->d_inode->i_mtime);
-               unionfs_unlock_dentry(dentry);
-               if (err)
-                       goto out;
+       odf_child_i = iget(odf_sb, child->d_inode->i_ino);
+       if (!odf_child_i) {
+               iput(odf_dentry_i);
+               dput(odf_dentry);
+               err = -ENOMEM;
+               goto out;
        }
 
-       dget(odf_cache);
-       mntget(UNIONFS_SB(dentry->d_sb)->odf.path.mnt);
-       file = dentry_open(odf_cache, UNIONFS_SB(dentry->d_sb)->odf.path.mnt,
-                          O_RDONLY);
-       if (IS_ERR(file)) {
-               err = PTR_ERR(file);
-               mntput(UNIONFS_SB(dentry->d_sb)->odf.path.mnt);
-               dput(odf_cache);
-               file = NULL;
+       odf_child = d_alloc_anon(odf_child_i);
+       if (!odf_child) {
+               iput(odf_dentry_i);
+               dput(odf_dentry);
+               iput(odf_child_i);
+               err = -ENOMEM;
                goto out;
        }
 
-       /* search for the inode in the dir */
-       file->f_pos = 0;
-       while (file->f_pos < i_size_read(file->f_path.dentry->d_inode)) {
-               err = odf_read_dirent(file, &tmp_name, &namelen, &ino,
-                                     &d_type);
-               if (err)
-                       break;
-               if (ino == child->d_inode->i_ino) {
-                       if (namelen >= NAME_MAX)
-                               namelen =  NAME_MAX;
-                       strncpy(name, tmp_name, namelen);
-                       name[namelen] = 0;
-                       err = 0;
-                       break;
-               }
-
-               err = -ENOENT;
-               kfree(tmp_name);
-               tmp_name = NULL;
-       }
+       err = exportfs_get_name(NULL, odf_dentry, name, odf_child);
+       dput(odf_child);
+       dput(odf_dentry);
 
 out:
-       kfree(tmp_name);
-       tmp_name = NULL;
-       if (file)
-               filp_close(file, NULL);
-       dput(odf_cache);
        return err;
 }
 
+static struct dentry *unionfs_export_iget(struct super_block *sb,
+                                       unsigned long ino, __u32 generation,
+                                       __u32 mode)
+{
+       struct inode *inode;
+       struct dentry *result;
+       int err = 0;
+
+       if (ino == 0 || mode == 0)
+               return ERR_PTR(-ESTALE);
+
+       inode = iget(sb, ino);
+       if (inode == NULL)
+               return ERR_PTR(-ENOMEM);
+
+        /* A generation of 0 means "accept any" */
+       if (is_bad_inode(inode) || (generation
+               && inode->i_generation != generation)) {
+               /* we didn't find the right inode.. */
+               printk(KERN_ERR "fh_verify: Inode %lu, Bad count: %d %d "
+                       "or version  %u %u\n", inode->i_ino, inode->i_nlink,
+                       atomic_read(&inode->i_count), inode->i_generation,
+                       generation);
+
+               iput(inode);
+               return ERR_PTR(-ESTALE);
+       }
+
+       /* Now find a dentry. If possible, get a well-connected one */
+       result = d_alloc_anon(inode);
+       if (!result) {
+               iput(inode);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       if (!UNIONFS_D(result))
+               err = new_dentry_private_data(result);
+       if (err) {
+               printk(KERN_INFO "unionfs_export_iget: Not enough memory to "
+                                       "allocate dentry's private data\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       result->d_inode->i_mode = mode;
+       return result;
+}
+
+static struct dentry *unionfs_fh_to_dentry(struct super_block *sb,
+                                               struct fid *fid, int fh_len,
+                                               int fh_type)
+{
+       __u32 child[3];
+       struct dentry *result;
+       result = ERR_PTR(-EINVAL);
+
+       if (fh_len < 3 || fh_type > FILEID_INO32_GEN_PARENT)
+               return result;
+
+       child[0] = fid->raw[0];
+       child[1] = fid->raw[1];
+       child[2] = fid->raw[2];
+
+       if (fh_type == FILEID_INO32_GEN || fh_type == FILEID_INO32_GEN_PARENT)
+               result = unionfs_export_iget(sb, child[0], child[1], child[2]);
+
+       return result;
+}
+
+static struct dentry *unionfs_fh_to_parent(struct super_block *sb,
+                                               struct fid *fid, int fh_len,
+                                               int fh_type)
+{
+       __u32 parent[3];
+       struct dentry *result;
+       result = ERR_PTR(-EINVAL);
+       if (fh_len < 3 || fh_type > FILEID_INO32_GEN_PARENT)
+               return result;
+
+       if (fh_len > 3)
+               parent[0] = fid->raw[3];
+       if (fh_len > 4)
+               parent[1] = fid->raw[4];
+       if (fh_len > 5)
+               parent[2] = fid->raw[5];
+
+       if (fh_type == FILEID_INO32_GEN_PARENT)
+               result = unionfs_export_iget(sb, parent[0], parent[1],
+                                               parent[2]);
+
+       return result;
+}
+
+/*
+ * unionfs_encode_fh: Encode dentry information into the filehandle.
+ * In addition to inode and generation, we also store the inode->i_mode
+ * in the filehandle to aid exportfs_decode_fh in identifying the inode.
+ */
+static int unionfs_encode_fh(struct dentry *dentry, __u32 *raw, int *max_len,
+                                               int connectable)
+{
+       struct inode *inode = dentry->d_inode;
+       int len = *max_len;
+       int type = FILEID_INO32_GEN;
+
+       connectable = 1;
+
+       if (len < 3 || (connectable && len < 6))
+               return 255;
+
+       len = 3;
+       raw[0] = inode->i_ino;
+       raw[1] = inode->i_generation;
+       raw[2] = inode->i_mode;
+       if (connectable && !S_ISDIR(inode->i_mode)) {
+               struct inode *parent;
+
+               spin_lock(&dentry->d_lock);
+               parent = dentry->d_parent->d_inode;
+               raw[3] = parent->i_ino;
+               raw[4] = parent->i_generation;
+               raw[5] = parent->i_mode;
+               spin_unlock(&dentry->d_lock);
+               len = 6;
+               type = FILEID_INO32_GEN_PARENT;
+       }
+       *max_len = len;
+       return type;
+}
+
 struct export_operations unionfs_export_ops = {
-       .get_parent = unionfs_get_parent,
-       .get_name = unionfs_get_name
+       .get_parent     = unionfs_get_parent,
+       .get_name       = unionfs_get_name,
+       .fh_to_dentry   = unionfs_fh_to_dentry,
+       .fh_to_parent   = unionfs_fh_to_parent,
+       .encode_fh      = unionfs_encode_fh
 };