diff options
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/dir.c | 117 |
1 files changed, 77 insertions, 40 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index be5450dd6387..51d0035ff07e 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -112,18 +112,16 @@ static void fuse_invalidate_entry(struct dentry *entry) | |||
112 | fuse_invalidate_entry_cache(entry); | 112 | fuse_invalidate_entry_cache(entry); |
113 | } | 113 | } |
114 | 114 | ||
115 | static void fuse_lookup_init(struct fuse_req *req, struct inode *dir, | 115 | static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_req *req, |
116 | struct dentry *entry, | 116 | u64 nodeid, struct qstr *name, |
117 | struct fuse_entry_out *outarg) | 117 | struct fuse_entry_out *outarg) |
118 | { | 118 | { |
119 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
120 | |||
121 | memset(outarg, 0, sizeof(struct fuse_entry_out)); | 119 | memset(outarg, 0, sizeof(struct fuse_entry_out)); |
122 | req->in.h.opcode = FUSE_LOOKUP; | 120 | req->in.h.opcode = FUSE_LOOKUP; |
123 | req->in.h.nodeid = get_node_id(dir); | 121 | req->in.h.nodeid = nodeid; |
124 | req->in.numargs = 1; | 122 | req->in.numargs = 1; |
125 | req->in.args[0].size = entry->d_name.len + 1; | 123 | req->in.args[0].size = name->len + 1; |
126 | req->in.args[0].value = entry->d_name.name; | 124 | req->in.args[0].value = name->name; |
127 | req->out.numargs = 1; | 125 | req->out.numargs = 1; |
128 | if (fc->minor < 9) | 126 | if (fc->minor < 9) |
129 | req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; | 127 | req->out.args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE; |
@@ -189,7 +187,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
189 | attr_version = fuse_get_attr_version(fc); | 187 | attr_version = fuse_get_attr_version(fc); |
190 | 188 | ||
191 | parent = dget_parent(entry); | 189 | parent = dget_parent(entry); |
192 | fuse_lookup_init(req, parent->d_inode, entry, &outarg); | 190 | fuse_lookup_init(fc, req, get_node_id(parent->d_inode), |
191 | &entry->d_name, &outarg); | ||
193 | request_send(fc, req); | 192 | request_send(fc, req); |
194 | dput(parent); | 193 | dput(parent); |
195 | err = req->out.h.error; | 194 | err = req->out.h.error; |
@@ -255,73 +254,111 @@ static struct dentry *fuse_d_add_directory(struct dentry *entry, | |||
255 | return d_splice_alias(inode, entry); | 254 | return d_splice_alias(inode, entry); |
256 | } | 255 | } |
257 | 256 | ||
258 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | 257 | int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, |
259 | struct nameidata *nd) | 258 | struct fuse_entry_out *outarg, struct inode **inode) |
260 | { | 259 | { |
261 | int err; | 260 | struct fuse_conn *fc = get_fuse_conn_super(sb); |
262 | struct fuse_entry_out outarg; | ||
263 | struct inode *inode = NULL; | ||
264 | struct dentry *newent; | ||
265 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
266 | struct fuse_req *req; | 261 | struct fuse_req *req; |
267 | struct fuse_req *forget_req; | 262 | struct fuse_req *forget_req; |
268 | u64 attr_version; | 263 | u64 attr_version; |
264 | int err; | ||
269 | 265 | ||
270 | if (entry->d_name.len > FUSE_NAME_MAX) | 266 | *inode = NULL; |
271 | return ERR_PTR(-ENAMETOOLONG); | 267 | err = -ENAMETOOLONG; |
268 | if (name->len > FUSE_NAME_MAX) | ||
269 | goto out; | ||
272 | 270 | ||
273 | req = fuse_get_req(fc); | 271 | req = fuse_get_req(fc); |
272 | err = PTR_ERR(req); | ||
274 | if (IS_ERR(req)) | 273 | if (IS_ERR(req)) |
275 | return ERR_CAST(req); | 274 | goto out; |
276 | 275 | ||
277 | forget_req = fuse_get_req(fc); | 276 | forget_req = fuse_get_req(fc); |
277 | err = PTR_ERR(forget_req); | ||
278 | if (IS_ERR(forget_req)) { | 278 | if (IS_ERR(forget_req)) { |
279 | fuse_put_request(fc, req); | 279 | fuse_put_request(fc, req); |
280 | return ERR_CAST(forget_req); | 280 | goto out; |
281 | } | 281 | } |
282 | 282 | ||
283 | attr_version = fuse_get_attr_version(fc); | 283 | attr_version = fuse_get_attr_version(fc); |
284 | 284 | ||
285 | fuse_lookup_init(req, dir, entry, &outarg); | 285 | fuse_lookup_init(fc, req, nodeid, name, outarg); |
286 | request_send(fc, req); | 286 | request_send(fc, req); |
287 | err = req->out.h.error; | 287 | err = req->out.h.error; |
288 | fuse_put_request(fc, req); | 288 | fuse_put_request(fc, req); |
289 | /* Zero nodeid is same as -ENOENT, but with valid timeout */ | 289 | /* Zero nodeid is same as -ENOENT, but with valid timeout */ |
290 | if (!err && outarg.nodeid && | 290 | if (err || !outarg->nodeid) |
291 | (invalid_nodeid(outarg.nodeid) || | 291 | goto out_put_forget; |
292 | !fuse_valid_type(outarg.attr.mode))) | 292 | |
293 | err = -EIO; | 293 | err = -EIO; |
294 | if (!err && outarg.nodeid) { | 294 | if (!outarg->nodeid) |
295 | inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, | 295 | goto out_put_forget; |
296 | &outarg.attr, entry_attr_timeout(&outarg), | 296 | if (!fuse_valid_type(outarg->attr.mode)) |
297 | attr_version); | 297 | goto out_put_forget; |
298 | if (!inode) { | 298 | |
299 | fuse_send_forget(fc, forget_req, outarg.nodeid, 1); | 299 | *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, |
300 | return ERR_PTR(-ENOMEM); | 300 | &outarg->attr, entry_attr_timeout(outarg), |
301 | } | 301 | attr_version); |
302 | err = -ENOMEM; | ||
303 | if (!*inode) { | ||
304 | fuse_send_forget(fc, forget_req, outarg->nodeid, 1); | ||
305 | goto out; | ||
302 | } | 306 | } |
307 | err = 0; | ||
308 | |||
309 | out_put_forget: | ||
303 | fuse_put_request(fc, forget_req); | 310 | fuse_put_request(fc, forget_req); |
304 | if (err && err != -ENOENT) | 311 | out: |
305 | return ERR_PTR(err); | 312 | return err; |
313 | } | ||
314 | |||
315 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | ||
316 | struct nameidata *nd) | ||
317 | { | ||
318 | int err; | ||
319 | struct fuse_entry_out outarg; | ||
320 | struct inode *inode; | ||
321 | struct dentry *newent; | ||
322 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
323 | bool outarg_valid = true; | ||
324 | |||
325 | err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, | ||
326 | &outarg, &inode); | ||
327 | if (err == -ENOENT) { | ||
328 | outarg_valid = false; | ||
329 | err = 0; | ||
330 | } | ||
331 | if (err) | ||
332 | goto out_err; | ||
333 | |||
334 | err = -EIO; | ||
335 | if (inode && get_node_id(inode) == FUSE_ROOT_ID) | ||
336 | goto out_iput; | ||
306 | 337 | ||
307 | if (inode && S_ISDIR(inode->i_mode)) { | 338 | if (inode && S_ISDIR(inode->i_mode)) { |
308 | mutex_lock(&fc->inst_mutex); | 339 | mutex_lock(&fc->inst_mutex); |
309 | newent = fuse_d_add_directory(entry, inode); | 340 | newent = fuse_d_add_directory(entry, inode); |
310 | mutex_unlock(&fc->inst_mutex); | 341 | mutex_unlock(&fc->inst_mutex); |
311 | if (IS_ERR(newent)) { | 342 | err = PTR_ERR(newent); |
312 | iput(inode); | 343 | if (IS_ERR(newent)) |
313 | return newent; | 344 | goto out_iput; |
314 | } | 345 | } else { |
315 | } else | ||
316 | newent = d_splice_alias(inode, entry); | 346 | newent = d_splice_alias(inode, entry); |
347 | } | ||
317 | 348 | ||
318 | entry = newent ? newent : entry; | 349 | entry = newent ? newent : entry; |
319 | entry->d_op = &fuse_dentry_operations; | 350 | entry->d_op = &fuse_dentry_operations; |
320 | if (!err) | 351 | if (outarg_valid) |
321 | fuse_change_entry_timeout(entry, &outarg); | 352 | fuse_change_entry_timeout(entry, &outarg); |
322 | else | 353 | else |
323 | fuse_invalidate_entry_cache(entry); | 354 | fuse_invalidate_entry_cache(entry); |
355 | |||
324 | return newent; | 356 | return newent; |
357 | |||
358 | out_iput: | ||
359 | iput(inode); | ||
360 | out_err: | ||
361 | return ERR_PTR(err); | ||
325 | } | 362 | } |
326 | 363 | ||
327 | /* | 364 | /* |