|
@@ -562,6 +562,7 @@ struct fuse_inode_handle
|
|
|
static struct dentry *fuse_get_dentry(struct super_block *sb,
|
|
|
struct fuse_inode_handle *handle)
|
|
|
{
|
|
|
+ struct fuse_conn *fc = get_fuse_conn_super(sb);
|
|
|
struct inode *inode;
|
|
|
struct dentry *entry;
|
|
|
int err = -ESTALE;
|
|
@@ -570,8 +571,27 @@ static struct dentry *fuse_get_dentry(struct super_block *sb,
|
|
|
goto out_err;
|
|
|
|
|
|
inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid);
|
|
|
- if (!inode)
|
|
|
- goto out_err;
|
|
|
+ if (!inode) {
|
|
|
+ struct fuse_entry_out outarg;
|
|
|
+ struct qstr name;
|
|
|
+
|
|
|
+ if (!fc->export_support)
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+ name.len = 1;
|
|
|
+ name.name = ".";
|
|
|
+ err = fuse_lookup_name(sb, handle->nodeid, &name, &outarg,
|
|
|
+ &inode);
|
|
|
+ if (err && err != -ENOENT)
|
|
|
+ goto out_err;
|
|
|
+ if (err || !inode) {
|
|
|
+ err = -ESTALE;
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+ err = -EIO;
|
|
|
+ if (get_node_id(inode) != handle->nodeid)
|
|
|
+ goto out_iput;
|
|
|
+ }
|
|
|
err = -ESTALE;
|
|
|
if (inode->i_generation != handle->generation)
|
|
|
goto out_iput;
|
|
@@ -659,11 +679,46 @@ static struct dentry *fuse_fh_to_parent(struct super_block *sb,
|
|
|
return fuse_get_dentry(sb, &parent);
|
|
|
}
|
|
|
|
|
|
+static struct dentry *fuse_get_parent(struct dentry *child)
|
|
|
+{
|
|
|
+ struct inode *child_inode = child->d_inode;
|
|
|
+ struct fuse_conn *fc = get_fuse_conn(child_inode);
|
|
|
+ struct inode *inode;
|
|
|
+ struct dentry *parent;
|
|
|
+ struct fuse_entry_out outarg;
|
|
|
+ struct qstr name;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!fc->export_support)
|
|
|
+ return ERR_PTR(-ESTALE);
|
|
|
+
|
|
|
+ name.len = 2;
|
|
|
+ name.name = "..";
|
|
|
+ err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode),
|
|
|
+ &name, &outarg, &inode);
|
|
|
+ if (err && err != -ENOENT)
|
|
|
+ return ERR_PTR(err);
|
|
|
+ if (err || !inode)
|
|
|
+ return ERR_PTR(-ESTALE);
|
|
|
+
|
|
|
+ parent = d_alloc_anon(inode);
|
|
|
+ if (!parent) {
|
|
|
+ iput(inode);
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+ }
|
|
|
+ if (get_node_id(inode) != FUSE_ROOT_ID) {
|
|
|
+ parent->d_op = &fuse_dentry_operations;
|
|
|
+ fuse_invalidate_entry_cache(parent);
|
|
|
+ }
|
|
|
+
|
|
|
+ return parent;
|
|
|
+}
|
|
|
|
|
|
static const struct export_operations fuse_export_operations = {
|
|
|
.fh_to_dentry = fuse_fh_to_dentry,
|
|
|
.fh_to_parent = fuse_fh_to_parent,
|
|
|
.encode_fh = fuse_encode_fh,
|
|
|
+ .get_parent = fuse_get_parent,
|
|
|
};
|
|
|
|
|
|
static const struct super_operations fuse_super_operations = {
|
|
@@ -695,6 +750,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
|
|
|
fc->no_lock = 1;
|
|
|
if (arg->flags & FUSE_ATOMIC_O_TRUNC)
|
|
|
fc->atomic_o_trunc = 1;
|
|
|
+ if (arg->minor >= 9) {
|
|
|
+ /* LOOKUP has dependency on proto version */
|
|
|
+ if (arg->flags & FUSE_EXPORT_SUPPORT)
|
|
|
+ fc->export_support = 1;
|
|
|
+ }
|
|
|
if (arg->flags & FUSE_BIG_WRITES)
|
|
|
fc->big_writes = 1;
|
|
|
} else {
|
|
@@ -721,7 +781,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
|
|
|
arg->minor = FUSE_KERNEL_MINOR_VERSION;
|
|
|
arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE;
|
|
|
arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
|
|
|
- FUSE_BIG_WRITES;
|
|
|
+ FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES;
|
|
|
req->in.h.opcode = FUSE_INIT;
|
|
|
req->in.numargs = 1;
|
|
|
req->in.args[0].size = sizeof(*arg);
|