xref: /netbsd-src/external/gpl3/gdb/dist/gnulib/import/fstatat.c (revision 4b169a6ba595ae283ca507b26b15fdff40495b1c)
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