Skip to content

Commit e896474

Browse files
author
Al Viro
committed
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 <[email protected]>
1 parent 5b313bc commit e896474

File tree

3 files changed

+37
-31
lines changed

3 files changed

+37
-31
lines changed

fs/namei.c

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,22 +211,38 @@ getname_flags(const char __user *filename, int flags)
211211
return result;
212212
}
213213

214-
struct filename *
215-
getname_uflags(const char __user *filename, int uflags)
214+
struct filename *getname_uflags(const char __user *filename, int uflags)
216215
{
217216
int flags = (uflags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0;
218217

219218
return getname_flags(filename, flags);
220219
}
221220

222-
struct filename *
223-
getname(const char __user * filename)
221+
struct filename *getname(const char __user * filename)
224222
{
225223
return getname_flags(filename, 0);
226224
}
227225

228-
struct filename *
229-
getname_kernel(const char * filename)
226+
struct filename *__getname_maybe_null(const char __user *pathname)
227+
{
228+
struct filename *name;
229+
char c;
230+
231+
/* try to save on allocations; loss on um, though */
232+
if (get_user(c, pathname))
233+
return ERR_PTR(-EFAULT);
234+
if (!c)
235+
return NULL;
236+
237+
name = getname_flags(pathname, LOOKUP_EMPTY);
238+
if (!IS_ERR(name) && !(name->name[0])) {
239+
putname(name);
240+
name = NULL;
241+
}
242+
return name;
243+
}
244+
245+
struct filename *getname_kernel(const char * filename)
230246
{
231247
struct filename *result;
232248
int len = strlen(filename) + 1;
@@ -264,7 +280,7 @@ EXPORT_SYMBOL(getname_kernel);
264280

265281
void putname(struct filename *name)
266282
{
267-
if (IS_ERR(name))
283+
if (IS_ERR_OR_NULL(name))
268284
return;
269285

270286
if (WARN_ON_ONCE(!atomic_read(&name->refcnt)))

fs/stat.c

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -326,18 +326,11 @@ int vfs_fstatat(int dfd, const char __user *filename,
326326
{
327327
int ret;
328328
int statx_flags = flags | AT_NO_AUTOMOUNT;
329-
struct filename *name;
329+
struct filename *name = getname_maybe_null(filename, flags);
330330

331-
/*
332-
* Work around glibc turning fstat() into fstatat(AT_EMPTY_PATH)
333-
*
334-
* If AT_EMPTY_PATH is set, we expect the common case to be that
335-
* empty path, and avoid doing all the extra pathname work.
336-
*/
337-
if (flags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename))
331+
if (!name && dfd >= 0)
338332
return vfs_fstat(dfd, stat);
339333

340-
name = getname_flags(filename, getname_statx_lookup_flags(statx_flags));
341334
ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS);
342335
putname(name);
343336

@@ -774,24 +767,11 @@ SYSCALL_DEFINE5(statx,
774767
struct statx __user *, buffer)
775768
{
776769
int ret;
777-
unsigned lflags;
778-
struct filename *name;
770+
struct filename *name = getname_maybe_null(filename, flags);
779771

780-
/*
781-
* Short-circuit handling of NULL and "" paths.
782-
*
783-
* For a NULL path we require and accept only the AT_EMPTY_PATH flag
784-
* (possibly |'d with AT_STATX flags).
785-
*
786-
* However, glibc on 32-bit architectures implements fstatat as statx
787-
* with the "" pathname and AT_NO_AUTOMOUNT | AT_EMPTY_PATH flags.
788-
* Supporting this results in the uglification below.
789-
*/
790-
lflags = flags & ~(AT_NO_AUTOMOUNT | AT_STATX_SYNC_TYPE);
791-
if (lflags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename))
772+
if (!name && dfd >= 0)
792773
return do_statx_fd(dfd, flags & ~AT_NO_AUTOMOUNT, mask, buffer);
793774

794-
name = getname_flags(filename, getname_statx_lookup_flags(flags));
795775
ret = do_statx(dfd, name, flags, mask, buffer);
796776
putname(name);
797777

include/linux/fs.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2766,6 +2766,16 @@ extern struct filename *getname_flags(const char __user *, int);
27662766
extern struct filename *getname_uflags(const char __user *, int);
27672767
extern struct filename *getname(const char __user *);
27682768
extern struct filename *getname_kernel(const char *);
2769+
extern struct filename *__getname_maybe_null(const char __user *);
2770+
static inline struct filename *getname_maybe_null(const char __user *name, int flags)
2771+
{
2772+
if (!(flags & AT_EMPTY_PATH))
2773+
return getname(name);
2774+
2775+
if (!name)
2776+
return NULL;
2777+
return __getname_maybe_null(name);
2778+
}
27692779
extern void putname(struct filename *name);
27702780

27712781
extern int finish_open(struct file *file, struct dentry *dentry,

0 commit comments

Comments
 (0)