Commit e896474f authored by Al Viro's avatar Al Viro
Browse files

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 5b313bcb
Loading
Loading
Loading
Loading
+23 −7
Original line number Diff line number Diff line
@@ -211,22 +211,38 @@ getname_flags(const char __user *filename, int flags)
	return result;
}

struct filename *
getname_uflags(const char __user *filename, int uflags)
struct filename *getname_uflags(const char __user *filename, int uflags)
{
	int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;

	return getname_flags(filename, flags);
}

struct filename *
getname(const char __user * filename)
struct filename *getname(const char __user * filename)
{
	return getname_flags(filename, 0);
}

struct filename *
getname_kernel(const char * filename)
struct filename *__getname_maybe_null(const char __user *pathname)
{
	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;
	int len = strlen(filename) + 1;
@@ -264,7 +280,7 @@ EXPORT_SYMBOL(getname_kernel);

void putname(struct filename *name)
{
	if (IS_ERR(name))
	if (IS_ERR_OR_NULL(name))
		return;

	if (WARN_ON_ONCE(!atomic_read(&name->refcnt)))
+4 −24
Original line number Diff line number Diff line
@@ -326,18 +326,11 @@ int vfs_fstatat(int dfd, const char __user *filename,
{
	int ret;
	int statx_flags = flags | AT_NO_AUTOMOUNT;
	struct filename *name;
	struct filename *name = getname_maybe_null(filename, flags);

	/*
	 * 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))
	if (!name && dfd >= 0)
		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);
	putname(name);

@@ -774,24 +767,11 @@ SYSCALL_DEFINE5(statx,
		struct statx __user *, buffer)
{
	int ret;
	unsigned lflags;
	struct filename *name;
	struct filename *name = getname_maybe_null(filename, flags);

	/*
	 * 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))
	if (!name && dfd >= 0)
		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);
	putname(name);

+10 −0
Original line number Diff line number Diff line
@@ -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(const char __user *);
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 int finish_open(struct file *file, struct dentry *dentry,