Commit 7e5cb5e1 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'vfs-cleanups' (random vfs cleanups)

This teaches vfs_fstat() to use the appropriate f[get|put]_light
functions, allowing it to avoid some unnecessary locking for the common
case.

More noticeably, it also cleans up and simplifies the "getname_flags()"
function, which now relies on the architecture strncpy_from_user() doing
all the user access checks properly, instead of hacking around the fact
that on x86 it didn't use to do it right (see commit 92ae03f2: "x86:
merge 32/64-bit versions of 'strncpy_from_user()' and speed it up").

* vfs-cleanups:
  VFS: make vfs_fstat() use f[get|put]_light()
  VFS: clean up and simplify getname_flags()
  x86: make word-at-a-time strncpy_from_user clear bytes at the end
parents 8c12fec9 e994defb
...@@ -44,13 +44,6 @@ copy_from_user_nmi(void *to, const void __user *from, unsigned long n) ...@@ -44,13 +44,6 @@ copy_from_user_nmi(void *to, const void __user *from, unsigned long n)
} }
EXPORT_SYMBOL_GPL(copy_from_user_nmi); EXPORT_SYMBOL_GPL(copy_from_user_nmi);
static inline unsigned long count_bytes(unsigned long mask)
{
mask = (mask - 1) & ~mask;
mask >>= 7;
return count_masked_bytes(mask);
}
/* /*
* Do a strncpy, return length of string without final '\0'. * Do a strncpy, return length of string without final '\0'.
* 'count' is the user-supplied count (return 'count' if we * 'count' is the user-supplied count (return 'count' if we
...@@ -69,16 +62,19 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long ...@@ -69,16 +62,19 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long
max = count; max = count;
while (max >= sizeof(unsigned long)) { while (max >= sizeof(unsigned long)) {
unsigned long c; unsigned long c, mask;
/* Fall back to byte-at-a-time if we get a page fault */ /* Fall back to byte-at-a-time if we get a page fault */
if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) if (unlikely(__get_user(c,(unsigned long __user *)(src+res))))
break; break;
/* This can write a few bytes past the NUL character, but that's ok */ mask = has_zero(c);
if (mask) {
mask = (mask - 1) & ~mask;
mask >>= 7;
*(unsigned long *)(dst+res) = c & mask;
return res + count_masked_bytes(mask);
}
*(unsigned long *)(dst+res) = c; *(unsigned long *)(dst+res) = c;
c = has_zero(c);
if (c)
return res + count_bytes(c);
res += sizeof(unsigned long); res += sizeof(unsigned long);
max -= sizeof(unsigned long); max -= sizeof(unsigned long);
} }
......
...@@ -116,47 +116,37 @@ ...@@ -116,47 +116,37 @@
* POSIX.1 2.4: an empty pathname is invalid (ENOENT). * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
* PATH_MAX includes the nul terminator --RR. * PATH_MAX includes the nul terminator --RR.
*/ */
static int do_getname(const char __user *filename, char *page)
{
int retval;
unsigned long len = PATH_MAX;
if (!segment_eq(get_fs(), KERNEL_DS)) {
if ((unsigned long) filename >= TASK_SIZE)
return -EFAULT;
if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
len = TASK_SIZE - (unsigned long) filename;
}
retval = strncpy_from_user(page, filename, len);
if (retval > 0) {
if (retval < len)
return 0;
return -ENAMETOOLONG;
} else if (!retval)
retval = -ENOENT;
return retval;
}
static char *getname_flags(const char __user *filename, int flags, int *empty) static char *getname_flags(const char __user *filename, int flags, int *empty)
{ {
char *result = __getname(); char *result = __getname(), *err;
int retval; int len;
if (!result) if (unlikely(!result))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
retval = do_getname(filename, result); len = strncpy_from_user(result, filename, PATH_MAX);
if (retval < 0) { err = ERR_PTR(len);
if (retval == -ENOENT && empty) if (unlikely(len < 0))
goto error;
/* The empty path is special. */
if (unlikely(!len)) {
if (empty)
*empty = 1; *empty = 1;
if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) { err = ERR_PTR(-ENOENT);
__putname(result); if (!(flags & LOOKUP_EMPTY))
return ERR_PTR(retval); goto error;
}
} }
audit_getname(result);
return result; err = ERR_PTR(-ENAMETOOLONG);
if (likely(len < PATH_MAX)) {
audit_getname(result);
return result;
}
error:
__putname(result);
return err;
} }
char *getname(const char __user * filename) char *getname(const char __user * filename)
......
...@@ -57,12 +57,13 @@ EXPORT_SYMBOL(vfs_getattr); ...@@ -57,12 +57,13 @@ EXPORT_SYMBOL(vfs_getattr);
int vfs_fstat(unsigned int fd, struct kstat *stat) int vfs_fstat(unsigned int fd, struct kstat *stat)
{ {
struct file *f = fget(fd); int fput_needed;
struct file *f = fget_light(fd, &fput_needed);
int error = -EBADF; int error = -EBADF;
if (f) { if (f) {
error = vfs_getattr(f->f_path.mnt, f->f_path.dentry, stat); error = vfs_getattr(f->f_path.mnt, f->f_path.dentry, stat);
fput(f); fput_light(f, fput_needed);
} }
return error; return error;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment