18dffb485Schristos /* Define at-style functions like fstatat, unlinkat, fchownat, etc.
2*4b169a6bSchristos Copyright (C) 2006, 2009-2022 Free Software Foundation, Inc.
38dffb485Schristos
48dffb485Schristos This program is free software: you can redistribute it and/or modify
58dffb485Schristos it under the terms of the GNU General Public License as published by
6*4b169a6bSchristos the Free Software Foundation, either version 3 of the License, or
78dffb485Schristos (at your option) any later version.
88dffb485Schristos
98dffb485Schristos This program is distributed in the hope that it will be useful,
108dffb485Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
118dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
128dffb485Schristos GNU General Public License for more details.
138dffb485Schristos
148dffb485Schristos You should have received a copy of the GNU General Public License
158dffb485Schristos along with this program. If not, see <https://www.gnu.org/licenses/>. */
168dffb485Schristos
178dffb485Schristos /* written by Jim Meyering */
188dffb485Schristos
198dffb485Schristos #include "filename.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
208dffb485Schristos
218dffb485Schristos #ifdef GNULIB_SUPPORT_ONLY_AT_FDCWD
228dffb485Schristos # include <errno.h>
238dffb485Schristos # ifndef ENOTSUP
248dffb485Schristos # define ENOTSUP EINVAL
258dffb485Schristos # endif
268dffb485Schristos #else
278dffb485Schristos # include "openat.h"
288dffb485Schristos # include "openat-priv.h"
298dffb485Schristos # include "save-cwd.h"
308dffb485Schristos #endif
318dffb485Schristos
328dffb485Schristos #ifdef AT_FUNC_USE_F1_COND
338dffb485Schristos # define CALL_FUNC(F) \
348dffb485Schristos (flag == AT_FUNC_USE_F1_COND \
358dffb485Schristos ? AT_FUNC_F1 (F AT_FUNC_POST_FILE_ARGS) \
368dffb485Schristos : AT_FUNC_F2 (F AT_FUNC_POST_FILE_ARGS))
378dffb485Schristos # define VALIDATE_FLAG(F) \
388dffb485Schristos if (flag & ~AT_FUNC_USE_F1_COND) \
398dffb485Schristos { \
408dffb485Schristos errno = EINVAL; \
418dffb485Schristos return FUNC_FAIL; \
428dffb485Schristos }
438dffb485Schristos #else
448dffb485Schristos # define CALL_FUNC(F) (AT_FUNC_F1 (F AT_FUNC_POST_FILE_ARGS))
458dffb485Schristos # define VALIDATE_FLAG(F) /* empty */
468dffb485Schristos #endif
478dffb485Schristos
488dffb485Schristos #ifdef AT_FUNC_RESULT
498dffb485Schristos # define FUNC_RESULT AT_FUNC_RESULT
508dffb485Schristos #else
518dffb485Schristos # define FUNC_RESULT int
528dffb485Schristos #endif
538dffb485Schristos
548dffb485Schristos #ifdef AT_FUNC_FAIL
558dffb485Schristos # define FUNC_FAIL AT_FUNC_FAIL
568dffb485Schristos #else
578dffb485Schristos # define FUNC_FAIL -1
588dffb485Schristos #endif
598dffb485Schristos
608dffb485Schristos /* Call AT_FUNC_F1 to operate on FILE, which is in the directory
618dffb485Schristos open on descriptor FD. If AT_FUNC_USE_F1_COND is defined to a value,
628dffb485Schristos AT_FUNC_POST_FILE_PARAM_DECLS must include a parameter named flag;
638dffb485Schristos call AT_FUNC_F2 if FLAG is 0 or fail if FLAG contains more bits than
648dffb485Schristos AT_FUNC_USE_F1_COND. Return int and fail with -1 unless AT_FUNC_RESULT
658dffb485Schristos or AT_FUNC_FAIL are defined. If possible, do it without changing the
668dffb485Schristos working directory. Otherwise, resort to using save_cwd/fchdir,
678dffb485Schristos then AT_FUNC_F?/restore_cwd. If either the save_cwd or the restore_cwd
688dffb485Schristos fails, then give a diagnostic and exit nonzero. */
698dffb485Schristos FUNC_RESULT
AT_FUNC_NAME(int fd,char const * file AT_FUNC_POST_FILE_PARAM_DECLS)708dffb485Schristos AT_FUNC_NAME (int fd, char const *file AT_FUNC_POST_FILE_PARAM_DECLS)
718dffb485Schristos {
728dffb485Schristos VALIDATE_FLAG (flag);
738dffb485Schristos
748dffb485Schristos if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
758dffb485Schristos return CALL_FUNC (file);
768dffb485Schristos
778dffb485Schristos #ifdef GNULIB_SUPPORT_ONLY_AT_FDCWD
788dffb485Schristos errno = ENOTSUP;
798dffb485Schristos return FUNC_FAIL;
808dffb485Schristos #else
818dffb485Schristos {
828dffb485Schristos /* Be careful to choose names unlikely to conflict with
838dffb485Schristos AT_FUNC_POST_FILE_PARAM_DECLS. */
848dffb485Schristos struct saved_cwd saved_cwd;
858dffb485Schristos int saved_errno;
868dffb485Schristos FUNC_RESULT err;
878dffb485Schristos
888dffb485Schristos {
898dffb485Schristos char proc_buf[OPENAT_BUFFER_SIZE];
908dffb485Schristos char *proc_file = openat_proc_name (proc_buf, fd, file);
918dffb485Schristos if (proc_file)
928dffb485Schristos {
938dffb485Schristos FUNC_RESULT proc_result = CALL_FUNC (proc_file);
948dffb485Schristos int proc_errno = errno;
958dffb485Schristos if (proc_file != proc_buf)
968dffb485Schristos free (proc_file);
978dffb485Schristos /* If the syscall succeeds, or if it fails with an unexpected
988dffb485Schristos errno value, then return right away. Otherwise, fall through
998dffb485Schristos and resort to using save_cwd/restore_cwd. */
1008dffb485Schristos if (FUNC_FAIL != proc_result)
1018dffb485Schristos return proc_result;
1028dffb485Schristos if (! EXPECTED_ERRNO (proc_errno))
1038dffb485Schristos {
1048dffb485Schristos errno = proc_errno;
1058dffb485Schristos return proc_result;
1068dffb485Schristos }
1078dffb485Schristos }
1088dffb485Schristos }
1098dffb485Schristos
1108dffb485Schristos if (save_cwd (&saved_cwd) != 0)
1118dffb485Schristos openat_save_fail (errno);
1128dffb485Schristos if (0 <= fd && fd == saved_cwd.desc)
1138dffb485Schristos {
1148dffb485Schristos /* If saving the working directory collides with the user's
1158dffb485Schristos requested fd, then the user's fd must have been closed to
1168dffb485Schristos begin with. */
1178dffb485Schristos free_cwd (&saved_cwd);
1188dffb485Schristos errno = EBADF;
1198dffb485Schristos return FUNC_FAIL;
1208dffb485Schristos }
1218dffb485Schristos
1228dffb485Schristos if (fchdir (fd) != 0)
1238dffb485Schristos {
1248dffb485Schristos saved_errno = errno;
1258dffb485Schristos free_cwd (&saved_cwd);
1268dffb485Schristos errno = saved_errno;
1278dffb485Schristos return FUNC_FAIL;
1288dffb485Schristos }
1298dffb485Schristos
1308dffb485Schristos err = CALL_FUNC (file);
1318dffb485Schristos saved_errno = (err == FUNC_FAIL ? errno : 0);
1328dffb485Schristos
1338dffb485Schristos if (restore_cwd (&saved_cwd) != 0)
1348dffb485Schristos openat_restore_fail (errno);
1358dffb485Schristos
1368dffb485Schristos free_cwd (&saved_cwd);
1378dffb485Schristos
1388dffb485Schristos if (saved_errno)
1398dffb485Schristos errno = saved_errno;
1408dffb485Schristos return err;
1418dffb485Schristos }
1428dffb485Schristos #endif
1438dffb485Schristos }
1448dffb485Schristos #undef CALL_FUNC
1458dffb485Schristos #undef FUNC_RESULT
1468dffb485Schristos #undef FUNC_FAIL
147