X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=lib%2Ffstatat.c;h=bd295b0c96ab0e90693ffb49b4eb9313c0b5f700;hb=d1fb15ba12436afdd742eaff1ec46f75ee397bda;hp=9b0c1afa243b621e682d0e70ac943caf3818697b;hpb=2c90f1af0fd6fcf444f5c16de9ff465651a854fc;p=gnulib.git diff --git a/lib/fstatat.c b/lib/fstatat.c index 9b0c1afa2..bd295b0c9 100644 --- a/lib/fstatat.c +++ b/lib/fstatat.c @@ -1,6 +1,6 @@ /* Work around an fstatat bug on Solaris 9. - Copyright (C) 2006, 2009 Free Software Foundation, Inc. + Copyright (C) 2006, 2009-2010 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 @@ -25,34 +25,86 @@ #include #include -#undef fstatat +#if HAVE_FSTATAT + +# undef fstatat /* fstatat should always follow symbolic links that end in /, but on - Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified. This is - the same problem that lstat.c addresses, so solve it in a similar - way. */ + Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified. + Likewise, trailing slash on a non-directory should be an error. + These are the same problems that lstat.c and stat.c address, so + solve it in a similar way. */ int rpl_fstatat (int fd, char const *file, struct stat *st, int flag) { int result = fstatat (fd, file, st, flag); + size_t len; - if (result == 0 && (flag & AT_SYMLINK_NOFOLLOW) && S_ISLNK (st->st_mode) - && file[strlen (file) - 1] == '/') + if (result != 0) + return result; + len = strlen (file); + if (flag & AT_SYMLINK_NOFOLLOW) { - /* FILE refers to a symbolic link and the name ends with a slash. - Get info about the link's referent. */ + /* Fix lstat behavior. */ + if (file[len - 1] != '/' || S_ISDIR (st->st_mode)) + return 0; + if (!S_ISLNK (st->st_mode)) + { + errno = ENOTDIR; + return -1; + } result = fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW); - if (result == 0 && ! S_ISDIR (st->st_mode)) - { - /* fstatat succeeded and FILE references a non-directory. - But it was specified via a name including a trailing - slash. Fail with errno set to ENOTDIR to indicate the - contradiction. */ - errno = ENOTDIR; - return -1; - } } - + /* Fix stat behavior. */ + if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/') + { + errno = ENOTDIR; + return -1; + } return result; } + +#else /* !HAVE_FSTATAT */ + +/* On mingw, the gnulib defines `stat' as a function-like + macro; but using it in AT_FUNC_F2 causes compilation failure + because the preprocessor sees a use of a macro that requires two + arguments but is only given one. Hence, we need an inline + forwarder to get past the preprocessor. */ +static inline int +stat_func (char const *name, struct stat *st) +{ + return stat (name, st); +} + +/* Likewise, if there is no native `lstat', then the gnulib + defined it as stat, which also needs adjustment. */ +# if !HAVE_LSTAT +# undef lstat +# define lstat stat_func +# endif + +/* Replacement for Solaris' function by the same name. + + First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE"). + Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd. + If either the save_cwd or the restore_cwd fails (relatively unlikely), + then give a diagnostic and exit nonzero. + Otherwise, this function works just like Solaris' fstatat. */ + +# define AT_FUNC_NAME fstatat +# define AT_FUNC_F1 lstat +# define AT_FUNC_F2 stat_func +# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW +# define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag +# define AT_FUNC_POST_FILE_ARGS , st +# include "at-func.c" +# undef AT_FUNC_NAME +# undef AT_FUNC_F1 +# undef AT_FUNC_F2 +# undef AT_FUNC_USE_F1_COND +# undef AT_FUNC_POST_FILE_PARAM_DECLS +# undef AT_FUNC_POST_FILE_ARGS + +#endif /* !HAVE_FSTATAT */