From: NeilBrown vfs_lookup_open() is a limited version of lookup_open() which is exported for nfsd to use - to replace dentry_create(). It is limited in that no filename is given (thus no auditing) and no LOOKUP_ flags are passed. A few "intent" LOOKUP flags are deduced from the open flags. If a non-regular file is found and appropriate error is returned and no file is opened. Signed-off-by: NeilBrown --- fs/namei.c | 96 +++++++++++++++++++++++++++++++++++++++++++ include/linux/namei.h | 3 ++ 2 files changed, 99 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index cb33a01d16eb..630f93608224 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4562,6 +4562,102 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, goto out; } +/** + * vfs_lookup_open - open and possibly create a regular file + * @parent: directory to contain file + * @last: final component of file name + * @open_flag: O_flags + * @mode: initial permissions for file + * + * Open a file after lookup and/or create. This provides similar + * functionality open_last_lookups() for non-VFS users, particularly + * nfsd. + * It uses ->atomic_open or ->lookup / ->create / ->open as appropriate. + * + * If the fs object found is not a regular file then an error is returned. + * In some cases, related errors are repurposed so that the caller can + * determine the type of file found from the error. + * -EISDIR : a directory was found + * -ELOOP : a symlink was found + * -ENODEV : a block or character device special file was found + * -EFTYPE : any other non-regular file was found, such as FIFO or SOCK. + * or ->atomic_open responded to __O_REGULAR. + * + * Returns: the opened struct file, or an error. + */ +struct file *vfs_lookup_open(struct path *parent, struct qstr *last, + int open_flag, umode_t mode) +{ + struct file *file __free(fput) = NULL; + struct nameidata nd = {}; + struct open_flags op = {}; + struct dentry *dentry; + int error = 0; + + WARN_ONCE(mode & ~S_IALLUGO, "mode must only have permission bits"); + WARN_ONCE(open_flag & ~(O_ACCMODE|O_CREAT|O_EXCL|O_TRUNC|__O_REGULAR), + "open_flag has unsupported flags"); + + mode |= S_IFREG; + open_flag |= __O_REGULAR; + + error = lookup_noperm_common(last, parent->dentry); + if (error) + return ERR_PTR(error); + + file = alloc_empty_file(open_flag, current_cred()); + if (IS_ERR(file)) + return file; + + nd.path = *parent; + nd.last = *last; + nd.flags = LOOKUP_OPEN; + if (open_flag & O_CREAT) { + nd.flags |= LOOKUP_CREATE; + if (open_flag & O_EXCL) + nd.flags |= LOOKUP_EXCL; + } + op.open_flag = open_flag; + op.mode = mode; + dentry = lookup_open(&nd, file, &op); + + if (IS_ERR(dentry)) + return ERR_CAST(dentry); + + if (d_really_is_negative(dentry)) { + error = -ENOENT; + } else if (!(file->f_mode & FMODE_CREATED) && (open_flag & O_EXCL)) { + error = -EEXIST; + } else if ((dentry->d_inode->i_mode & S_IFMT) != S_IFREG) { + switch (dentry->d_inode->i_mode & S_IFMT) { + case S_IFDIR: + error = -EISDIR; + break; + case S_IFLNK: + error = -ELOOP; + break; + case S_IFBLK: + case S_IFCHR: + error = -ENODEV; + break; + case S_IFIFO: + case S_IFSOCK: + default: + error = -EFTYPE; + break; + } + } else if (!(file->f_mode & FMODE_OPENED)) { + nd.path.dentry = dentry; + error = vfs_open(&nd.path, file); + } + dput(dentry); + + if (error) + return ERR_PTR(error); + return no_free_ptr(file); +} +EXPORT_SYMBOL_FOR_MODULES(vfs_lookup_open, "nfsd"); + static inline bool trailing_slashes(struct nameidata *nd) { return (bool)nd->last.name[nd->last.len]; diff --git a/include/linux/namei.h b/include/linux/namei.h index ebe6e29f7e93..86d657b24fc6 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -97,6 +97,9 @@ struct dentry *start_creating_dentry(struct dentry *parent, struct dentry *start_removing_dentry(struct dentry *parent, struct dentry *child); +struct file *vfs_lookup_open(struct path *parent, struct qstr *last, + int open_flag, umode_t mode); + /* end_creating - finish action started with start_creating * @child: dentry returned by start_creating() or vfs_mkdir() * -- 2.50.0.107.gf914562f5916.dirty