1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 1995 Linus Torvalds
8 #include <linux/stddef.h>
9 #include <linux/kernel.h>
10 #include <linux/export.h>
11 #include <linux/time.h>
13 #include <linux/errno.h>
14 #include <linux/stat.h>
15 #include <linux/file.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>
24 #include <linux/uaccess.h>
26 int iterate_dir(struct file
*file
, struct dir_context
*ctx
)
28 struct inode
*inode
= file_inode(file
);
31 if (file
->f_op
->iterate_shared
)
33 else if (!file
->f_op
->iterate
)
36 res
= security_file_permission(file
, MAY_READ
);
41 inode_lock_shared(inode
);
43 res
= down_write_killable(&inode
->i_rwsem
);
49 if (!IS_DEADDIR(inode
)) {
50 ctx
->pos
= file
->f_pos
;
52 res
= file
->f_op
->iterate_shared(file
, ctx
);
54 res
= file
->f_op
->iterate(file
, ctx
);
55 file
->f_pos
= ctx
->pos
;
56 fsnotify_access(file
);
60 inode_unlock_shared(inode
);
66 EXPORT_SYMBOL(iterate_dir
);
69 * Traditional linux readdir() handling..
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).
77 #ifdef __ARCH_WANT_OLD_READDIR
79 struct old_linux_dirent
{
81 unsigned long d_offset
;
82 unsigned short d_namlen
;
86 struct readdir_callback
{
87 struct dir_context ctx
;
88 struct old_linux_dirent __user
* dirent
;
92 static int fillonedir(struct dir_context
*ctx
, const char *name
, int namlen
,
93 loff_t offset
, u64 ino
, unsigned int d_type
)
95 struct readdir_callback
*buf
=
96 container_of(ctx
, struct readdir_callback
, ctx
);
97 struct old_linux_dirent __user
* dirent
;
103 if (sizeof(d_ino
) < sizeof(ino
) && d_ino
!= ino
) {
104 buf
->result
= -EOVERFLOW
;
108 dirent
= buf
->dirent
;
109 if (!access_ok(VERIFY_WRITE
, dirent
,
110 (unsigned long)(dirent
->d_name
+ namlen
+ 1) -
111 (unsigned long)dirent
))
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
))
121 buf
->result
= -EFAULT
;
125 SYSCALL_DEFINE3(old_readdir
, unsigned int, fd
,
126 struct old_linux_dirent __user
*, dirent
, unsigned int, count
)
129 struct fd f
= fdget_pos(fd
);
130 struct readdir_callback buf
= {
131 .ctx
.actor
= fillonedir
,
138 error
= iterate_dir(f
.file
, &buf
.ctx
);
146 #endif /* __ARCH_WANT_OLD_READDIR */
149 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
152 struct linux_dirent
{
155 unsigned short d_reclen
;
159 struct getdents_callback
{
160 struct dir_context ctx
;
161 struct linux_dirent __user
* current_dir
;
162 struct linux_dirent __user
* previous
;
167 static int filldir(struct dir_context
*ctx
, const char *name
, int namlen
,
168 loff_t offset
, u64 ino
, unsigned int d_type
)
170 struct linux_dirent __user
* dirent
;
171 struct getdents_callback
*buf
=
172 container_of(ctx
, struct getdents_callback
, ctx
);
174 int reclen
= ALIGN(offsetof(struct linux_dirent
, d_name
) + namlen
+ 2,
177 buf
->error
= -EINVAL
; /* only used if we fail.. */
178 if (reclen
> buf
->count
)
181 if (sizeof(d_ino
) < sizeof(ino
) && d_ino
!= ino
) {
182 buf
->error
= -EOVERFLOW
;
185 dirent
= buf
->previous
;
187 if (signal_pending(current
))
189 if (__put_user(offset
, &dirent
->d_off
))
192 dirent
= buf
->current_dir
;
193 if (__put_user(d_ino
, &dirent
->d_ino
))
195 if (__put_user(reclen
, &dirent
->d_reclen
))
197 if (copy_to_user(dirent
->d_name
, name
, namlen
))
199 if (__put_user(0, dirent
->d_name
+ namlen
))
201 if (__put_user(d_type
, (char __user
*) dirent
+ reclen
- 1))
203 buf
->previous
= dirent
;
204 dirent
= (void __user
*)dirent
+ reclen
;
205 buf
->current_dir
= dirent
;
206 buf
->count
-= reclen
;
209 buf
->error
= -EFAULT
;
213 SYSCALL_DEFINE3(getdents
, unsigned int, fd
,
214 struct linux_dirent __user
*, dirent
, unsigned int, count
)
217 struct linux_dirent __user
* lastdirent
;
218 struct getdents_callback buf
= {
219 .ctx
.actor
= filldir
,
221 .current_dir
= dirent
225 if (!access_ok(VERIFY_WRITE
, dirent
, count
))
232 error
= iterate_dir(f
.file
, &buf
.ctx
);
235 lastdirent
= buf
.previous
;
237 if (put_user(buf
.ctx
.pos
, &lastdirent
->d_off
))
240 error
= count
- buf
.count
;
246 struct getdents_callback64
{
247 struct dir_context ctx
;
248 struct linux_dirent64 __user
* current_dir
;
249 struct linux_dirent64 __user
* previous
;
254 static int filldir64(struct dir_context
*ctx
, const char *name
, int namlen
,
255 loff_t offset
, u64 ino
, unsigned int d_type
)
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,
263 buf
->error
= -EINVAL
; /* only used if we fail.. */
264 if (reclen
> buf
->count
)
266 dirent
= buf
->previous
;
268 if (signal_pending(current
))
270 if (__put_user(offset
, &dirent
->d_off
))
273 dirent
= buf
->current_dir
;
274 if (__put_user(ino
, &dirent
->d_ino
))
276 if (__put_user(0, &dirent
->d_off
))
278 if (__put_user(reclen
, &dirent
->d_reclen
))
280 if (__put_user(d_type
, &dirent
->d_type
))
282 if (copy_to_user(dirent
->d_name
, name
, namlen
))
284 if (__put_user(0, dirent
->d_name
+ namlen
))
286 buf
->previous
= dirent
;
287 dirent
= (void __user
*)dirent
+ reclen
;
288 buf
->current_dir
= dirent
;
289 buf
->count
-= reclen
;
292 buf
->error
= -EFAULT
;
296 SYSCALL_DEFINE3(getdents64
, unsigned int, fd
,
297 struct linux_dirent64 __user
*, dirent
, unsigned int, count
)
300 struct linux_dirent64 __user
* lastdirent
;
301 struct getdents_callback64 buf
= {
302 .ctx
.actor
= filldir64
,
304 .current_dir
= dirent
308 if (!access_ok(VERIFY_WRITE
, dirent
, count
))
315 error
= iterate_dir(f
.file
, &buf
.ctx
);
318 lastdirent
= buf
.previous
;
320 typeof(lastdirent
->d_off
) d_off
= buf
.ctx
.pos
;
321 if (__put_user(d_off
, &lastdirent
->d_off
))
324 error
= count
- buf
.count
;
331 struct compat_old_linux_dirent
{
332 compat_ulong_t d_ino
;
333 compat_ulong_t d_offset
;
334 unsigned short d_namlen
;
338 struct compat_readdir_callback
{
339 struct dir_context ctx
;
340 struct compat_old_linux_dirent __user
*dirent
;
344 static int compat_fillonedir(struct dir_context
*ctx
, const char *name
,
345 int namlen
, loff_t offset
, u64 ino
,
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
;
356 if (sizeof(d_ino
) < sizeof(ino
) && d_ino
!= ino
) {
357 buf
->result
= -EOVERFLOW
;
361 dirent
= buf
->dirent
;
362 if (!access_ok(VERIFY_WRITE
, dirent
,
363 (unsigned long)(dirent
->d_name
+ namlen
+ 1) -
364 (unsigned long)dirent
))
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
))
374 buf
->result
= -EFAULT
;
378 COMPAT_SYSCALL_DEFINE3(old_readdir
, unsigned int, fd
,
379 struct compat_old_linux_dirent __user
*, dirent
, unsigned int, count
)
382 struct fd f
= fdget_pos(fd
);
383 struct compat_readdir_callback buf
= {
384 .ctx
.actor
= compat_fillonedir
,
391 error
= iterate_dir(f
.file
, &buf
.ctx
);
399 struct compat_linux_dirent
{
400 compat_ulong_t d_ino
;
401 compat_ulong_t d_off
;
402 unsigned short d_reclen
;
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
;
414 static int compat_filldir(struct dir_context
*ctx
, const char *name
, int namlen
,
415 loff_t offset
, u64 ino
, unsigned int d_type
)
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
));
424 buf
->error
= -EINVAL
; /* only used if we fail.. */
425 if (reclen
> buf
->count
)
428 if (sizeof(d_ino
) < sizeof(ino
) && d_ino
!= ino
) {
429 buf
->error
= -EOVERFLOW
;
432 dirent
= buf
->previous
;
434 if (signal_pending(current
))
436 if (__put_user(offset
, &dirent
->d_off
))
439 dirent
= buf
->current_dir
;
440 if (__put_user(d_ino
, &dirent
->d_ino
))
442 if (__put_user(reclen
, &dirent
->d_reclen
))
444 if (copy_to_user(dirent
->d_name
, name
, namlen
))
446 if (__put_user(0, dirent
->d_name
+ namlen
))
448 if (__put_user(d_type
, (char __user
*) dirent
+ reclen
- 1))
450 buf
->previous
= dirent
;
451 dirent
= (void __user
*)dirent
+ reclen
;
452 buf
->current_dir
= dirent
;
453 buf
->count
-= reclen
;
456 buf
->error
= -EFAULT
;
460 COMPAT_SYSCALL_DEFINE3(getdents
, unsigned int, fd
,
461 struct compat_linux_dirent __user
*, dirent
, unsigned int, count
)
464 struct compat_linux_dirent __user
* lastdirent
;
465 struct compat_getdents_callback buf
= {
466 .ctx
.actor
= compat_filldir
,
467 .current_dir
= dirent
,
472 if (!access_ok(VERIFY_WRITE
, dirent
, count
))
479 error
= iterate_dir(f
.file
, &buf
.ctx
);
482 lastdirent
= buf
.previous
;
484 if (put_user(buf
.ctx
.pos
, &lastdirent
->d_off
))
487 error
= count
- buf
.count
;