Blame view
fs/readdir.c
6.77 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 |
/* * linux/fs/readdir.c * * Copyright (C) 1995 Linus Torvalds */ |
85c9fe8fc vfs: fix warning:... |
6 |
#include <linux/stddef.h> |
022a16924 ROUND_UP macro cl... |
7 |
#include <linux/kernel.h> |
630d9c472 fs: reduce the us... |
8 |
#include <linux/export.h> |
1da177e4c Linux-2.6.12-rc2 |
9 10 11 12 13 |
#include <linux/time.h> #include <linux/mm.h> #include <linux/errno.h> #include <linux/stat.h> #include <linux/file.h> |
1da177e4c Linux-2.6.12-rc2 |
14 15 16 17 18 19 20 21 22 23 |
#include <linux/fs.h> #include <linux/dirent.h> #include <linux/security.h> #include <linux/syscalls.h> #include <linux/unistd.h> #include <asm/uaccess.h> int vfs_readdir(struct file *file, filldir_t filler, void *buf) { |
0f7fc9e4d [PATCH] VFS: chan... |
24 |
struct inode *inode = file->f_path.dentry->d_inode; |
1da177e4c Linux-2.6.12-rc2 |
25 26 27 28 29 30 31 |
int res = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; |
da7845119 Use mutex_lock_ki... |
32 33 34 |
res = mutex_lock_killable(&inode->i_mutex); if (res) goto out; |
1da177e4c Linux-2.6.12-rc2 |
35 36 37 38 39 |
res = -ENOENT; if (!IS_DEADDIR(inode)) { res = file->f_op->readdir(file, buf, filler); file_accessed(file); } |
1b1dcc1b5 [PATCH] mutex sub... |
40 |
mutex_unlock(&inode->i_mutex); |
1da177e4c Linux-2.6.12-rc2 |
41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
out: return res; } EXPORT_SYMBOL(vfs_readdir); /* * Traditional linux readdir() handling.. * * "count=1" is a special case, meaning that the buffer is one * dirent-structure in size and that the code can't handle more * anyway. Thus the special "fillonedir()" function for that * case (the low-level handlers don't need to care about this). */ |
1da177e4c Linux-2.6.12-rc2 |
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
#ifdef __ARCH_WANT_OLD_READDIR struct old_linux_dirent { unsigned long d_ino; unsigned long d_offset; unsigned short d_namlen; char d_name[1]; }; struct readdir_callback { struct old_linux_dirent __user * dirent; int result; }; static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset, |
afefdbb28 [PATCH] VFS: Make... |
71 |
u64 ino, unsigned int d_type) |
1da177e4c Linux-2.6.12-rc2 |
72 73 74 |
{ struct readdir_callback * buf = (struct readdir_callback *) __buf; struct old_linux_dirent __user * dirent; |
afefdbb28 [PATCH] VFS: Make... |
75 |
unsigned long d_ino; |
1da177e4c Linux-2.6.12-rc2 |
76 77 78 |
if (buf->result) return -EINVAL; |
afefdbb28 [PATCH] VFS: Make... |
79 |
d_ino = ino; |
8f3f655da [PATCH] fix regul... |
80 81 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; |
afefdbb28 [PATCH] VFS: Make... |
82 |
return -EOVERFLOW; |
8f3f655da [PATCH] fix regul... |
83 |
} |
1da177e4c Linux-2.6.12-rc2 |
84 85 86 87 88 89 |
buf->result++; dirent = buf->dirent; if (!access_ok(VERIFY_WRITE, dirent, (unsigned long)(dirent->d_name + namlen + 1) - (unsigned long)dirent)) goto efault; |
afefdbb28 [PATCH] VFS: Make... |
90 |
if ( __put_user(d_ino, &dirent->d_ino) || |
1da177e4c Linux-2.6.12-rc2 |
91 92 93 94 95 96 97 98 99 100 |
__put_user(offset, &dirent->d_offset) || __put_user(namlen, &dirent->d_namlen) || __copy_to_user(dirent->d_name, name, namlen) || __put_user(0, dirent->d_name + namlen)) goto efault; return 0; efault: buf->result = -EFAULT; return -EFAULT; } |
d4e82042c [CVE-2009-0029] S... |
101 102 |
SYSCALL_DEFINE3(old_readdir, unsigned int, fd, struct old_linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c Linux-2.6.12-rc2 |
103 104 |
{ int error; |
2903ff019 switch simple cas... |
105 |
struct fd f = fdget(fd); |
1da177e4c Linux-2.6.12-rc2 |
106 |
struct readdir_callback buf; |
2903ff019 switch simple cas... |
107 |
if (!f.file) |
863ced7fe switch readdir/ge... |
108 |
return -EBADF; |
1da177e4c Linux-2.6.12-rc2 |
109 110 111 |
buf.result = 0; buf.dirent = dirent; |
2903ff019 switch simple cas... |
112 |
error = vfs_readdir(f.file, fillonedir, &buf); |
53c9c5c0e [PATCH] prepare v... |
113 |
if (buf.result) |
1da177e4c Linux-2.6.12-rc2 |
114 |
error = buf.result; |
2903ff019 switch simple cas... |
115 |
fdput(f); |
1da177e4c Linux-2.6.12-rc2 |
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
return error; } #endif /* __ARCH_WANT_OLD_READDIR */ /* * New, all-improved, singing, dancing, iBCS2-compliant getdents() * interface. */ struct linux_dirent { unsigned long d_ino; unsigned long d_off; unsigned short d_reclen; char d_name[1]; }; struct getdents_callback { struct linux_dirent __user * current_dir; struct linux_dirent __user * previous; int count; int error; }; static int filldir(void * __buf, const char * name, int namlen, loff_t offset, |
afefdbb28 [PATCH] VFS: Make... |
140 |
u64 ino, unsigned int d_type) |
1da177e4c Linux-2.6.12-rc2 |
141 142 143 |
{ struct linux_dirent __user * dirent; struct getdents_callback * buf = (struct getdents_callback *) __buf; |
afefdbb28 [PATCH] VFS: Make... |
144 |
unsigned long d_ino; |
85c9fe8fc vfs: fix warning:... |
145 146 |
int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); |
1da177e4c Linux-2.6.12-rc2 |
147 148 149 150 |
buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; |
afefdbb28 [PATCH] VFS: Make... |
151 |
d_ino = ino; |
8f3f655da [PATCH] fix regul... |
152 153 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->error = -EOVERFLOW; |
afefdbb28 [PATCH] VFS: Make... |
154 |
return -EOVERFLOW; |
8f3f655da [PATCH] fix regul... |
155 |
} |
1da177e4c Linux-2.6.12-rc2 |
156 157 158 159 160 161 |
dirent = buf->previous; if (dirent) { if (__put_user(offset, &dirent->d_off)) goto efault; } dirent = buf->current_dir; |
afefdbb28 [PATCH] VFS: Make... |
162 |
if (__put_user(d_ino, &dirent->d_ino)) |
1da177e4c Linux-2.6.12-rc2 |
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
goto efault; if (__put_user(reclen, &dirent->d_reclen)) goto efault; if (copy_to_user(dirent->d_name, name, namlen)) goto efault; if (__put_user(0, dirent->d_name + namlen)) goto efault; if (__put_user(d_type, (char __user *) dirent + reclen - 1)) goto efault; buf->previous = dirent; dirent = (void __user *)dirent + reclen; buf->current_dir = dirent; buf->count -= reclen; return 0; efault: buf->error = -EFAULT; return -EFAULT; } |
20f37034f [CVE-2009-0029] S... |
181 182 |
SYSCALL_DEFINE3(getdents, unsigned int, fd, struct linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c Linux-2.6.12-rc2 |
183 |
{ |
2903ff019 switch simple cas... |
184 |
struct fd f; |
1da177e4c Linux-2.6.12-rc2 |
185 186 187 |
struct linux_dirent __user * lastdirent; struct getdents_callback buf; int error; |
1da177e4c Linux-2.6.12-rc2 |
188 |
if (!access_ok(VERIFY_WRITE, dirent, count)) |
863ced7fe switch readdir/ge... |
189 |
return -EFAULT; |
1da177e4c Linux-2.6.12-rc2 |
190 |
|
2903ff019 switch simple cas... |
191 192 |
f = fdget(fd); if (!f.file) |
863ced7fe switch readdir/ge... |
193 |
return -EBADF; |
1da177e4c Linux-2.6.12-rc2 |
194 195 196 197 198 |
buf.current_dir = dirent; buf.previous = NULL; buf.count = count; buf.error = 0; |
2903ff019 switch simple cas... |
199 |
error = vfs_readdir(f.file, filldir, &buf); |
53c9c5c0e [PATCH] prepare v... |
200 201 |
if (error >= 0) error = buf.error; |
1da177e4c Linux-2.6.12-rc2 |
202 203 |
lastdirent = buf.previous; if (lastdirent) { |
2903ff019 switch simple cas... |
204 |
if (put_user(f.file->f_pos, &lastdirent->d_off)) |
1da177e4c Linux-2.6.12-rc2 |
205 206 207 208 |
error = -EFAULT; else error = count - buf.count; } |
2903ff019 switch simple cas... |
209 |
fdput(f); |
1da177e4c Linux-2.6.12-rc2 |
210 211 |
return error; } |
1da177e4c Linux-2.6.12-rc2 |
212 213 214 215 216 217 218 219 |
struct getdents_callback64 { struct linux_dirent64 __user * current_dir; struct linux_dirent64 __user * previous; int count; int error; }; static int filldir64(void * __buf, const char * name, int namlen, loff_t offset, |
afefdbb28 [PATCH] VFS: Make... |
220 |
u64 ino, unsigned int d_type) |
1da177e4c Linux-2.6.12-rc2 |
221 222 223 |
{ struct linux_dirent64 __user *dirent; struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; |
85c9fe8fc vfs: fix warning:... |
224 225 |
int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); |
1da177e4c Linux-2.6.12-rc2 |
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; dirent = buf->previous; if (dirent) { if (__put_user(offset, &dirent->d_off)) goto efault; } dirent = buf->current_dir; if (__put_user(ino, &dirent->d_ino)) goto efault; if (__put_user(0, &dirent->d_off)) goto efault; if (__put_user(reclen, &dirent->d_reclen)) goto efault; if (__put_user(d_type, &dirent->d_type)) goto efault; if (copy_to_user(dirent->d_name, name, namlen)) goto efault; if (__put_user(0, dirent->d_name + namlen)) goto efault; buf->previous = dirent; dirent = (void __user *)dirent + reclen; buf->current_dir = dirent; buf->count -= reclen; return 0; efault: buf->error = -EFAULT; return -EFAULT; } |
20f37034f [CVE-2009-0029] S... |
257 258 |
SYSCALL_DEFINE3(getdents64, unsigned int, fd, struct linux_dirent64 __user *, dirent, unsigned int, count) |
1da177e4c Linux-2.6.12-rc2 |
259 |
{ |
2903ff019 switch simple cas... |
260 |
struct fd f; |
1da177e4c Linux-2.6.12-rc2 |
261 262 263 |
struct linux_dirent64 __user * lastdirent; struct getdents_callback64 buf; int error; |
1da177e4c Linux-2.6.12-rc2 |
264 |
if (!access_ok(VERIFY_WRITE, dirent, count)) |
863ced7fe switch readdir/ge... |
265 |
return -EFAULT; |
1da177e4c Linux-2.6.12-rc2 |
266 |
|
2903ff019 switch simple cas... |
267 268 |
f = fdget(fd); if (!f.file) |
863ced7fe switch readdir/ge... |
269 |
return -EBADF; |
1da177e4c Linux-2.6.12-rc2 |
270 271 272 273 274 |
buf.current_dir = dirent; buf.previous = NULL; buf.count = count; buf.error = 0; |
2903ff019 switch simple cas... |
275 |
error = vfs_readdir(f.file, filldir64, &buf); |
53c9c5c0e [PATCH] prepare v... |
276 277 |
if (error >= 0) error = buf.error; |
1da177e4c Linux-2.6.12-rc2 |
278 279 |
lastdirent = buf.previous; if (lastdirent) { |
2903ff019 switch simple cas... |
280 |
typeof(lastdirent->d_off) d_off = f.file->f_pos; |
1da177e4c Linux-2.6.12-rc2 |
281 |
if (__put_user(d_off, &lastdirent->d_off)) |
53c9c5c0e [PATCH] prepare v... |
282 283 284 |
error = -EFAULT; else error = count - buf.count; |
1da177e4c Linux-2.6.12-rc2 |
285 |
} |
2903ff019 switch simple cas... |
286 |
fdput(f); |
1da177e4c Linux-2.6.12-rc2 |
287 288 |
return error; } |