Bash-4.3 distribution sources and documentation
[platform/upstream/bash.git] / lib / sh / eaccess.c
1 /* eaccess.c - eaccess replacement for the shell, plus other access functions. */
2
3 /* Copyright (C) 2006-2010 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    Bash is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    Bash is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #if defined (HAVE_CONFIG_H)
22 #  include <config.h>
23 #endif
24
25 #include <stdio.h>
26
27 #include "bashtypes.h"
28
29 #if defined (HAVE_UNISTD_H)
30 #  include <unistd.h>
31 #endif
32
33 #include "bashansi.h"
34
35 #include <errno.h>
36 #if !defined (errno)
37 extern int errno;
38 #endif /* !errno */
39
40 #if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
41 #  include <sys/file.h>
42 #endif /* !_POSIX_VERSION */
43 #include "posixstat.h"
44 #include "filecntl.h"
45
46 #include "shell.h"
47
48 #if !defined (R_OK)
49 #define R_OK 4
50 #define W_OK 2
51 #define X_OK 1
52 #define F_OK 0
53 #endif /* R_OK */
54
55 static int path_is_devfd __P((const char *));
56 static int sh_stataccess __P((char *, int));
57 #if HAVE_DECL_SETREGID
58 static int sh_euidaccess __P((char *, int));
59 #endif
60
61 static int
62 path_is_devfd (path)
63      const char *path;
64 {
65   if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
66     return 1;
67   else if (STREQN (path, "/dev/std", 8))
68     {
69       if (STREQ (path+8, "in") || STREQ (path+8, "out") || STREQ (path+8, "err"))
70         return 1;
71       else
72         return 0;
73     }
74   else
75     return 0;
76 }
77
78 /* A wrapper for stat () which disallows pathnames that are empty strings
79    and handles /dev/fd emulation on systems that don't have it. */
80 int
81 sh_stat (path, finfo)
82      const char *path;
83      struct stat *finfo;
84 {
85   static char *pbuf = 0;
86
87   if (*path == '\0')
88     {
89       errno = ENOENT;
90       return (-1);
91     }
92   if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
93     {
94 #if !defined (HAVE_DEV_FD)
95       intmax_t fd;
96       int r;
97
98       if (legal_number (path + 8, &fd) && fd == (int)fd)
99         {
100           r = fstat ((int)fd, finfo);
101           if (r == 0 || errno != EBADF)
102             return (r);
103         }
104       errno = ENOENT;
105       return (-1);
106 #else
107   /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
108      trailing slash.  Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
109      On most systems, with the notable exception of linux, this is
110      effectively a no-op. */
111       pbuf = xrealloc (pbuf, sizeof (DEV_FD_PREFIX) + strlen (path + 8));
112       strcpy (pbuf, DEV_FD_PREFIX);
113       strcat (pbuf, path + 8);
114       return (stat (pbuf, finfo));
115 #endif /* !HAVE_DEV_FD */
116     }
117 #if !defined (HAVE_DEV_STDIN)
118   else if (STREQN (path, "/dev/std", 8))
119     {
120       if (STREQ (path+8, "in"))
121         return (fstat (0, finfo));
122       else if (STREQ (path+8, "out"))
123         return (fstat (1, finfo));
124       else if (STREQ (path+8, "err"))
125         return (fstat (2, finfo));
126       else
127         return (stat (path, finfo));
128     }
129 #endif /* !HAVE_DEV_STDIN */
130   return (stat (path, finfo));
131 }
132
133 /* Do the same thing access(2) does, but use the effective uid and gid,
134    and don't make the mistake of telling root that any file is
135    executable.  This version uses stat(2). */
136 static int
137 sh_stataccess (path, mode)
138      char *path;
139      int mode;
140 {
141   struct stat st;
142
143   if (sh_stat (path, &st) < 0)
144     return (-1);
145
146   if (current_user.euid == 0)
147     {
148       /* Root can read or write any file. */
149       if ((mode & X_OK) == 0)
150         return (0);
151
152       /* Root can execute any file that has any one of the execute
153          bits set. */
154       if (st.st_mode & S_IXUGO)
155         return (0);
156     }
157
158   if (st.st_uid == current_user.euid)   /* owner */
159     mode <<= 6;
160   else if (group_member (st.st_gid))
161     mode <<= 3;
162
163   if (st.st_mode & mode)
164     return (0);
165
166   errno = EACCES;
167   return (-1);
168 }
169
170 #if HAVE_DECL_SETREGID
171 /* Version to call when uid != euid or gid != egid.  We temporarily swap
172    the effective and real uid and gid as appropriate. */
173 static int
174 sh_euidaccess (path, mode)
175      char *path;
176      int mode;
177 {
178   int r, e;
179
180   if (current_user.uid != current_user.euid)
181     setreuid (current_user.euid, current_user.uid);
182   if (current_user.gid != current_user.egid)
183     setregid (current_user.egid, current_user.gid);
184
185   r = access (path, mode);
186   e = errno;
187
188   if (current_user.uid != current_user.euid)
189     setreuid (current_user.uid, current_user.euid);
190   if (current_user.gid != current_user.egid)
191     setregid (current_user.gid, current_user.egid);
192
193   errno = e;
194   return r;  
195 }
196 #endif
197
198 int
199 sh_eaccess (path, mode)
200      char *path;
201      int mode;
202 {
203   int ret;
204
205   if (path_is_devfd (path))
206     return (sh_stataccess (path, mode));
207
208 #if (defined (HAVE_FACCESSAT) && defined (AT_EACCESS)) || defined (HAVE_EACCESS)
209 #  if defined (HAVE_FACCESSAT) && defined (AT_EACCESS)
210   ret = faccessat (AT_FDCWD, path, mode, AT_EACCESS);
211 #  else         /* HAVE_EACCESS */      /* FreeBSD */
212   ret = eaccess (path, mode);   /* XXX -- not always correct for X_OK */
213 #  endif        /* HAVE_EACCESS */
214 #  if defined (__FreeBSD__) || defined (SOLARIS)
215   if (ret == 0 && current_user.euid == 0 && mode == X_OK)
216     return (sh_stataccess (path, mode));
217 #  endif        /* __FreeBSD__ || SOLARIS */
218   return ret;
219 #elif defined (EFF_ONLY_OK)             /* SVR4(?), SVR4.2 */
220   return access (path, mode|EFF_ONLY_OK);
221 #else
222   if (mode == F_OK)
223     return (sh_stataccess (path, mode));
224     
225 #  if HAVE_DECL_SETREGID
226   if (current_user.uid != current_user.euid || current_user.gid != current_user.egid)
227     return (sh_euidaccess (path, mode));
228 #  endif
229
230   if (current_user.uid == current_user.euid && current_user.gid == current_user.egid)
231     {
232       ret = access (path, mode);
233 #if defined (__FreeBSD__) || defined (SOLARIS)
234       if (ret == 0 && current_user.euid == 0 && mode == X_OK)
235         return (sh_stataccess (path, mode));
236 #endif
237       return ret;
238     }
239
240   return (sh_stataccess (path, mode));
241 #endif
242 }