diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2012-06-05 09:10:22 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-07-14 08:33:14 -0400 |
commit | c8ccbe032feb127a977c66865cb63d72d9a6e08b (patch) | |
tree | c8f4ebff9ae19a49634a8ade2cf3d3372091e970 /fs | |
parent | eda72afb9ef9f45941fb09260c0f268ff81ec40d (diff) |
fuse: implement i_op->atomic_open()
Add an ->atomic_open implementation which replaces the atomic open+create
operation implemented via ->create. No functionality is changed.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fuse/dir.c | 94 |
1 files changed, 67 insertions, 27 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index f7543f72897e..e42442f1da16 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -369,8 +369,9 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
369 | * If the filesystem doesn't support this, then fall back to separate | 369 | * If the filesystem doesn't support this, then fall back to separate |
370 | * 'mknod' + 'open' requests. | 370 | * 'mknod' + 'open' requests. |
371 | */ | 371 | */ |
372 | static int fuse_create_open(struct inode *dir, struct dentry *entry, | 372 | static struct file *fuse_create_open(struct inode *dir, struct dentry *entry, |
373 | umode_t mode, struct nameidata *nd) | 373 | struct opendata *od, unsigned flags, |
374 | umode_t mode) | ||
374 | { | 375 | { |
375 | int err; | 376 | int err; |
376 | struct inode *inode; | 377 | struct inode *inode; |
@@ -382,14 +383,11 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, | |||
382 | struct fuse_entry_out outentry; | 383 | struct fuse_entry_out outentry; |
383 | struct fuse_file *ff; | 384 | struct fuse_file *ff; |
384 | struct file *file; | 385 | struct file *file; |
385 | int flags = nd->intent.open.flags; | ||
386 | |||
387 | if (fc->no_create) | ||
388 | return -ENOSYS; | ||
389 | 386 | ||
390 | forget = fuse_alloc_forget(); | 387 | forget = fuse_alloc_forget(); |
388 | err = -ENOMEM; | ||
391 | if (!forget) | 389 | if (!forget) |
392 | return -ENOMEM; | 390 | goto out_err; |
393 | 391 | ||
394 | req = fuse_get_req(fc); | 392 | req = fuse_get_req(fc); |
395 | err = PTR_ERR(req); | 393 | err = PTR_ERR(req); |
@@ -428,11 +426,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, | |||
428 | req->out.args[1].value = &outopen; | 426 | req->out.args[1].value = &outopen; |
429 | fuse_request_send(fc, req); | 427 | fuse_request_send(fc, req); |
430 | err = req->out.h.error; | 428 | err = req->out.h.error; |
431 | if (err) { | 429 | if (err) |
432 | if (err == -ENOSYS) | ||
433 | fc->no_create = 1; | ||
434 | goto out_free_ff; | 430 | goto out_free_ff; |
435 | } | ||
436 | 431 | ||
437 | err = -EIO; | 432 | err = -EIO; |
438 | if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid)) | 433 | if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid)) |
@@ -448,28 +443,78 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, | |||
448 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); | 443 | flags &= ~(O_CREAT | O_EXCL | O_TRUNC); |
449 | fuse_sync_release(ff, flags); | 444 | fuse_sync_release(ff, flags); |
450 | fuse_queue_forget(fc, forget, outentry.nodeid, 1); | 445 | fuse_queue_forget(fc, forget, outentry.nodeid, 1); |
451 | return -ENOMEM; | 446 | err = -ENOMEM; |
447 | goto out_err; | ||
452 | } | 448 | } |
453 | kfree(forget); | 449 | kfree(forget); |
454 | d_instantiate(entry, inode); | 450 | d_instantiate(entry, inode); |
455 | fuse_change_entry_timeout(entry, &outentry); | 451 | fuse_change_entry_timeout(entry, &outentry); |
456 | fuse_invalidate_attr(dir); | 452 | fuse_invalidate_attr(dir); |
457 | file = lookup_instantiate_filp(nd, entry, generic_file_open); | 453 | file = finish_open(od, entry, generic_file_open); |
458 | if (IS_ERR(file)) { | 454 | if (IS_ERR(file)) { |
459 | fuse_sync_release(ff, flags); | 455 | fuse_sync_release(ff, flags); |
460 | return PTR_ERR(file); | 456 | } else { |
457 | file->private_data = fuse_file_get(ff); | ||
458 | fuse_finish_open(inode, file); | ||
461 | } | 459 | } |
462 | file->private_data = fuse_file_get(ff); | 460 | return file; |
463 | fuse_finish_open(inode, file); | ||
464 | return 0; | ||
465 | 461 | ||
466 | out_free_ff: | 462 | out_free_ff: |
467 | fuse_file_free(ff); | 463 | fuse_file_free(ff); |
468 | out_put_request: | 464 | out_put_request: |
469 | fuse_put_request(fc, req); | 465 | fuse_put_request(fc, req); |
470 | out_put_forget_req: | 466 | out_put_forget_req: |
471 | kfree(forget); | 467 | kfree(forget); |
472 | return err; | 468 | out_err: |
469 | return ERR_PTR(err); | ||
470 | } | ||
471 | |||
472 | static int fuse_mknod(struct inode *, struct dentry *, umode_t, dev_t); | ||
473 | static struct file *fuse_atomic_open(struct inode *dir, struct dentry *entry, | ||
474 | struct opendata *od, unsigned flags, | ||
475 | umode_t mode, bool *created) | ||
476 | { | ||
477 | int err; | ||
478 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
479 | struct file *file; | ||
480 | struct dentry *res = NULL; | ||
481 | |||
482 | if (d_unhashed(entry)) { | ||
483 | res = fuse_lookup(dir, entry, NULL); | ||
484 | if (IS_ERR(res)) | ||
485 | return ERR_CAST(res); | ||
486 | |||
487 | if (res) | ||
488 | entry = res; | ||
489 | } | ||
490 | |||
491 | if (!(flags & O_CREAT) || entry->d_inode) | ||
492 | goto no_open; | ||
493 | |||
494 | /* Only creates */ | ||
495 | *created = true; | ||
496 | |||
497 | if (fc->no_create) | ||
498 | goto mknod; | ||
499 | |||
500 | file = fuse_create_open(dir, entry, od, flags, mode); | ||
501 | if (PTR_ERR(file) == -ENOSYS) { | ||
502 | fc->no_create = 1; | ||
503 | goto mknod; | ||
504 | } | ||
505 | out_dput: | ||
506 | dput(res); | ||
507 | return file; | ||
508 | |||
509 | mknod: | ||
510 | err = fuse_mknod(dir, entry, mode, 0); | ||
511 | if (err) { | ||
512 | file = ERR_PTR(err); | ||
513 | goto out_dput; | ||
514 | } | ||
515 | no_open: | ||
516 | finish_no_open(od, res); | ||
517 | return NULL; | ||
473 | } | 518 | } |
474 | 519 | ||
475 | /* | 520 | /* |
@@ -573,12 +618,6 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode, | |||
573 | static int fuse_create(struct inode *dir, struct dentry *entry, umode_t mode, | 618 | static int fuse_create(struct inode *dir, struct dentry *entry, umode_t mode, |
574 | struct nameidata *nd) | 619 | struct nameidata *nd) |
575 | { | 620 | { |
576 | if (nd) { | ||
577 | int err = fuse_create_open(dir, entry, mode, nd); | ||
578 | if (err != -ENOSYS) | ||
579 | return err; | ||
580 | /* Fall back on mknod */ | ||
581 | } | ||
582 | return fuse_mknod(dir, entry, mode, 0); | 621 | return fuse_mknod(dir, entry, mode, 0); |
583 | } | 622 | } |
584 | 623 | ||
@@ -1646,6 +1685,7 @@ static const struct inode_operations fuse_dir_inode_operations = { | |||
1646 | .link = fuse_link, | 1685 | .link = fuse_link, |
1647 | .setattr = fuse_setattr, | 1686 | .setattr = fuse_setattr, |
1648 | .create = fuse_create, | 1687 | .create = fuse_create, |
1688 | .atomic_open = fuse_atomic_open, | ||
1649 | .mknod = fuse_mknod, | 1689 | .mknod = fuse_mknod, |
1650 | .permission = fuse_permission, | 1690 | .permission = fuse_permission, |
1651 | .getattr = fuse_getattr, | 1691 | .getattr = fuse_getattr, |