]> git.karo-electronics.de Git - karo-tx-linux.git/blob - fs/readdir.c
Merge tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
[karo-tx-linux.git] / fs / readdir.c
1 /*
2  *  linux/fs/readdir.c
3  *
4  *  Copyright (C) 1995  Linus Torvalds
5  */
6
7 #include <linux/stddef.h>
8 #include <linux/kernel.h>
9 #include <linux/export.h>
10 #include <linux/time.h>
11 #include <linux/mm.h>
12 #include <linux/errno.h>
13 #include <linux/stat.h>
14 #include <linux/file.h>
15 #include <linux/fs.h>
16 #include <linux/fsnotify.h>
17 #include <linux/dirent.h>
18 #include <linux/security.h>
19 #include <linux/syscalls.h>
20 #include <linux/unistd.h>
21 #include <linux/compat.h>
22
23 #include <linux/uaccess.h>
24
25 int iterate_dir(struct file *file, struct dir_context *ctx)
26 {
27         struct inode *inode = file_inode(file);
28         bool shared = false;
29         int res = -ENOTDIR;
30         if (file->f_op->iterate_shared)
31                 shared = true;
32         else if (!file->f_op->iterate)
33                 goto out;
34
35         res = security_file_permission(file, MAY_READ);
36         if (res)
37                 goto out;
38
39         if (shared) {
40                 inode_lock_shared(inode);
41         } else {
42                 res = down_write_killable(&inode->i_rwsem);
43                 if (res)
44                         goto out;
45         }
46
47         res = -ENOENT;
48         if (!IS_DEADDIR(inode)) {
49                 ctx->pos = file->f_pos;
50                 if (shared)
51                         res = file->f_op->iterate_shared(file, ctx);
52                 else
53                         res = file->f_op->iterate(file, ctx);
54                 file->f_pos = ctx->pos;
55                 fsnotify_access(file);
56                 file_accessed(file);
57         }
58         if (shared)
59                 inode_unlock_shared(inode);
60         else
61                 inode_unlock(inode);
62 out:
63         return res;
64 }
65 EXPORT_SYMBOL(iterate_dir);
66
67 /*
68  * Traditional linux readdir() handling..
69  *
70  * "count=1" is a special case, meaning that the buffer is one
71  * dirent-structure in size and that the code can't handle more
72  * anyway. Thus the special "fillonedir()" function for that
73  * case (the low-level handlers don't need to care about this).
74  */
75
76 #ifdef __ARCH_WANT_OLD_READDIR
77
78 struct old_linux_dirent {
79         unsigned long   d_ino;
80         unsigned long   d_offset;
81         unsigned short  d_namlen;
82         char            d_name[1];
83 };
84
85 struct readdir_callback {
86         struct dir_context ctx;
87         struct old_linux_dirent __user * dirent;
88         int result;
89 };
90
91 static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
92                       loff_t offset, u64 ino, unsigned int d_type)
93 {
94         struct readdir_callback *buf =
95                 container_of(ctx, struct readdir_callback, ctx);
96         struct old_linux_dirent __user * dirent;
97         unsigned long d_ino;
98
99         if (buf->result)
100                 return -EINVAL;
101         d_ino = ino;
102         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
103                 buf->result = -EOVERFLOW;
104                 return -EOVERFLOW;
105         }
106         buf->result++;
107         dirent = buf->dirent;
108         if (!access_ok(VERIFY_WRITE, dirent,
109                         (unsigned long)(dirent->d_name + namlen + 1) -
110                                 (unsigned long)dirent))
111                 goto efault;
112         if (    __put_user(d_ino, &dirent->d_ino) ||
113                 __put_user(offset, &dirent->d_offset) ||
114                 __put_user(namlen, &dirent->d_namlen) ||
115                 __copy_to_user(dirent->d_name, name, namlen) ||
116                 __put_user(0, dirent->d_name + namlen))
117                 goto efault;
118         return 0;
119 efault:
120         buf->result = -EFAULT;
121         return -EFAULT;
122 }
123
124 SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
125                 struct old_linux_dirent __user *, dirent, unsigned int, count)
126 {
127         int error;
128         struct fd f = fdget_pos(fd);
129         struct readdir_callback buf = {
130                 .ctx.actor = fillonedir,
131                 .dirent = dirent
132         };
133
134         if (!f.file)
135                 return -EBADF;
136
137         error = iterate_dir(f.file, &buf.ctx);
138         if (buf.result)
139                 error = buf.result;
140
141         fdput_pos(f);
142         return error;
143 }
144
145 #endif /* __ARCH_WANT_OLD_READDIR */
146
147 /*
148  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
149  * interface. 
150  */
151 struct linux_dirent {
152         unsigned long   d_ino;
153         unsigned long   d_off;
154         unsigned short  d_reclen;
155         char            d_name[1];
156 };
157
158 struct getdents_callback {
159         struct dir_context ctx;
160         struct linux_dirent __user * current_dir;
161         struct linux_dirent __user * previous;
162         int count;
163         int error;
164 };
165
166 static int filldir(struct dir_context *ctx, const char *name, int namlen,
167                    loff_t offset, u64 ino, unsigned int d_type)
168 {
169         struct linux_dirent __user * dirent;
170         struct getdents_callback *buf =
171                 container_of(ctx, struct getdents_callback, ctx);
172         unsigned long d_ino;
173         int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
174                 sizeof(long));
175
176         buf->error = -EINVAL;   /* only used if we fail.. */
177         if (reclen > buf->count)
178                 return -EINVAL;
179         d_ino = ino;
180         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
181                 buf->error = -EOVERFLOW;
182                 return -EOVERFLOW;
183         }
184         dirent = buf->previous;
185         if (dirent) {
186                 if (signal_pending(current))
187                         return -EINTR;
188                 if (__put_user(offset, &dirent->d_off))
189                         goto efault;
190         }
191         dirent = buf->current_dir;
192         if (__put_user(d_ino, &dirent->d_ino))
193                 goto efault;
194         if (__put_user(reclen, &dirent->d_reclen))
195                 goto efault;
196         if (copy_to_user(dirent->d_name, name, namlen))
197                 goto efault;
198         if (__put_user(0, dirent->d_name + namlen))
199                 goto efault;
200         if (__put_user(d_type, (char __user *) dirent + reclen - 1))
201                 goto efault;
202         buf->previous = dirent;
203         dirent = (void __user *)dirent + reclen;
204         buf->current_dir = dirent;
205         buf->count -= reclen;
206         return 0;
207 efault:
208         buf->error = -EFAULT;
209         return -EFAULT;
210 }
211
212 SYSCALL_DEFINE3(getdents, unsigned int, fd,
213                 struct linux_dirent __user *, dirent, unsigned int, count)
214 {
215         struct fd f;
216         struct linux_dirent __user * lastdirent;
217         struct getdents_callback buf = {
218                 .ctx.actor = filldir,
219                 .count = count,
220                 .current_dir = dirent
221         };
222         int error;
223
224         if (!access_ok(VERIFY_WRITE, dirent, count))
225                 return -EFAULT;
226
227         f = fdget_pos(fd);
228         if (!f.file)
229                 return -EBADF;
230
231         error = iterate_dir(f.file, &buf.ctx);
232         if (error >= 0)
233                 error = buf.error;
234         lastdirent = buf.previous;
235         if (lastdirent) {
236                 if (put_user(buf.ctx.pos, &lastdirent->d_off))
237                         error = -EFAULT;
238                 else
239                         error = count - buf.count;
240         }
241         fdput_pos(f);
242         return error;
243 }
244
245 struct getdents_callback64 {
246         struct dir_context ctx;
247         struct linux_dirent64 __user * current_dir;
248         struct linux_dirent64 __user * previous;
249         int count;
250         int error;
251 };
252
253 static int filldir64(struct dir_context *ctx, const char *name, int namlen,
254                      loff_t offset, u64 ino, unsigned int d_type)
255 {
256         struct linux_dirent64 __user *dirent;
257         struct getdents_callback64 *buf =
258                 container_of(ctx, struct getdents_callback64, ctx);
259         int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
260                 sizeof(u64));
261
262         buf->error = -EINVAL;   /* only used if we fail.. */
263         if (reclen > buf->count)
264                 return -EINVAL;
265         dirent = buf->previous;
266         if (dirent) {
267                 if (signal_pending(current))
268                         return -EINTR;
269                 if (__put_user(offset, &dirent->d_off))
270                         goto efault;
271         }
272         dirent = buf->current_dir;
273         if (__put_user(ino, &dirent->d_ino))
274                 goto efault;
275         if (__put_user(0, &dirent->d_off))
276                 goto efault;
277         if (__put_user(reclen, &dirent->d_reclen))
278                 goto efault;
279         if (__put_user(d_type, &dirent->d_type))
280                 goto efault;
281         if (copy_to_user(dirent->d_name, name, namlen))
282                 goto efault;
283         if (__put_user(0, dirent->d_name + namlen))
284                 goto efault;
285         buf->previous = dirent;
286         dirent = (void __user *)dirent + reclen;
287         buf->current_dir = dirent;
288         buf->count -= reclen;
289         return 0;
290 efault:
291         buf->error = -EFAULT;
292         return -EFAULT;
293 }
294
295 SYSCALL_DEFINE3(getdents64, unsigned int, fd,
296                 struct linux_dirent64 __user *, dirent, unsigned int, count)
297 {
298         struct fd f;
299         struct linux_dirent64 __user * lastdirent;
300         struct getdents_callback64 buf = {
301                 .ctx.actor = filldir64,
302                 .count = count,
303                 .current_dir = dirent
304         };
305         int error;
306
307         if (!access_ok(VERIFY_WRITE, dirent, count))
308                 return -EFAULT;
309
310         f = fdget_pos(fd);
311         if (!f.file)
312                 return -EBADF;
313
314         error = iterate_dir(f.file, &buf.ctx);
315         if (error >= 0)
316                 error = buf.error;
317         lastdirent = buf.previous;
318         if (lastdirent) {
319                 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
320                 if (__put_user(d_off, &lastdirent->d_off))
321                         error = -EFAULT;
322                 else
323                         error = count - buf.count;
324         }
325         fdput_pos(f);
326         return error;
327 }
328
329 #ifdef CONFIG_COMPAT
330 struct compat_old_linux_dirent {
331         compat_ulong_t  d_ino;
332         compat_ulong_t  d_offset;
333         unsigned short  d_namlen;
334         char            d_name[1];
335 };
336
337 struct compat_readdir_callback {
338         struct dir_context ctx;
339         struct compat_old_linux_dirent __user *dirent;
340         int result;
341 };
342
343 static int compat_fillonedir(struct dir_context *ctx, const char *name,
344                              int namlen, loff_t offset, u64 ino,
345                              unsigned int d_type)
346 {
347         struct compat_readdir_callback *buf =
348                 container_of(ctx, struct compat_readdir_callback, ctx);
349         struct compat_old_linux_dirent __user *dirent;
350         compat_ulong_t d_ino;
351
352         if (buf->result)
353                 return -EINVAL;
354         d_ino = ino;
355         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
356                 buf->result = -EOVERFLOW;
357                 return -EOVERFLOW;
358         }
359         buf->result++;
360         dirent = buf->dirent;
361         if (!access_ok(VERIFY_WRITE, dirent,
362                         (unsigned long)(dirent->d_name + namlen + 1) -
363                                 (unsigned long)dirent))
364                 goto efault;
365         if (    __put_user(d_ino, &dirent->d_ino) ||
366                 __put_user(offset, &dirent->d_offset) ||
367                 __put_user(namlen, &dirent->d_namlen) ||
368                 __copy_to_user(dirent->d_name, name, namlen) ||
369                 __put_user(0, dirent->d_name + namlen))
370                 goto efault;
371         return 0;
372 efault:
373         buf->result = -EFAULT;
374         return -EFAULT;
375 }
376
377 COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
378                 struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
379 {
380         int error;
381         struct fd f = fdget_pos(fd);
382         struct compat_readdir_callback buf = {
383                 .ctx.actor = compat_fillonedir,
384                 .dirent = dirent
385         };
386
387         if (!f.file)
388                 return -EBADF;
389
390         error = iterate_dir(f.file, &buf.ctx);
391         if (buf.result)
392                 error = buf.result;
393
394         fdput_pos(f);
395         return error;
396 }
397
398 struct compat_linux_dirent {
399         compat_ulong_t  d_ino;
400         compat_ulong_t  d_off;
401         unsigned short  d_reclen;
402         char            d_name[1];
403 };
404
405 struct compat_getdents_callback {
406         struct dir_context ctx;
407         struct compat_linux_dirent __user *current_dir;
408         struct compat_linux_dirent __user *previous;
409         int count;
410         int error;
411 };
412
413 static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
414                 loff_t offset, u64 ino, unsigned int d_type)
415 {
416         struct compat_linux_dirent __user * dirent;
417         struct compat_getdents_callback *buf =
418                 container_of(ctx, struct compat_getdents_callback, ctx);
419         compat_ulong_t d_ino;
420         int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
421                 namlen + 2, sizeof(compat_long_t));
422
423         buf->error = -EINVAL;   /* only used if we fail.. */
424         if (reclen > buf->count)
425                 return -EINVAL;
426         d_ino = ino;
427         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
428                 buf->error = -EOVERFLOW;
429                 return -EOVERFLOW;
430         }
431         dirent = buf->previous;
432         if (dirent) {
433                 if (signal_pending(current))
434                         return -EINTR;
435                 if (__put_user(offset, &dirent->d_off))
436                         goto efault;
437         }
438         dirent = buf->current_dir;
439         if (__put_user(d_ino, &dirent->d_ino))
440                 goto efault;
441         if (__put_user(reclen, &dirent->d_reclen))
442                 goto efault;
443         if (copy_to_user(dirent->d_name, name, namlen))
444                 goto efault;
445         if (__put_user(0, dirent->d_name + namlen))
446                 goto efault;
447         if (__put_user(d_type, (char  __user *) dirent + reclen - 1))
448                 goto efault;
449         buf->previous = dirent;
450         dirent = (void __user *)dirent + reclen;
451         buf->current_dir = dirent;
452         buf->count -= reclen;
453         return 0;
454 efault:
455         buf->error = -EFAULT;
456         return -EFAULT;
457 }
458
459 COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
460                 struct compat_linux_dirent __user *, dirent, unsigned int, count)
461 {
462         struct fd f;
463         struct compat_linux_dirent __user * lastdirent;
464         struct compat_getdents_callback buf = {
465                 .ctx.actor = compat_filldir,
466                 .current_dir = dirent,
467                 .count = count
468         };
469         int error;
470
471         if (!access_ok(VERIFY_WRITE, dirent, count))
472                 return -EFAULT;
473
474         f = fdget_pos(fd);
475         if (!f.file)
476                 return -EBADF;
477
478         error = iterate_dir(f.file, &buf.ctx);
479         if (error >= 0)
480                 error = buf.error;
481         lastdirent = buf.previous;
482         if (lastdirent) {
483                 if (put_user(buf.ctx.pos, &lastdirent->d_off))
484                         error = -EFAULT;
485                 else
486                         error = count - buf.count;
487         }
488         fdput_pos(f);
489         return error;
490 }
491 #endif