Blame sysdeps/posix/opendir.c

Packit Service 82fcde
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <dirent.h>
Packit Service 82fcde
#include <fcntl.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <stdio.h>	/* For BUFSIZ.  */
Packit Service 82fcde
#include <sys/param.h>	/* For MIN and MAX.  */
Packit Service 82fcde
Packit Service 82fcde
#include <not-cancel.h>
Packit Service 82fcde
Packit Service 82fcde
/* The st_blksize value of the directory is used as a hint for the
Packit Service 82fcde
   size of the buffer which receives struct dirent values from the
Packit Service 82fcde
   kernel.  st_blksize is limited to MAX_DIR_BUFFER_SIZE, in case the
Packit Service 82fcde
   file system provides a bogus value.  */
Packit Service 82fcde
#define MAX_DIR_BUFFER_SIZE 1048576U
Packit Service 82fcde
Packit Service 82fcde
enum {
Packit Service 82fcde
  opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
static bool
Packit Service 82fcde
invalid_name (const char *name)
Packit Service 82fcde
{
Packit Service 82fcde
  if (__glibc_unlikely (name[0] == '\0'))
Packit Service 82fcde
    {
Packit Service 82fcde
      /* POSIX.1-1990 says an empty name gets ENOENT;
Packit Service 82fcde
	 but `open' might like it fine.  */
Packit Service 82fcde
      __set_errno (ENOENT);
Packit Service 82fcde
      return true;
Packit Service 82fcde
    }
Packit Service 82fcde
  return false;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static DIR *
Packit Service 82fcde
opendir_tail (int fd)
Packit Service 82fcde
{
Packit Service 82fcde
  if (__glibc_unlikely (fd < 0))
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
Packit Service 82fcde
  /* Now make sure this really is a directory and nothing changed since the
Packit Service 82fcde
     `stat' call.  The S_ISDIR check is superfluous if O_DIRECTORY works,
Packit Service 82fcde
     but it's cheap and we need the stat call for st_blksize anyway.  */
Packit Service 82fcde
  struct stat64 statbuf;
Packit Service 82fcde
  if (__glibc_unlikely (__fxstat64 (_STAT_VER, fd, &statbuf) < 0))
Packit Service 82fcde
    goto lose;
Packit Service 82fcde
  if (__glibc_unlikely (! S_ISDIR (statbuf.st_mode)))
Packit Service 82fcde
    {
Packit Service 82fcde
      __set_errno (ENOTDIR);
Packit Service 82fcde
    lose:
Packit Service 82fcde
      __close_nocancel_nostatus (fd);
Packit Service 82fcde
      return NULL;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  return __alloc_dir (fd, true, 0, &statbuf);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
#if IS_IN (libc)
Packit Service 82fcde
DIR *
Packit Service 82fcde
__opendirat (int dfd, const char *name)
Packit Service 82fcde
{
Packit Service 82fcde
  if (__glibc_unlikely (invalid_name (name)))
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
Packit Service 82fcde
  return opendir_tail (__openat_nocancel (dfd, name, opendir_oflags));
Packit Service 82fcde
}
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Open a directory stream on NAME.  */
Packit Service 82fcde
DIR *
Packit Service 82fcde
__opendir (const char *name)
Packit Service 82fcde
{
Packit Service 82fcde
  if (__glibc_unlikely (invalid_name (name)))
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
Packit Service 82fcde
  return opendir_tail (__open_nocancel (name, opendir_oflags));
Packit Service 82fcde
}
Packit Service 82fcde
weak_alias (__opendir, opendir)
Packit Service 82fcde
Packit Service 82fcde
DIR *
Packit Service 82fcde
__alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp)
Packit Service 82fcde
{
Packit Service 82fcde
  /* We have to set the close-on-exit flag if the user provided the
Packit Service 82fcde
     file descriptor.  */
Packit Service 82fcde
  if (!close_fd
Packit Service 82fcde
      && __glibc_unlikely (__fcntl64_nocancel (fd, F_SETFD, FD_CLOEXEC) < 0))
Packit Service 82fcde
	goto lose;
Packit Service 82fcde
Packit Service 82fcde
  const size_t default_allocation = (4 * BUFSIZ < sizeof (struct dirent64)
Packit Service 82fcde
				     ? sizeof (struct dirent64) : 4 * BUFSIZ);
Packit Service 82fcde
  const size_t small_allocation = (BUFSIZ < sizeof (struct dirent64)
Packit Service 82fcde
				   ? sizeof (struct dirent64) : BUFSIZ);
Packit Service 82fcde
  size_t allocation = default_allocation;
Packit Service 82fcde
#ifdef _STATBUF_ST_BLKSIZE
Packit Service 82fcde
  /* Increase allocation if requested, but not if the value appears to
Packit Service 82fcde
     be bogus.  */
Packit Service 82fcde
  if (statp != NULL)
Packit Service 82fcde
    allocation = MIN (MAX ((size_t) statp->st_blksize, default_allocation),
Packit Service 82fcde
		      MAX_DIR_BUFFER_SIZE);
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
  DIR *dirp = (DIR *) malloc (sizeof (DIR) + allocation);
Packit Service 82fcde
  if (dirp == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      allocation = small_allocation;
Packit Service 82fcde
      dirp = (DIR *) malloc (sizeof (DIR) + allocation);
Packit Service 82fcde
Packit Service 82fcde
      if (dirp == NULL)
Packit Service 82fcde
      lose:
Packit Service 82fcde
	{
Packit Service 82fcde
	  if (close_fd)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      int save_errno = errno;
Packit Service 82fcde
	      __close_nocancel_nostatus (fd);
Packit Service 82fcde
	      __set_errno (save_errno);
Packit Service 82fcde
	    }
Packit Service 82fcde
	  return NULL;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  dirp->fd = fd;
Packit Service 82fcde
#if IS_IN (libc)
Packit Service 82fcde
  __libc_lock_init (dirp->lock);
Packit Service 82fcde
#endif
Packit Service 82fcde
  dirp->allocation = allocation;
Packit Service 82fcde
  dirp->size = 0;
Packit Service 82fcde
  dirp->offset = 0;
Packit Service 82fcde
  dirp->filepos = 0;
Packit Service 82fcde
  dirp->errcode = 0;
Packit Service 82fcde
Packit Service 82fcde
  return dirp;
Packit Service 82fcde
}