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
rpl_openat(int fd,char const * file,int flags,...)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 *
fdopendir(int fd)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
fstatat(int fd,char const * file,struct stat * st,int flag)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 }
182