diff options
Diffstat (limited to 'fs/fuse/file.c')
-rw-r--r-- | fs/fuse/file.c | 132 |
1 files changed, 132 insertions, 0 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 | ||