diff options
Diffstat (limited to 'sysdeps/unix/sysv/linux')
-rw-r--r-- | sysdeps/unix/sysv/linux/getdents.c | 191 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/kernel-features.h | 4 |
2 files changed, 81 insertions, 114 deletions
diff --git a/sysdeps/unix/sysv/linux/getdents.c b/sysdeps/unix/sysv/linux/getdents.c index 1f2404e142..d438547be9 100644 --- a/sysdeps/unix/sysv/linux/getdents.c +++ b/sysdeps/unix/sysv/linux/getdents.c @@ -33,21 +33,6 @@ #include <kernel-features.h> -#ifdef __NR_getdents64 -# ifndef __ASSUME_GETDENTS64_SYSCALL -# ifndef __GETDENTS -/* The variable is shared between all *getdents* calls. */ -int __have_no_getdents64 attribute_hidden; -# else -extern int __have_no_getdents64 attribute_hidden; -# endif -# define have_no_getdents64_defined 1 -# endif -#endif -#ifndef have_no_getdents64_defined -# define __have_no_getdents64 0 -#endif - /* For Linux we need a special version of this file since the definition of `struct dirent' is not the same for the kernel and the libc. There is one additional field which might be introduced @@ -137,108 +122,94 @@ __GETDENTS (int fd, char *buf, size_t nbytes) off64_t last_offset = -1; #ifdef __NR_getdents64 - if (!__have_no_getdents64) + { + union { -# ifndef __ASSUME_GETDENTS64_SYSCALL - int saved_errno = errno; -# endif - union + struct kernel_dirent64 k; + DIRENT_TYPE u; + char b[1]; + } *kbuf = (void *) buf, *outp, *inp; + size_t kbytes = nbytes; + if (offsetof (DIRENT_TYPE, d_name) + < offsetof (struct kernel_dirent64, d_name) + && nbytes <= sizeof (DIRENT_TYPE)) { - struct kernel_dirent64 k; - DIRENT_TYPE u; - char b[1]; - } *kbuf = (void *) buf, *outp, *inp; - size_t kbytes = nbytes; - if (offsetof (DIRENT_TYPE, d_name) - < offsetof (struct kernel_dirent64, d_name) - && nbytes <= sizeof (DIRENT_TYPE)) - { - kbytes = nbytes + offsetof (struct kernel_dirent64, d_name) - - offsetof (DIRENT_TYPE, d_name); - kbuf = __alloca(kbytes); - } - retval = INLINE_SYSCALL (getdents64, 3, fd, kbuf, kbytes); -# ifndef __ASSUME_GETDENTS64_SYSCALL - if (retval != -1 || (errno != EINVAL && errno != ENOSYS)) -# endif - { - const size_t size_diff = (offsetof (struct kernel_dirent64, d_name) - - offsetof (DIRENT_TYPE, d_name)); + kbytes = (nbytes + offsetof (struct kernel_dirent64, d_name) + - offsetof (DIRENT_TYPE, d_name)); + kbuf = __alloca(kbytes); + } + retval = INLINE_SYSCALL (getdents64, 3, fd, kbuf, kbytes); + const size_t size_diff = (offsetof (struct kernel_dirent64, d_name) + - offsetof (DIRENT_TYPE, d_name)); + + /* Return the error if encountered. */ + if (retval == -1) + return -1; + + /* If the structure returned by the kernel is identical to what we + need, don't do any conversions. */ + if (offsetof (DIRENT_TYPE, d_name) + == offsetof (struct kernel_dirent64, d_name) + && sizeof (outp->u.d_ino) == sizeof (inp->k.d_ino) + && sizeof (outp->u.d_off) == sizeof (inp->k.d_off)) + return retval; + + /* These two pointers might alias the same memory buffer. + Standard C requires that we always use the same type for them, + so we must use the union type. */ + inp = kbuf; + outp = (void *) buf; + + while (&inp->b < &kbuf->b + retval) + { + const size_t alignment = __alignof__ (DIRENT_TYPE); + /* Since inp->k.d_reclen is already aligned for the kernel + structure this may compute a value that is bigger + than necessary. */ + size_t old_reclen = inp->k.d_reclen; + size_t new_reclen = ((old_reclen - size_diff + alignment - 1) + & ~(alignment - 1)); + + /* Copy the data out of the old structure into temporary space. + Then copy the name, which may overlap if BUF == KBUF. */ + const uint64_t d_ino = inp->k.d_ino; + const int64_t d_off = inp->k.d_off; + const uint8_t d_type = inp->k.d_type; - /* Return the error if encountered. */ - if (retval == -1) + memmove (outp->u.d_name, inp->k.d_name, + old_reclen - offsetof (struct kernel_dirent64, d_name)); + + /* Now we have copied the data from INP and access only OUTP. */ + + DIRENT_SET_DP_INO (&outp->u, d_ino); + outp->u.d_off = d_off; + if ((sizeof (outp->u.d_ino) != sizeof (inp->k.d_ino) + && outp->u.d_ino != d_ino) + || (sizeof (outp->u.d_off) != sizeof (inp->k.d_off) + && outp->u.d_off != d_off)) + { + /* Overflow. If there was at least one entry + before this one, return them without error, + otherwise signal overflow. */ + if (last_offset != -1) + { + __lseek64 (fd, last_offset, SEEK_SET); + return outp->b - buf; + } + __set_errno (EOVERFLOW); return -1; + } - /* If the structure returned by the kernel is identical to what we - need, don't do any conversions. */ - if (offsetof (DIRENT_TYPE, d_name) - == offsetof (struct kernel_dirent64, d_name) - && sizeof (outp->u.d_ino) == sizeof (inp->k.d_ino) - && sizeof (outp->u.d_off) == sizeof (inp->k.d_off)) - return retval; - - /* These two pointers might alias the same memory buffer. - Standard C requires that we always use the same type for them, - so we must use the union type. */ - inp = kbuf; - outp = (void *) buf; - - while (&inp->b < &kbuf->b + retval) - { - const size_t alignment = __alignof__ (DIRENT_TYPE); - /* Since inp->k.d_reclen is already aligned for the kernel - structure this may compute a value that is bigger - than necessary. */ - size_t old_reclen = inp->k.d_reclen; - size_t new_reclen = ((old_reclen - size_diff + alignment - 1) - & ~(alignment - 1)); - - /* Copy the data out of the old structure into temporary space. - Then copy the name, which may overlap if BUF == KBUF. */ - const uint64_t d_ino = inp->k.d_ino; - const int64_t d_off = inp->k.d_off; - const uint8_t d_type = inp->k.d_type; - - memmove (outp->u.d_name, inp->k.d_name, - old_reclen - offsetof (struct kernel_dirent64, d_name)); - - /* Now we have copied the data from INP and access only OUTP. */ - - DIRENT_SET_DP_INO (&outp->u, d_ino); - outp->u.d_off = d_off; - if ((sizeof (outp->u.d_ino) != sizeof (inp->k.d_ino) - && outp->u.d_ino != d_ino) - || (sizeof (outp->u.d_off) != sizeof (inp->k.d_off) - && outp->u.d_off != d_off)) - { - /* Overflow. If there was at least one entry - before this one, return them without error, - otherwise signal overflow. */ - if (last_offset != -1) - { - __lseek64 (fd, last_offset, SEEK_SET); - return outp->b - buf; - } - __set_errno (EOVERFLOW); - return -1; - } - - last_offset = d_off; - outp->u.d_reclen = new_reclen; - outp->u.d_type = d_type; - - inp = (void *) inp + old_reclen; - outp = (void *) outp + new_reclen; - } + last_offset = d_off; + outp->u.d_reclen = new_reclen; + outp->u.d_type = d_type; - return outp->b - buf; - } + inp = (void *) inp + old_reclen; + outp = (void *) outp + new_reclen; + } -# ifndef __ASSUME_GETDENTS64_SYSCALL - __set_errno (saved_errno); - __have_no_getdents64 = 1; -# endif - } + return outp->b - buf; + } #endif { size_t red_nbytes; diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h index 28195e5629..5a1b204033 100644 --- a/sysdeps/unix/sysv/linux/kernel-features.h +++ b/sysdeps/unix/sysv/linux/kernel-features.h @@ -48,10 +48,6 @@ and still does not have a 64-bit inode field. */ #define __ASSUME_ST_INO_64_BIT 1 -/* The getdents64 syscall was introduced in 2.4.0-test7 (but later for - MIPS n32). */ -#define __ASSUME_GETDENTS64_SYSCALL 1 - /* The statfs64 syscalls are available in 2.5.74 (but not for alpha). */ #define __ASSUME_STATFS64 1 |