about summary refs log tree commit diff
path: root/REORG.TODO/sysdeps/unix/sysv/linux/generic/wordsize-32/getdents.c
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/sysdeps/unix/sysv/linux/generic/wordsize-32/getdents.c')
-rw-r--r--REORG.TODO/sysdeps/unix/sysv/linux/generic/wordsize-32/getdents.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/REORG.TODO/sysdeps/unix/sysv/linux/generic/wordsize-32/getdents.c b/REORG.TODO/sysdeps/unix/sysv/linux/generic/wordsize-32/getdents.c
new file mode 100644
index 0000000000..9f1c991694
--- /dev/null
+++ b/REORG.TODO/sysdeps/unix/sysv/linux/generic/wordsize-32/getdents.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 1993-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Simplified from sysdeps/unix/sysv/linux/getdents.c.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <alloca.h>
+#include <assert.h>
+#include <errno.h>
+#include <dirent.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sysdep.h>
+#include <sys/syscall.h>
+
+/* Pack the dirent64 struct down into 32-bit offset/inode fields, and
+   ensure that no overflow occurs.  */
+ssize_t
+__getdents (int fd, char *buf, size_t nbytes)
+{
+  union
+  {
+    struct dirent64 k;  /* Kernel structure.  */
+    struct dirent u;
+    char b[1];
+  } *kbuf = (void *) buf, *outp, *inp;
+  size_t kbytes = nbytes;
+  off64_t last_offset = -1;
+  ssize_t retval;
+
+  const size_t size_diff = (offsetof (struct dirent64, d_name)
+                            - offsetof (struct dirent, d_name));
+  if (nbytes <= sizeof (struct dirent))
+    {
+      kbytes = nbytes + offsetof (struct dirent64, d_name)
+        - offsetof (struct dirent, d_name);
+      kbuf = __alloca(kbytes);
+    }
+
+  retval = INLINE_SYSCALL (getdents64, 3, fd, kbuf, kbytes);
+  if (retval == -1)
+    return -1;
+
+  /* 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__ (struct dirent);
+      /* 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 dirent64, d_name));
+
+      /* Now we have copied the data from INP and access only OUTP.  */
+
+      outp->u.d_ino = 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;
+    }
+
+  return outp->b - buf;
+}