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 92ae03f2ef
: "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
This commit is contained in:
commit
7e5cb5e151
3 changed files with 35 additions and 48 deletions
|
@ -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
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
58
fs/namei.c
58
fs/namei.c
|
@ -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);
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue