Blame view
fs/readdir.c
7.3 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); |
619226944
|
25 |
bool shared = false; |
1da177e4c
|
26 |
int res = -ENOTDIR; |
619226944
|
27 28 29 |
if (file->f_op->iterate_shared) shared = true; else if (!file->f_op->iterate) |
1da177e4c
|
30 31 32 33 34 |
goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; |
002354112
|
35 |
if (shared) { |
619226944
|
36 |
inode_lock_shared(inode); |
002354112
|
37 38 39 40 41 |
} else { res = down_write_killable(&inode->i_rwsem); if (res) goto out; } |
da7845119
|
42 |
|
1da177e4c
|
43 44 |
res = -ENOENT; if (!IS_DEADDIR(inode)) { |
2233f31aa
|
45 |
ctx->pos = file->f_pos; |
619226944
|
46 47 48 49 |
if (shared) res = file->f_op->iterate_shared(file, ctx); else res = file->f_op->iterate(file, ctx); |
2233f31aa
|
50 |
file->f_pos = ctx->pos; |
d4c7cf6cf
|
51 |
fsnotify_access(file); |
1da177e4c
|
52 53 |
file_accessed(file); } |
619226944
|
54 55 56 57 |
if (shared) inode_unlock_shared(inode); else inode_unlock(inode); |
1da177e4c
|
58 59 60 |
out: return res; } |
5c0ba4e07
|
61 |
EXPORT_SYMBOL(iterate_dir); |
1da177e4c
|
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
|
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
|
82 |
struct dir_context ctx; |
1da177e4c
|
83 84 85 |
struct old_linux_dirent __user * dirent; int result; }; |
ac7576f4b
|
86 87 |
static int fillonedir(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) |
1da177e4c
|
88 |
{ |
ac7576f4b
|
89 90 |
struct readdir_callback *buf = container_of(ctx, struct readdir_callback, ctx); |
1da177e4c
|
91 |
struct old_linux_dirent __user * dirent; |
afefdbb28
|
92 |
unsigned long d_ino; |
1da177e4c
|
93 94 95 |
if (buf->result) return -EINVAL; |
afefdbb28
|
96 |
d_ino = ino; |
8f3f655da
|
97 98 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->result = -EOVERFLOW; |
afefdbb28
|
99 |
return -EOVERFLOW; |
8f3f655da
|
100 |
} |
1da177e4c
|
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
|
107 |
if ( __put_user(d_ino, &dirent->d_ino) || |
1da177e4c
|
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
|
118 119 |
SYSCALL_DEFINE3(old_readdir, unsigned int, fd, struct old_linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c
|
120 121 |
{ int error; |
63b6df141
|
122 |
struct fd f = fdget_pos(fd); |
ac6614b76
|
123 124 125 126 |
struct readdir_callback buf = { .ctx.actor = fillonedir, .dirent = dirent }; |
1da177e4c
|
127 |
|
2903ff019
|
128 |
if (!f.file) |
863ced7fe
|
129 |
return -EBADF; |
1da177e4c
|
130 |
|
5c0ba4e07
|
131 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e
|
132 |
if (buf.result) |
1da177e4c
|
133 |
error = buf.result; |
63b6df141
|
134 |
fdput_pos(f); |
1da177e4c
|
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
|
152 |
struct dir_context ctx; |
1da177e4c
|
153 154 155 156 157 |
struct linux_dirent __user * current_dir; struct linux_dirent __user * previous; int count; int error; }; |
ac7576f4b
|
158 159 |
static int filldir(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) |
1da177e4c
|
160 161 |
{ struct linux_dirent __user * dirent; |
ac7576f4b
|
162 163 |
struct getdents_callback *buf = container_of(ctx, struct getdents_callback, ctx); |
afefdbb28
|
164 |
unsigned long d_ino; |
85c9fe8fc
|
165 166 |
int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); |
1da177e4c
|
167 168 169 170 |
buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; |
afefdbb28
|
171 |
d_ino = ino; |
8f3f655da
|
172 173 |
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { buf->error = -EOVERFLOW; |
afefdbb28
|
174 |
return -EOVERFLOW; |
8f3f655da
|
175 |
} |
1da177e4c
|
176 177 |
dirent = buf->previous; if (dirent) { |
1f60fbe72
|
178 179 |
if (signal_pending(current)) return -EINTR; |
1da177e4c
|
180 181 182 183 |
if (__put_user(offset, &dirent->d_off)) goto efault; } dirent = buf->current_dir; |
afefdbb28
|
184 |
if (__put_user(d_ino, &dirent->d_ino)) |
1da177e4c
|
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
|
203 204 |
SYSCALL_DEFINE3(getdents, unsigned int, fd, struct linux_dirent __user *, dirent, unsigned int, count) |
1da177e4c
|
205 |
{ |
2903ff019
|
206 |
struct fd f; |
1da177e4c
|
207 |
struct linux_dirent __user * lastdirent; |
ac6614b76
|
208 209 210 211 212 |
struct getdents_callback buf = { .ctx.actor = filldir, .count = count, .current_dir = dirent }; |
1da177e4c
|
213 |
int error; |
1da177e4c
|
214 |
if (!access_ok(VERIFY_WRITE, dirent, count)) |
863ced7fe
|
215 |
return -EFAULT; |
1da177e4c
|
216 |
|
63b6df141
|
217 |
f = fdget_pos(fd); |
2903ff019
|
218 |
if (!f.file) |
863ced7fe
|
219 |
return -EBADF; |
1da177e4c
|
220 |
|
5c0ba4e07
|
221 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e
|
222 223 |
if (error >= 0) error = buf.error; |
1da177e4c
|
224 225 |
lastdirent = buf.previous; if (lastdirent) { |
bb6f619b3
|
226 |
if (put_user(buf.ctx.pos, &lastdirent->d_off)) |
1da177e4c
|
227 228 229 230 |
error = -EFAULT; else error = count - buf.count; } |
63b6df141
|
231 |
fdput_pos(f); |
1da177e4c
|
232 233 |
return error; } |
1da177e4c
|
234 |
struct getdents_callback64 { |
5c0ba4e07
|
235 |
struct dir_context ctx; |
1da177e4c
|
236 237 238 239 240 |
struct linux_dirent64 __user * current_dir; struct linux_dirent64 __user * previous; int count; int error; }; |
ac7576f4b
|
241 242 |
static int filldir64(struct dir_context *ctx, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) |
1da177e4c
|
243 244 |
{ struct linux_dirent64 __user *dirent; |
ac7576f4b
|
245 246 |
struct getdents_callback64 *buf = container_of(ctx, struct getdents_callback64, ctx); |
85c9fe8fc
|
247 248 |
int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); |
1da177e4c
|
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
|
255 256 |
if (signal_pending(current)) return -EINTR; |
1da177e4c
|
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
|
282 283 |
SYSCALL_DEFINE3(getdents64, unsigned int, fd, struct linux_dirent64 __user *, dirent, unsigned int, count) |
1da177e4c
|
284 |
{ |
2903ff019
|
285 |
struct fd f; |
1da177e4c
|
286 |
struct linux_dirent64 __user * lastdirent; |
ac6614b76
|
287 288 289 290 291 |
struct getdents_callback64 buf = { .ctx.actor = filldir64, .count = count, .current_dir = dirent }; |
1da177e4c
|
292 |
int error; |
1da177e4c
|
293 |
if (!access_ok(VERIFY_WRITE, dirent, count)) |
863ced7fe
|
294 |
return -EFAULT; |
1da177e4c
|
295 |
|
63b6df141
|
296 |
f = fdget_pos(fd); |
2903ff019
|
297 |
if (!f.file) |
863ced7fe
|
298 |
return -EBADF; |
1da177e4c
|
299 |
|
5c0ba4e07
|
300 |
error = iterate_dir(f.file, &buf.ctx); |
53c9c5c0e
|
301 302 |
if (error >= 0) error = buf.error; |
1da177e4c
|
303 304 |
lastdirent = buf.previous; if (lastdirent) { |
bb6f619b3
|
305 |
typeof(lastdirent->d_off) d_off = buf.ctx.pos; |
1da177e4c
|
306 |
if (__put_user(d_off, &lastdirent->d_off)) |
53c9c5c0e
|
307 308 309 |
error = -EFAULT; else error = count - buf.count; |
1da177e4c
|
310 |
} |
63b6df141
|
311 |
fdput_pos(f); |
1da177e4c
|
312 313 |
return error; } |