* modules/openat (Files): Add lib/openat-die.c.
[gnulib.git] / 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 /* Replacement for Solaris' function by the same name.
93    <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
94    Simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
95    If either the save_cwd or the restore_cwd fails (relatively unlikely,
96    and usually indicative of a problem that deserves close attention),
97    then give a diagnostic and exit nonzero.
98    Otherwise, this function works just like Solaris' fdopendir.
99
100    W A R N I N G:
101    Unlike the other fd-related functions here, this one
102    effectively consumes its FD parameter.  The caller should not
103    close or otherwise manipulate FD after calling this function.  */
104 DIR *
105 fdopendir (int fd)
106 {
107   struct saved_cwd saved_cwd;
108   int saved_errno;
109   DIR *dir;
110
111   if (fd == AT_FDCWD)
112     return opendir (".");
113
114   if (save_cwd (&saved_cwd) != 0)
115     openat_save_fail (errno);
116
117   if (fchdir (fd) != 0)
118     {
119       saved_errno = errno;
120       free_cwd (&saved_cwd);
121       close (fd);
122       errno = saved_errno;
123       return NULL;
124     }
125
126   dir = opendir (".");
127   saved_errno = errno;
128
129   if (restore_cwd (&saved_cwd) != 0)
130     openat_restore_fail (errno);
131
132   free_cwd (&saved_cwd);
133   close (fd);
134
135   errno = saved_errno;
136   return dir;
137 }
138
139 /* Replacement for Solaris' function by the same name.
140    <http://www.google.com/search?q=fstatat+site:docs.sun.com>
141    Simulate it by doing save_cwd/fchdir/(stat|lstat)/restore_cwd.
142    If either the save_cwd or the restore_cwd fails (relatively unlikely,
143    and usually indicative of a problem that deserves close attention),
144    then give a diagnostic and exit nonzero.
145    Otherwise, this function works just like Solaris' fstatat.  */
146 int
147 fstatat (int fd, char const *file, struct stat *st, int flag)
148 {
149   struct saved_cwd saved_cwd;
150   int saved_errno;
151   int err;
152
153   if (fd == AT_FDCWD)
154     return (flag == AT_SYMLINK_NOFOLLOW
155             ? lstat (file, st)
156             : stat (file, st));
157
158   if (save_cwd (&saved_cwd) != 0)
159     openat_save_fail (errno);
160
161   if (fchdir (fd) != 0)
162     {
163       saved_errno = errno;
164       free_cwd (&saved_cwd);
165       errno = saved_errno;
166       return -1;
167     }
168
169   err = (flag == AT_SYMLINK_NOFOLLOW
170          ? lstat (file, st)
171          : stat (file, st));
172   saved_errno = errno;
173
174   if (restore_cwd (&saved_cwd) != 0)
175     openat_restore_fail (errno);
176
177   free_cwd (&saved_cwd);
178
179   errno = saved_errno;
180   return err;
181 }
182
183 /* Replacement for Solaris' function by the same name.
184    <http://www.google.com/search?q=unlinkat+site:docs.sun.com>
185    Simulate it by doing save_cwd/fchdir/(unlink|rmdir)/restore_cwd.
186    If either the save_cwd or the restore_cwd fails (relatively unlikely,
187    and usually indicative of a problem that deserves close attention),
188    then give a diagnostic and exit nonzero.
189    Otherwise, this function works just like Solaris' unlinkat.  */
190 int
191 unlinkat (int fd, char const *file, int flag)
192 {
193   struct saved_cwd saved_cwd;
194   int saved_errno;
195   int err;
196
197   if (fd == AT_FDCWD)
198     return (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
199
200   if (save_cwd (&saved_cwd) != 0)
201     openat_save_fail (errno);
202
203   if (fchdir (fd) != 0)
204     {
205       saved_errno = errno;
206       free_cwd (&saved_cwd);
207       errno = saved_errno;
208       return -1;
209     }
210
211   err = (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
212   saved_errno = errno;
213
214   if (restore_cwd (&saved_cwd) != 0)
215     openat_restore_fail (errno);
216
217   free_cwd (&saved_cwd);
218
219   errno = saved_errno;
220   return err;
221 }