diff options
Diffstat (limited to 'lib/stat.c')
| -rw-r--r-- | lib/stat.c | 104 | 
1 files changed, 104 insertions, 0 deletions
| diff --git a/lib/stat.c b/lib/stat.c new file mode 100644 index 00000000000..f16d9144afc --- /dev/null +++ b/lib/stat.c @@ -0,0 +1,104 @@ +/* Work around platform bugs in stat. +   Copyright (C) 2009-2011 Free Software Foundation, Inc. + +   This program is free software: you can redistribute it and/or modify +   it under the terms of the GNU General Public License as published by +   the Free Software Foundation; either version 3 of the License, or +   (at your option) any later version. + +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   GNU General Public License for more details. + +   You should have received a copy of the GNU General Public License +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ + +/* written by Eric Blake */ + +#include <config.h> + +/* Get the original definition of stat.  It might be defined as a macro.  */ +#define __need_system_sys_stat_h +#include <sys/types.h> +#include <sys/stat.h> +#undef __need_system_sys_stat_h + +static inline int +orig_stat (const char *filename, struct stat *buf) +{ +  return stat (filename, buf); +} + +/* Specification.  */ +#include <sys/stat.h> + +#include <errno.h> +#include <limits.h> +#include <stdbool.h> +#include <string.h> + +/* Store information about NAME into ST.  Work around bugs with +   trailing slashes.  Mingw has other bugs (such as st_ino always +   being 0 on success) which this wrapper does not work around.  But +   at least this implementation provides the ability to emulate fchdir +   correctly.  */ + +int +rpl_stat (char const *name, struct stat *st) +{ +  int result = orig_stat (name, st); +#if REPLACE_FUNC_STAT_FILE +  /* Solaris 9 mistakenly succeeds when given a non-directory with a +     trailing slash.  */ +  if (result == 0 && !S_ISDIR (st->st_mode)) +    { +      size_t len = strlen (name); +      if (ISSLASH (name[len - 1])) +        { +          errno = ENOTDIR; +          return -1; +        } +    } +#endif /* REPLACE_FUNC_STAT_FILE */ +#if REPLACE_FUNC_STAT_DIR +  if (result == -1 && errno == ENOENT) +    { +      /* Due to mingw's oddities, there are some directories (like +         c:\) where stat() only succeeds with a trailing slash, and +         other directories (like c:\windows) where stat() only +         succeeds without a trailing slash.  But we want the two to be +         synonymous, since chdir() manages either style.  Likewise, Mingw also +         reports ENOENT for names longer than PATH_MAX, when we want +         ENAMETOOLONG, and for stat("file/"), when we want ENOTDIR. +         Fortunately, mingw PATH_MAX is small enough for stack +         allocation.  */ +      char fixed_name[PATH_MAX + 1] = {0}; +      size_t len = strlen (name); +      bool check_dir = false; +      if (PATH_MAX <= len) +        errno = ENAMETOOLONG; +      else if (len) +        { +          strcpy (fixed_name, name); +          if (ISSLASH (fixed_name[len - 1])) +            { +              check_dir = true; +              while (len && ISSLASH (fixed_name[len - 1])) +                fixed_name[--len] = '\0'; +              if (!len) +                fixed_name[0] = '/'; +            } +          else +            fixed_name[len++] = '/'; +          result = orig_stat (fixed_name, st); +          if (result == 0 && check_dir && !S_ISDIR (st->st_mode)) +            { +              result = -1; +              errno = ENOTDIR; +            } +        } +    } +#endif /* REPLACE_FUNC_STAT_DIR */ +  return result; +} | 
