diff options
-rw-r--r-- | fs/fuse/fuse_i.h | 6 | ||||
-rw-r--r-- | fs/fuse/inode.c | 66 | ||||
-rw-r--r-- | include/linux/fuse.h | 3 |
3 files changed, 72 insertions, 3 deletions
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 5d3146da64e6..3a876076bdd1 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -363,6 +363,9 @@ struct fuse_conn { | |||
363 | /** Do not send separate SETATTR request before open(O_TRUNC) */ | 363 | /** Do not send separate SETATTR request before open(O_TRUNC) */ |
364 | unsigned atomic_o_trunc : 1; | 364 | unsigned atomic_o_trunc : 1; |
365 | 365 | ||
366 | /** Filesystem supports NFS exporting. Only set in INIT */ | ||
367 | unsigned export_support : 1; | ||
368 | |||
366 | /* | 369 | /* |
367 | * The following bitfields are only for optimization purposes | 370 | * The following bitfields are only for optimization purposes |
368 | * and hence races in setting them will not cause malfunction | 371 | * and hence races in setting them will not cause malfunction |
@@ -473,6 +476,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, | |||
473 | int generation, struct fuse_attr *attr, | 476 | int generation, struct fuse_attr *attr, |
474 | u64 attr_valid, u64 attr_version); | 477 | u64 attr_valid, u64 attr_version); |
475 | 478 | ||
479 | int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, | ||
480 | struct fuse_entry_out *outarg, struct inode **inode); | ||
481 | |||
476 | /** | 482 | /** |
477 | * Send FORGET command | 483 | * Send FORGET command |
478 | */ | 484 | */ |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 71fa76a48a31..7d2f7d6e22e2 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -562,6 +562,7 @@ struct fuse_inode_handle | |||
562 | static struct dentry *fuse_get_dentry(struct super_block *sb, | 562 | static struct dentry *fuse_get_dentry(struct super_block *sb, |
563 | struct fuse_inode_handle *handle) | 563 | struct fuse_inode_handle *handle) |
564 | { | 564 | { |
565 | struct fuse_conn *fc = get_fuse_conn_super(sb); | ||
565 | struct inode *inode; | 566 | struct inode *inode; |
566 | struct dentry *entry; | 567 | struct dentry *entry; |
567 | int err = -ESTALE; | 568 | int err = -ESTALE; |
@@ -570,8 +571,27 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, | |||
570 | goto out_err; | 571 | goto out_err; |
571 | 572 | ||
572 | inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); | 573 | inode = ilookup5(sb, handle->nodeid, fuse_inode_eq, &handle->nodeid); |
573 | if (!inode) | 574 | if (!inode) { |
574 | goto out_err; | 575 | struct fuse_entry_out outarg; |
576 | struct qstr name; | ||
577 | |||
578 | if (!fc->export_support) | ||
579 | goto out_err; | ||
580 | |||
581 | name.len = 1; | ||
582 | name.name = "."; | ||
583 | err = fuse_lookup_name(sb, handle->nodeid, &name, &outarg, | ||
584 | &inode); | ||
585 | if (err && err != -ENOENT) | ||
586 | goto out_err; | ||
587 | if (err || !inode) { | ||
588 | err = -ESTALE; | ||
589 | goto out_err; | ||
590 | } | ||
591 | err = -EIO; | ||
592 | if (get_node_id(inode) != handle->nodeid) | ||
593 | goto out_iput; | ||
594 | } | ||
575 | err = -ESTALE; | 595 | err = -ESTALE; |
576 | if (inode->i_generation != handle->generation) | 596 | if (inode->i_generation != handle->generation) |
577 | goto out_iput; | 597 | goto out_iput; |
@@ -659,11 +679,46 @@ static struct dentry *fuse_fh_to_parent(struct super_block *sb, | |||
659 | return fuse_get_dentry(sb, &parent); | 679 | return fuse_get_dentry(sb, &parent); |
660 | } | 680 | } |
661 | 681 | ||
682 | static struct dentry *fuse_get_parent(struct dentry *child) | ||
683 | { | ||
684 | struct inode *child_inode = child->d_inode; | ||
685 | struct fuse_conn *fc = get_fuse_conn(child_inode); | ||
686 | struct inode *inode; | ||
687 | struct dentry *parent; | ||
688 | struct fuse_entry_out outarg; | ||
689 | struct qstr name; | ||
690 | int err; | ||
691 | |||
692 | if (!fc->export_support) | ||
693 | return ERR_PTR(-ESTALE); | ||
694 | |||
695 | name.len = 2; | ||
696 | name.name = ".."; | ||
697 | err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), | ||
698 | &name, &outarg, &inode); | ||
699 | if (err && err != -ENOENT) | ||
700 | return ERR_PTR(err); | ||
701 | if (err || !inode) | ||
702 | return ERR_PTR(-ESTALE); | ||
703 | |||
704 | parent = d_alloc_anon(inode); | ||
705 | if (!parent) { | ||
706 | iput(inode); | ||
707 | return ERR_PTR(-ENOMEM); | ||
708 | } | ||
709 | if (get_node_id(inode) != FUSE_ROOT_ID) { | ||
710 | parent->d_op = &fuse_dentry_operations; | ||
711 | fuse_invalidate_entry_cache(parent); | ||
712 | } | ||
713 | |||
714 | return parent; | ||
715 | } | ||
662 | 716 | ||
663 | static const struct export_operations fuse_export_operations = { | 717 | static const struct export_operations fuse_export_operations = { |
664 | .fh_to_dentry = fuse_fh_to_dentry, | 718 | .fh_to_dentry = fuse_fh_to_dentry, |
665 | .fh_to_parent = fuse_fh_to_parent, | 719 | .fh_to_parent = fuse_fh_to_parent, |
666 | .encode_fh = fuse_encode_fh, | 720 | .encode_fh = fuse_encode_fh, |
721 | .get_parent = fuse_get_parent, | ||
667 | }; | 722 | }; |
668 | 723 | ||
669 | static const struct super_operations fuse_super_operations = { | 724 | 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) | |||
695 | fc->no_lock = 1; | 750 | fc->no_lock = 1; |
696 | if (arg->flags & FUSE_ATOMIC_O_TRUNC) | 751 | if (arg->flags & FUSE_ATOMIC_O_TRUNC) |
697 | fc->atomic_o_trunc = 1; | 752 | fc->atomic_o_trunc = 1; |
753 | if (arg->minor >= 9) { | ||
754 | /* LOOKUP has dependency on proto version */ | ||
755 | if (arg->flags & FUSE_EXPORT_SUPPORT) | ||
756 | fc->export_support = 1; | ||
757 | } | ||
698 | if (arg->flags & FUSE_BIG_WRITES) | 758 | if (arg->flags & FUSE_BIG_WRITES) |
699 | fc->big_writes = 1; | 759 | fc->big_writes = 1; |
700 | } else { | 760 | } else { |
@@ -721,7 +781,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) | |||
721 | arg->minor = FUSE_KERNEL_MINOR_VERSION; | 781 | arg->minor = FUSE_KERNEL_MINOR_VERSION; |
722 | arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE; | 782 | arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE; |
723 | arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC | | 783 | arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC | |
724 | FUSE_BIG_WRITES; | 784 | FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES; |
725 | req->in.h.opcode = FUSE_INIT; | 785 | req->in.h.opcode = FUSE_INIT; |
726 | req->in.numargs = 1; | 786 | req->in.numargs = 1; |
727 | req->in.args[0].size = sizeof(*arg); | 787 | req->in.args[0].size = sizeof(*arg); |
diff --git a/include/linux/fuse.h b/include/linux/fuse.h index d48282197696..265635dc9908 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h | |||
@@ -104,11 +104,14 @@ struct fuse_file_lock { | |||
104 | 104 | ||
105 | /** | 105 | /** |
106 | * INIT request/reply flags | 106 | * INIT request/reply flags |
107 | * | ||
108 | * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." | ||
107 | */ | 109 | */ |
108 | #define FUSE_ASYNC_READ (1 << 0) | 110 | #define FUSE_ASYNC_READ (1 << 0) |
109 | #define FUSE_POSIX_LOCKS (1 << 1) | 111 | #define FUSE_POSIX_LOCKS (1 << 1) |
110 | #define FUSE_FILE_OPS (1 << 2) | 112 | #define FUSE_FILE_OPS (1 << 2) |
111 | #define FUSE_ATOMIC_O_TRUNC (1 << 3) | 113 | #define FUSE_ATOMIC_O_TRUNC (1 << 3) |
114 | #define FUSE_EXPORT_SUPPORT (1 << 4) | ||
112 | #define FUSE_BIG_WRITES (1 << 5) | 115 | #define FUSE_BIG_WRITES (1 << 5) |
113 | 116 | ||
114 | /** | 117 | /** |