diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2018-06-02 01:31:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-03 13:58:23 -0400 |
commit | af04fadcaa932d2d804699409d9d96dd5d85ce7f (patch) | |
tree | ad6d262eed4b0e19ee7d43ed27aee8eb22565dfb | |
parent | 4faa99965e027cc057c5145ce45fa772caa04e8d (diff) |
Revert "fs: fold open_check_o_direct into do_dentry_open"
This reverts commit cab64df194667dc5d9d786f0a895f647f5501c0d.
Having vfs_open() in some cases drop the reference to
struct file combined with
error = vfs_open(path, f, cred);
if (error) {
put_filp(f);
return ERR_PTR(error);
}
return f;
is flat-out wrong. It used to be
error = vfs_open(path, f, cred);
if (!error) {
/* from now on we need fput() to dispose of f */
error = open_check_o_direct(f);
if (error) {
fput(f);
f = ERR_PTR(error);
}
} else {
put_filp(f);
f = ERR_PTR(error);
}
and sure, having that open_check_o_direct() boilerplate gotten rid of is
nice, but not that way...
Worse, another call chain (via finish_open()) is FUBAR now wrt
FILE_OPENED handling - in that case we get error returned, with file
already hit by fput() *AND* FILE_OPENED not set. Guess what happens in
path_openat(), when it hits
if (!(opened & FILE_OPENED)) {
BUG_ON(!error);
put_filp(file);
}
The root cause of all that crap is that the callers of do_dentry_open()
have no way to tell which way did it fail; while that could be fixed up
(by passing something like int *opened to do_dentry_open() and have it
marked if we'd called ->open()), it's probably much too late in the
cycle to do so right now.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/internal.h | 1 | ||||
-rw-r--r-- | fs/namei.c | 7 | ||||
-rw-r--r-- | fs/open.c | 44 |
3 files changed, 33 insertions, 19 deletions
diff --git a/fs/internal.h b/fs/internal.h index e08972db0303..980d005b21b4 100644 --- a/fs/internal.h +++ b/fs/internal.h | |||
@@ -125,6 +125,7 @@ int do_fchmodat(int dfd, const char __user *filename, umode_t mode); | |||
125 | int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, | 125 | int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, |
126 | int flag); | 126 | int flag); |
127 | 127 | ||
128 | extern int open_check_o_direct(struct file *f); | ||
128 | extern int vfs_open(const struct path *, struct file *, const struct cred *); | 129 | extern int vfs_open(const struct path *, struct file *, const struct cred *); |
129 | extern struct file *filp_clone_open(struct file *); | 130 | extern struct file *filp_clone_open(struct file *); |
130 | 131 | ||
diff --git a/fs/namei.c b/fs/namei.c index 186bd2464fd5..4eb916996345 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -3367,7 +3367,9 @@ finish_open_created: | |||
3367 | goto out; | 3367 | goto out; |
3368 | *opened |= FILE_OPENED; | 3368 | *opened |= FILE_OPENED; |
3369 | opened: | 3369 | opened: |
3370 | error = ima_file_check(file, op->acc_mode, *opened); | 3370 | error = open_check_o_direct(file); |
3371 | if (!error) | ||
3372 | error = ima_file_check(file, op->acc_mode, *opened); | ||
3371 | if (!error && will_truncate) | 3373 | if (!error && will_truncate) |
3372 | error = handle_truncate(file); | 3374 | error = handle_truncate(file); |
3373 | out: | 3375 | out: |
@@ -3447,6 +3449,9 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags, | |||
3447 | error = finish_open(file, child, NULL, opened); | 3449 | error = finish_open(file, child, NULL, opened); |
3448 | if (error) | 3450 | if (error) |
3449 | goto out2; | 3451 | goto out2; |
3452 | error = open_check_o_direct(file); | ||
3453 | if (error) | ||
3454 | fput(file); | ||
3450 | out2: | 3455 | out2: |
3451 | mnt_drop_write(path.mnt); | 3456 | mnt_drop_write(path.mnt); |
3452 | out: | 3457 | out: |
@@ -724,6 +724,16 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group) | |||
724 | return ksys_fchown(fd, user, group); | 724 | return ksys_fchown(fd, user, group); |
725 | } | 725 | } |
726 | 726 | ||
727 | int open_check_o_direct(struct file *f) | ||
728 | { | ||
729 | /* NB: we're sure to have correct a_ops only after f_op->open */ | ||
730 | if (f->f_flags & O_DIRECT) { | ||
731 | if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO) | ||
732 | return -EINVAL; | ||
733 | } | ||
734 | return 0; | ||
735 | } | ||
736 | |||
727 | static int do_dentry_open(struct file *f, | 737 | static int do_dentry_open(struct file *f, |
728 | struct inode *inode, | 738 | struct inode *inode, |
729 | int (*open)(struct inode *, struct file *), | 739 | int (*open)(struct inode *, struct file *), |
@@ -745,7 +755,7 @@ static int do_dentry_open(struct file *f, | |||
745 | if (unlikely(f->f_flags & O_PATH)) { | 755 | if (unlikely(f->f_flags & O_PATH)) { |
746 | f->f_mode = FMODE_PATH; | 756 | f->f_mode = FMODE_PATH; |
747 | f->f_op = &empty_fops; | 757 | f->f_op = &empty_fops; |
748 | goto done; | 758 | return 0; |
749 | } | 759 | } |
750 | 760 | ||
751 | if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) { | 761 | if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) { |
@@ -798,12 +808,7 @@ static int do_dentry_open(struct file *f, | |||
798 | f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); | 808 | f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); |
799 | 809 | ||
800 | file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping); | 810 | file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping); |
801 | done: | 811 | |
802 | /* NB: we're sure to have correct a_ops only after f_op->open */ | ||
803 | error = -EINVAL; | ||
804 | if ((f->f_flags & O_DIRECT) && | ||
805 | (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO)) | ||
806 | goto out_fput; | ||
807 | return 0; | 812 | return 0; |
808 | 813 | ||
809 | cleanup_all: | 814 | cleanup_all: |
@@ -818,9 +823,6 @@ cleanup_file: | |||
818 | f->f_path.dentry = NULL; | 823 | f->f_path.dentry = NULL; |
819 | f->f_inode = NULL; | 824 | f->f_inode = NULL; |
820 | return error; | 825 | return error; |
821 | out_fput: | ||
822 | fput(f); | ||
823 | return error; | ||
824 | } | 826 | } |
825 | 827 | ||
826 | /** | 828 | /** |
@@ -918,14 +920,20 @@ struct file *dentry_open(const struct path *path, int flags, | |||
918 | BUG_ON(!path->mnt); | 920 | BUG_ON(!path->mnt); |
919 | 921 | ||
920 | f = get_empty_filp(); | 922 | f = get_empty_filp(); |
921 | if (IS_ERR(f)) | 923 | if (!IS_ERR(f)) { |
922 | return f; | 924 | f->f_flags = flags; |
923 | 925 | error = vfs_open(path, f, cred); | |
924 | f->f_flags = flags; | 926 | if (!error) { |
925 | error = vfs_open(path, f, cred); | 927 | /* from now on we need fput() to dispose of f */ |
926 | if (error) { | 928 | error = open_check_o_direct(f); |
927 | put_filp(f); | 929 | if (error) { |
928 | return ERR_PTR(error); | 930 | fput(f); |
931 | f = ERR_PTR(error); | ||
932 | } | ||
933 | } else { | ||
934 | put_filp(f); | ||
935 | f = ERR_PTR(error); | ||
936 | } | ||
929 | } | 937 | } |
930 | return f; | 938 | return f; |
931 | } | 939 | } |