1 /* eaccess.c - eaccess replacement for the shell, plus other access functions. */
3 /* Copyright (C) 2006-2010 Free Software Foundation, Inc.
5 This file is part of GNU Bash, the Bourne Again SHell.
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.
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.
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/>.
21 #if defined (HAVE_CONFIG_H)
27 #include "bashtypes.h"
29 #if defined (HAVE_UNISTD_H)
40 #if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
41 # include <sys/file.h>
42 #endif /* !_POSIX_VERSION */
43 #include "posixstat.h"
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));
65 if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
67 else if (STREQN (path, "/dev/std", 8))
69 if (STREQ (path+8, "in") || STREQ (path+8, "out") || STREQ (path+8, "err"))
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. */
85 static char *pbuf = 0;
92 if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
94 #if !defined (HAVE_DEV_FD)
98 if (legal_number (path + 8, &fd) && fd == (int)fd)
100 r = fstat ((int)fd, finfo);
101 if (r == 0 || errno != EBADF)
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 */
117 #if !defined (HAVE_DEV_STDIN)
118 else if (STREQN (path, "/dev/std", 8))
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));
127 return (stat (path, finfo));
129 #endif /* !HAVE_DEV_STDIN */
130 return (stat (path, finfo));
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). */
137 sh_stataccess (path, mode)
143 if (sh_stat (path, &st) < 0)
146 if (current_user.euid == 0)
148 /* Root can read or write any file. */
149 if ((mode & X_OK) == 0)
152 /* Root can execute any file that has any one of the execute
154 if (st.st_mode & S_IXUGO)
158 if (st.st_uid == current_user.euid) /* owner */
160 else if (group_member (st.st_gid))
163 if (st.st_mode & mode)
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. */
174 sh_euidaccess (path, mode)
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);
185 r = access (path, mode);
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);
199 sh_eaccess (path, mode)
205 if (path_is_devfd (path))
206 return (sh_stataccess (path, mode));
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 */
219 #elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */
220 return access (path, mode|EFF_ONLY_OK);
223 return (sh_stataccess (path, mode));
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));
230 if (current_user.uid == current_user.euid && current_user.gid == current_user.egid)
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));
240 return (sh_stataccess (path, mode));