xref: /netbsd-src/external/gpl2/xcvs/dist/lib/openat.c (revision 5a6c14c844c4c665da5632061aebde7bb2cb5766)
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