aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-06-25 08:48:52 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-25 13:01:19 -0400
commit7142125937e1482ad3ae4366594c6586153dfc86 (patch)
tree8d85908a36485df0c80de2032e7fcfa493621fe4
parentbafa96541b250a7051e3fbc5de6e8369daf8ffec (diff)
[PATCH] fuse: add POSIX file locking support
This patch adds POSIX file locking support to the fuse interface. This implementation doesn't keep any locking state in kernel. Unlocking on close() is handled by the FLUSH message, which now contains the lock owner id. Mandatory locking is not supported. The filesystem may enfoce mandatory locking in userspace if needed. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/fuse/file.c132
-rw-r--r--fs/fuse/fuse_i.h4
-rw-r--r--fs/fuse/inode.c20
-rw-r--r--include/linux/fuse.h26
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 */
170static inline u64 fuse_lock_owner_id(fl_owner_t id)
171{
172 return (unsigned long) id;
173}
174
163static int fuse_flush(struct file *file, fl_owner_t id) 175static 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
621static 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
646static 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
666static 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
691static 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
715static 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
607static const struct file_operations fuse_file_operations = { 737static 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
101static 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
101void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) 109void 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
62struct 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
87enum fuse_opcode { 95enum 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
205struct fuse_read_in { 217struct fuse_read_in {
@@ -248,6 +260,16 @@ struct fuse_getxattr_out {
248 __u32 padding; 260 __u32 padding;
249}; 261};
250 262
263struct fuse_lk_in {
264 __u64 fh;
265 __u64 owner;
266 struct fuse_file_lock lk;
267};
268
269struct fuse_lk_out {
270 struct fuse_file_lock lk;
271};
272
251struct fuse_access_in { 273struct fuse_access_in {
252 __u32 mask; 274 __u32 mask;
253 __u32 padding; 275 __u32 padding;