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