Blame gnu/stat.c

Packit 1ef1a9
/* Work around platform bugs in stat.
Packit 1ef1a9
   Copyright (C) 2009-2015 Free Software Foundation, Inc.
Packit 1ef1a9
Packit 1ef1a9
   This program is free software: you can redistribute it and/or modify
Packit 1ef1a9
   it under the terms of the GNU General Public License as published by
Packit 1ef1a9
   the Free Software Foundation; either version 3 of the License, or
Packit 1ef1a9
   (at your option) any later version.
Packit 1ef1a9
Packit 1ef1a9
   This program is distributed in the hope that it will be useful,
Packit 1ef1a9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 1ef1a9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 1ef1a9
   GNU General Public License for more details.
Packit 1ef1a9
Packit 1ef1a9
   You should have received a copy of the GNU General Public License
Packit 1ef1a9
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 1ef1a9
Packit 1ef1a9
/* written by Eric Blake */
Packit 1ef1a9
Packit 1ef1a9
/* If the user's config.h happens to include <sys/stat.h>, let it include only
Packit 1ef1a9
   the system's <sys/stat.h> here, so that orig_stat doesn't recurse to
Packit 1ef1a9
   rpl_stat.  */
Packit 1ef1a9
#define __need_system_sys_stat_h
Packit 1ef1a9
#include <config.h>
Packit 1ef1a9
Packit 1ef1a9
/* Get the original definition of stat.  It might be defined as a macro.  */
Packit 1ef1a9
#include <sys/types.h>
Packit 1ef1a9
#include <sys/stat.h>
Packit 1ef1a9
#undef __need_system_sys_stat_h
Packit 1ef1a9
Packit 1ef1a9
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit 1ef1a9
# if _GL_WINDOWS_64_BIT_ST_SIZE
Packit 1ef1a9
#  undef stat /* avoid warning on mingw64 with _FILE_OFFSET_BITS=64 */
Packit 1ef1a9
#  define stat _stati64
Packit 1ef1a9
#  define REPLACE_FUNC_STAT_DIR 1
Packit 1ef1a9
#  undef REPLACE_FUNC_STAT_FILE
Packit 1ef1a9
# elif REPLACE_FUNC_STAT_FILE
Packit 1ef1a9
/* mingw64 has a broken stat() function, based on _stat(), in libmingwex.a.
Packit 1ef1a9
   Bypass it.  */
Packit 1ef1a9
#  define stat _stat
Packit 1ef1a9
#  define REPLACE_FUNC_STAT_DIR 1
Packit 1ef1a9
#  undef REPLACE_FUNC_STAT_FILE
Packit 1ef1a9
# endif
Packit 1ef1a9
#endif
Packit 1ef1a9
Packit 1ef1a9
static int
Packit 1ef1a9
orig_stat (const char *filename, struct stat *buf)
Packit 1ef1a9
{
Packit 1ef1a9
  return stat (filename, buf);
Packit 1ef1a9
}
Packit 1ef1a9
Packit 1ef1a9
/* Specification.  */
Packit 1ef1a9
/* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
Packit 1ef1a9
   eliminates this include because of the preliminary #include <sys/stat.h>
Packit 1ef1a9
   above.  */
Packit 1ef1a9
#include "sys/stat.h"
Packit 1ef1a9
Packit 1ef1a9
#include <errno.h>
Packit 1ef1a9
#include <limits.h>
Packit 1ef1a9
#include <stdbool.h>
Packit 1ef1a9
#include <string.h>
Packit 1ef1a9
#include "dosname.h"
Packit 1ef1a9
#include "verify.h"
Packit 1ef1a9
Packit 1ef1a9
#if REPLACE_FUNC_STAT_DIR
Packit 1ef1a9
# include "pathmax.h"
Packit 1ef1a9
  /* The only known systems where REPLACE_FUNC_STAT_DIR is needed also
Packit 1ef1a9
     have a constant PATH_MAX.  */
Packit 1ef1a9
# ifndef PATH_MAX
Packit 1ef1a9
#  error "Please port this replacement to your platform"
Packit 1ef1a9
# endif
Packit 1ef1a9
#endif
Packit 1ef1a9
Packit 1ef1a9
/* Store information about NAME into ST.  Work around bugs with
Packit 1ef1a9
   trailing slashes.  Mingw has other bugs (such as st_ino always
Packit 1ef1a9
   being 0 on success) which this wrapper does not work around.  But
Packit 1ef1a9
   at least this implementation provides the ability to emulate fchdir
Packit 1ef1a9
   correctly.  */
Packit 1ef1a9
Packit 1ef1a9
int
Packit 1ef1a9
rpl_stat (char const *name, struct stat *st)
Packit 1ef1a9
{
Packit 1ef1a9
  int result = orig_stat (name, st);
Packit 1ef1a9
#if REPLACE_FUNC_STAT_FILE
Packit 1ef1a9
  /* Solaris 9 mistakenly succeeds when given a non-directory with a
Packit 1ef1a9
     trailing slash.  */
Packit 1ef1a9
  if (result == 0 && !S_ISDIR (st->st_mode))
Packit 1ef1a9
    {
Packit 1ef1a9
      size_t len = strlen (name);
Packit 1ef1a9
      if (ISSLASH (name[len - 1]))
Packit 1ef1a9
        {
Packit 1ef1a9
          errno = ENOTDIR;
Packit 1ef1a9
          return -1;
Packit 1ef1a9
        }
Packit 1ef1a9
    }
Packit 1ef1a9
#endif /* REPLACE_FUNC_STAT_FILE */
Packit 1ef1a9
#if REPLACE_FUNC_STAT_DIR
Packit 1ef1a9
Packit 1ef1a9
  if (result == -1 && errno == ENOENT)
Packit 1ef1a9
    {
Packit 1ef1a9
      /* Due to mingw's oddities, there are some directories (like
Packit 1ef1a9
         c:\) where stat() only succeeds with a trailing slash, and
Packit 1ef1a9
         other directories (like c:\windows) where stat() only
Packit 1ef1a9
         succeeds without a trailing slash.  But we want the two to be
Packit 1ef1a9
         synonymous, since chdir() manages either style.  Likewise, Mingw also
Packit 1ef1a9
         reports ENOENT for names longer than PATH_MAX, when we want
Packit 1ef1a9
         ENAMETOOLONG, and for stat("file/"), when we want ENOTDIR.
Packit 1ef1a9
         Fortunately, mingw PATH_MAX is small enough for stack
Packit 1ef1a9
         allocation.  */
Packit 1ef1a9
      char fixed_name[PATH_MAX + 1] = {0};
Packit 1ef1a9
      size_t len = strlen (name);
Packit 1ef1a9
      bool check_dir = false;
Packit 1ef1a9
      verify (PATH_MAX <= 4096);
Packit 1ef1a9
      if (PATH_MAX <= len)
Packit 1ef1a9
        errno = ENAMETOOLONG;
Packit 1ef1a9
      else if (len)
Packit 1ef1a9
        {
Packit 1ef1a9
          strcpy (fixed_name, name);
Packit 1ef1a9
          if (ISSLASH (fixed_name[len - 1]))
Packit 1ef1a9
            {
Packit 1ef1a9
              check_dir = true;
Packit 1ef1a9
              while (len && ISSLASH (fixed_name[len - 1]))
Packit 1ef1a9
                fixed_name[--len] = '\0';
Packit 1ef1a9
              if (!len)
Packit 1ef1a9
                fixed_name[0] = '/';
Packit 1ef1a9
            }
Packit 1ef1a9
          else
Packit 1ef1a9
            fixed_name[len++] = '/';
Packit 1ef1a9
          result = orig_stat (fixed_name, st);
Packit 1ef1a9
          if (result == 0 && check_dir && !S_ISDIR (st->st_mode))
Packit 1ef1a9
            {
Packit 1ef1a9
              result = -1;
Packit 1ef1a9
              errno = ENOTDIR;
Packit 1ef1a9
            }
Packit 1ef1a9
        }
Packit 1ef1a9
    }
Packit 1ef1a9
#endif /* REPLACE_FUNC_STAT_DIR */
Packit 1ef1a9
  return result;
Packit 1ef1a9
}