/* 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);
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
};