]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/uclibc/lib/contrib/uclibc/libc/sysdeps/linux/common/getdents64.c
37cb4c6f8bf0f55f3dad6248699e4102b5d9cf7e
[l4.git] / l4 / pkg / uclibc / lib / contrib / uclibc / libc / sysdeps / linux / common / getdents64.c
1 /*
2  * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
3  *
4  * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
5  */
6
7 #include <features.h>
8 #include <alloca.h>
9 #include <assert.h>
10 #include <errno.h>
11 #include <dirent.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <sys/param.h>
17 #include <sys/types.h>
18 #include <sys/syscall.h>
19 #include <bits/kernel_types.h>
20
21 #if defined __UCLIBC_HAS_LFS__ && defined __NR_getdents64
22
23 # ifndef offsetof
24 #  define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
25 # endif
26
27 struct kernel_dirent64
28 {
29     uint64_t            d_ino;
30     int64_t             d_off;
31     unsigned short      d_reclen;
32     unsigned char       d_type;
33     char                d_name[256];
34 };
35
36 # define __NR___syscall_getdents64 __NR_getdents64
37 static __inline__ _syscall3(int, __syscall_getdents64, int, fd, unsigned char *, dirp, size_t, count)
38
39 ssize_t __getdents64 (int fd, char *buf, size_t nbytes) attribute_hidden;
40 ssize_t __getdents64 (int fd, char *buf, size_t nbytes)
41 {
42     struct dirent64 *dp;
43     off64_t last_offset = -1;
44     ssize_t retval;
45     size_t red_nbytes;
46     struct kernel_dirent64 *skdp, *kdp;
47     const size_t size_diff = (offsetof (struct dirent64, d_name)
48             - offsetof (struct kernel_dirent64, d_name));
49
50     red_nbytes = MIN (nbytes - ((nbytes /
51                     (offsetof (struct dirent64, d_name) + 14)) * size_diff),
52             nbytes - size_diff);
53
54     dp = (struct dirent64 *) buf;
55     skdp = kdp = alloca (red_nbytes);
56
57     retval = __syscall_getdents64(fd, (unsigned char *)kdp, red_nbytes);
58     if (retval == -1)
59         return -1;
60
61     while ((char *) kdp < (char *) skdp + retval) {
62         const size_t alignment = __alignof__ (struct dirent64);
63         /* Since kdp->d_reclen is already aligned for the kernel structure
64            this may compute a value that is bigger than necessary.  */
65         size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1)
66                 & ~(alignment - 1));
67         if ((char *) dp + new_reclen > buf + nbytes) {
68             /* Our heuristic failed.  We read too many entries.  Reset
69                the stream.  */
70             assert (last_offset != -1);
71             lseek64(fd, last_offset, SEEK_SET);
72
73             if ((char *) dp == buf) {
74                 /* The buffer the user passed in is too small to hold even
75                    one entry.  */
76                 __set_errno (EINVAL);
77                 return -1;
78             }
79             break;
80         }
81
82         last_offset = kdp->d_off;
83         dp->d_ino = kdp->d_ino;
84         dp->d_off = kdp->d_off;
85         dp->d_reclen = new_reclen;
86         dp->d_type = kdp->d_type;
87         memcpy (dp->d_name, kdp->d_name,
88                 kdp->d_reclen - offsetof (struct kernel_dirent64, d_name));
89         dp = (struct dirent64 *) ((char *) dp + new_reclen);
90         kdp = (struct kernel_dirent64 *) (((char *) kdp) + kdp->d_reclen);
91     }
92     return (char *) dp - buf;
93 }
94
95 #if __WORDSIZE == 64
96 /* since getdents doesnt give us d_type but getdents64 does, try and
97  * use getdents64 as much as possible */
98 attribute_hidden strong_alias(__getdents64,__getdents)
99 #endif
100
101 #endif