(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
tst-getdents64.c
       1  /* Test for reading directories with getdents64.
       2     Copyright (C) 2019-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <dirent.h>
      20  #include <errno.h>
      21  #include <fcntl.h>
      22  #include <limits.h>
      23  #include <stdbool.h>
      24  #include <stdio.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <support/check.h>
      28  #include <support/support.h>
      29  #include <support/xunistd.h>
      30  #include <sys/mman.h>
      31  #include <unistd.h>
      32  
      33  /* Called by large_buffer_checks below.  */
      34  static void
      35  large_buffer_check (int fd, char *large_buffer, size_t large_buffer_size)
      36  {
      37    xlseek (fd, 0, SEEK_SET);
      38    ssize_t ret = getdents64 (fd, large_buffer, large_buffer_size);
      39    if (ret < 0)
      40      FAIL_EXIT1 ("getdents64 for buffer of %zu bytes failed: %m",
      41                  large_buffer_size);
      42    if (ret < offsetof (struct dirent64, d_name))
      43      FAIL_EXIT1 ("getdents64 for buffer of %zu returned small value %zd",
      44                  large_buffer_size, ret);
      45  }
      46  
      47  /* Bug 24740: Make sure that the system call argument is adjusted
      48     properly for the int type.  A large value should stay a large
      49     value, and not wrap around to something small, causing the system
      50     call to fail with EINVAL.  */
      51  static void
      52  large_buffer_checks (int fd)
      53  {
      54    size_t large_buffer_size;
      55    if (!__builtin_add_overflow (UINT_MAX, 2, &large_buffer_size))
      56      {
      57        int flags = MAP_ANONYMOUS | MAP_PRIVATE;
      58  #ifdef MAP_NORESERVE
      59        flags |= MAP_NORESERVE;
      60  #endif
      61        void *large_buffer = mmap (NULL, large_buffer_size,
      62                                   PROT_READ | PROT_WRITE, flags, -1, 0);
      63        if (large_buffer == MAP_FAILED)
      64          printf ("warning: could not allocate %zu bytes of memory,"
      65                  " subtests skipped\n", large_buffer_size);
      66        else
      67          {
      68            large_buffer_check (fd, large_buffer, INT_MAX);
      69            large_buffer_check (fd, large_buffer, (size_t) INT_MAX + 1);
      70            large_buffer_check (fd, large_buffer, (size_t) INT_MAX + 2);
      71            large_buffer_check (fd, large_buffer, UINT_MAX);
      72            large_buffer_check (fd, large_buffer, (size_t) UINT_MAX + 1);
      73            large_buffer_check (fd, large_buffer, (size_t) UINT_MAX + 2);
      74            xmunmap (large_buffer, large_buffer_size);
      75          }
      76      }
      77  }
      78  
      79  static void
      80  do_test_large_size (void)
      81  {
      82    int fd = xopen (".", O_RDONLY | O_DIRECTORY, 0);
      83    TEST_VERIFY (fd >= 0);
      84    large_buffer_checks (fd);
      85  
      86    xclose (fd);
      87  }
      88  
      89  static void
      90  do_test_by_size (size_t buffer_size)
      91  {
      92    /* The test compares the iteration order with readdir64.  */
      93    DIR *reference = opendir (".");
      94    TEST_VERIFY_EXIT (reference != NULL);
      95  
      96    int fd = xopen (".", O_RDONLY | O_DIRECTORY, 0);
      97    TEST_VERIFY (fd >= 0);
      98  
      99    /* Perform two passes, with a rewind operating between passes.  */
     100    for (int pass = 0; pass < 2; ++pass)
     101      {
     102        /* Check that we need to fill the buffer multiple times.  */
     103        int read_count = 0;
     104  
     105        while (true)
     106          {
     107            /* Simple way to make sure that the memcpy below does not read
     108               non-existing data.  */
     109            struct
     110            {
     111              char buffer[buffer_size];
     112              struct dirent64 pad;
     113            } data;
     114  
     115            ssize_t ret = getdents64 (fd, &data.buffer, sizeof (data.buffer));
     116            if (ret < 0)
     117              FAIL_EXIT1 ("getdents64: %m");
     118            if (ret == 0)
     119              break;
     120            ++read_count;
     121  
     122            char *current = data.buffer;
     123            char *end = data.buffer + ret;
     124            while (current != end)
     125              {
     126                struct dirent64 entry;
     127                memcpy (&entry, current, sizeof (entry));
     128                /* Truncate overlong strings.  */
     129                entry.d_name[sizeof (entry.d_name) - 1] = '\0';
     130                TEST_VERIFY (strlen (entry.d_name) < sizeof (entry.d_name) - 1);
     131  
     132                errno = 0;
     133                struct dirent64 *refentry = readdir64 (reference);
     134                if (refentry == NULL && errno == 0)
     135                  FAIL_EXIT1 ("readdir64 failed too early, at: %s",
     136                              entry.d_name);
     137                else if (refentry == NULL)
     138                  FAIL_EXIT1 ("readdir64: %m");
     139  
     140                TEST_COMPARE_STRING (entry.d_name, refentry->d_name);
     141                TEST_COMPARE (entry.d_ino, refentry->d_ino);
     142                TEST_COMPARE (entry.d_off, refentry->d_off);
     143                TEST_COMPARE (entry.d_type, refentry->d_type);
     144  
     145                /* Offset zero is reserved for the first entry.  */
     146                TEST_VERIFY (entry.d_off != 0);
     147  
     148                TEST_VERIFY_EXIT (entry.d_reclen <= end - current);
     149                current += entry.d_reclen;
     150              }
     151          }
     152  
     153        /* We expect to have reached the end of the stream.  */
     154        errno = 0;
     155        TEST_VERIFY (readdir64 (reference) == NULL);
     156        TEST_COMPARE (errno, 0);
     157  
     158        /* direntries_read has been called more than once.  */
     159        TEST_VERIFY (read_count > 0);
     160  
     161        /* Rewind both directory streams.  */
     162        xlseek (fd, 0, SEEK_SET);
     163        rewinddir (reference);
     164      }
     165  
     166    xclose (fd);
     167    closedir (reference);
     168  }
     169  
     170  static int
     171  do_test (void)
     172  {
     173    do_test_by_size (512);
     174    do_test_by_size (1024);
     175    do_test_by_size (4096);
     176  
     177    do_test_large_size ();
     178  
     179    return 0;
     180  }
     181  
     182  #include <support/test-driver.c>