Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | /* * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org> * * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. */ #include <sys/syscall.h> #include <bits/wordsize.h> #if !(defined __NR_getdents64 && __WORDSIZE == 64) #include <dirent.h> #include <string.h> #include <sys/types.h> #include <bits/kernel_types.h> #include <bits/kernel-features.h> /* If the condition above is met, __getdents is defined as an alias * for __getdents64 (see getdents64.c). Otherwise... */ /* With newer versions of linux, the getdents syscall returns d_type * information after the name field. * * See __ASSUME_GETDENTS32_D_TYPE in glibc's kernel-features.h for specific * version / arch details. */ # ifdef __ARCH_HAS_DEPRECATED_SYSCALLS__ struct kernel_dirent { long int d_ino; __kernel_off_t d_off; unsigned short int d_reclen; char d_name[256]; }; # else # define kernel_dirent dirent # endif # if defined __NR_getdents # define __NR___syscall_getdents __NR_getdents static __always_inline _syscall3(int, __syscall_getdents, int, fd, unsigned char *, kdirp, size_t, count) # endif # if defined __ASSUME_GETDENTS32_D_TYPE && defined __NR_getdents ssize_t __getdents (int fd, char *buf, size_t nbytes) { ssize_t retval; retval = __syscall_getdents(fd, (unsigned char *)buf, nbytes); /* The kernel added the d_type value after the name. Change this now. */ if (retval != -1) { union { struct kernel_dirent k; struct dirent u; } *kbuf = (void *) buf; while ((char *) kbuf < buf + retval) { char d_type = *((char *) kbuf + kbuf->k.d_reclen - 1); memmove (kbuf->u.d_name, kbuf->k.d_name, strlen (kbuf->k.d_name) + 1); kbuf->u.d_type = d_type; kbuf = (void *) ((char *) kbuf + kbuf->k.d_reclen); } } return retval; } # elif !defined __NR_getdents64 # include <assert.h> # include <stddef.h> # include <errno.h> # include <unistd.h> # include <sys/param.h> # include <bits/uClibc_alloc.h> ssize_t __getdents (int fd, char *buf, size_t nbytes) { struct dirent *dp; off_t last_offset = -1; ssize_t retval; size_t red_nbytes; struct kernel_dirent *skdp, *kdp; const size_t size_diff = (offsetof (struct dirent, d_name) - offsetof (struct kernel_dirent, d_name)); # ifndef __ARCH_HAS_DEPRECATED_SYSCALLS__ red_nbytes = MIN (nbytes - ((nbytes / (offsetof (struct dirent, d_name) + 14)) * size_diff), nbytes - size_diff); dp = (struct dirent *) buf; skdp = kdp = stack_heap_alloc(red_nbytes); retval = __syscall_getdents(fd, (unsigned char *)kdp, red_nbytes); # else dp = (struct dirent *) buf; skdp = kdp = stack_heap_alloc(nbytes); retval = INLINE_SYSCALL(getdents64, 3, fd, (unsigned char *)kdp, nbytes); if (retval > 0) { /* Did we overflow? */ if (kdp->__pad1 || kdp->__pad2) { __set_errno(EINVAL); return -1; } } # endif if (retval == -1) { stack_heap_free(skdp); return -1; } while ((char *) kdp < (char *) skdp + retval) { const size_t alignment = __alignof__ (struct dirent); /* Since kdp->d_reclen is already aligned for the kernel structure this may compute a value that is bigger than necessary. */ size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1) & ~(alignment - 1)); if ((char *) dp + new_reclen > buf + nbytes) { /* Our heuristic failed. We read too many entries. Reset the stream. */ assert (last_offset != -1); lseek(fd, last_offset, SEEK_SET); if ((char *) dp == buf) { /* The buffer the user passed in is too small to hold even one entry. */ stack_heap_free(skdp); __set_errno (EINVAL); return -1; } break; } last_offset = kdp->d_off; dp->d_ino = kdp->d_ino; dp->d_off = kdp->d_off; dp->d_reclen = new_reclen; dp->d_type = DT_UNKNOWN; memcpy (dp->d_name, kdp->d_name, kdp->d_reclen - offsetof (struct kernel_dirent, d_name)); dp = (struct dirent *) ((char *) dp + new_reclen); kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen); } stack_heap_free(skdp); return (char *) dp - buf; } # elif __WORDSIZE == 32 && !defined __NR_getdents64 # include <stddef.h> ssize_t __getdents (int fd, char *buf, size_t nbytes) { struct dirent *dp; struct dirent64 *dp64; ssize_t ret = __getdents64 (fd, buf, nbytes); if (ret <= 0) return ret; dp64 = (struct dirent64 *) buf; buf += ret; while ((void *) dp64 < (void *) buf) { dp = (struct dirent *) dp64; dp->d_ino = dp64->d_ino; dp->d_off = dp64->d_off; dp->d_reclen = dp64->d_reclen; dp->d_type = dp64->d_type; memmove (dp->d_name, dp64->d_name, dp->d_reclen - offsetof (struct dirent64, d_name)); memmove (dp64, dp, dp->d_reclen); dp64 = ((void *) dp64) + dp->d_reclen; } return ret; } # endif # if ! defined __NR_getdents64 strong_alias(__getdents,__getdents64) # endif #endif |