1 /* provide a replacement openat function
2 Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 /* written by Jim Meyering */
29 #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
32 #include "openat-priv.h"
35 /* Replacement for Solaris' openat function.
36 <http://www.google.com/search?q=openat+site:docs.sun.com>
37 First, try to simulate it via open ("/proc/self/fd/FD/FILE").
38 Failing that, simulate it by doing save_cwd/fchdir/open/restore_cwd.
39 If either the save_cwd or the restore_cwd fails (relatively unlikely),
40 then give a diagnostic and exit nonzero.
41 Otherwise, upon failure, set errno and return -1, as openat does.
42 Upon successful completion, return a file descriptor. */
44 openat (int fd, char const *file, int flags, ...)
51 va_start (arg, flags);
53 /* If mode_t is narrower than int, use the promoted type (int),
54 not mode_t. Use sizeof to guess whether mode_t is narrower;
55 we don't know of any practical counterexamples. */
56 mode = (sizeof (mode_t) < sizeof (int)
58 : va_arg (arg, mode_t));
63 return openat_permissive (fd, file, flags, mode, NULL);
66 /* Like openat (FD, FILE, FLAGS, MODE), but if CWD_ERRNO is
67 nonnull, set *CWD_ERRNO to an errno value if unable to save
68 or restore the initial working directory. This is needed only
69 the first time remove.c's remove_dir opens a command-line
72 If a previous attempt to restore the current working directory
73 failed, then we must not even try to access a `.'-relative name.
74 It is the caller's responsibility not to call this function
78 openat_permissive (int fd, char const *file, int flags, mode_t mode,
81 struct saved_cwd saved_cwd;
86 if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
87 return open (file, flags, mode);
91 BUILD_PROC_NAME (proc_file, fd, file);
92 err = open (proc_file, flags, mode);
93 /* If the syscall succeeds, or if it fails with an unexpected
94 errno value, then return right away. Otherwise, fall through
95 and resort to using save_cwd/restore_cwd. */
96 if (0 <= err || ! EXPECTED_ERRNO (errno))
100 save_ok = (save_cwd (&saved_cwd) == 0);
104 openat_save_fail (errno);
113 err = open (file, flags, mode);
115 if (save_ok && restore_cwd (&saved_cwd) != 0)
118 openat_restore_fail (errno);
123 free_cwd (&saved_cwd);
128 /* Return true if our openat implementation must resort to
129 using save_cwd and restore_cwd. */
131 openat_needs_fchdir (void)
134 int fd = open ("/", O_RDONLY);
139 BUILD_PROC_NAME (proc_file, fd, ".");
140 fd2 = open (proc_file, O_RDONLY);
150 /* Replacement for Solaris' function by the same name.
151 <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
152 First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing
153 that, simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
154 If either the save_cwd or the restore_cwd fails (relatively unlikely),
155 then give a diagnostic and exit nonzero.
156 Otherwise, this function works just like Solaris' fdopendir.
159 Unlike the other fd-related functions here, this one
160 effectively consumes its FD parameter. The caller should not
161 close or otherwise manipulate FD if this function returns successfully. */
165 struct saved_cwd saved_cwd;
170 BUILD_PROC_NAME (proc_file, fd, ".");
171 dir = opendir (proc_file);
174 /* If the syscall fails with an expected errno value, resort to
175 save_cwd/restore_cwd. */
176 if (! dir && EXPECTED_ERRNO (saved_errno))
178 if (save_cwd (&saved_cwd) != 0)
179 openat_save_fail (errno);
181 if (fchdir (fd) != 0)
191 if (restore_cwd (&saved_cwd) != 0)
192 openat_restore_fail (errno);
195 free_cwd (&saved_cwd);
206 /* Replacement for Solaris' function by the same name.
207 <http://www.google.com/search?q=fstatat+site:docs.sun.com>
208 First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
209 Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd.
210 If either the save_cwd or the restore_cwd fails (relatively unlikely),
211 then give a diagnostic and exit nonzero.
212 Otherwise, this function works just like Solaris' fstatat. */
214 #define AT_FUNC_NAME fstatat
215 #define AT_FUNC_F1 lstat
216 #define AT_FUNC_F2 stat
217 #define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW
218 #define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
219 #define AT_FUNC_POST_FILE_ARGS , st
224 #undef AT_FUNC_USE_F1_COND
225 #undef AT_FUNC_POST_FILE_PARAM_DECLS
226 #undef AT_FUNC_POST_FILE_ARGS
228 /* Replacement for Solaris' function by the same name.
229 <http://www.google.com/search?q=unlinkat+site:docs.sun.com>
230 First, try to simulate it via (unlink|rmdir) ("/proc/self/fd/FD/FILE").
231 Failing that, simulate it via save_cwd/fchdir/(unlink|rmdir)/restore_cwd.
232 If either the save_cwd or the restore_cwd fails (relatively unlikely),
233 then give a diagnostic and exit nonzero.
234 Otherwise, this function works just like Solaris' unlinkat. */
236 #define AT_FUNC_NAME unlinkat
237 #define AT_FUNC_F1 rmdir
238 #define AT_FUNC_F2 unlink
239 #define AT_FUNC_USE_F1_COND flag == AT_REMOVEDIR
240 #define AT_FUNC_POST_FILE_PARAM_DECLS , int flag
241 #define AT_FUNC_POST_FILE_ARGS /* empty */
246 #undef AT_FUNC_USE_F1_COND
247 #undef AT_FUNC_POST_FILE_PARAM_DECLS
248 #undef AT_FUNC_POST_FILE_ARGS
250 /* Replacement for Solaris' function by the same name.
251 Invoke chown or lchown on file, FILE, using OWNER and GROUP, in the
252 directory open on descriptor FD. If FLAG is AT_SYMLINK_NOFOLLOW, then
253 use lchown, otherwise, use chown. If possible, do it without changing
254 the working directory. Otherwise, resort to using save_cwd/fchdir,
255 then mkdir/restore_cwd. If either the save_cwd or the restore_cwd
256 fails, then give a diagnostic and exit nonzero. */
258 #define AT_FUNC_NAME fchownat
259 #define AT_FUNC_F1 lchown
260 #define AT_FUNC_F2 chown
261 #define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW
262 #define AT_FUNC_POST_FILE_PARAM_DECLS , uid_t owner, gid_t group, int flag
263 #define AT_FUNC_POST_FILE_ARGS , owner, group