source: trunk/essentials/app-arch/tar/lib/openat.c

Last change on this file was 3342, checked in by bird, 18 years ago

tar 1.16.1

File size: 8.3 KB
Line 
1/* provide a replacement openat function
2 Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
3
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)
7 any later version.
8
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.
13
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. */
17
18/* written by Jim Meyering */
19
20#include <config.h>
21
22#include "openat.h"
23
24#include <stdarg.h>
25#include <stddef.h>
26
27#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
28#include "fcntl--.h"
29#include "lchown.h"
30#include "lstat.h"
31#include "openat-priv.h"
32#include "save-cwd.h"
33
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. */
42int
43openat (int fd, char const *file, int flags, ...)
44{
45 mode_t mode = 0;
46
47 if (flags & O_CREAT)
48 {
49 va_list arg;
50 va_start (arg, flags);
51
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)
56 ? va_arg (arg, int)
57 : va_arg (arg, mode_t));
58
59 va_end (arg);
60 }
61
62 return openat_permissive (fd, file, flags, mode, NULL);
63}
64
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
69 directory argument.
70
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
74 in that case. */
75
76int
77openat_permissive (int fd, char const *file, int flags, mode_t mode,
78 int *cwd_errno)
79{
80 struct saved_cwd saved_cwd;
81 int saved_errno;
82 int err;
83 bool save_ok;
84
85 if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
86 return open (file, flags, mode);
87
88 {
89 char buf[OPENAT_BUFFER_SIZE];
90 char *proc_file = openat_proc_name (buf, fd, file);
91 if (proc_file)
92 {
93 int open_result = open (proc_file, flags, mode);
94 int open_errno = errno;
95 if (proc_file != buf)
96 free (proc_file);
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))
101 {
102 errno = open_errno;
103 return open_result;
104 }
105 }
106 }
107
108 save_ok = (save_cwd (&saved_cwd) == 0);
109 if (! save_ok)
110 {
111 if (! cwd_errno)
112 openat_save_fail (errno);
113 *cwd_errno = errno;
114 }
115
116 err = fchdir (fd);
117 saved_errno = errno;
118
119 if (! err)
120 {
121 err = open (file, flags, mode);
122 saved_errno = errno;
123 if (save_ok && restore_cwd (&saved_cwd) != 0)
124 {
125 if (! cwd_errno)
126 openat_restore_fail (errno);
127 *cwd_errno = errno;
128 }
129 }
130
131 free_cwd (&saved_cwd);
132 errno = saved_errno;
133 return err;
134}
135
136/* Return true if our openat implementation must resort to
137 using save_cwd and restore_cwd. */
138bool
139openat_needs_fchdir (void)
140{
141 bool needs_fchdir = true;
142 int fd = open ("/", O_RDONLY);
143
144 if (0 <= fd)
145 {
146 char buf[OPENAT_BUFFER_SIZE];
147 char *proc_file = openat_proc_name (buf, fd, ".");
148 if (proc_file)
149 {
150 needs_fchdir = false;
151 if (proc_file != buf)
152 free (proc_file);
153 }
154 close (fd);
155 }
156
157 return needs_fchdir;
158}
159
160#if !HAVE_FDOPENDIR
161
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.
169
170 W A R N I N G:
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. */
174DIR *
175fdopendir (int fd)
176{
177 struct saved_cwd saved_cwd;
178 int saved_errno;
179 DIR *dir;
180
181 char buf[OPENAT_BUFFER_SIZE];
182 char *proc_file = openat_proc_name (buf, fd, ".");
183 if (proc_file)
184 {
185 dir = opendir (proc_file);
186 saved_errno = errno;
187 }
188 else
189 {
190 dir = NULL;
191 saved_errno = EOPNOTSUPP;
192 }
193
194 /* If the syscall fails with an expected errno value, resort to
195 save_cwd/restore_cwd. */
196 if (! dir && EXPECTED_ERRNO (saved_errno))
197 {
198 if (save_cwd (&saved_cwd) != 0)
199 openat_save_fail (errno);
200
201 if (fchdir (fd) != 0)
202 {
203 dir = NULL;
204 saved_errno = errno;
205 }
206 else
207 {
208 dir = opendir (".");
209 saved_errno = errno;
210
211 if (restore_cwd (&saved_cwd) != 0)
212 openat_restore_fail (errno);
213 }
214
215 free_cwd (&saved_cwd);
216 }
217
218 if (dir)
219 close (fd);
220 if (proc_file != buf)
221 free (proc_file);
222 errno = saved_errno;
223 return dir;
224}
225
226#endif
227
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. */
235
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
242#include "at-func.c"
243#undef AT_FUNC_NAME
244#undef AT_FUNC_F1
245#undef AT_FUNC_F2
246#undef AT_FUNC_USE_F1_COND
247#undef AT_FUNC_POST_FILE_PARAM_DECLS
248#undef AT_FUNC_POST_FILE_ARGS
249
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. */
257
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 */
264#include "at-func.c"
265#undef AT_FUNC_NAME
266#undef AT_FUNC_F1
267#undef AT_FUNC_F2
268#undef AT_FUNC_USE_F1_COND
269#undef AT_FUNC_POST_FILE_PARAM_DECLS
270#undef AT_FUNC_POST_FILE_ARGS
271
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. */
279
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
286#include "at-func.c"
Note: See TracBrowser for help on using the repository browser.