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