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 */
27 #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
30 #include "openat-priv.h"
33 /* Replacement for Solaris' openat function.
34 <http://www.google.com/search?q=openat+site:docs.sun.com>
35 First, try to simulate it via open ("/proc/self/fd/FD/FILE").
36 Failing that, simulate it by doing save_cwd/fchdir/open/restore_cwd.
37 If either the save_cwd or the restore_cwd fails (relatively unlikely),
38 then give a diagnostic and exit nonzero.
39 Otherwise, upon failure, set errno and return -1, as openat does.
40 Upon successful completion, return a file descriptor. */
42 openat (int fd, char const *file, int flags, ...)
49 va_start (arg, flags);
51 /* If mode_t is narrower than int, use the promoted type (int),
52 not mode_t. Use sizeof to guess whether mode_t is narrower;
53 we don't know of any practical counterexamples. */
54 mode = (sizeof (mode_t) < sizeof (int)
56 : va_arg (arg, mode_t));
61 return openat_permissive (fd, file, flags, mode, NULL);
64 /* Like openat (FD, FILE, FLAGS, MODE), but if CWD_ERRNO is
65 nonnull, set *CWD_ERRNO to an errno value if unable to save
66 or restore the initial working directory. This is needed only
67 the first time remove.c's remove_dir opens a command-line
70 If a previous attempt to restore the current working directory
71 failed, then we must not even try to access a `.'-relative name.
72 It is the caller's responsibility not to call this function
76 openat_permissive (int fd, char const *file, int flags, mode_t mode,
79 struct saved_cwd saved_cwd;
84 if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
85 return open (file, flags, mode);
89 BUILD_PROC_NAME (proc_file, fd, file);
90 err = open (proc_file, flags, mode);
91 /* If the syscall succeeds, or if it fails with an unexpected
92 errno value, then return right away. Otherwise, fall through
93 and resort to using save_cwd/restore_cwd. */
94 if (0 <= err || ! EXPECTED_ERRNO (errno))
98 save_ok = (save_cwd (&saved_cwd) == 0);
102 openat_save_fail (errno);
111 err = open (file, flags, mode);
113 if (save_ok && restore_cwd (&saved_cwd) != 0)
116 openat_restore_fail (errno);
121 free_cwd (&saved_cwd);
126 /* Return true if our openat implementation must resort to
127 using save_cwd and restore_cwd. */
129 openat_needs_fchdir (void)
132 int fd = open ("/", O_RDONLY);
137 BUILD_PROC_NAME (proc_file, fd, ".");
138 fd2 = open (proc_file, O_RDONLY);
148 /* Replacement for Solaris' function by the same name.
149 <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
150 First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing
151 that, simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
152 If either the save_cwd or the restore_cwd fails (relatively unlikely),
153 then give a diagnostic and exit nonzero.
154 Otherwise, this function works just like Solaris' fdopendir.
157 Unlike the other fd-related functions here, this one
158 effectively consumes its FD parameter. The caller should not
159 close or otherwise manipulate FD if this function returns successfully. */
163 struct saved_cwd saved_cwd;
168 BUILD_PROC_NAME (proc_file, fd, ".");
169 dir = opendir (proc_file);
172 /* If the syscall fails with an expected errno value, resort to
173 save_cwd/restore_cwd. */
174 if (! dir && EXPECTED_ERRNO (saved_errno))
176 if (save_cwd (&saved_cwd) != 0)
177 openat_save_fail (errno);
179 if (fchdir (fd) != 0)
189 if (restore_cwd (&saved_cwd) != 0)
190 openat_restore_fail (errno);
193 free_cwd (&saved_cwd);
204 /* Replacement for Solaris' function by the same name.
205 <http://www.google.com/search?q=fstatat+site:docs.sun.com>
206 First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
207 Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd.
208 If either the save_cwd or the restore_cwd fails (relatively unlikely),
209 then give a diagnostic and exit nonzero.
210 Otherwise, this function works just like Solaris' fstatat. */
212 #define AT_FUNC_NAME fstatat
213 #define AT_FUNC_F1 lstat
214 #define AT_FUNC_F2 stat
215 #define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW
216 #define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
217 #define AT_FUNC_POST_FILE_ARGS , st
222 #undef AT_FUNC_USE_F1_COND
223 #undef AT_FUNC_POST_FILE_PARAM_DECLS
224 #undef AT_FUNC_POST_FILE_ARGS
226 /* Replacement for Solaris' function by the same name.
227 <http://www.google.com/search?q=unlinkat+site:docs.sun.com>
228 First, try to simulate it via (unlink|rmdir) ("/proc/self/fd/FD/FILE").
229 Failing that, simulate it via save_cwd/fchdir/(unlink|rmdir)/restore_cwd.
230 If either the save_cwd or the restore_cwd fails (relatively unlikely),
231 then give a diagnostic and exit nonzero.
232 Otherwise, this function works just like Solaris' unlinkat. */
234 #define AT_FUNC_NAME unlinkat
235 #define AT_FUNC_F1 rmdir
236 #define AT_FUNC_F2 unlink
237 #define AT_FUNC_USE_F1_COND flag == AT_REMOVEDIR
238 #define AT_FUNC_POST_FILE_PARAM_DECLS , int flag
239 #define AT_FUNC_POST_FILE_ARGS /* empty */
244 #undef AT_FUNC_USE_F1_COND
245 #undef AT_FUNC_POST_FILE_PARAM_DECLS
246 #undef AT_FUNC_POST_FILE_ARGS
248 /* Replacement for Solaris' function by the same name.
249 Invoke chown or lchown on file, FILE, using OWNER and GROUP, in the
250 directory open on descriptor FD. If FLAG is AT_SYMLINK_NOFOLLOW, then
251 use lchown, otherwise, use chown. If possible, do it without changing
252 the working directory. Otherwise, resort to using save_cwd/fchdir,
253 then mkdir/restore_cwd. If either the save_cwd or the restore_cwd
254 fails, then give a diagnostic and exit nonzero. */
256 #define AT_FUNC_NAME fchownat
257 #define AT_FUNC_F1 lchown
258 #define AT_FUNC_F2 chown
259 #define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW
260 #define AT_FUNC_POST_FILE_PARAM_DECLS , uid_t owner, gid_t group, int flag
261 #define AT_FUNC_POST_FILE_ARGS , owner, group