18dffb485Schristos /* Work around an fstatat bug on Solaris 9.
28dffb485Schristos
3*4b169a6bSchristos Copyright (C) 2006, 2009-2022 Free Software Foundation, Inc.
48dffb485Schristos
58dffb485Schristos This program is free software: you can redistribute it and/or modify
68dffb485Schristos it under the terms of the GNU General Public License as published by
7*4b169a6bSchristos the Free Software Foundation, either version 3 of the License, or
88dffb485Schristos (at your option) any later version.
98dffb485Schristos
108dffb485Schristos This program is distributed in the hope that it will be useful,
118dffb485Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
128dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
138dffb485Schristos GNU General Public License for more details.
148dffb485Schristos
158dffb485Schristos You should have received a copy of the GNU General Public License
168dffb485Schristos along with this program. If not, see <https://www.gnu.org/licenses/>. */
178dffb485Schristos
188dffb485Schristos /* Written by Paul Eggert and Jim Meyering. */
198dffb485Schristos
208dffb485Schristos /* If the user's config.h happens to include <sys/stat.h>, let it include only
218dffb485Schristos the system's <sys/stat.h> here, so that orig_fstatat doesn't recurse to
228dffb485Schristos rpl_fstatat. */
238dffb485Schristos #define __need_system_sys_stat_h
248dffb485Schristos #include <config.h>
258dffb485Schristos
268dffb485Schristos /* Get the original definition of fstatat. It might be defined as a macro. */
278dffb485Schristos #include <sys/types.h>
288dffb485Schristos #include <sys/stat.h>
298dffb485Schristos #undef __need_system_sys_stat_h
308dffb485Schristos
318dffb485Schristos #if HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG
328dffb485Schristos static int
orig_fstatat(int fd,char const * filename,struct stat * buf,int flags)338dffb485Schristos orig_fstatat (int fd, char const *filename, struct stat *buf, int flags)
348dffb485Schristos {
358dffb485Schristos return fstatat (fd, filename, buf, flags);
368dffb485Schristos }
378dffb485Schristos #endif
388dffb485Schristos
398dffb485Schristos #ifdef __osf__
408dffb485Schristos /* Write "sys/stat.h" here, not <sys/stat.h>, otherwise OSF/1 5.1 DTK cc
418dffb485Schristos eliminates this include because of the preliminary #include <sys/stat.h>
428dffb485Schristos above. */
438dffb485Schristos # include "sys/stat.h"
448dffb485Schristos #else
458dffb485Schristos # include <sys/stat.h>
468dffb485Schristos #endif
478dffb485Schristos
488dffb485Schristos #include "stat-time.h"
498dffb485Schristos
508dffb485Schristos #include <errno.h>
518dffb485Schristos #include <fcntl.h>
528dffb485Schristos #include <stdlib.h>
538dffb485Schristos #include <string.h>
548dffb485Schristos
558dffb485Schristos #if HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG
568dffb485Schristos
578dffb485Schristos # ifndef LSTAT_FOLLOWS_SLASHED_SYMLINK
588dffb485Schristos # define LSTAT_FOLLOWS_SLASHED_SYMLINK 0
598dffb485Schristos # endif
608dffb485Schristos
618dffb485Schristos static int
normal_fstatat(int fd,char const * file,struct stat * st,int flag)628dffb485Schristos normal_fstatat (int fd, char const *file, struct stat *st, int flag)
638dffb485Schristos {
648dffb485Schristos return stat_time_normalize (orig_fstatat (fd, file, st, flag), st);
658dffb485Schristos }
668dffb485Schristos
678dffb485Schristos /* fstatat should always follow symbolic links that end in /, but on
688dffb485Schristos Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified.
698dffb485Schristos Likewise, trailing slash on a non-directory should be an error.
708dffb485Schristos These are the same problems that lstat.c and stat.c address, so
718dffb485Schristos solve it in a similar way.
728dffb485Schristos
738dffb485Schristos AIX 7.1 fstatat (AT_FDCWD, ..., 0) always fails, which is a bug.
748dffb485Schristos Work around this bug if FSTATAT_AT_FDCWD_0_BROKEN is nonzero. */
758dffb485Schristos
768dffb485Schristos int
rpl_fstatat(int fd,char const * file,struct stat * st,int flag)778dffb485Schristos rpl_fstatat (int fd, char const *file, struct stat *st, int flag)
788dffb485Schristos {
798dffb485Schristos int result = normal_fstatat (fd, file, st, flag);
808dffb485Schristos size_t len;
818dffb485Schristos
828dffb485Schristos if (LSTAT_FOLLOWS_SLASHED_SYMLINK || result != 0)
838dffb485Schristos return result;
848dffb485Schristos len = strlen (file);
858dffb485Schristos if (flag & AT_SYMLINK_NOFOLLOW)
868dffb485Schristos {
878dffb485Schristos /* Fix lstat behavior. */
888dffb485Schristos if (file[len - 1] != '/' || S_ISDIR (st->st_mode))
898dffb485Schristos return 0;
908dffb485Schristos if (!S_ISLNK (st->st_mode))
918dffb485Schristos {
928dffb485Schristos errno = ENOTDIR;
938dffb485Schristos return -1;
948dffb485Schristos }
958dffb485Schristos result = normal_fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
968dffb485Schristos }
978dffb485Schristos /* Fix stat behavior. */
988dffb485Schristos if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/')
998dffb485Schristos {
1008dffb485Schristos errno = ENOTDIR;
1018dffb485Schristos return -1;
1028dffb485Schristos }
1038dffb485Schristos return result;
1048dffb485Schristos }
1058dffb485Schristos
1068dffb485Schristos #else /* ! (HAVE_FSTATAT && HAVE_WORKING_FSTATAT_ZERO_FLAG) */
1078dffb485Schristos
1088dffb485Schristos /* On mingw, the gnulib <sys/stat.h> defines 'stat' as a function-like
1098dffb485Schristos macro; but using it in AT_FUNC_F2 causes compilation failure
1108dffb485Schristos because the preprocessor sees a use of a macro that requires two
1118dffb485Schristos arguments but is only given one. Hence, we need an inline
1128dffb485Schristos forwarder to get past the preprocessor. */
1138dffb485Schristos static int
stat_func(char const * name,struct stat * st)1148dffb485Schristos stat_func (char const *name, struct stat *st)
1158dffb485Schristos {
1168dffb485Schristos return stat (name, st);
1178dffb485Schristos }
1188dffb485Schristos
1198dffb485Schristos /* Likewise, if there is no native 'lstat', then the gnulib
1208dffb485Schristos <sys/stat.h> defined it as stat, which also needs adjustment. */
1218dffb485Schristos # if !HAVE_LSTAT
1228dffb485Schristos # undef lstat
1238dffb485Schristos # define lstat stat_func
1248dffb485Schristos # endif
1258dffb485Schristos
1268dffb485Schristos /* Replacement for Solaris' function by the same name.
1278dffb485Schristos <https://www.google.com/search?q=fstatat+site:docs.oracle.com>
1288dffb485Schristos First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
1298dffb485Schristos Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd.
1308dffb485Schristos If either the save_cwd or the restore_cwd fails (relatively unlikely),
1318dffb485Schristos then give a diagnostic and exit nonzero.
1328dffb485Schristos Otherwise, this function works just like Solaris' fstatat. */
1338dffb485Schristos
1348dffb485Schristos # define AT_FUNC_NAME fstatat
1358dffb485Schristos # define AT_FUNC_F1 lstat
1368dffb485Schristos # define AT_FUNC_F2 stat_func
1378dffb485Schristos # define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW
1388dffb485Schristos # define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
1398dffb485Schristos # define AT_FUNC_POST_FILE_ARGS , st
1408dffb485Schristos # include "at-func.c"
1418dffb485Schristos # undef AT_FUNC_NAME
1428dffb485Schristos # undef AT_FUNC_F1
1438dffb485Schristos # undef AT_FUNC_F2
1448dffb485Schristos # undef AT_FUNC_USE_F1_COND
1458dffb485Schristos # undef AT_FUNC_POST_FILE_PARAM_DECLS
1468dffb485Schristos # undef AT_FUNC_POST_FILE_ARGS
1478dffb485Schristos
1488dffb485Schristos #endif /* !HAVE_FSTATAT */
149