VFS: add vfs_lookup_open() for nfsd

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 <neil@brown.name>
Link: https://patch.msgid.link/20260714230534.776886-4-neilb@ownmail.net
Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
NeilBrown
2026-07-15 09:04:14 +10:00
committed by Christian Brauner
parent a5438415be
commit 536227b814
2 changed files with 99 additions and 0 deletions

View File

@@ -4568,6 +4568,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];

View File

@@ -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()
*