(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
opendir.c
       1  /* Copyright (C) 1991-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <dirent.h>
      19  #include <fcntl.h>
      20  #include <errno.h>
      21  #include <stdio.h>	/* For BUFSIZ.  */
      22  #include <sys/param.h>	/* For MIN and MAX.  */
      23  
      24  #include <not-cancel.h>
      25  
      26  enum {
      27    opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC
      28  };
      29  
      30  static bool
      31  invalid_name (const char *name)
      32  {
      33    if (__glibc_unlikely (name[0] == '\0'))
      34      {
      35        /* POSIX.1-1990 says an empty name gets ENOENT;
      36  	 but `open' might like it fine.  */
      37        __set_errno (ENOENT);
      38        return true;
      39      }
      40    return false;
      41  }
      42  
      43  static DIR *
      44  opendir_tail (int fd)
      45  {
      46    if (__glibc_unlikely (fd < 0))
      47      return NULL;
      48  
      49    /* Now make sure this really is a directory and nothing changed since the
      50       `stat' call.  The S_ISDIR check is superfluous if O_DIRECTORY works,
      51       but it's cheap and we need the stat call for st_blksize anyway.  */
      52    struct __stat64_t64 statbuf;
      53    if (__glibc_unlikely (__fstat64_time64 (fd, &statbuf) < 0))
      54      goto lose;
      55    if (__glibc_unlikely (! S_ISDIR (statbuf.st_mode)))
      56      {
      57        __set_errno (ENOTDIR);
      58      lose:
      59        __close_nocancel_nostatus (fd);
      60        return NULL;
      61      }
      62  
      63    return __alloc_dir (fd, true, 0, &statbuf);
      64  }
      65  
      66  
      67  #if IS_IN (libc)
      68  DIR *
      69  __opendirat (int dfd, const char *name)
      70  {
      71    if (__glibc_unlikely (invalid_name (name)))
      72      return NULL;
      73  
      74    return opendir_tail (__openat_nocancel (dfd, name, opendir_oflags));
      75  }
      76  #endif
      77  
      78  
      79  /* Open a directory stream on NAME.  */
      80  DIR *
      81  __opendir (const char *name)
      82  {
      83    if (__glibc_unlikely (invalid_name (name)))
      84      return NULL;
      85  
      86    return opendir_tail (__open_nocancel (name, opendir_oflags));
      87  }
      88  weak_alias (__opendir, opendir)
      89  
      90  DIR *
      91  __alloc_dir (int fd, bool close_fd, int flags,
      92  	     const struct __stat64_t64 *statp)
      93  {
      94    /* We have to set the close-on-exit flag if the user provided the
      95       file descriptor.  */
      96    if (!close_fd
      97        && __glibc_unlikely (__fcntl64_nocancel (fd, F_SETFD, FD_CLOEXEC) < 0))
      98      return NULL;
      99  
     100    /* The st_blksize value of the directory is used as a hint for the
     101       size of the buffer which receives struct dirent values from the
     102       kernel.  st_blksize is limited to max_buffer_size, in case the
     103       file system provides a bogus value.  */
     104    enum { max_buffer_size = 1048576 };
     105  
     106    enum { allocation_size = 32768 };
     107    _Static_assert (allocation_size >= sizeof (struct dirent64),
     108  		  "allocation_size < sizeof (struct dirent64)");
     109  
     110    /* Increase allocation if requested, but not if the value appears to
     111       be bogus.  It will be between 32Kb and 1Mb.  */
     112    size_t allocation = MIN (MAX ((size_t) statp->st_blksize, (size_t)
     113                                  allocation_size), (size_t) max_buffer_size);
     114  
     115    DIR *dirp = (DIR *) malloc (sizeof (DIR) + allocation);
     116    if (dirp == NULL)
     117      {
     118        if (close_fd)
     119  	__close_nocancel_nostatus (fd);
     120        return NULL;
     121      }
     122  
     123    dirp->fd = fd;
     124  #if IS_IN (libc)
     125    __libc_lock_init (dirp->lock);
     126  #endif
     127    dirp->allocation = allocation;
     128    dirp->size = 0;
     129    dirp->offset = 0;
     130    dirp->filepos = 0;
     131    dirp->errcode = 0;
     132  
     133    return dirp;
     134  }