1 /* provide a replacement openat function
2 Copyright (C) 2004, 2005 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 */
26 #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
28 #include "openat-priv.h"
36 /* Replacement for Solaris' openat function.
37 <http://www.google.com/search?q=openat+site:docs.sun.com>
38 Simulate it by doing save_cwd/fchdir/open/restore_cwd.
39 If either the save_cwd or the restore_cwd fails (relatively unlikely,
40 and usually indicative of a problem that deserves close attention),
41 then give a diagnostic and exit nonzero.
42 Otherwise, upon failure, set errno and return -1, as openat does.
43 Upon successful completion, return a file descriptor. */
45 openat (int fd, char const *file, int flags, ...)
52 va_start (arg, flags);
54 /* If mode_t is narrower than int, use the promoted type (int),
55 not mode_t. Use sizeof to guess whether mode_t is nerrower;
56 we don't know of any practical counterexamples. */
57 if (sizeof (mode_t) < sizeof (int))
58 mode = va_arg (arg, int);
60 mode = va_arg (arg, mode_t);
65 return openat_permissive (fd, file, flags, mode, NULL);
68 /* Like openat (FD, FILE, FLAGS, MODE), but if CWD_ERRNO is
69 nonnull, set *CWD_ERRNO to an errno value if unable to save
70 or restore the initial working directory. This is needed only
71 the first time remove.c's remove_dir opens a command-line
74 If a previous attempt to restore the current working directory
75 failed, then we must not even try to access a `.'-relative name.
76 It is the caller's responsibility not to call this function
80 openat_permissive (int fd, char const *file, int flags, mode_t mode,
83 struct saved_cwd saved_cwd;
88 if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
89 return open (file, flags, mode);
93 BUILD_PROC_NAME (proc_file, fd, file);
94 err = open (proc_file, flags, mode);
95 /* If the syscall succeeds, or if it fails with an unexpected
96 errno value, then return right away. Otherwise, fall through
97 and resort to using save_cwd/restore_cwd. */
98 if (0 <= err || ! EXPECTED_ERRNO (errno))
102 save_ok = (save_cwd (&saved_cwd) == 0);
106 openat_save_fail (errno);
115 err = open (file, flags, mode);
117 if (save_ok && restore_cwd (&saved_cwd) != 0)
120 openat_restore_fail (errno);
125 free_cwd (&saved_cwd);
132 /* Replacement for Solaris' function by the same name.
133 <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
134 Simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
135 If either the save_cwd or the restore_cwd fails (relatively unlikely,
136 and usually indicative of a problem that deserves close attention),
137 then give a diagnostic and exit nonzero.
138 Otherwise, this function works just like Solaris' fdopendir.
141 Unlike the other fd-related functions here, this one
142 effectively consumes its FD parameter. The caller should not
143 close or otherwise manipulate FD if this function returns successfully. */
147 struct saved_cwd saved_cwd;
152 BUILD_PROC_NAME (proc_file, fd, ".");
153 dir = opendir (proc_file);
156 /* If the syscall fails with an expected errno value, resort to
157 save_cwd/restore_cwd. */
158 if (! dir && EXPECTED_ERRNO (saved_errno))
160 if (save_cwd (&saved_cwd) != 0)
161 openat_save_fail (errno);
163 if (fchdir (fd) != 0)
173 if (restore_cwd (&saved_cwd) != 0)
174 openat_restore_fail (errno);
177 free_cwd (&saved_cwd);
188 /* Replacement for Solaris' function by the same name.
189 <http://www.google.com/search?q=fstatat+site:docs.sun.com>
190 Simulate it by doing save_cwd/fchdir/(stat|lstat)/restore_cwd.
191 If either the save_cwd or the restore_cwd fails (relatively unlikely,
192 and usually indicative of a problem that deserves close attention),
193 then give a diagnostic and exit nonzero.
194 Otherwise, this function works just like Solaris' fstatat. */
196 fstatat (int fd, char const *file, struct stat *st, int flag)
198 struct saved_cwd saved_cwd;
202 if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
203 return (flag == AT_SYMLINK_NOFOLLOW
209 BUILD_PROC_NAME (proc_file, fd, file);
210 err = (flag == AT_SYMLINK_NOFOLLOW
211 ? lstat (proc_file, st)
212 : stat (proc_file, st));
213 /* If the syscall succeeds, or if it fails with an unexpected
214 errno value, then return right away. Otherwise, fall through
215 and resort to using save_cwd/restore_cwd. */
216 if (0 <= err || ! EXPECTED_ERRNO (errno))
220 if (save_cwd (&saved_cwd) != 0)
221 openat_save_fail (errno);
228 err = (flag == AT_SYMLINK_NOFOLLOW
233 if (restore_cwd (&saved_cwd) != 0)
234 openat_restore_fail (errno);
237 free_cwd (&saved_cwd);
242 /* Replacement for Solaris' function by the same name.
243 <http://www.google.com/search?q=unlinkat+site:docs.sun.com>
244 Simulate it by doing save_cwd/fchdir/(unlink|rmdir)/restore_cwd.
245 If either the save_cwd or the restore_cwd fails (relatively unlikely,
246 and usually indicative of a problem that deserves close attention),
247 then give a diagnostic and exit nonzero.
248 Otherwise, this function works just like Solaris' unlinkat. */
250 unlinkat (int fd, char const *file, int flag)
252 struct saved_cwd saved_cwd;
256 if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
257 return (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
261 BUILD_PROC_NAME (proc_file, fd, file);
262 err = (flag == AT_REMOVEDIR ? rmdir (proc_file) : unlink (proc_file));
263 /* If the syscall succeeds, or if it fails with an unexpected
264 errno value, then return right away. Otherwise, fall through
265 and resort to using save_cwd/restore_cwd. */
266 if (0 <= err || ! EXPECTED_ERRNO (errno))
270 if (save_cwd (&saved_cwd) != 0)
271 openat_save_fail (errno);
278 err = (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
281 if (restore_cwd (&saved_cwd) != 0)
282 openat_restore_fail (errno);
285 free_cwd (&saved_cwd);