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 #include <sys/cdefs.h>
18 __RCSID("$NetBSD: openat.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
19
20
21 /* written by Jim Meyering */
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include "openat.h"
28
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <fcntl.h>
34
35 #include "error.h"
36 #include "exitfail.h"
37 #include "save-cwd.h"
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41
42 /* Replacement for Solaris' openat function.
43 <http://www.google.com/search?q=openat+site:docs.sun.com>
44 Simulate it by doing save_cwd/fchdir/open/restore_cwd.
45 If either the save_cwd or the restore_cwd fails (relatively unlikely,
46 and usually indicative of a problem that deserves close attention),
47 then give a diagnostic and exit nonzero.
48 Otherwise, upon failure, set errno and return -1, as openat does.
49 Upon successful completion, return a file descriptor. */
50 int
rpl_openat(int fd,char const * file,int flags,...)51 rpl_openat (int fd, char const *file, int flags, ...)
52 {
53 struct saved_cwd saved_cwd;
54 int saved_errno;
55 int new_fd;
56 mode_t mode = 0;
57
58 if (flags & O_CREAT)
59 {
60 va_list arg;
61 va_start (arg, flags);
62
63 /* Assume that mode_t is passed compatibly with mode_t's type
64 after argument promotion. */
65 mode = va_arg (arg, mode_t);
66
67 va_end (arg);
68 }
69
70 if (fd == AT_FDCWD || *file == '/')
71 return open (file, flags, mode);
72
73 if (save_cwd (&saved_cwd) != 0)
74 error (exit_failure, errno,
75 _("openat: unable to record current working directory"));
76
77 if (fchdir (fd) != 0)
78 {
79 saved_errno = errno;
80 free_cwd (&saved_cwd);
81 errno = saved_errno;
82 return -1;
83 }
84
85 new_fd = open (file, flags, mode);
86 saved_errno = errno;
87
88 if (restore_cwd (&saved_cwd) != 0)
89 error (exit_failure, errno,
90 _("openat: unable to restore working directory"));
91
92 free_cwd (&saved_cwd);
93
94 errno = saved_errno;
95 return new_fd;
96 }
97
98 /* Replacement for Solaris' function by the same name.
99 <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
100 Simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
101 If either the save_cwd or the restore_cwd fails (relatively unlikely,
102 and usually indicative of a problem that deserves close attention),
103 then give a diagnostic and exit nonzero.
104 Otherwise, this function works just like Solaris' fdopendir. */
105 DIR *
fdopendir(int fd)106 fdopendir (int fd)
107 {
108 struct saved_cwd saved_cwd;
109 int saved_errno;
110 DIR *dir;
111
112 if (fd == AT_FDCWD)
113 return opendir (".");
114
115 if (save_cwd (&saved_cwd) != 0)
116 error (exit_failure, errno,
117 _("fdopendir: unable to record current working directory"));
118
119 if (fchdir (fd) != 0)
120 {
121 saved_errno = errno;
122 free_cwd (&saved_cwd);
123 errno = saved_errno;
124 return NULL;
125 }
126
127 dir = opendir (".");
128 saved_errno = errno;
129
130 if (restore_cwd (&saved_cwd) != 0)
131 error (exit_failure, errno,
132 _("fdopendir: unable to restore working directory"));
133
134 free_cwd (&saved_cwd);
135
136 errno = saved_errno;
137 return dir;
138 }
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
fstatat(int fd,char const * file,struct stat * st,int flag)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 error (exit_failure, errno,
161 _("fstatat: unable to record current working directory"));
162
163 if (fchdir (fd) != 0)
164 {
165 saved_errno = errno;
166 free_cwd (&saved_cwd);
167 errno = saved_errno;
168 return -1;
169 }
170
171 err = (flag == AT_SYMLINK_NOFOLLOW
172 ? lstat (file, st)
173 : stat (file, st));
174 saved_errno = errno;
175
176 if (restore_cwd (&saved_cwd) != 0)
177 error (exit_failure, errno,
178 _("fstatat: unable to restore working directory"));
179
180 free_cwd (&saved_cwd);
181
182 errno = saved_errno;
183 return err;
184 }
185