Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 92ae03f2ef99: "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

+35 -48
+8 -12
arch/x86/lib/usercopy.c
··· 44 44 } 45 45 EXPORT_SYMBOL_GPL(copy_from_user_nmi); 46 46 47 - static inline unsigned long count_bytes(unsigned long mask) 48 - { 49 - mask = (mask - 1) & ~mask; 50 - mask >>= 7; 51 - return count_masked_bytes(mask); 52 - } 53 - 54 47 /* 55 48 * Do a strncpy, return length of string without final '\0'. 56 49 * 'count' is the user-supplied count (return 'count' if we ··· 62 69 max = count; 63 70 64 71 while (max >= sizeof(unsigned long)) { 65 - unsigned long c; 72 + unsigned long c, mask; 66 73 67 74 /* Fall back to byte-at-a-time if we get a page fault */ 68 75 if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) 69 76 break; 70 - /* This can write a few bytes past the NUL character, but that's ok */ 77 + mask = has_zero(c); 78 + if (mask) { 79 + mask = (mask - 1) & ~mask; 80 + mask >>= 7; 81 + *(unsigned long *)(dst+res) = c & mask; 82 + return res + count_masked_bytes(mask); 83 + } 71 84 *(unsigned long *)(dst+res) = c; 72 - c = has_zero(c); 73 - if (c) 74 - return res + count_bytes(c); 75 85 res += sizeof(unsigned long); 76 86 max -= sizeof(unsigned long); 77 87 }
+24 -34
fs/namei.c
··· 116 116 * POSIX.1 2.4: an empty pathname is invalid (ENOENT). 117 117 * PATH_MAX includes the nul terminator --RR. 118 118 */ 119 - static int do_getname(const char __user *filename, char *page) 120 - { 121 - int retval; 122 - unsigned long len = PATH_MAX; 123 - 124 - if (!segment_eq(get_fs(), KERNEL_DS)) { 125 - if ((unsigned long) filename >= TASK_SIZE) 126 - return -EFAULT; 127 - if (TASK_SIZE - (unsigned long) filename < PATH_MAX) 128 - len = TASK_SIZE - (unsigned long) filename; 129 - } 130 - 131 - retval = strncpy_from_user(page, filename, len); 132 - if (retval > 0) { 133 - if (retval < len) 134 - return 0; 135 - return -ENAMETOOLONG; 136 - } else if (!retval) 137 - retval = -ENOENT; 138 - return retval; 139 - } 140 - 141 119 static char *getname_flags(const char __user *filename, int flags, int *empty) 142 120 { 143 - char *result = __getname(); 144 - int retval; 121 + char *result = __getname(), *err; 122 + int len; 145 123 146 - if (!result) 124 + if (unlikely(!result)) 147 125 return ERR_PTR(-ENOMEM); 148 126 149 - retval = do_getname(filename, result); 150 - if (retval < 0) { 151 - if (retval == -ENOENT && empty) 127 + len = strncpy_from_user(result, filename, PATH_MAX); 128 + err = ERR_PTR(len); 129 + if (unlikely(len < 0)) 130 + goto error; 131 + 132 + /* The empty path is special. */ 133 + if (unlikely(!len)) { 134 + if (empty) 152 135 *empty = 1; 153 - if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) { 154 - __putname(result); 155 - return ERR_PTR(retval); 156 - } 136 + err = ERR_PTR(-ENOENT); 137 + if (!(flags & LOOKUP_EMPTY)) 138 + goto error; 157 139 } 158 - audit_getname(result); 159 - return result; 140 + 141 + err = ERR_PTR(-ENAMETOOLONG); 142 + if (likely(len < PATH_MAX)) { 143 + audit_getname(result); 144 + return result; 145 + } 146 + 147 + error: 148 + __putname(result); 149 + return err; 160 150 } 161 151 162 152 char *getname(const char __user * filename)
+3 -2
fs/stat.c
··· 57 57 58 58 int vfs_fstat(unsigned int fd, struct kstat *stat) 59 59 { 60 - struct file *f = fget(fd); 60 + int fput_needed; 61 + struct file *f = fget_light(fd, &fput_needed); 61 62 int error = -EBADF; 62 63 63 64 if (f) { 64 65 error = vfs_getattr(f->f_path.mnt, f->f_path.dentry, stat); 65 - fput(f); 66 + fput_light(f, fput_needed); 66 67 } 67 68 return error; 68 69 }