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 */
31 #include "openat-priv.h"
34 /* Replacement for Solaris' openat function.
35 <http://www.google.com/search?q=openat+site:docs.sun.com>
36 First, try to simulate it via open ("/proc/self/fd/FD/FILE").
37 Failing that, simulate it by doing save_cwd/fchdir/open/restore_cwd.
38 If either the save_cwd or the restore_cwd fails (relatively unlikely),
39 then give a diagnostic and exit nonzero.
40 Otherwise, upon failure, set errno and return -1, as openat does.
41 Upon successful completion, return a file descriptor. */
43 openat (int fd, char const *file, int flags, ...)
50 va_start (arg, flags);
52 /* If mode_t is narrower than int, use the promoted type (int),
53 not mode_t. Use sizeof to guess whether mode_t is narrower;
54 we don't know of any practical counterexamples. */
55 mode = (sizeof (mode_t) < sizeof (int)
57 : va_arg (arg, mode_t));
62 return openat_permissive (fd, file, flags, mode, NULL);
65 /* Like openat (FD, FILE, FLAGS, MODE), but if CWD_ERRNO is
66 nonnull, set *CWD_ERRNO to an errno value if unable to save
67 or restore the initial working directory. This is needed only
68 the first time remove.c's remove_dir opens a command-line
71 If a previous attempt to restore the current working directory
72 failed, then we must not even try to access a `.'-relative name.
73 It is the caller's responsibility not to call this function
77 openat_permissive (int fd, char const *file, int flags, mode_t mode,
80 struct saved_cwd saved_cwd;
85 if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
86 return open (file, flags, mode);
89 char buf[OPENAT_BUFFER_SIZE];
90 char *proc_file = openat_proc_name (buf, fd, file);
93 int open_result = open (proc_file, flags, mode);
94 int open_errno = errno;
97 /* If the syscall succeeds, or if it fails with an unexpected
98 errno value, then return right away. Otherwise, fall through
99 and resort to using save_cwd/restore_cwd. */
100 if (0 <= open_result || ! EXPECTED_ERRNO (open_errno))
108 save_ok = (save_cwd (&saved_cwd) == 0);
112 openat_save_fail (errno);
121 err = open (file, flags, mode);
123 if (save_ok && restore_cwd (&saved_cwd) != 0)
126 openat_restore_fail (errno);
131 free_cwd (&saved_cwd);
136 /* Return true if our openat implementation must resort to
137 using save_cwd and restore_cwd. */
139 openat_needs_fchdir (void)
141 bool needs_fchdir = true;
142 int fd = open ("/", O_RDONLY);
146 char buf[OPENAT_BUFFER_SIZE];
147 char *proc_file = openat_proc_name (buf, fd, ".");
150 needs_fchdir = false;
151 if (proc_file != buf)
162 /* Replacement for Solaris' function by the same name.
163 <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
164 First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing
165 that, simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
166 If either the save_cwd or the restore_cwd fails (relatively unlikely),
167 then give a diagnostic and exit nonzero.
168 Otherwise, this function works just like Solaris' fdopendir.
171 Unlike the other fd-related functions here, this one
172 effectively consumes its FD parameter. The caller should not
173 close or otherwise manipulate FD if this function returns successfully. */
177 struct saved_cwd saved_cwd;
181 char buf[OPENAT_BUFFER_SIZE];
182 char *proc_file = openat_proc_name (buf, fd, ".");
185 dir = opendir (proc_file);
191 saved_errno = EOPNOTSUPP;
194 /* If the syscall fails with an expected errno value, resort to
195 save_cwd/restore_cwd. */
196 if (! dir && EXPECTED_ERRNO (saved_errno))
198 if (save_cwd (&saved_cwd) != 0)
199 openat_save_fail (errno);
201 if (fchdir (fd) != 0)
211 if (restore_cwd (&saved_cwd) != 0)
212 openat_restore_fail (errno);
215 free_cwd (&saved_cwd);
220 if (proc_file != buf)
228 /* Replacement for Solaris' function by the same name.
229 <http://www.google.com/search?q=fstatat+site:docs.sun.com>
230 First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE").
231 Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/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' fstatat. */
236 #define AT_FUNC_NAME fstatat
237 #define AT_FUNC_F1 lstat
238 #define AT_FUNC_F2 stat
239 #define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW
240 #define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag
241 #define AT_FUNC_POST_FILE_ARGS , st
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 <http://www.google.com/search?q=unlinkat+site:docs.sun.com>
252 First, try to simulate it via (unlink|rmdir) ("/proc/self/fd/FD/FILE").
253 Failing that, simulate it via save_cwd/fchdir/(unlink|rmdir)/restore_cwd.
254 If either the save_cwd or the restore_cwd fails (relatively unlikely),
255 then give a diagnostic and exit nonzero.
256 Otherwise, this function works just like Solaris' unlinkat. */
258 #define AT_FUNC_NAME unlinkat
259 #define AT_FUNC_F1 rmdir
260 #define AT_FUNC_F2 unlink
261 #define AT_FUNC_USE_F1_COND flag == AT_REMOVEDIR
262 #define AT_FUNC_POST_FILE_PARAM_DECLS , int flag
263 #define AT_FUNC_POST_FILE_ARGS /* empty */
268 #undef AT_FUNC_USE_F1_COND
269 #undef AT_FUNC_POST_FILE_PARAM_DECLS
270 #undef AT_FUNC_POST_FILE_ARGS
272 /* Replacement for Solaris' function by the same name.
273 Invoke chown or lchown on file, FILE, using OWNER and GROUP, in the
274 directory open on descriptor FD. If FLAG is AT_SYMLINK_NOFOLLOW, then
275 use lchown, otherwise, use chown. If possible, do it without changing
276 the working directory. Otherwise, resort to using save_cwd/fchdir,
277 then mkdir/restore_cwd. If either the save_cwd or the restore_cwd
278 fails, then give a diagnostic and exit nonzero. */
280 #define AT_FUNC_NAME fchownat
281 #define AT_FUNC_F1 lchown
282 #define AT_FUNC_F2 chown
283 #define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW
284 #define AT_FUNC_POST_FILE_PARAM_DECLS , uid_t owner, gid_t group, int flag
285 #define AT_FUNC_POST_FILE_ARGS , owner, group