mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
getname_maybe_null() - the third variant of pathname copy-in
Semantics used by statx(2) (and later *xattrat(2)): without AT_EMPTY_PATH it's standard getname() (i.e. ERR_PTR(-ENOENT) on empty string, ERR_PTR(-EFAULT) on NULL), with AT_EMPTY_PATH both empty string and NULL are accepted. Calling conventions: getname_maybe_null(user_pointer, flags) returns * pointer to struct filename when non-empty string had been successfully read * ERR_PTR(...) on error * NULL if an empty string or NULL pointer had been given with AT_EMPTY_PATH in the flags argument. It tries to avoid allocation in the last case; it's not always able to do so, in which case the temporary struct filename instance is freed and NULL returned anyway. Fast path is inlined. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
5b313bcb6e
commit
e896474fe4
30
fs/namei.c
30
fs/namei.c
@ -211,22 +211,38 @@ getname_flags(const char __user *filename, int flags)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct filename *
|
struct filename *getname_uflags(const char __user *filename, int uflags)
|
||||||
getname_uflags(const char __user *filename, int uflags)
|
|
||||||
{
|
{
|
||||||
int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
|
int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
|
||||||
|
|
||||||
return getname_flags(filename, flags);
|
return getname_flags(filename, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct filename *
|
struct filename *getname(const char __user * filename)
|
||||||
getname(const char __user * filename)
|
|
||||||
{
|
{
|
||||||
return getname_flags(filename, 0);
|
return getname_flags(filename, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct filename *
|
struct filename *__getname_maybe_null(const char __user *pathname)
|
||||||
getname_kernel(const char * filename)
|
{
|
||||||
|
struct filename *name;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
/* try to save on allocations; loss on um, though */
|
||||||
|
if (get_user(c, pathname))
|
||||||
|
return ERR_PTR(-EFAULT);
|
||||||
|
if (!c)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
name = getname_flags(pathname, LOOKUP_EMPTY);
|
||||||
|
if (!IS_ERR(name) && !(name->name[0])) {
|
||||||
|
putname(name);
|
||||||
|
name = NULL;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct filename *getname_kernel(const char * filename)
|
||||||
{
|
{
|
||||||
struct filename *result;
|
struct filename *result;
|
||||||
int len = strlen(filename) + 1;
|
int len = strlen(filename) + 1;
|
||||||
@ -264,7 +280,7 @@ EXPORT_SYMBOL(getname_kernel);
|
|||||||
|
|
||||||
void putname(struct filename *name)
|
void putname(struct filename *name)
|
||||||
{
|
{
|
||||||
if (IS_ERR(name))
|
if (IS_ERR_OR_NULL(name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (WARN_ON_ONCE(!atomic_read(&name->refcnt)))
|
if (WARN_ON_ONCE(!atomic_read(&name->refcnt)))
|
||||||
|
28
fs/stat.c
28
fs/stat.c
@ -326,18 +326,11 @@ int vfs_fstatat(int dfd, const char __user *filename,
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int statx_flags = flags | AT_NO_AUTOMOUNT;
|
int statx_flags = flags | AT_NO_AUTOMOUNT;
|
||||||
struct filename *name;
|
struct filename *name = getname_maybe_null(filename, flags);
|
||||||
|
|
||||||
/*
|
if (!name && dfd >= 0)
|
||||||
* Work around glibc turning fstat() into fstatat(AT_EMPTY_PATH)
|
|
||||||
*
|
|
||||||
* If AT_EMPTY_PATH is set, we expect the common case to be that
|
|
||||||
* empty path, and avoid doing all the extra pathname work.
|
|
||||||
*/
|
|
||||||
if (flags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename))
|
|
||||||
return vfs_fstat(dfd, stat);
|
return vfs_fstat(dfd, stat);
|
||||||
|
|
||||||
name = getname_flags(filename, getname_statx_lookup_flags(statx_flags));
|
|
||||||
ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS);
|
ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS);
|
||||||
putname(name);
|
putname(name);
|
||||||
|
|
||||||
@ -774,24 +767,11 @@ SYSCALL_DEFINE5(statx,
|
|||||||
struct statx __user *, buffer)
|
struct statx __user *, buffer)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
unsigned lflags;
|
struct filename *name = getname_maybe_null(filename, flags);
|
||||||
struct filename *name;
|
|
||||||
|
|
||||||
/*
|
if (!name && dfd >= 0)
|
||||||
* Short-circuit handling of NULL and "" paths.
|
|
||||||
*
|
|
||||||
* For a NULL path we require and accept only the AT_EMPTY_PATH flag
|
|
||||||
* (possibly |'d with AT_STATX flags).
|
|
||||||
*
|
|
||||||
* However, glibc on 32-bit architectures implements fstatat as statx
|
|
||||||
* with the "" pathname and AT_NO_AUTOMOUNT | AT_EMPTY_PATH flags.
|
|
||||||
* Supporting this results in the uglification below.
|
|
||||||
*/
|
|
||||||
lflags = flags & ~(AT_NO_AUTOMOUNT | AT_STATX_SYNC_TYPE);
|
|
||||||
if (lflags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename))
|
|
||||||
return do_statx_fd(dfd, flags & ~AT_NO_AUTOMOUNT, mask, buffer);
|
return do_statx_fd(dfd, flags & ~AT_NO_AUTOMOUNT, mask, buffer);
|
||||||
|
|
||||||
name = getname_flags(filename, getname_statx_lookup_flags(flags));
|
|
||||||
ret = do_statx(dfd, name, flags, mask, buffer);
|
ret = do_statx(dfd, name, flags, mask, buffer);
|
||||||
putname(name);
|
putname(name);
|
||||||
|
|
||||||
|
@ -2766,6 +2766,16 @@ extern struct filename *getname_flags(const char __user *, int);
|
|||||||
extern struct filename *getname_uflags(const char __user *, int);
|
extern struct filename *getname_uflags(const char __user *, int);
|
||||||
extern struct filename *getname(const char __user *);
|
extern struct filename *getname(const char __user *);
|
||||||
extern struct filename *getname_kernel(const char *);
|
extern struct filename *getname_kernel(const char *);
|
||||||
|
extern struct filename *__getname_maybe_null(const char __user *);
|
||||||
|
static inline struct filename *getname_maybe_null(const char __user *name, int flags)
|
||||||
|
{
|
||||||
|
if (!(flags & AT_EMPTY_PATH))
|
||||||
|
return getname(name);
|
||||||
|
|
||||||
|
if (!name)
|
||||||
|
return NULL;
|
||||||
|
return __getname_maybe_null(name);
|
||||||
|
}
|
||||||
extern void putname(struct filename *name);
|
extern void putname(struct filename *name);
|
||||||
|
|
||||||
extern int finish_open(struct file *file, struct dentry *dentry,
|
extern int finish_open(struct file *file, struct dentry *dentry,
|
||||||
|
Loading…
Reference in New Issue
Block a user