X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Ffstatat.c;h=1c6c2d30b709968bc7e38b65c925dfc047cdf51a;hb=964a52336aad0cf4a4d6c08c128560c87742c921;hp=2bf547e51bb094f19b1f48669469cf34997cf765;hpb=52c658e92436e7ef2d7c7b6f7ee69ae4431b6d7d;p=gnulib.git diff --git a/lib/fstatat.c b/lib/fstatat.c index 2bf547e51..1c6c2d30b 100644 --- a/lib/fstatat.c +++ b/lib/fstatat.c @@ -22,36 +22,89 @@ #include #include +#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. */ - result = fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW); - if (result == 0 && ! S_ISDIR (st->st_mode)) + /* Fix lstat behavior. */ + if (file[len - 1] != '/' || S_ISDIR (st->st_mode)) + return 0; + if (!S_ISLNK (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; } + result = fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW); + } + /* 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 */