1cff692c1527d861d222e8891cbb4db7611b4bf5
[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 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 it under
8    the terms of the GNU General Public License as published by the Free
9    Software Foundation; either version 2, or (at your option) any later
10    version.
11
12    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13    WARRANTY; without even the implied warranty of MERCHANTABILITY or
14    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15    for more details.
16
17    You should have received a copy of the GNU General Public License along
18    with Bash; see the file COPYING.  If not, write to the Free Software
19    Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
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   if (*path == '\0')
86     {
87       errno = ENOENT;
88       return (-1);
89     }
90   if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
91     {
92 #if !defined (HAVE_DEV_FD)
93       intmax_t fd;
94       int r;
95
96       if (legal_number (path + 8, &fd) && fd == (int)fd)
97         {
98           r = fstat ((int)fd, finfo);
99           if (r == 0 || errno != EBADF)
100             return (r);
101         }
102       errno = ENOENT;
103       return (-1);
104 #else
105   /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
106      trailing slash.  Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
107      On most systems, with the notable exception of linux, this is
108      effectively a no-op. */
109       char pbuf[32];
110       strcpy (pbuf, DEV_FD_PREFIX);
111       strcat (pbuf, path + 8);
112       return (stat (pbuf, finfo));
113 #endif /* !HAVE_DEV_FD */
114     }
115 #if !defined (HAVE_DEV_STDIN)
116   else if (STREQN (path, "/dev/std", 8))
117     {
118       if (STREQ (path+8, "in"))
119         return (fstat (0, finfo));
120       else if (STREQ (path+8, "out"))
121         return (fstat (1, finfo));
122       else if (STREQ (path+8, "err"))
123         return (fstat (2, finfo));
124       else
125         return (stat (path, finfo));
126     }
127 #endif /* !HAVE_DEV_STDIN */
128   return (stat (path, finfo));
129 }
130
131 /* Do the same thing access(2) does, but use the effective uid and gid,
132    and don't make the mistake of telling root that any file is
133    executable.  This version uses stat(2). */
134 static int
135 sh_stataccess (path, mode)
136      char *path;
137      int mode;
138 {
139   struct stat st;
140
141   if (sh_stat (path, &st) < 0)
142     return (-1);
143
144   if (current_user.euid == 0)
145     {
146       /* Root can read or write any file. */
147       if ((mode & X_OK) == 0)
148         return (0);
149
150       /* Root can execute any file that has any one of the execute
151          bits set. */
152       if (st.st_mode & S_IXUGO)
153         return (0);
154     }
155
156   if (st.st_uid == current_user.euid)   /* owner */
157     mode <<= 6;
158   else if (group_member (st.st_gid))
159     mode <<= 3;
160
161   if (st.st_mode & mode)
162     return (0);
163
164   errno = EACCES;
165   return (-1);
166 }
167
168 #if HAVE_DECL_SETREGID
169 /* Version to call when uid != euid or gid != egid.  We temporarily swap
170    the effective and real uid and gid as appropriate. */
171 static int
172 sh_euidaccess (path, mode)
173      char *path;
174      int mode;
175 {
176   int r, e;
177
178   if (current_user.uid != current_user.euid)
179     setreuid (current_user.euid, current_user.uid);
180   if (current_user.gid != current_user.egid)
181     setregid (current_user.egid, current_user.gid);
182
183   r = access (path, mode);
184   e = errno;
185
186   if (current_user.uid != current_user.euid)
187     setreuid (current_user.uid, current_user.euid);
188   if (current_user.gid != current_user.egid)
189     setregid (current_user.gid, current_user.egid);
190
191   errno = e;
192   return r;  
193 }
194 #endif
195
196 int
197 sh_eaccess (path, mode)
198      char *path;
199      int mode;
200 {
201   if (path_is_devfd (path))
202     return (sh_stataccess (path, mode));
203
204 #if defined (HAVE_EACCESS)              /* FreeBSD */
205   return (eaccess (path, mode));
206 #elif defined (EFF_ONLY_OK)             /* SVR4(?), SVR4.2 */
207   return access (path, mode|EFF_ONLY_OK);
208 #else
209   if (mode == F_OK)
210     return (sh_stataccess (path, mode));
211     
212 #  if HAVE_DECL_SETREGID
213   if (current_user.uid != current_user.euid || current_user.gid != current_user.egid)
214     return (sh_euidaccess (path, mode));
215 #  endif
216
217   if (current_user.uid == current_user.euid && current_user.gid == current_user.egid)
218     return (access (path, mode));  
219
220   return (sh_stataccess (path, mode));
221 #endif
222 }