Blame view
fs/readdir.c
13.7 KB
b24413180 License cleanup: ... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
1da177e4c Linux-2.6.12-rc2 |
2 3 4 5 6 |
/* * linux/fs/readdir.c * * Copyright (C) 1995 Linus Torvalds */ |
85c9fe8fc vfs: fix warning:... |
7 |
#include <linux/stddef.h> |
022a16924 ROUND_UP macro cl... |
8 |
#include <linux/kernel.h> |
630d9c472 fs: reduce the us... |
9 |
#include <linux/export.h> |
1da177e4c Linux-2.6.12-rc2 |
10 11 12 13 14 |
#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 |
15 |
#include <linux/fs.h> |
d4c7cf6cf fanotify: create ... |
16 |
#include <linux/fsnotify.h> |
1da177e4c Linux-2.6.12-rc2 |
17 18 19 20 |
#include <linux/dirent.h> #include <linux/security.h> #include <linux/syscalls.h> #include <linux/unistd.h> |
0460b2a28 readdir: move com... |
21 |
#include <linux/compat.h> |
7c0f6ba68 Replace <asm/uacc... |
22 |
#include <linux/uaccess.h> |
1da177e4c Linux-2.6.12-rc2 |
23 |
|
9f79b78ef Convert filldir[6... |
24 25 26 27 28 |
#include <asm/unaligned.h> /* * Note the "unsafe_put_user() semantics: we goto a * label for errors. |
9f79b78ef Convert filldir[6... |
29 30 31 32 33 |
*/ #define unsafe_copy_dirent_name(_dst, _src, _len, label) do { \ char __user *dst = (_dst); \ const char *src = (_src); \ size_t len = (_len); \ |
c512c6918 uaccess: implemen... |
34 35 |
unsafe_put_user(0, dst+len, label); \ unsafe_copy_to_user(dst, src, len, label); \ |
9f79b78ef Convert filldir[6... |
36 |
} while (0) |
5c0ba4e07 [readdir] introdu... |
37 |
int iterate_dir(struct file *file, struct dir_context *ctx) |
1da177e4c Linux-2.6.12-rc2 |
38 |
{ |
496ad9aa8 new helper: file_... |
39 |
struct inode *inode = file_inode(file); |
619226944 introduce a paral... |
40 |
bool shared = false; |
1da177e4c Linux-2.6.12-rc2 |
41 |
int res = -ENOTDIR; |
619226944 introduce a paral... |
42 43 44 |
if (file->f_op->iterate_shared) shared = true; else if (!file->f_op->iterate) |
1da177e4c Linux-2.6.12-rc2 |
45 46 47 48 49 |
goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; |
0dc208b5d locking/rwsem, fs... |
50 51 52 |
if (shared) res = down_read_killable(&inode->i_rwsem); else |
002354112 restore killabili... |
53 |
res = down_write_killable(&inode->i_rwsem); |
0dc208b5d locking/rwsem, fs... |
54 55 |
if (res) goto out; |
da7845119 Use mutex_lock_ki... |
56 |
|
1da177e4c Linux-2.6.12-rc2 |
57 58 |
res = -ENOENT; if (!IS_DEADDIR(inode)) { |
2233f31aa [readdir] ->readd... |
59 |
ctx->pos = file->f_pos; |
619226944 introduce a paral... |
60 61 62 63 |
if (shared) res = file->f_op->iterate_shared(file, ctx); else res = file->f_op->iterate(file, ctx); |
2233f31aa [readdir] ->readd... |
64 |
file->f_pos = ctx->pos; |
d4c7cf6cf fanotify: create ... |
65 |
fsnotify_access(file); |
1da177e4c Linux-2.6.12-rc2 |
66 67 |
file_accessed(file); } |
619226944 introduce a paral... |
68 69 70 71 |
if (shared) inode_unlock_shared(inode); else inode_unlock(inode); |
1da177e4c Linux-2.6.12-rc2 |
72 73 74 |
out: return res; } |
5c0ba4e07 [readdir] introdu... |
75 |
EXPORT_SYMBOL(iterate_dir); |
1da177e4c Linux-2.6.12-rc2 |
76 77 |
/* |
8a23eb804 Make filldir[64](... |
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
* POSIX says that a dirent name cannot contain NULL or a '/'. * * It's not 100% clear what we should really do in this case. * The filesystem is clearly corrupted, but returning a hard * error means that you now don't see any of the other names * either, so that isn't a perfect alternative. * * And if you return an error, what error do you use? Several * filesystems seem to have decided on EUCLEAN being the error * code for EFSCORRUPTED, and that may be the error to use. Or * just EIO, which is perhaps more obvious to users. * * In order to see the other file names in the directory, the * caller might want to make this a "soft" error: skip the * entry, and return the error at the end instead. * * Note that this should likely do a "memchr(name, 0, len)" * check too, since that would be filesystem corruption as * well. However, that case can't actually confuse user space, * which has to do a strlen() on the name anyway to find the * filename length, and the above "soft error" worry means * that it's probably better left alone until we have that * issue clarified. |
2c6b7bcd7 readdir: be more ... |
101 102 103 104 |
* * Note the PATH_MAX check - it's arbitrary but the real * kernel limit on a possible path component, not NAME_MAX, * which is the technical standard limit. |
8a23eb804 Make filldir[64](... |
105 106 107 |
*/ static int verify_dirent_name(const char *name, int len) { |
2c6b7bcd7 readdir: be more ... |
108 |
if (len <= 0 || len >= PATH_MAX) |
8a23eb804 Make filldir[64](... |
109 |
return -EIO; |
b9959c7a3 filldir[64]: remo... |
110 |
if (memchr(name, '/', len)) |
8a23eb804 Make filldir[64](... |
111 112 113 114 115 |
return -EIO; return 0; } /* |
1da177e4c Linux-2.6.12-rc2 |
116 117 118 119 120 121 122 |
* 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 |
123 124 125 126 127 128 129 130 131 132 133 |
#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 { |
5c0ba4e07 [readdir] introdu... |
134 |
struct dir_context ctx; |
1da177e4c Linux-2.6.12-rc2 |
135 136 137 |
struct old_linux_dirent __user * dirent; int result; }; |
ac7576f4b vfs: make first a... |
138 139 |
static int fillonedir(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) |
1da177e4c Linux-2.6.12-rc2 |
140 |
{ |
ac7576f4b vfs: make first a... |
141 142 |
struct readdir_callback *buf = container_of(ctx, struct readdir_callback, ctx); |
1da177e4c Linux-2.6.12-rc2 |
143 |
struct old_linux_dirent __user * dirent; |
afefdbb28 [PATCH] VFS: Make... |
144 |
unsigned long d_ino; |
1da177e4c Linux-2.6.12-rc2 |
145 146 147 |
if (buf->result) return -EINVAL; |
afefdbb28 [PATCH] VFS: Make... |
148 |
d_ino = ino; |
8f3f655da [PATCH] fix regul... |
149 150 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; |
afefdbb28 [PATCH] VFS: Make... |
151 |
return -EOVERFLOW; |
8f3f655da [PATCH] fix regul... |
152 |
} |
1da177e4c Linux-2.6.12-rc2 |
153 154 |
buf->result++; dirent = buf->dirent; |
391b7461d switch readdir(2)... |
155 |
if (!user_write_access_begin(dirent, |
1da177e4c Linux-2.6.12-rc2 |
156 157 158 |
(unsigned long)(dirent->d_name + namlen + 1) - (unsigned long)dirent)) goto efault; |
391b7461d switch readdir(2)... |
159 160 161 162 163 |
unsafe_put_user(d_ino, &dirent->d_ino, efault_end); unsafe_put_user(offset, &dirent->d_offset, efault_end); unsafe_put_user(namlen, &dirent->d_namlen, efault_end); unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); user_write_access_end(); |
1da177e4c Linux-2.6.12-rc2 |
164 |
return 0; |
391b7461d switch readdir(2)... |
165 166 |
efault_end: user_write_access_end(); |
1da177e4c Linux-2.6.12-rc2 |
167 168 169 170 |
efault: buf->result = -EFAULT; return -EFAULT; } |
d4e82042c [CVE-2009-0029] S... |
171 172 |
SYSCALL_DEFINE3(old_readdir, unsigned int, fd, struct old_linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c Linux-2.6.12-rc2 |
173 174 |
{ int error; |
63b6df141 give readdir(2)/g... |
175 |
struct fd f = fdget_pos(fd); |
ac6614b76 [readdir] constif... |
176 177 178 179 |
struct readdir_callback buf = { .ctx.actor = fillonedir, .dirent = dirent }; |
1da177e4c Linux-2.6.12-rc2 |
180 |
|
2903ff019 switch simple cas... |
181 |
if (!f.file) |
863ced7fe switch readdir/ge... |
182 |
return -EBADF; |
1da177e4c Linux-2.6.12-rc2 |
183 |
|
5c0ba4e07 [readdir] introdu... |
184 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e [PATCH] prepare v... |
185 |
if (buf.result) |
1da177e4c Linux-2.6.12-rc2 |
186 |
error = buf.result; |
63b6df141 give readdir(2)/g... |
187 |
fdput_pos(f); |
1da177e4c Linux-2.6.12-rc2 |
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
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 { |
5c0ba4e07 [readdir] introdu... |
205 |
struct dir_context ctx; |
1da177e4c Linux-2.6.12-rc2 |
206 |
struct linux_dirent __user * current_dir; |
3c2659bd1 readdir: make use... |
207 |
int prev_reclen; |
1da177e4c Linux-2.6.12-rc2 |
208 209 210 |
int count; int error; }; |
ac7576f4b vfs: make first a... |
211 212 |
static int filldir(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) |
1da177e4c Linux-2.6.12-rc2 |
213 |
{ |
3c2659bd1 readdir: make use... |
214 |
struct linux_dirent __user *dirent, *prev; |
ac7576f4b vfs: make first a... |
215 216 |
struct getdents_callback *buf = container_of(ctx, struct getdents_callback, ctx); |
afefdbb28 [PATCH] VFS: Make... |
217 |
unsigned long d_ino; |
85c9fe8fc vfs: fix warning:... |
218 219 |
int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); |
3c2659bd1 readdir: make use... |
220 |
int prev_reclen; |
1da177e4c Linux-2.6.12-rc2 |
221 |
|
8a23eb804 Make filldir[64](... |
222 223 224 |
buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) return buf->error; |
1da177e4c Linux-2.6.12-rc2 |
225 226 227 |
buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; |
afefdbb28 [PATCH] VFS: Make... |
228 |
d_ino = ino; |
8f3f655da [PATCH] fix regul... |
229 230 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->error = -EOVERFLOW; |
afefdbb28 [PATCH] VFS: Make... |
231 |
return -EOVERFLOW; |
8f3f655da [PATCH] fix regul... |
232 |
} |
3c2659bd1 readdir: make use... |
233 234 |
prev_reclen = buf->prev_reclen; if (prev_reclen && signal_pending(current)) |
9f79b78ef Convert filldir[6... |
235 |
return -EINTR; |
9f79b78ef Convert filldir[6... |
236 |
dirent = buf->current_dir; |
3c2659bd1 readdir: make use... |
237 |
prev = (void __user *) dirent - prev_reclen; |
41cd78052 uaccess: Selectiv... |
238 |
if (!user_write_access_begin(prev, reclen + prev_reclen)) |
3c2659bd1 readdir: make use... |
239 240 241 242 |
goto efault; /* This might be 'dirent->d_off', but if so it will get overwritten */ unsafe_put_user(offset, &prev->d_off, efault_end); |
9f79b78ef Convert filldir[6... |
243 244 245 246 |
unsafe_put_user(d_ino, &dirent->d_ino, efault_end); unsafe_put_user(reclen, &dirent->d_reclen, efault_end); unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end); unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); |
41cd78052 uaccess: Selectiv... |
247 |
user_write_access_end(); |
9f79b78ef Convert filldir[6... |
248 |
|
3c2659bd1 readdir: make use... |
249 250 |
buf->current_dir = (void __user *)dirent + reclen; buf->prev_reclen = reclen; |
1da177e4c Linux-2.6.12-rc2 |
251 252 |
buf->count -= reclen; return 0; |
9f79b78ef Convert filldir[6... |
253 |
efault_end: |
41cd78052 uaccess: Selectiv... |
254 |
user_write_access_end(); |
1da177e4c Linux-2.6.12-rc2 |
255 256 257 258 |
efault: buf->error = -EFAULT; return -EFAULT; } |
20f37034f [CVE-2009-0029] S... |
259 260 |
SYSCALL_DEFINE3(getdents, unsigned int, fd, struct linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c Linux-2.6.12-rc2 |
261 |
{ |
2903ff019 switch simple cas... |
262 |
struct fd f; |
ac6614b76 [readdir] constif... |
263 264 265 266 267 |
struct getdents_callback buf = { .ctx.actor = filldir, .count = count, .current_dir = dirent }; |
1da177e4c Linux-2.6.12-rc2 |
268 |
int error; |
63b6df141 give readdir(2)/g... |
269 |
f = fdget_pos(fd); |
2903ff019 switch simple cas... |
270 |
if (!f.file) |
863ced7fe switch readdir/ge... |
271 |
return -EBADF; |
1da177e4c Linux-2.6.12-rc2 |
272 |
|
5c0ba4e07 [readdir] introdu... |
273 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e [PATCH] prepare v... |
274 275 |
if (error >= 0) error = buf.error; |
3c2659bd1 readdir: make use... |
276 277 278 |
if (buf.prev_reclen) { struct linux_dirent __user * lastdirent; lastdirent = (void __user *)buf.current_dir - buf.prev_reclen; |
bb6f619b3 [readdir] introdu... |
279 |
if (put_user(buf.ctx.pos, &lastdirent->d_off)) |
1da177e4c Linux-2.6.12-rc2 |
280 281 282 283 |
error = -EFAULT; else error = count - buf.count; } |
63b6df141 give readdir(2)/g... |
284 |
fdput_pos(f); |
1da177e4c Linux-2.6.12-rc2 |
285 286 |
return error; } |
1da177e4c Linux-2.6.12-rc2 |
287 |
struct getdents_callback64 { |
5c0ba4e07 [readdir] introdu... |
288 |
struct dir_context ctx; |
1da177e4c Linux-2.6.12-rc2 |
289 |
struct linux_dirent64 __user * current_dir; |
3c2659bd1 readdir: make use... |
290 |
int prev_reclen; |
1da177e4c Linux-2.6.12-rc2 |
291 292 293 |
int count; int error; }; |
ac7576f4b vfs: make first a... |
294 295 |
static int filldir64(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) |
1da177e4c Linux-2.6.12-rc2 |
296 |
{ |
3c2659bd1 readdir: make use... |
297 |
struct linux_dirent64 __user *dirent, *prev; |
ac7576f4b vfs: make first a... |
298 299 |
struct getdents_callback64 *buf = container_of(ctx, struct getdents_callback64, ctx); |
85c9fe8fc vfs: fix warning:... |
300 301 |
int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); |
3c2659bd1 readdir: make use... |
302 |
int prev_reclen; |
1da177e4c Linux-2.6.12-rc2 |
303 |
|
8a23eb804 Make filldir[64](... |
304 305 306 |
buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) return buf->error; |
1da177e4c Linux-2.6.12-rc2 |
307 308 309 |
buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; |
3c2659bd1 readdir: make use... |
310 311 |
prev_reclen = buf->prev_reclen; if (prev_reclen && signal_pending(current)) |
9f79b78ef Convert filldir[6... |
312 |
return -EINTR; |
9f79b78ef Convert filldir[6... |
313 |
dirent = buf->current_dir; |
3c2659bd1 readdir: make use... |
314 |
prev = (void __user *)dirent - prev_reclen; |
41cd78052 uaccess: Selectiv... |
315 |
if (!user_write_access_begin(prev, reclen + prev_reclen)) |
3c2659bd1 readdir: make use... |
316 317 318 319 |
goto efault; /* This might be 'dirent->d_off', but if so it will get overwritten */ unsafe_put_user(offset, &prev->d_off, efault_end); |
9f79b78ef Convert filldir[6... |
320 321 322 323 |
unsafe_put_user(ino, &dirent->d_ino, efault_end); unsafe_put_user(reclen, &dirent->d_reclen, efault_end); unsafe_put_user(d_type, &dirent->d_type, efault_end); unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); |
41cd78052 uaccess: Selectiv... |
324 |
user_write_access_end(); |
9f79b78ef Convert filldir[6... |
325 |
|
3c2659bd1 readdir: make use... |
326 327 |
buf->prev_reclen = reclen; buf->current_dir = (void __user *)dirent + reclen; |
1da177e4c Linux-2.6.12-rc2 |
328 329 |
buf->count -= reclen; return 0; |
3c2659bd1 readdir: make use... |
330 |
|
9f79b78ef Convert filldir[6... |
331 |
efault_end: |
41cd78052 uaccess: Selectiv... |
332 |
user_write_access_end(); |
1da177e4c Linux-2.6.12-rc2 |
333 334 335 336 |
efault: buf->error = -EFAULT; return -EFAULT; } |
fb2da16cd fs: remove ksys_g... |
337 338 |
SYSCALL_DEFINE3(getdents64, unsigned int, fd, struct linux_dirent64 __user *, dirent, unsigned int, count) |
1da177e4c Linux-2.6.12-rc2 |
339 |
{ |
2903ff019 switch simple cas... |
340 |
struct fd f; |
ac6614b76 [readdir] constif... |
341 342 343 344 345 |
struct getdents_callback64 buf = { .ctx.actor = filldir64, .count = count, .current_dir = dirent }; |
1da177e4c Linux-2.6.12-rc2 |
346 |
int error; |
63b6df141 give readdir(2)/g... |
347 |
f = fdget_pos(fd); |
2903ff019 switch simple cas... |
348 |
if (!f.file) |
863ced7fe switch readdir/ge... |
349 |
return -EBADF; |
1da177e4c Linux-2.6.12-rc2 |
350 |
|
5c0ba4e07 [readdir] introdu... |
351 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e [PATCH] prepare v... |
352 353 |
if (error >= 0) error = buf.error; |
3c2659bd1 readdir: make use... |
354 355 |
if (buf.prev_reclen) { struct linux_dirent64 __user * lastdirent; |
bb6f619b3 [readdir] introdu... |
356 |
typeof(lastdirent->d_off) d_off = buf.ctx.pos; |
3c2659bd1 readdir: make use... |
357 358 |
lastdirent = (void __user *) buf.current_dir - buf.prev_reclen; |
5fb151416 readdir.c: get ri... |
359 |
if (put_user(d_off, &lastdirent->d_off)) |
53c9c5c0e [PATCH] prepare v... |
360 361 362 |
error = -EFAULT; else error = count - buf.count; |
1da177e4c Linux-2.6.12-rc2 |
363 |
} |
63b6df141 give readdir(2)/g... |
364 |
fdput_pos(f); |
1da177e4c Linux-2.6.12-rc2 |
365 366 |
return error; } |
0460b2a28 readdir: move com... |
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
#ifdef CONFIG_COMPAT struct compat_old_linux_dirent { compat_ulong_t d_ino; compat_ulong_t d_offset; unsigned short d_namlen; char d_name[1]; }; struct compat_readdir_callback { struct dir_context ctx; struct compat_old_linux_dirent __user *dirent; int result; }; static int compat_fillonedir(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) { struct compat_readdir_callback *buf = container_of(ctx, struct compat_readdir_callback, ctx); struct compat_old_linux_dirent __user *dirent; compat_ulong_t d_ino; if (buf->result) return -EINVAL; d_ino = ino; if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; return -EOVERFLOW; } buf->result++; dirent = buf->dirent; |
391b7461d switch readdir(2)... |
400 |
if (!user_write_access_begin(dirent, |
0460b2a28 readdir: move com... |
401 402 403 |
(unsigned long)(dirent->d_name + namlen + 1) - (unsigned long)dirent)) goto efault; |
391b7461d switch readdir(2)... |
404 405 406 407 408 |
unsafe_put_user(d_ino, &dirent->d_ino, efault_end); unsafe_put_user(offset, &dirent->d_offset, efault_end); unsafe_put_user(namlen, &dirent->d_namlen, efault_end); unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); user_write_access_end(); |
0460b2a28 readdir: move com... |
409 |
return 0; |
391b7461d switch readdir(2)... |
410 411 |
efault_end: user_write_access_end(); |
0460b2a28 readdir: move com... |
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
efault: buf->result = -EFAULT; return -EFAULT; } COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd, struct compat_old_linux_dirent __user *, dirent, unsigned int, count) { int error; struct fd f = fdget_pos(fd); struct compat_readdir_callback buf = { .ctx.actor = compat_fillonedir, .dirent = dirent }; if (!f.file) return -EBADF; error = iterate_dir(f.file, &buf.ctx); if (buf.result) error = buf.result; fdput_pos(f); return error; } struct compat_linux_dirent { compat_ulong_t d_ino; compat_ulong_t d_off; unsigned short d_reclen; char d_name[1]; }; struct compat_getdents_callback { struct dir_context ctx; struct compat_linux_dirent __user *current_dir; |
82af599b7 readdir.c: get co... |
448 |
int prev_reclen; |
0460b2a28 readdir: move com... |
449 450 451 452 453 454 455 |
int count; int error; }; static int compat_filldir(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) { |
82af599b7 readdir.c: get co... |
456 |
struct compat_linux_dirent __user *dirent, *prev; |
0460b2a28 readdir: move com... |
457 458 459 460 461 |
struct compat_getdents_callback *buf = container_of(ctx, struct compat_getdents_callback, ctx); compat_ulong_t d_ino; int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + namlen + 2, sizeof(compat_long_t)); |
82af599b7 readdir.c: get co... |
462 |
int prev_reclen; |
0460b2a28 readdir: move com... |
463 |
|
82af599b7 readdir.c: get co... |
464 465 466 |
buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) return buf->error; |
0460b2a28 readdir: move com... |
467 468 469 470 471 472 473 474 |
buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; d_ino = ino; if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->error = -EOVERFLOW; return -EOVERFLOW; } |
82af599b7 readdir.c: get co... |
475 476 477 |
prev_reclen = buf->prev_reclen; if (prev_reclen && signal_pending(current)) return -EINTR; |
0460b2a28 readdir: move com... |
478 |
dirent = buf->current_dir; |
82af599b7 readdir.c: get co... |
479 480 |
prev = (void __user *) dirent - prev_reclen; if (!user_write_access_begin(prev, reclen + prev_reclen)) |
0460b2a28 readdir: move com... |
481 |
goto efault; |
82af599b7 readdir.c: get co... |
482 483 484 485 486 487 488 489 490 491 |
unsafe_put_user(offset, &prev->d_off, efault_end); unsafe_put_user(d_ino, &dirent->d_ino, efault_end); unsafe_put_user(reclen, &dirent->d_reclen, efault_end); unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end); unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); user_write_access_end(); buf->prev_reclen = reclen; buf->current_dir = (void __user *)dirent + reclen; |
0460b2a28 readdir: move com... |
492 493 |
buf->count -= reclen; return 0; |
82af599b7 readdir.c: get co... |
494 495 |
efault_end: user_write_access_end(); |
0460b2a28 readdir: move com... |
496 497 498 499 500 501 502 503 504 |
efault: buf->error = -EFAULT; return -EFAULT; } COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd, struct compat_linux_dirent __user *, dirent, unsigned int, count) { struct fd f; |
0460b2a28 readdir: move com... |
505 506 507 508 509 510 |
struct compat_getdents_callback buf = { .ctx.actor = compat_filldir, .current_dir = dirent, .count = count }; int error; |
0460b2a28 readdir: move com... |
511 512 513 514 515 516 517 |
f = fdget_pos(fd); if (!f.file) return -EBADF; error = iterate_dir(f.file, &buf.ctx); if (error >= 0) error = buf.error; |
82af599b7 readdir.c: get co... |
518 519 520 |
if (buf.prev_reclen) { struct compat_linux_dirent __user * lastdirent; lastdirent = (void __user *)buf.current_dir - buf.prev_reclen; |
0460b2a28 readdir: move com... |
521 522 523 524 525 526 527 528 529 |
if (put_user(buf.ctx.pos, &lastdirent->d_off)) error = -EFAULT; else error = count - buf.count; } fdput_pos(f); return error; } #endif |