diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2005-11-07 03:59:51 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-07 10:53:42 -0500 |
commit | fd72faac95d7e47610e981d7ed7b3c1529e55c88 (patch) | |
tree | 65cde1eb6958f410c4b4c72e322fd59db463150f /fs/fuse/file.c | |
parent | 31d40d74b402a6fa18a006fb3745f64609f35b77 (diff) |
[PATCH] FUSE: atomic create+open
This patch adds an atomic create+open operation. This does not yet work if
the file type changes between lookup and create+open, but solves the
permission checking problems for the separte create and open methods.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/fuse/file.c')
-rw-r--r-- | fs/fuse/file.c | 132 |
1 files changed, 85 insertions, 47 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 657ab11c173b..2ca86141d13a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -14,11 +14,69 @@ | |||
14 | 14 | ||
15 | static struct file_operations fuse_direct_io_file_operations; | 15 | static struct file_operations fuse_direct_io_file_operations; |
16 | 16 | ||
17 | int fuse_open_common(struct inode *inode, struct file *file, int isdir) | 17 | static int fuse_send_open(struct inode *inode, struct file *file, int isdir, |
18 | struct fuse_open_out *outargp) | ||
18 | { | 19 | { |
19 | struct fuse_conn *fc = get_fuse_conn(inode); | 20 | struct fuse_conn *fc = get_fuse_conn(inode); |
20 | struct fuse_req *req; | ||
21 | struct fuse_open_in inarg; | 21 | struct fuse_open_in inarg; |
22 | struct fuse_req *req; | ||
23 | int err; | ||
24 | |||
25 | req = fuse_get_request(fc); | ||
26 | if (!req) | ||
27 | return -EINTR; | ||
28 | |||
29 | memset(&inarg, 0, sizeof(inarg)); | ||
30 | inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); | ||
31 | req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN; | ||
32 | req->in.h.nodeid = get_node_id(inode); | ||
33 | req->inode = inode; | ||
34 | req->in.numargs = 1; | ||
35 | req->in.args[0].size = sizeof(inarg); | ||
36 | req->in.args[0].value = &inarg; | ||
37 | req->out.numargs = 1; | ||
38 | req->out.args[0].size = sizeof(*outargp); | ||
39 | req->out.args[0].value = outargp; | ||
40 | request_send(fc, req); | ||
41 | err = req->out.h.error; | ||
42 | fuse_put_request(fc, req); | ||
43 | |||
44 | return err; | ||
45 | } | ||
46 | |||
47 | struct fuse_file *fuse_file_alloc(void) | ||
48 | { | ||
49 | struct fuse_file *ff; | ||
50 | ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL); | ||
51 | if (ff) { | ||
52 | ff->release_req = fuse_request_alloc(); | ||
53 | if (!ff->release_req) { | ||
54 | kfree(ff); | ||
55 | ff = NULL; | ||
56 | } | ||
57 | } | ||
58 | return ff; | ||
59 | } | ||
60 | |||
61 | void fuse_file_free(struct fuse_file *ff) | ||
62 | { | ||
63 | fuse_request_free(ff->release_req); | ||
64 | kfree(ff); | ||
65 | } | ||
66 | |||
67 | void fuse_finish_open(struct inode *inode, struct file *file, | ||
68 | struct fuse_file *ff, struct fuse_open_out *outarg) | ||
69 | { | ||
70 | if (outarg->open_flags & FOPEN_DIRECT_IO) | ||
71 | file->f_op = &fuse_direct_io_file_operations; | ||
72 | if (!(outarg->open_flags & FOPEN_KEEP_CACHE)) | ||
73 | invalidate_inode_pages(inode->i_mapping); | ||
74 | ff->fh = outarg->fh; | ||
75 | file->private_data = ff; | ||
76 | } | ||
77 | |||
78 | int fuse_open_common(struct inode *inode, struct file *file, int isdir) | ||
79 | { | ||
22 | struct fuse_open_out outarg; | 80 | struct fuse_open_out outarg; |
23 | struct fuse_file *ff; | 81 | struct fuse_file *ff; |
24 | int err; | 82 | int err; |
@@ -34,73 +92,53 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir) | |||
34 | /* If opening the root node, no lookup has been performed on | 92 | /* If opening the root node, no lookup has been performed on |
35 | it, so the attributes must be refreshed */ | 93 | it, so the attributes must be refreshed */ |
36 | if (get_node_id(inode) == FUSE_ROOT_ID) { | 94 | if (get_node_id(inode) == FUSE_ROOT_ID) { |
37 | int err = fuse_do_getattr(inode); | 95 | err = fuse_do_getattr(inode); |
38 | if (err) | 96 | if (err) |
39 | return err; | 97 | return err; |
40 | } | 98 | } |
41 | 99 | ||
42 | req = fuse_get_request(fc); | 100 | ff = fuse_file_alloc(); |
43 | if (!req) | ||
44 | return -EINTR; | ||
45 | |||
46 | err = -ENOMEM; | ||
47 | ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL); | ||
48 | if (!ff) | 101 | if (!ff) |
49 | goto out_put_request; | 102 | return -ENOMEM; |
50 | 103 | ||
51 | ff->release_req = fuse_request_alloc(); | 104 | err = fuse_send_open(inode, file, isdir, &outarg); |
52 | if (!ff->release_req) { | 105 | if (err) |
53 | kfree(ff); | 106 | fuse_file_free(ff); |
54 | goto out_put_request; | 107 | else { |
55 | } | 108 | if (isdir) |
56 | 109 | outarg.open_flags &= ~FOPEN_DIRECT_IO; | |
57 | memset(&inarg, 0, sizeof(inarg)); | 110 | fuse_finish_open(inode, file, ff, &outarg); |
58 | inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); | ||
59 | req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN; | ||
60 | req->in.h.nodeid = get_node_id(inode); | ||
61 | req->inode = inode; | ||
62 | req->in.numargs = 1; | ||
63 | req->in.args[0].size = sizeof(inarg); | ||
64 | req->in.args[0].value = &inarg; | ||
65 | req->out.numargs = 1; | ||
66 | req->out.args[0].size = sizeof(outarg); | ||
67 | req->out.args[0].value = &outarg; | ||
68 | request_send(fc, req); | ||
69 | err = req->out.h.error; | ||
70 | if (err) { | ||
71 | fuse_request_free(ff->release_req); | ||
72 | kfree(ff); | ||
73 | } else { | ||
74 | if (!isdir && (outarg.open_flags & FOPEN_DIRECT_IO)) | ||
75 | file->f_op = &fuse_direct_io_file_operations; | ||
76 | if (!(outarg.open_flags & FOPEN_KEEP_CACHE)) | ||
77 | invalidate_inode_pages(inode->i_mapping); | ||
78 | ff->fh = outarg.fh; | ||
79 | file->private_data = ff; | ||
80 | } | 111 | } |
81 | 112 | ||
82 | out_put_request: | ||
83 | fuse_put_request(fc, req); | ||
84 | return err; | 113 | return err; |
85 | } | 114 | } |
86 | 115 | ||
87 | int fuse_release_common(struct inode *inode, struct file *file, int isdir) | 116 | void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff, |
117 | u64 nodeid, struct inode *inode, int flags, int isdir) | ||
88 | { | 118 | { |
89 | struct fuse_conn *fc = get_fuse_conn(inode); | 119 | struct fuse_req * req = ff->release_req; |
90 | struct fuse_file *ff = file->private_data; | ||
91 | struct fuse_req *req = ff->release_req; | ||
92 | struct fuse_release_in *inarg = &req->misc.release_in; | 120 | struct fuse_release_in *inarg = &req->misc.release_in; |
93 | 121 | ||
94 | inarg->fh = ff->fh; | 122 | inarg->fh = ff->fh; |
95 | inarg->flags = file->f_flags & ~O_EXCL; | 123 | inarg->flags = flags; |
96 | req->in.h.opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; | 124 | req->in.h.opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; |
97 | req->in.h.nodeid = get_node_id(inode); | 125 | req->in.h.nodeid = nodeid; |
98 | req->inode = inode; | 126 | req->inode = inode; |
99 | req->in.numargs = 1; | 127 | req->in.numargs = 1; |
100 | req->in.args[0].size = sizeof(struct fuse_release_in); | 128 | req->in.args[0].size = sizeof(struct fuse_release_in); |
101 | req->in.args[0].value = inarg; | 129 | req->in.args[0].value = inarg; |
102 | request_send_background(fc, req); | 130 | request_send_background(fc, req); |
103 | kfree(ff); | 131 | kfree(ff); |
132 | } | ||
133 | |||
134 | int fuse_release_common(struct inode *inode, struct file *file, int isdir) | ||
135 | { | ||
136 | struct fuse_file *ff = file->private_data; | ||
137 | if (ff) { | ||
138 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
139 | u64 nodeid = get_node_id(inode); | ||
140 | fuse_send_release(fc, ff, nodeid, inode, file->f_flags, isdir); | ||
141 | } | ||
104 | 142 | ||
105 | /* Return value is ignored by VFS */ | 143 | /* Return value is ignored by VFS */ |
106 | return 0; | 144 | return 0; |