* srclist.txt: Bug 1423 is closed, but 1439 remains.
[pspp] / lib / openat.c
1 /* provide a replacement openat function
2    Copyright (C) 2004, 2005 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 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include "openat.h"
25
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <fcntl.h>
31
32 #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
33 #include "save-cwd.h"
34
35 #include "gettext.h"
36 #define _(msgid) gettext (msgid)
37
38 /* Replacement for Solaris' openat function.
39    <http://www.google.com/search?q=openat+site:docs.sun.com>
40    Simulate it by doing save_cwd/fchdir/open/restore_cwd.
41    If either the save_cwd or the restore_cwd fails (relatively unlikely,
42    and usually indicative of a problem that deserves close attention),
43    then give a diagnostic and exit nonzero.
44    Otherwise, upon failure, set errno and return -1, as openat does.
45    Upon successful completion, return a file descriptor.  */
46 int
47 rpl_openat (int fd, char const *file, int flags, ...)
48 {
49   struct saved_cwd saved_cwd;
50   int saved_errno;
51   int new_fd;
52   mode_t mode = 0;
53
54   if (flags & O_CREAT)
55     {
56       va_list arg;
57       va_start (arg, flags);
58
59       /* Assume that mode_t is passed compatibly with mode_t's type
60          after argument promotion.  */
61       mode = va_arg (arg, mode_t);
62
63       va_end (arg);
64     }
65
66   if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
67     return open (file, flags, mode);
68
69   if (save_cwd (&saved_cwd) != 0)
70     openat_save_fail (errno);
71
72   if (fchdir (fd) != 0)
73     {
74       saved_errno = errno;
75       free_cwd (&saved_cwd);
76       errno = saved_errno;
77       return -1;
78     }
79
80   new_fd = open (file, flags, mode);
81   saved_errno = errno;
82
83   if (restore_cwd (&saved_cwd) != 0)
84     openat_restore_fail (errno);
85
86   free_cwd (&saved_cwd);
87
88   errno = saved_errno;
89   return new_fd;
90 }
91
92 #if !HAVE_FDOPENDIR
93
94 /* Replacement for Solaris' function by the same name.
95    <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
96    Simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
97    If either the save_cwd or the restore_cwd fails (relatively unlikely,
98    and usually indicative of a problem that deserves close attention),
99    then give a diagnostic and exit nonzero.
100    Otherwise, this function works just like Solaris' fdopendir.
101
102    W A R N I N G:
103    Unlike the other fd-related functions here, this one
104    effectively consumes its FD parameter.  The caller should not
105    close or otherwise manipulate FD if this function returns successfully.  */
106 DIR *
107 fdopendir (int fd)
108 {
109   struct saved_cwd saved_cwd;
110   int saved_errno;
111   DIR *dir;
112
113   if (save_cwd (&saved_cwd) != 0)
114     openat_save_fail (errno);
115
116   if (fchdir (fd) != 0)
117     {
118       saved_errno = errno;
119       free_cwd (&saved_cwd);
120       errno = saved_errno;
121       return NULL;
122     }
123
124   dir = opendir (".");
125   saved_errno = errno;
126
127   if (restore_cwd (&saved_cwd) != 0)
128     openat_restore_fail (errno);
129
130   free_cwd (&saved_cwd);
131   if (dir)
132     close (fd);
133
134   errno = saved_errno;
135   return dir;
136 }
137
138 #endif
139
140 /* Replacement for Solaris' function by the same name.
141    <http://www.google.com/search?q=fstatat+site:docs.sun.com>
142    Simulate it by doing save_cwd/fchdir/(stat|lstat)/restore_cwd.
143    If either the save_cwd or the restore_cwd fails (relatively unlikely,
144    and usually indicative of a problem that deserves close attention),
145    then give a diagnostic and exit nonzero.
146    Otherwise, this function works just like Solaris' fstatat.  */
147 int
148 fstatat (int fd, char const *file, struct stat *st, int flag)
149 {
150   struct saved_cwd saved_cwd;
151   int saved_errno;
152   int err;
153
154   if (fd == AT_FDCWD)
155     return (flag == AT_SYMLINK_NOFOLLOW
156             ? lstat (file, st)
157             : stat (file, st));
158
159   if (save_cwd (&saved_cwd) != 0)
160     openat_save_fail (errno);
161
162   if (fchdir (fd) != 0)
163     {
164       saved_errno = errno;
165       free_cwd (&saved_cwd);
166       errno = saved_errno;
167       return -1;
168     }
169
170   err = (flag == AT_SYMLINK_NOFOLLOW
171          ? lstat (file, st)
172          : stat (file, st));
173   saved_errno = errno;
174
175   if (restore_cwd (&saved_cwd) != 0)
176     openat_restore_fail (errno);
177
178   free_cwd (&saved_cwd);
179
180   errno = saved_errno;
181   return err;
182 }
183
184 /* Replacement for Solaris' function by the same name.
185    <http://www.google.com/search?q=unlinkat+site:docs.sun.com>
186    Simulate it by doing save_cwd/fchdir/(unlink|rmdir)/restore_cwd.
187    If either the save_cwd or the restore_cwd fails (relatively unlikely,
188    and usually indicative of a problem that deserves close attention),
189    then give a diagnostic and exit nonzero.
190    Otherwise, this function works just like Solaris' unlinkat.  */
191 int
192 unlinkat (int fd, char const *file, int flag)
193 {
194   struct saved_cwd saved_cwd;
195   int saved_errno;
196   int err;
197
198   if (fd == AT_FDCWD)
199     return (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
200
201   if (save_cwd (&saved_cwd) != 0)
202     openat_save_fail (errno);
203
204   if (fchdir (fd) != 0)
205     {
206       saved_errno = errno;
207       free_cwd (&saved_cwd);
208       errno = saved_errno;
209       return -1;
210     }
211
212   err = (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
213   saved_errno = errno;
214
215   if (restore_cwd (&saved_cwd) != 0)
216     openat_restore_fail (errno);
217
218   free_cwd (&saved_cwd);
219
220   errno = saved_errno;
221   return err;
222 }