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