diff options
-rw-r--r-- | fs/fuse/file.c | 132 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 4 | ||||
-rw-r--r-- | fs/fuse/inode.c | 20 | ||||
-rw-r--r-- | include/linux/fuse.h | 26 |
4 files changed, 178 insertions, 4 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 1d59af306b28..d9a8289297c0 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -160,6 +160,18 @@ static int fuse_release(struct inode *inode, struct file *file) | |||
160 | return fuse_release_common(inode, file, 0); | 160 | return fuse_release_common(inode, file, 0); |
161 | } | 161 | } |
162 | 162 | ||
163 | /* | ||
164 | * It would be nice to scramble the ID space, so that the value of the | ||
165 | * files_struct pointer is not exposed to userspace. Symmetric crypto | ||
166 | * functions are overkill, since the inverse function doesn't need to | ||
167 | * be implemented (though it does have to exist). Is there something | ||
168 | * simpler? | ||
169 | */ | ||
170 | static inline u64 fuse_lock_owner_id(fl_owner_t id) | ||
171 | { | ||
172 | return (unsigned long) id; | ||
173 | } | ||
174 | |||
163 | static int fuse_flush(struct file *file, fl_owner_t id) | 175 | static int fuse_flush(struct file *file, fl_owner_t id) |
164 | { | 176 | { |
165 | struct inode *inode = file->f_dentry->d_inode; | 177 | struct inode *inode = file->f_dentry->d_inode; |
@@ -181,11 +193,13 @@ static int fuse_flush(struct file *file, fl_owner_t id) | |||
181 | 193 | ||
182 | memset(&inarg, 0, sizeof(inarg)); | 194 | memset(&inarg, 0, sizeof(inarg)); |
183 | inarg.fh = ff->fh; | 195 | inarg.fh = ff->fh; |
196 | inarg.lock_owner = fuse_lock_owner_id(id); | ||
184 | req->in.h.opcode = FUSE_FLUSH; | 197 | req->in.h.opcode = FUSE_FLUSH; |
185 | req->in.h.nodeid = get_node_id(inode); | 198 | req->in.h.nodeid = get_node_id(inode); |
186 | req->in.numargs = 1; | 199 | req->in.numargs = 1; |
187 | req->in.args[0].size = sizeof(inarg); | 200 | req->in.args[0].size = sizeof(inarg); |
188 | req->in.args[0].value = &inarg; | 201 | req->in.args[0].value = &inarg; |
202 | req->force = 1; | ||
189 | request_send(fc, req); | 203 | request_send(fc, req); |
190 | err = req->out.h.error; | 204 | err = req->out.h.error; |
191 | fuse_put_request(fc, req); | 205 | fuse_put_request(fc, req); |
@@ -604,6 +618,122 @@ static int fuse_set_page_dirty(struct page *page) | |||
604 | return 0; | 618 | return 0; |
605 | } | 619 | } |
606 | 620 | ||
621 | static int convert_fuse_file_lock(const struct fuse_file_lock *ffl, | ||
622 | struct file_lock *fl) | ||
623 | { | ||
624 | switch (ffl->type) { | ||
625 | case F_UNLCK: | ||
626 | break; | ||
627 | |||
628 | case F_RDLCK: | ||
629 | case F_WRLCK: | ||
630 | if (ffl->start > OFFSET_MAX || ffl->end > OFFSET_MAX || | ||
631 | ffl->end < ffl->start) | ||
632 | return -EIO; | ||
633 | |||
634 | fl->fl_start = ffl->start; | ||
635 | fl->fl_end = ffl->end; | ||
636 | fl->fl_pid = ffl->pid; | ||
637 | break; | ||
638 | |||
639 | default: | ||
640 | return -EIO; | ||
641 | } | ||
642 | fl->fl_type = ffl->type; | ||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | static void fuse_lk_fill(struct fuse_req *req, struct file *file, | ||
647 | const struct file_lock *fl, int opcode, pid_t pid) | ||
648 | { | ||
649 | struct inode *inode = file->f_dentry->d_inode; | ||
650 | struct fuse_file *ff = file->private_data; | ||
651 | struct fuse_lk_in *arg = &req->misc.lk_in; | ||
652 | |||
653 | arg->fh = ff->fh; | ||
654 | arg->owner = fuse_lock_owner_id(fl->fl_owner); | ||
655 | arg->lk.start = fl->fl_start; | ||
656 | arg->lk.end = fl->fl_end; | ||
657 | arg->lk.type = fl->fl_type; | ||
658 | arg->lk.pid = pid; | ||
659 | req->in.h.opcode = opcode; | ||
660 | req->in.h.nodeid = get_node_id(inode); | ||
661 | req->in.numargs = 1; | ||
662 | req->in.args[0].size = sizeof(*arg); | ||
663 | req->in.args[0].value = arg; | ||
664 | } | ||
665 | |||
666 | static int fuse_getlk(struct file *file, struct file_lock *fl) | ||
667 | { | ||
668 | struct inode *inode = file->f_dentry->d_inode; | ||
669 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
670 | struct fuse_req *req; | ||
671 | struct fuse_lk_out outarg; | ||
672 | int err; | ||
673 | |||
674 | req = fuse_get_req(fc); | ||
675 | if (IS_ERR(req)) | ||
676 | return PTR_ERR(req); | ||
677 | |||
678 | fuse_lk_fill(req, file, fl, FUSE_GETLK, 0); | ||
679 | req->out.numargs = 1; | ||
680 | req->out.args[0].size = sizeof(outarg); | ||
681 | req->out.args[0].value = &outarg; | ||
682 | request_send(fc, req); | ||
683 | err = req->out.h.error; | ||
684 | fuse_put_request(fc, req); | ||
685 | if (!err) | ||
686 | err = convert_fuse_file_lock(&outarg.lk, fl); | ||
687 | |||
688 | return err; | ||
689 | } | ||
690 | |||
691 | static int fuse_setlk(struct file *file, struct file_lock *fl) | ||
692 | { | ||
693 | struct inode *inode = file->f_dentry->d_inode; | ||
694 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
695 | struct fuse_req *req; | ||
696 | int opcode = (fl->fl_flags & FL_SLEEP) ? FUSE_SETLKW : FUSE_SETLK; | ||
697 | pid_t pid = fl->fl_type != F_UNLCK ? current->tgid : 0; | ||
698 | int err; | ||
699 | |||
700 | /* Unlock on close is handled by the flush method */ | ||
701 | if (fl->fl_flags & FL_CLOSE) | ||
702 | return 0; | ||
703 | |||
704 | req = fuse_get_req(fc); | ||
705 | if (IS_ERR(req)) | ||
706 | return PTR_ERR(req); | ||
707 | |||
708 | fuse_lk_fill(req, file, fl, opcode, pid); | ||
709 | request_send(fc, req); | ||
710 | err = req->out.h.error; | ||
711 | fuse_put_request(fc, req); | ||
712 | return err; | ||
713 | } | ||
714 | |||
715 | static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl) | ||
716 | { | ||
717 | struct inode *inode = file->f_dentry->d_inode; | ||
718 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
719 | int err; | ||
720 | |||
721 | if (cmd == F_GETLK) { | ||
722 | if (fc->no_lock) { | ||
723 | if (!posix_test_lock(file, fl, fl)) | ||
724 | fl->fl_type = F_UNLCK; | ||
725 | err = 0; | ||
726 | } else | ||
727 | err = fuse_getlk(file, fl); | ||
728 | } else { | ||
729 | if (fc->no_lock) | ||
730 | err = posix_lock_file_wait(file, fl); | ||
731 | else | ||
732 | err = fuse_setlk(file, fl); | ||
733 | } | ||
734 | return err; | ||
735 | } | ||
736 | |||
607 | static const struct file_operations fuse_file_operations = { | 737 | static const struct file_operations fuse_file_operations = { |
608 | .llseek = generic_file_llseek, | 738 | .llseek = generic_file_llseek, |
609 | .read = generic_file_read, | 739 | .read = generic_file_read, |
@@ -613,6 +743,7 @@ static const struct file_operations fuse_file_operations = { | |||
613 | .flush = fuse_flush, | 743 | .flush = fuse_flush, |
614 | .release = fuse_release, | 744 | .release = fuse_release, |
615 | .fsync = fuse_fsync, | 745 | .fsync = fuse_fsync, |
746 | .lock = fuse_file_lock, | ||
616 | .sendfile = generic_file_sendfile, | 747 | .sendfile = generic_file_sendfile, |
617 | }; | 748 | }; |
618 | 749 | ||
@@ -624,6 +755,7 @@ static const struct file_operations fuse_direct_io_file_operations = { | |||
624 | .flush = fuse_flush, | 755 | .flush = fuse_flush, |
625 | .release = fuse_release, | 756 | .release = fuse_release, |
626 | .fsync = fuse_fsync, | 757 | .fsync = fuse_fsync, |
758 | .lock = fuse_file_lock, | ||
627 | /* no mmap and sendfile */ | 759 | /* no mmap and sendfile */ |
628 | }; | 760 | }; |
629 | 761 | ||
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index ac12b01f4446..eb3166625ca9 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -190,6 +190,7 @@ struct fuse_req { | |||
190 | struct fuse_init_in init_in; | 190 | struct fuse_init_in init_in; |
191 | struct fuse_init_out init_out; | 191 | struct fuse_init_out init_out; |
192 | struct fuse_read_in read_in; | 192 | struct fuse_read_in read_in; |
193 | struct fuse_lk_in lk_in; | ||
193 | } misc; | 194 | } misc; |
194 | 195 | ||
195 | /** page vector */ | 196 | /** page vector */ |
@@ -307,6 +308,9 @@ struct fuse_conn { | |||
307 | /** Is removexattr not implemented by fs? */ | 308 | /** Is removexattr not implemented by fs? */ |
308 | unsigned no_removexattr : 1; | 309 | unsigned no_removexattr : 1; |
309 | 310 | ||
311 | /** Are file locking primitives not implemented by fs? */ | ||
312 | unsigned no_lock : 1; | ||
313 | |||
310 | /** Is access not implemented by fs? */ | 314 | /** Is access not implemented by fs? */ |
311 | unsigned no_access : 1; | 315 | unsigned no_access : 1; |
312 | 316 | ||
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 13a7e8ab7a78..412892905838 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -98,6 +98,14 @@ static void fuse_clear_inode(struct inode *inode) | |||
98 | } | 98 | } |
99 | } | 99 | } |
100 | 100 | ||
101 | static int fuse_remount_fs(struct super_block *sb, int *flags, char *data) | ||
102 | { | ||
103 | if (*flags & MS_MANDLOCK) | ||
104 | return -EINVAL; | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
101 | void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) | 109 | void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) |
102 | { | 110 | { |
103 | if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) | 111 | if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) |
@@ -409,6 +417,7 @@ static struct super_operations fuse_super_operations = { | |||
409 | .destroy_inode = fuse_destroy_inode, | 417 | .destroy_inode = fuse_destroy_inode, |
410 | .read_inode = fuse_read_inode, | 418 | .read_inode = fuse_read_inode, |
411 | .clear_inode = fuse_clear_inode, | 419 | .clear_inode = fuse_clear_inode, |
420 | .remount_fs = fuse_remount_fs, | ||
412 | .put_super = fuse_put_super, | 421 | .put_super = fuse_put_super, |
413 | .umount_begin = fuse_umount_begin, | 422 | .umount_begin = fuse_umount_begin, |
414 | .statfs = fuse_statfs, | 423 | .statfs = fuse_statfs, |
@@ -428,8 +437,12 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | |||
428 | ra_pages = arg->max_readahead / PAGE_CACHE_SIZE; | 437 | ra_pages = arg->max_readahead / PAGE_CACHE_SIZE; |
429 | if (arg->flags & FUSE_ASYNC_READ) | 438 | if (arg->flags & FUSE_ASYNC_READ) |
430 | fc->async_read = 1; | 439 | fc->async_read = 1; |
431 | } else | 440 | if (!(arg->flags & FUSE_POSIX_LOCKS)) |
441 | fc->no_lock = 1; | ||
442 | } else { | ||
432 | ra_pages = fc->max_read / PAGE_CACHE_SIZE; | 443 | ra_pages = fc->max_read / PAGE_CACHE_SIZE; |
444 | fc->no_lock = 1; | ||
445 | } | ||
433 | 446 | ||
434 | fc->bdi.ra_pages = min(fc->bdi.ra_pages, ra_pages); | 447 | fc->bdi.ra_pages = min(fc->bdi.ra_pages, ra_pages); |
435 | fc->minor = arg->minor; | 448 | fc->minor = arg->minor; |
@@ -447,7 +460,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) | |||
447 | arg->major = FUSE_KERNEL_VERSION; | 460 | arg->major = FUSE_KERNEL_VERSION; |
448 | arg->minor = FUSE_KERNEL_MINOR_VERSION; | 461 | arg->minor = FUSE_KERNEL_MINOR_VERSION; |
449 | arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE; | 462 | arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE; |
450 | arg->flags |= FUSE_ASYNC_READ; | 463 | arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS; |
451 | req->in.h.opcode = FUSE_INIT; | 464 | req->in.h.opcode = FUSE_INIT; |
452 | req->in.numargs = 1; | 465 | req->in.numargs = 1; |
453 | req->in.args[0].size = sizeof(*arg); | 466 | req->in.args[0].size = sizeof(*arg); |
@@ -479,6 +492,9 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
479 | struct fuse_req *init_req; | 492 | struct fuse_req *init_req; |
480 | int err; | 493 | int err; |
481 | 494 | ||
495 | if (sb->s_flags & MS_MANDLOCK) | ||
496 | return -EINVAL; | ||
497 | |||
482 | if (!parse_fuse_opt((char *) data, &d)) | 498 | if (!parse_fuse_opt((char *) data, &d)) |
483 | return -EINVAL; | 499 | return -EINVAL; |
484 | 500 | ||
diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 8e4319614bef..e7a76ec0f05c 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | FUSE: Filesystem in Userspace | 2 | FUSE: Filesystem in Userspace |
3 | Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu> | 3 | Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu> |
4 | 4 | ||
5 | This program can be distributed under the terms of the GNU GPL. | 5 | This program can be distributed under the terms of the GNU GPL. |
6 | See the file COPYING. | 6 | See the file COPYING. |
@@ -15,7 +15,7 @@ | |||
15 | #define FUSE_KERNEL_VERSION 7 | 15 | #define FUSE_KERNEL_VERSION 7 |
16 | 16 | ||
17 | /** Minor version number of this interface */ | 17 | /** Minor version number of this interface */ |
18 | #define FUSE_KERNEL_MINOR_VERSION 6 | 18 | #define FUSE_KERNEL_MINOR_VERSION 7 |
19 | 19 | ||
20 | /** The node ID of the root inode */ | 20 | /** The node ID of the root inode */ |
21 | #define FUSE_ROOT_ID 1 | 21 | #define FUSE_ROOT_ID 1 |
@@ -59,6 +59,13 @@ struct fuse_kstatfs { | |||
59 | __u32 spare[6]; | 59 | __u32 spare[6]; |
60 | }; | 60 | }; |
61 | 61 | ||
62 | struct fuse_file_lock { | ||
63 | __u64 start; | ||
64 | __u64 end; | ||
65 | __u32 type; | ||
66 | __u32 pid; /* tgid */ | ||
67 | }; | ||
68 | |||
62 | /** | 69 | /** |
63 | * Bitmasks for fuse_setattr_in.valid | 70 | * Bitmasks for fuse_setattr_in.valid |
64 | */ | 71 | */ |
@@ -83,6 +90,7 @@ struct fuse_kstatfs { | |||
83 | * INIT request/reply flags | 90 | * INIT request/reply flags |
84 | */ | 91 | */ |
85 | #define FUSE_ASYNC_READ (1 << 0) | 92 | #define FUSE_ASYNC_READ (1 << 0) |
93 | #define FUSE_POSIX_LOCKS (1 << 1) | ||
86 | 94 | ||
87 | enum fuse_opcode { | 95 | enum fuse_opcode { |
88 | FUSE_LOOKUP = 1, | 96 | FUSE_LOOKUP = 1, |
@@ -113,6 +121,9 @@ enum fuse_opcode { | |||
113 | FUSE_READDIR = 28, | 121 | FUSE_READDIR = 28, |
114 | FUSE_RELEASEDIR = 29, | 122 | FUSE_RELEASEDIR = 29, |
115 | FUSE_FSYNCDIR = 30, | 123 | FUSE_FSYNCDIR = 30, |
124 | FUSE_GETLK = 31, | ||
125 | FUSE_SETLK = 32, | ||
126 | FUSE_SETLKW = 33, | ||
116 | FUSE_ACCESS = 34, | 127 | FUSE_ACCESS = 34, |
117 | FUSE_CREATE = 35 | 128 | FUSE_CREATE = 35 |
118 | }; | 129 | }; |
@@ -200,6 +211,7 @@ struct fuse_flush_in { | |||
200 | __u64 fh; | 211 | __u64 fh; |
201 | __u32 flush_flags; | 212 | __u32 flush_flags; |
202 | __u32 padding; | 213 | __u32 padding; |
214 | __u64 lock_owner; | ||
203 | }; | 215 | }; |
204 | 216 | ||
205 | struct fuse_read_in { | 217 | struct fuse_read_in { |
@@ -248,6 +260,16 @@ struct fuse_getxattr_out { | |||
248 | __u32 padding; | 260 | __u32 padding; |
249 | }; | 261 | }; |
250 | 262 | ||
263 | struct fuse_lk_in { | ||
264 | __u64 fh; | ||
265 | __u64 owner; | ||
266 | struct fuse_file_lock lk; | ||
267 | }; | ||
268 | |||
269 | struct fuse_lk_out { | ||
270 | struct fuse_file_lock lk; | ||
271 | }; | ||
272 | |||
251 | struct fuse_access_in { | 273 | struct fuse_access_in { |
252 | __u32 mask; | 274 | __u32 mask; |
253 | __u32 padding; | 275 | __u32 padding; |