Blame view
fs/readdir.c
7.3 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 |
#include <linux/fs.h> |
d4c7cf6cf fanotify: create ... |
15 |
#include <linux/fsnotify.h> |
1da177e4c Linux-2.6.12-rc2 |
16 17 18 19 20 21 |
#include <linux/dirent.h> #include <linux/security.h> #include <linux/syscalls.h> #include <linux/unistd.h> #include <asm/uaccess.h> |
5c0ba4e07 [readdir] introdu... |
22 |
int iterate_dir(struct file *file, struct dir_context *ctx) |
1da177e4c Linux-2.6.12-rc2 |
23 |
{ |
496ad9aa8 new helper: file_... |
24 |
struct inode *inode = file_inode(file); |
619226944 introduce a paral... |
25 |
bool shared = false; |
1da177e4c Linux-2.6.12-rc2 |
26 |
int res = -ENOTDIR; |
619226944 introduce a paral... |
27 28 29 |
if (file->f_op->iterate_shared) shared = true; else if (!file->f_op->iterate) |
1da177e4c Linux-2.6.12-rc2 |
30 31 32 33 34 |
goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; |
002354112 restore killabili... |
35 |
if (shared) { |
619226944 introduce a paral... |
36 |
inode_lock_shared(inode); |
002354112 restore killabili... |
37 38 39 40 41 |
} else { res = down_write_killable(&inode->i_rwsem); if (res) goto out; } |
da7845119 Use mutex_lock_ki... |
42 |
|
1da177e4c Linux-2.6.12-rc2 |
43 44 |
res = -ENOENT; if (!IS_DEADDIR(inode)) { |
2233f31aa [readdir] ->readd... |
45 |
ctx->pos = file->f_pos; |
619226944 introduce a paral... |
46 47 48 49 |
if (shared) res = file->f_op->iterate_shared(file, ctx); else res = file->f_op->iterate(file, ctx); |
2233f31aa [readdir] ->readd... |
50 |
file->f_pos = ctx->pos; |
d4c7cf6cf fanotify: create ... |
51 |
fsnotify_access(file); |
1da177e4c Linux-2.6.12-rc2 |
52 53 |
file_accessed(file); } |
619226944 introduce a paral... |
54 55 56 57 |
if (shared) inode_unlock_shared(inode); else inode_unlock(inode); |
1da177e4c Linux-2.6.12-rc2 |
58 59 60 |
out: return res; } |
5c0ba4e07 [readdir] introdu... |
61 |
EXPORT_SYMBOL(iterate_dir); |
1da177e4c Linux-2.6.12-rc2 |
62 63 64 65 66 67 68 69 70 |
/* * 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 |
71 72 73 74 75 76 77 78 79 80 81 |
#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... |
82 |
struct dir_context ctx; |
1da177e4c Linux-2.6.12-rc2 |
83 84 85 |
struct old_linux_dirent __user * dirent; int result; }; |
ac7576f4b vfs: make first a... |
86 87 |
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 |
88 |
{ |
ac7576f4b vfs: make first a... |
89 90 |
struct readdir_callback *buf = container_of(ctx, struct readdir_callback, ctx); |
1da177e4c Linux-2.6.12-rc2 |
91 |
struct old_linux_dirent __user * dirent; |
afefdbb28 [PATCH] VFS: Make... |
92 |
unsigned long d_ino; |
1da177e4c Linux-2.6.12-rc2 |
93 94 95 |
if (buf->result) return -EINVAL; |
afefdbb28 [PATCH] VFS: Make... |
96 |
d_ino = ino; |
8f3f655da [PATCH] fix regul... |
97 98 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; |
afefdbb28 [PATCH] VFS: Make... |
99 |
return -EOVERFLOW; |
8f3f655da [PATCH] fix regul... |
100 |
} |
1da177e4c Linux-2.6.12-rc2 |
101 102 103 104 105 106 |
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... |
107 |
if ( __put_user(d_ino, &dirent->d_ino) || |
1da177e4c Linux-2.6.12-rc2 |
108 109 110 111 112 113 114 115 116 117 |
__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... |
118 119 |
SYSCALL_DEFINE3(old_readdir, unsigned int, fd, struct old_linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c Linux-2.6.12-rc2 |
120 121 |
{ int error; |
63b6df141 give readdir(2)/g... |
122 |
struct fd f = fdget_pos(fd); |
ac6614b76 [readdir] constif... |
123 124 125 126 |
struct readdir_callback buf = { .ctx.actor = fillonedir, .dirent = dirent }; |
1da177e4c Linux-2.6.12-rc2 |
127 |
|
2903ff019 switch simple cas... |
128 |
if (!f.file) |
863ced7fe switch readdir/ge... |
129 |
return -EBADF; |
1da177e4c Linux-2.6.12-rc2 |
130 |
|
5c0ba4e07 [readdir] introdu... |
131 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e [PATCH] prepare v... |
132 |
if (buf.result) |
1da177e4c Linux-2.6.12-rc2 |
133 |
error = buf.result; |
63b6df141 give readdir(2)/g... |
134 |
fdput_pos(f); |
1da177e4c Linux-2.6.12-rc2 |
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
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... |
152 |
struct dir_context ctx; |
1da177e4c Linux-2.6.12-rc2 |
153 154 155 156 157 |
struct linux_dirent __user * current_dir; struct linux_dirent __user * previous; int count; int error; }; |
ac7576f4b vfs: make first a... |
158 159 |
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 |
160 161 |
{ struct linux_dirent __user * dirent; |
ac7576f4b vfs: make first a... |
162 163 |
struct getdents_callback *buf = container_of(ctx, struct getdents_callback, ctx); |
afefdbb28 [PATCH] VFS: Make... |
164 |
unsigned long d_ino; |
85c9fe8fc vfs: fix warning:... |
165 166 |
int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); |
1da177e4c Linux-2.6.12-rc2 |
167 168 169 170 |
buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; |
afefdbb28 [PATCH] VFS: Make... |
171 |
d_ino = ino; |
8f3f655da [PATCH] fix regul... |
172 173 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->error = -EOVERFLOW; |
afefdbb28 [PATCH] VFS: Make... |
174 |
return -EOVERFLOW; |
8f3f655da [PATCH] fix regul... |
175 |
} |
1da177e4c Linux-2.6.12-rc2 |
176 177 |
dirent = buf->previous; if (dirent) { |
1f60fbe72 ext4: allow readd... |
178 179 |
if (signal_pending(current)) return -EINTR; |
1da177e4c Linux-2.6.12-rc2 |
180 181 182 183 |
if (__put_user(offset, &dirent->d_off)) goto efault; } dirent = buf->current_dir; |
afefdbb28 [PATCH] VFS: Make... |
184 |
if (__put_user(d_ino, &dirent->d_ino)) |
1da177e4c Linux-2.6.12-rc2 |
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
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... |
203 204 |
SYSCALL_DEFINE3(getdents, unsigned int, fd, struct linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c Linux-2.6.12-rc2 |
205 |
{ |
2903ff019 switch simple cas... |
206 |
struct fd f; |
1da177e4c Linux-2.6.12-rc2 |
207 |
struct linux_dirent __user * lastdirent; |
ac6614b76 [readdir] constif... |
208 209 210 211 212 |
struct getdents_callback buf = { .ctx.actor = filldir, .count = count, .current_dir = dirent }; |
1da177e4c Linux-2.6.12-rc2 |
213 |
int error; |
1da177e4c Linux-2.6.12-rc2 |
214 |
if (!access_ok(VERIFY_WRITE, dirent, count)) |
863ced7fe switch readdir/ge... |
215 |
return -EFAULT; |
1da177e4c Linux-2.6.12-rc2 |
216 |
|
63b6df141 give readdir(2)/g... |
217 |
f = fdget_pos(fd); |
2903ff019 switch simple cas... |
218 |
if (!f.file) |
863ced7fe switch readdir/ge... |
219 |
return -EBADF; |
1da177e4c Linux-2.6.12-rc2 |
220 |
|
5c0ba4e07 [readdir] introdu... |
221 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e [PATCH] prepare v... |
222 223 |
if (error >= 0) error = buf.error; |
1da177e4c Linux-2.6.12-rc2 |
224 225 |
lastdirent = buf.previous; if (lastdirent) { |
bb6f619b3 [readdir] introdu... |
226 |
if (put_user(buf.ctx.pos, &lastdirent->d_off)) |
1da177e4c Linux-2.6.12-rc2 |
227 228 229 230 |
error = -EFAULT; else error = count - buf.count; } |
63b6df141 give readdir(2)/g... |
231 |
fdput_pos(f); |
1da177e4c Linux-2.6.12-rc2 |
232 233 |
return error; } |
1da177e4c Linux-2.6.12-rc2 |
234 |
struct getdents_callback64 { |
5c0ba4e07 [readdir] introdu... |
235 |
struct dir_context ctx; |
1da177e4c Linux-2.6.12-rc2 |
236 237 238 239 240 |
struct linux_dirent64 __user * current_dir; struct linux_dirent64 __user * previous; int count; int error; }; |
ac7576f4b vfs: make first a... |
241 242 |
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 |
243 244 |
{ struct linux_dirent64 __user *dirent; |
ac7576f4b vfs: make first a... |
245 246 |
struct getdents_callback64 *buf = container_of(ctx, struct getdents_callback64, ctx); |
85c9fe8fc vfs: fix warning:... |
247 248 |
int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); |
1da177e4c Linux-2.6.12-rc2 |
249 250 251 252 253 254 |
buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; dirent = buf->previous; if (dirent) { |
1f60fbe72 ext4: allow readd... |
255 256 |
if (signal_pending(current)) return -EINTR; |
1da177e4c Linux-2.6.12-rc2 |
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
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... |
282 283 |
SYSCALL_DEFINE3(getdents64, unsigned int, fd, struct linux_dirent64 __user *, dirent, unsigned int, count) |
1da177e4c Linux-2.6.12-rc2 |
284 |
{ |
2903ff019 switch simple cas... |
285 |
struct fd f; |
1da177e4c Linux-2.6.12-rc2 |
286 |
struct linux_dirent64 __user * lastdirent; |
ac6614b76 [readdir] constif... |
287 288 289 290 291 |
struct getdents_callback64 buf = { .ctx.actor = filldir64, .count = count, .current_dir = dirent }; |
1da177e4c Linux-2.6.12-rc2 |
292 |
int error; |
1da177e4c Linux-2.6.12-rc2 |
293 |
if (!access_ok(VERIFY_WRITE, dirent, count)) |
863ced7fe switch readdir/ge... |
294 |
return -EFAULT; |
1da177e4c Linux-2.6.12-rc2 |
295 |
|
63b6df141 give readdir(2)/g... |
296 |
f = fdget_pos(fd); |
2903ff019 switch simple cas... |
297 |
if (!f.file) |
863ced7fe switch readdir/ge... |
298 |
return -EBADF; |
1da177e4c Linux-2.6.12-rc2 |
299 |
|
5c0ba4e07 [readdir] introdu... |
300 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e [PATCH] prepare v... |
301 302 |
if (error >= 0) error = buf.error; |
1da177e4c Linux-2.6.12-rc2 |
303 304 |
lastdirent = buf.previous; if (lastdirent) { |
bb6f619b3 [readdir] introdu... |
305 |
typeof(lastdirent->d_off) d_off = buf.ctx.pos; |
1da177e4c Linux-2.6.12-rc2 |
306 |
if (__put_user(d_off, &lastdirent->d_off)) |
53c9c5c0e [PATCH] prepare v... |
307 308 309 |
error = -EFAULT; else error = count - buf.count; |
1da177e4c Linux-2.6.12-rc2 |
310 |
} |
63b6df141 give readdir(2)/g... |
311 |
fdput_pos(f); |
1da177e4c Linux-2.6.12-rc2 |
312 313 |
return error; } |