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/dir.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/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 106 |
1 files changed, 105 insertions, 1 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 4bc1afcc476d..83be119ef067 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/gfp.h> | 13 | #include <linux/gfp.h> |
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/namei.h> | 15 | #include <linux/namei.h> |
16 | #include <linux/mount.h> | ||
16 | 17 | ||
17 | static inline unsigned long time_to_jiffies(unsigned long sec, | 18 | static inline unsigned long time_to_jiffies(unsigned long sec, |
18 | unsigned long nsec) | 19 | unsigned long nsec) |
@@ -134,6 +135,101 @@ static void fuse_invalidate_entry(struct dentry *entry) | |||
134 | entry->d_time = jiffies - 1; | 135 | entry->d_time = jiffies - 1; |
135 | } | 136 | } |
136 | 137 | ||
138 | static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode, | ||
139 | struct nameidata *nd) | ||
140 | { | ||
141 | int err; | ||
142 | struct inode *inode; | ||
143 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
144 | struct fuse_req *req; | ||
145 | struct fuse_open_in inarg; | ||
146 | struct fuse_open_out outopen; | ||
147 | struct fuse_entry_out outentry; | ||
148 | struct fuse_inode *fi; | ||
149 | struct fuse_file *ff; | ||
150 | struct file *file; | ||
151 | int flags = nd->intent.open.flags - 1; | ||
152 | |||
153 | err = -ENOSYS; | ||
154 | if (fc->no_create) | ||
155 | goto out; | ||
156 | |||
157 | err = -ENAMETOOLONG; | ||
158 | if (entry->d_name.len > FUSE_NAME_MAX) | ||
159 | goto out; | ||
160 | |||
161 | err = -EINTR; | ||
162 | req = fuse_get_request(fc); | ||
163 | if (!req) | ||
164 | goto out; | ||
165 | |||
166 | ff = fuse_file_alloc(); | ||
167 | if (!ff) | ||
168 | goto out_put_request; | ||
169 | |||
170 | flags &= ~O_NOCTTY; | ||
171 | memset(&inarg, 0, sizeof(inarg)); | ||
172 | inarg.flags = flags; | ||
173 | inarg.mode = mode; | ||
174 | req->in.h.opcode = FUSE_CREATE; | ||
175 | req->in.h.nodeid = get_node_id(dir); | ||
176 | req->inode = dir; | ||
177 | req->in.numargs = 2; | ||
178 | req->in.args[0].size = sizeof(inarg); | ||
179 | req->in.args[0].value = &inarg; | ||
180 | req->in.args[1].size = entry->d_name.len + 1; | ||
181 | req->in.args[1].value = entry->d_name.name; | ||
182 | req->out.numargs = 2; | ||
183 | req->out.args[0].size = sizeof(outentry); | ||
184 | req->out.args[0].value = &outentry; | ||
185 | req->out.args[1].size = sizeof(outopen); | ||
186 | req->out.args[1].value = &outopen; | ||
187 | request_send(fc, req); | ||
188 | err = req->out.h.error; | ||
189 | if (err) { | ||
190 | if (err == -ENOSYS) | ||
191 | fc->no_create = 1; | ||
192 | goto out_free_ff; | ||
193 | } | ||
194 | |||
195 | err = -EIO; | ||
196 | if (!S_ISREG(outentry.attr.mode)) | ||
197 | goto out_free_ff; | ||
198 | |||
199 | inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, | ||
200 | &outentry.attr); | ||
201 | err = -ENOMEM; | ||
202 | if (!inode) { | ||
203 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); | ||
204 | ff->fh = outopen.fh; | ||
205 | fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); | ||
206 | goto out_put_request; | ||
207 | } | ||
208 | fuse_put_request(fc, req); | ||
209 | entry->d_time = time_to_jiffies(outentry.entry_valid, | ||
210 | outentry.entry_valid_nsec); | ||
211 | fi = get_fuse_inode(inode); | ||
212 | fi->i_time = time_to_jiffies(outentry.attr_valid, | ||
213 | outentry.attr_valid_nsec); | ||
214 | |||
215 | d_instantiate(entry, inode); | ||
216 | file = lookup_instantiate_filp(nd, entry, generic_file_open); | ||
217 | if (IS_ERR(file)) { | ||
218 | ff->fh = outopen.fh; | ||
219 | fuse_send_release(fc, ff, outentry.nodeid, inode, flags, 0); | ||
220 | return PTR_ERR(file); | ||
221 | } | ||
222 | fuse_finish_open(inode, file, ff, &outopen); | ||
223 | return 0; | ||
224 | |||
225 | out_free_ff: | ||
226 | fuse_file_free(ff); | ||
227 | out_put_request: | ||
228 | fuse_put_request(fc, req); | ||
229 | out: | ||
230 | return err; | ||
231 | } | ||
232 | |||
137 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | 233 | static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, |
138 | struct inode *dir, struct dentry *entry, | 234 | struct inode *dir, struct dentry *entry, |
139 | int mode) | 235 | int mode) |
@@ -208,6 +304,12 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, | |||
208 | static int fuse_create(struct inode *dir, struct dentry *entry, int mode, | 304 | static int fuse_create(struct inode *dir, struct dentry *entry, int mode, |
209 | struct nameidata *nd) | 305 | struct nameidata *nd) |
210 | { | 306 | { |
307 | if (nd && (nd->flags & LOOKUP_CREATE)) { | ||
308 | int err = fuse_create_open(dir, entry, mode, nd); | ||
309 | if (err != -ENOSYS) | ||
310 | return err; | ||
311 | /* Fall back on mknod */ | ||
312 | } | ||
211 | return fuse_mknod(dir, entry, mode, 0); | 313 | return fuse_mknod(dir, entry, mode, 0); |
212 | } | 314 | } |
213 | 315 | ||
@@ -767,7 +869,9 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
767 | struct nameidata *nd) | 869 | struct nameidata *nd) |
768 | { | 870 | { |
769 | struct inode *inode; | 871 | struct inode *inode; |
770 | int err = fuse_lookup_iget(dir, entry, &inode); | 872 | int err; |
873 | |||
874 | err = fuse_lookup_iget(dir, entry, &inode); | ||
771 | if (err) | 875 | if (err) |
772 | return ERR_PTR(err); | 876 | return ERR_PTR(err); |
773 | if (inode && S_ISDIR(inode->i_mode)) { | 877 | if (inode && S_ISDIR(inode->i_mode)) { |