From 1643b43fbd0524e7da7259075032936c8fb68a05 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 27 Apr 2016 19:14:10 -0400 Subject: [PATCH] lookup_open(): lift the "fallback to !O_CREAT" logics from atomic_open() Signed-off-by: Al Viro --- fs/namei.c | 144 ++++++++++++++++++++--------------------------------- 1 file changed, 55 insertions(+), 89 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 8d562a7a7e01..4359d22f43f4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2824,63 +2824,19 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode) static int atomic_open(struct nameidata *nd, struct dentry *dentry, struct path *path, struct file *file, const struct open_flags *op, - bool got_write, bool need_lookup, + int open_flag, umode_t mode, int *opened) { struct inode *dir = nd->path.dentry->d_inode; - unsigned open_flag = op->open_flag; - umode_t mode; int error; int acc_mode; - int create_error = 0; struct dentry *const DENTRY_NOT_SET = (void *) -1UL; bool excl; - BUG_ON(dentry->d_inode); - - mode = op->mode; - if ((open_flag & O_CREAT) && !IS_POSIXACL(dir)) - mode &= ~current_umask(); - excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT); if (excl) open_flag &= ~O_TRUNC; - /* - * Checking write permission is tricky, bacuse we don't know if we are - * going to actually need it: O_CREAT opens should work as long as the - * file exists. But checking existence breaks atomicity. The trick is - * to check access and if not granted clear O_CREAT from the flags. - * - * Another problem is returing the "right" error value (e.g. for an - * O_EXCL open we want to return EEXIST not EROFS). - */ - if (open_flag & O_CREAT) { - if (unlikely(!got_write)) { - create_error = -EROFS; - if (open_flag & (O_EXCL | O_TRUNC)) { - /* Fall back and fail with the right error */ - goto no_open; - } - /* No side effects, safe to clear O_CREAT */ - open_flag &= ~O_CREAT; - } else { - create_error = may_o_create(&nd->path, dentry, mode); - if (create_error) { - if (open_flag & O_EXCL) - goto no_open; - open_flag &= ~O_CREAT; - } - } - } else if ((open_flag & (O_TRUNC|O_WRONLY|O_RDWR)) && - unlikely(!got_write)) { - /* - * No O_CREATE -> atomicity not a requirement -> fall - * back to lookup + open - */ - goto no_open; - } - if (nd->flags & LOOKUP_DIRECTORY) open_flag |= O_DIRECTORY; @@ -2889,11 +2845,8 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, error = dir->i_op->atomic_open(dir, dentry, file, open_to_namei_flags(open_flag), mode, opened); - if (error < 0) { - if (create_error && error == -ENOENT) - error = create_error; + if (error < 0) goto out; - } if (error) { /* returned 1, that is */ if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) { @@ -2906,7 +2859,9 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, } if (*opened & FILE_CREATED) fsnotify_create(dir, dentry); - goto looked_up; + path->dentry = dentry; + path->mnt = nd->path.mnt; + return 1; } /* @@ -2925,21 +2880,6 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, out: dput(dentry); return error; - -no_open: - if (need_lookup) { - dentry = lookup_real(dir, dentry, nd->flags); - if (IS_ERR(dentry)) - return PTR_ERR(dentry); - } -looked_up: - if (create_error && !dentry->d_inode) { - error = create_error; - goto out; - } - path->dentry = dentry; - path->mnt = nd->path.mnt; - return 1; } /* @@ -2967,9 +2907,11 @@ static int lookup_open(struct nameidata *nd, struct path *path, { struct dentry *dir = nd->path.dentry; struct inode *dir_inode = dir->d_inode; + int open_flag = op->open_flag; struct dentry *dentry; - int error; + int error, create_error = 0; bool need_lookup = false; + umode_t mode = op->mode; if (unlikely(IS_DEADDIR(dir_inode))) return -ENOENT; @@ -2989,50 +2931,74 @@ static int lookup_open(struct nameidata *nd, struct path *path, goto out_no_open; } + /* + * Checking write permission is tricky, bacuse we don't know if we are + * going to actually need it: O_CREAT opens should work as long as the + * file exists. But checking existence breaks atomicity. The trick is + * to check access and if not granted clear O_CREAT from the flags. + * + * Another problem is returing the "right" error value (e.g. for an + * O_EXCL open we want to return EEXIST not EROFS). + */ + if (open_flag & O_CREAT) { + if (!IS_POSIXACL(dir->d_inode)) + mode &= ~current_umask(); + if (unlikely(!got_write)) { + create_error = -EROFS; + open_flag &= ~O_CREAT; + if (open_flag & (O_EXCL | O_TRUNC)) + goto no_open; + /* No side effects, safe to clear O_CREAT */ + } else { + create_error = may_o_create(&nd->path, dentry, mode); + if (create_error) { + open_flag &= ~O_CREAT; + if (open_flag & O_EXCL) + goto no_open; + } + } + } else if ((open_flag & (O_TRUNC|O_WRONLY|O_RDWR)) && + unlikely(!got_write)) { + /* + * No O_CREATE -> atomicity not a requirement -> fall + * back to lookup + open + */ + goto no_open; + } + if (dir_inode->i_op->atomic_open) { - return atomic_open(nd, dentry, path, file, op, got_write, - need_lookup, opened); + error = atomic_open(nd, dentry, path, file, op, open_flag, + mode, opened); + if (unlikely(error == -ENOENT) && create_error) + error = create_error; + return error; } +no_open: if (need_lookup) { - BUG_ON(dentry->d_inode); - dentry = lookup_real(dir_inode, dentry, nd->flags); if (IS_ERR(dentry)) return PTR_ERR(dentry); } /* Negative dentry, just create the file */ - if (!dentry->d_inode && (op->open_flag & O_CREAT)) { - umode_t mode = op->mode; - if (!IS_POSIXACL(dir->d_inode)) - mode &= ~current_umask(); - /* - * This write is needed to ensure that a - * rw->ro transition does not occur between - * the time when the file is created and when - * a permanent write count is taken through - * the 'struct file' in finish_open(). - */ - if (!got_write) { - error = -EROFS; - goto out_dput; - } + if (!dentry->d_inode && (open_flag & O_CREAT)) { *opened |= FILE_CREATED; audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE); - error = may_o_create(&nd->path, dentry, mode); - if (error) - goto out_dput; if (!dir_inode->i_op->create) { error = -EACCES; goto out_dput; } error = dir_inode->i_op->create(dir_inode, dentry, mode, - op->open_flag & O_EXCL); + open_flag & O_EXCL); if (error) goto out_dput; fsnotify_create(dir_inode, dentry); } + if (unlikely(create_error) && !dentry->d_inode) { + error = create_error; + goto out_dput; + } out_no_open: path->dentry = dentry; path->mnt = nd->path.mnt; -- 2.20.1