Blame sysdeps/posix/opendir.c

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