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);
88 char buf[OPENAT_BUFFER_SIZE];
89 char *proc_file = openat_proc_name (buf, fd, file);
92 int open_result = open (proc_file, flags, mode);
93 int open_errno = errno;
96 /* If the syscall succeeds, or if it fails with an unexpected
97 errno value, then return right away. Otherwise, fall through
98 and resort to using save_cwd/restore_cwd. */
99 if (0 <= open_result || ! EXPECTED_ERRNO (open_errno))
107 save_ok = (save_cwd (&saved_cwd) == 0);
111 openat_save_fail (errno);
120 err = open (file, flags, mode);
122 if (save_ok && restore_cwd (&saved_cwd) != 0)
125 openat_restore_fail (errno);
130 free_cwd (&saved_cwd);
135 /* Return true if our openat implementation must resort to
136 using save_cwd and restore_cwd. */
138 openat_needs_fchdir (void)
140 bool needs_fchdir = true;
141 int fd = open ("/", O_RDONLY);
145 char buf[OPENAT_BUFFER_SIZE];
146 char *proc_file = openat_proc_name (buf, fd, ".");
149 needs_fchdir = false;
150 if (proc_file != buf)
161 /* Replacement for Solaris' function by the same name.
162 <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
163 First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing
164 that, simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
165 If either the save_cwd or the restore_cwd fails (relatively unlikely),
166 then give a diagnostic and exit nonzero.
167 Otherwise, this function works just like Solaris' fdopendir.
170 Unlike the other fd-related functions here, this one
171 effectively consumes its FD parameter. The caller should not
172 close or otherwise manipulate FD if this function returns successfully. */
176 struct saved_cwd saved_cwd;
180 char buf[OPENAT_BUFFER_SIZE];
181 char *proc_file = openat_proc_name (buf, fd, ".");
184 dir = opendir (proc_file);
190 saved_errno = EOPNOTSUPP;
193 /* If the syscall fails with an expected errno value, resort to
194 save_cwd/restore_cwd. */
195 if (! dir && EXPECTED_ERRNO (saved_errno))
197 if (save_cwd (&saved_cwd) != 0)
198 openat_save_fail (errno);
200 if (fchdir (fd) != 0)
210 if (restore_cwd (&saved_cwd) != 0)
211 openat_restore_fail (errno);
214 free_cwd (&saved_cwd);
219 if (proc_file != buf)
227 /* Replacement for Solaris' function by the same name.
228 <http://www.google.com/search?q=fstatat+site:docs.sun.com>
229 First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
230 Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd.
231 If either the save_cwd or the restore_cwd fails (relatively unlikely),
232 then give a diagnostic and exit nonzero.
233 Otherwise, this function works just like Solaris' fstatat. */
235 #define AT_FUNC_NAME fstatat
236 #define AT_FUNC_F1 lstat
237 #define AT_FUNC_F2 stat
238 #define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW
239 #define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
240 #define AT_FUNC_POST_FILE_ARGS , st
245 #undef AT_FUNC_USE_F1_COND
246 #undef AT_FUNC_POST_FILE_PARAM_DECLS
247 #undef AT_FUNC_POST_FILE_ARGS
249 /* Replacement for Solaris' function by the same name.
250 <http://www.google.com/search?q=unlinkat+site:docs.sun.com>
251 First, try to simulate it via (unlink|rmdir) ("/proc/self/fd/FD/FILE").
252 Failing that, simulate it via save_cwd/fchdir/(unlink|rmdir)/restore_cwd.
253 If either the save_cwd or the restore_cwd fails (relatively unlikely),
254 then give a diagnostic and exit nonzero.
255 Otherwise, this function works just like Solaris' unlinkat. */
257 #define AT_FUNC_NAME unlinkat
258 #define AT_FUNC_F1 rmdir
259 #define AT_FUNC_F2 unlink
260 #define AT_FUNC_USE_F1_COND flag == AT_REMOVEDIR
261 #define AT_FUNC_POST_FILE_PARAM_DECLS , int flag
262 #define AT_FUNC_POST_FILE_ARGS /* empty */
267 #undef AT_FUNC_USE_F1_COND
268 #undef AT_FUNC_POST_FILE_PARAM_DECLS
269 #undef AT_FUNC_POST_FILE_ARGS
271 /* Replacement for Solaris' function by the same name.
272 Invoke chown or lchown on file, FILE, using OWNER and GROUP, in the
273 directory open on descriptor FD. If FLAG is AT_SYMLINK_NOFOLLOW, then
274 use lchown, otherwise, use chown. If possible, do it without changing
275 the working directory. Otherwise, resort to using save_cwd/fchdir,
276 then mkdir/restore_cwd. If either the save_cwd or the restore_cwd
277 fails, then give a diagnostic and exit nonzero. */
279 #define AT_FUNC_NAME fchownat
280 #define AT_FUNC_F1 lchown
281 #define AT_FUNC_F2 chown
282 #define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW
283 #define AT_FUNC_POST_FILE_PARAM_DECLS , uid_t owner, gid_t group, int flag
284 #define AT_FUNC_POST_FILE_ARGS , owner, group