Blame view
fs/readdir.c
6.94 KB
1da177e4c
|
1 2 3 4 5 |
/* * linux/fs/readdir.c * * Copyright (C) 1995 Linus Torvalds */ |
85c9fe8fc
|
6 |
#include <linux/stddef.h> |
022a16924
|
7 |
#include <linux/kernel.h> |
630d9c472
|
8 |
#include <linux/export.h> |
1da177e4c
|
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
|
14 |
#include <linux/fs.h> |
d4c7cf6cf
|
15 |
#include <linux/fsnotify.h> |
1da177e4c
|
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
|
22 |
int iterate_dir(struct file *file, struct dir_context *ctx) |
1da177e4c
|
23 |
{ |
496ad9aa8
|
24 |
struct inode *inode = file_inode(file); |
1da177e4c
|
25 |
int res = -ENOTDIR; |
72c2d5319
|
26 |
if (!file->f_op->iterate) |
1da177e4c
|
27 28 29 30 31 |
goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; |
da7845119
|
32 33 34 |
res = mutex_lock_killable(&inode->i_mutex); if (res) goto out; |
1da177e4c
|
35 36 |
res = -ENOENT; if (!IS_DEADDIR(inode)) { |
2233f31aa
|
37 38 39 |
ctx->pos = file->f_pos; res = file->f_op->iterate(file, ctx); file->f_pos = ctx->pos; |
d4c7cf6cf
|
40 |
fsnotify_access(file); |
1da177e4c
|
41 42 |
file_accessed(file); } |
1b1dcc1b5
|
43 |
mutex_unlock(&inode->i_mutex); |
1da177e4c
|
44 45 46 |
out: return res; } |
5c0ba4e07
|
47 |
EXPORT_SYMBOL(iterate_dir); |
1da177e4c
|
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
|
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
|
68 |
struct dir_context ctx; |
1da177e4c
|
69 70 71 |
struct old_linux_dirent __user * dirent; int result; }; |
ac7576f4b
|
72 73 |
static int fillonedir(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) |
1da177e4c
|
74 |
{ |
ac7576f4b
|
75 76 |
struct readdir_callback *buf = container_of(ctx, struct readdir_callback, ctx); |
1da177e4c
|
77 |
struct old_linux_dirent __user * dirent; |
afefdbb28
|
78 |
unsigned long d_ino; |
1da177e4c
|
79 80 81 |
if (buf->result) return -EINVAL; |
afefdbb28
|
82 |
d_ino = ino; |
8f3f655da
|
83 84 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; |
afefdbb28
|
85 |
return -EOVERFLOW; |
8f3f655da
|
86 |
} |
1da177e4c
|
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
|
93 |
if ( __put_user(d_ino, &dirent->d_ino) || |
1da177e4c
|
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
|
104 105 |
SYSCALL_DEFINE3(old_readdir, unsigned int, fd, struct old_linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c
|
106 107 |
{ int error; |
2903ff019
|
108 |
struct fd f = fdget(fd); |
ac6614b76
|
109 110 111 112 |
struct readdir_callback buf = { .ctx.actor = fillonedir, .dirent = dirent }; |
1da177e4c
|
113 |
|
2903ff019
|
114 |
if (!f.file) |
863ced7fe
|
115 |
return -EBADF; |
1da177e4c
|
116 |
|
5c0ba4e07
|
117 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e
|
118 |
if (buf.result) |
1da177e4c
|
119 |
error = buf.result; |
2903ff019
|
120 |
fdput(f); |
1da177e4c
|
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
|
138 |
struct dir_context ctx; |
1da177e4c
|
139 140 141 142 143 |
struct linux_dirent __user * current_dir; struct linux_dirent __user * previous; int count; int error; }; |
ac7576f4b
|
144 145 |
static int filldir(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) |
1da177e4c
|
146 147 |
{ struct linux_dirent __user * dirent; |
ac7576f4b
|
148 149 |
struct getdents_callback *buf = container_of(ctx, struct getdents_callback, ctx); |
afefdbb28
|
150 |
unsigned long d_ino; |
85c9fe8fc
|
151 152 |
int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); |
1da177e4c
|
153 154 155 156 |
buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; |
afefdbb28
|
157 |
d_ino = ino; |
8f3f655da
|
158 159 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->error = -EOVERFLOW; |
afefdbb28
|
160 |
return -EOVERFLOW; |
8f3f655da
|
161 |
} |
1da177e4c
|
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
|
168 |
if (__put_user(d_ino, &dirent->d_ino)) |
1da177e4c
|
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
|
187 188 |
SYSCALL_DEFINE3(getdents, unsigned int, fd, struct linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c
|
189 |
{ |
2903ff019
|
190 |
struct fd f; |
1da177e4c
|
191 |
struct linux_dirent __user * lastdirent; |
ac6614b76
|
192 193 194 195 196 |
struct getdents_callback buf = { .ctx.actor = filldir, .count = count, .current_dir = dirent }; |
1da177e4c
|
197 |
int error; |
1da177e4c
|
198 |
if (!access_ok(VERIFY_WRITE, dirent, count)) |
863ced7fe
|
199 |
return -EFAULT; |
1da177e4c
|
200 |
|
2903ff019
|
201 202 |
f = fdget(fd); if (!f.file) |
863ced7fe
|
203 |
return -EBADF; |
1da177e4c
|
204 |
|
5c0ba4e07
|
205 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e
|
206 207 |
if (error >= 0) error = buf.error; |
1da177e4c
|
208 209 |
lastdirent = buf.previous; if (lastdirent) { |
bb6f619b3
|
210 |
if (put_user(buf.ctx.pos, &lastdirent->d_off)) |
1da177e4c
|
211 212 213 214 |
error = -EFAULT; else error = count - buf.count; } |
2903ff019
|
215 |
fdput(f); |
1da177e4c
|
216 217 |
return error; } |
1da177e4c
|
218 |
struct getdents_callback64 { |
5c0ba4e07
|
219 |
struct dir_context ctx; |
1da177e4c
|
220 221 222 223 224 |
struct linux_dirent64 __user * current_dir; struct linux_dirent64 __user * previous; int count; int error; }; |
ac7576f4b
|
225 226 |
static int filldir64(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) |
1da177e4c
|
227 228 |
{ struct linux_dirent64 __user *dirent; |
ac7576f4b
|
229 230 |
struct getdents_callback64 *buf = container_of(ctx, struct getdents_callback64, ctx); |
85c9fe8fc
|
231 232 |
int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); |
1da177e4c
|
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
|
264 265 |
SYSCALL_DEFINE3(getdents64, unsigned int, fd, struct linux_dirent64 __user *, dirent, unsigned int, count) |
1da177e4c
|
266 |
{ |
2903ff019
|
267 |
struct fd f; |
1da177e4c
|
268 |
struct linux_dirent64 __user * lastdirent; |
ac6614b76
|
269 270 271 272 273 |
struct getdents_callback64 buf = { .ctx.actor = filldir64, .count = count, .current_dir = dirent }; |
1da177e4c
|
274 |
int error; |
1da177e4c
|
275 |
if (!access_ok(VERIFY_WRITE, dirent, count)) |
863ced7fe
|
276 |
return -EFAULT; |
1da177e4c
|
277 |
|
2903ff019
|
278 279 |
f = fdget(fd); if (!f.file) |
863ced7fe
|
280 |
return -EBADF; |
1da177e4c
|
281 |
|
5c0ba4e07
|
282 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e
|
283 284 |
if (error >= 0) error = buf.error; |
1da177e4c
|
285 286 |
lastdirent = buf.previous; if (lastdirent) { |
bb6f619b3
|
287 |
typeof(lastdirent->d_off) d_off = buf.ctx.pos; |
1da177e4c
|
288 |
if (__put_user(d_off, &lastdirent->d_off)) |
53c9c5c0e
|
289 290 291 |
error = -EFAULT; else error = count - buf.count; |
1da177e4c
|
292 |
} |
2903ff019
|
293 |
fdput(f); |
1da177e4c
|
294 295 |
return error; } |