prepare for running sanity.sh and uploading if it works
[alioth/cvs.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 "error.h"
33 #include "exitfail.h"
34 #include "save-cwd.h"
35
36 #include "gettext.h"
37 #define _(msgid) gettext (msgid)
38
39 /* Replacement for Solaris' openat function.
40    <http://www.google.com/search?q=openat+site:docs.sun.com>
41    Simulate it by doing save_cwd/fchdir/open/restore_cwd.
42    If either the save_cwd or the restore_cwd fails (relatively unlikely,
43    and usually indicative of a problem that deserves close attention),
44    then give a diagnostic and exit nonzero.
45    Otherwise, upon failure, set errno and return -1, as openat does.
46    Upon successful completion, return a file descriptor.  */
47 int
48 rpl_openat (int fd, char const *file, int flags, ...)
49 {
50   struct saved_cwd saved_cwd;
51   int saved_errno;
52   int new_fd;
53   mode_t mode = 0;
54
55   if (flags & O_CREAT)
56     {
57       va_list arg;
58       va_start (arg, flags);
59
60       /* Assume that mode_t is passed compatibly with mode_t's type
61          after argument promotion.  */
62       mode = va_arg (arg, mode_t);
63
64       va_end (arg);
65     }
66
67   if (fd == AT_FDCWD || *file == '/')
68     return open (file, flags, mode);
69
70   if (save_cwd (&saved_cwd) != 0)
71     error (exit_failure, errno,
72            _("openat: unable to record current working directory"));
73
74   if (fchdir (fd) != 0)
75     {
76       saved_errno = errno;
77       free_cwd (&saved_cwd);
78       errno = saved_errno;
79       return -1;
80     }
81
82   new_fd = open (file, flags, mode);
83   saved_errno = errno;
84
85   if (restore_cwd (&saved_cwd) != 0)
86     error (exit_failure, errno,
87            _("openat: unable to restore working directory"));
88
89   free_cwd (&saved_cwd);
90
91   errno = saved_errno;
92   return new_fd;
93 }
94
95 /* Replacement for Solaris' function by the same name.
96    <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
97    Simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
98    If either the save_cwd or the restore_cwd fails (relatively unlikely,
99    and usually indicative of a problem that deserves close attention),
100    then give a diagnostic and exit nonzero.
101    Otherwise, this function works just like Solaris' fdopendir.  */
102 DIR *
103 fdopendir (int fd)
104 {
105   struct saved_cwd saved_cwd;
106   int saved_errno;
107   DIR *dir;
108
109   if (fd == AT_FDCWD)
110     return opendir (".");
111
112   if (save_cwd (&saved_cwd) != 0)
113     error (exit_failure, errno,
114            _("fdopendir: unable to record current working directory"));
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     error (exit_failure, errno,
129            _("fdopendir: unable to restore working directory"));
130
131   free_cwd (&saved_cwd);
132
133   errno = saved_errno;
134   return dir;
135 }
136
137 /* Replacement for Solaris' function by the same name.
138    <http://www.google.com/search?q=fstatat+site:docs.sun.com>
139    Simulate it by doing save_cwd/fchdir/(stat|lstat)/restore_cwd.
140    If either the save_cwd or the restore_cwd fails (relatively unlikely,
141    and usually indicative of a problem that deserves close attention),
142    then give a diagnostic and exit nonzero.
143    Otherwise, this function works just like Solaris' fstatat.  */
144 int
145 fstatat (int fd, char const *file, struct stat *st, int flag)
146 {
147   struct saved_cwd saved_cwd;
148   int saved_errno;
149   int err;
150
151   if (fd == AT_FDCWD)
152     return (flag == AT_SYMLINK_NOFOLLOW
153             ? lstat (file, st)
154             : stat (file, st));
155
156   if (save_cwd (&saved_cwd) != 0)
157     error (exit_failure, errno,
158            _("fstatat: unable to record current working directory"));
159
160   if (fchdir (fd) != 0)
161     {
162       saved_errno = errno;
163       free_cwd (&saved_cwd);
164       errno = saved_errno;
165       return -1;
166     }
167
168   err = (flag == AT_SYMLINK_NOFOLLOW
169          ? lstat (file, st)
170          : stat (file, st));
171   saved_errno = errno;
172
173   if (restore_cwd (&saved_cwd) != 0)
174     error (exit_failure, errno,
175            _("fstatat: unable to restore working directory"));
176
177   free_cwd (&saved_cwd);
178
179   errno = saved_errno;
180   return err;
181 }