1 /* Get permissions of a file. -*- coding: utf-8 -*-
2
3 Copyright (C) 2002-2003, 2005-2021 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18 Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
19
20 #include <config.h>
21
22 #include <string.h>
23 #include "acl.h"
24
25 #include "acl-internal.h"
26
27 /* Read the permissions of a file into CTX. If DESC is a valid file descriptor,
28 use file descriptor operations, else use filename based operations on NAME.
29 MODE is the file mode obtained from a previous stat call.
30 Return 0 if successful. Return -1 and set errno upon failure. */
31
32 int
get_permissions(const char * name,int desc,mode_t mode,struct permission_context * ctx)33 get_permissions (const char *name, int desc, mode_t mode,
34 struct permission_context *ctx)
35 {
36 memset (ctx, 0, sizeof *ctx);
37 ctx->mode = mode;
38
39 #if USE_ACL && HAVE_ACL_GET_FILE
40 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
41 /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
42 # if !HAVE_ACL_TYPE_EXTENDED
43 /* Linux, FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
44
45 if (HAVE_ACL_GET_FD && desc != -1)
46 ctx->acl = acl_get_fd (desc);
47 else
48 ctx->acl = acl_get_file (name, ACL_TYPE_ACCESS);
49 if (ctx->acl == NULL)
50 return acl_errno_valid (errno) ? -1 : 0;
51
52 /* With POSIX ACLs, a file cannot have "no" acl; a file without
53 extended permissions has a "minimal" acl which is equivalent to the
54 file mode. */
55
56 if (S_ISDIR (mode))
57 {
58 ctx->default_acl = acl_get_file (name, ACL_TYPE_DEFAULT);
59 if (ctx->default_acl == NULL)
60 return -1;
61 }
62
63 # if HAVE_ACL_TYPE_NFS4 /* FreeBSD */
64
65 /* TODO (see set_permissions). */
66
67 # endif
68
69 # else /* HAVE_ACL_TYPE_EXTENDED */
70 /* Mac OS X */
71
72 /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
73 and acl_get_file (name, ACL_TYPE_DEFAULT)
74 always return NULL / EINVAL. You have to use
75 acl_get_file (name, ACL_TYPE_EXTENDED)
76 or acl_get_fd (open (name, ...))
77 to retrieve an ACL.
78 On the other hand,
79 acl_set_file (name, ACL_TYPE_ACCESS, acl)
80 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
81 have the same effect as
82 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
83 Each of these calls sets the file's ACL. */
84
85 if (HAVE_ACL_GET_FD && desc != -1)
86 ctx->acl = acl_get_fd (desc);
87 else
88 ctx->acl = acl_get_file (name, ACL_TYPE_EXTENDED);
89 if (ctx->acl == NULL)
90 return acl_errno_valid (errno) ? -1 : 0;
91
92 # endif
93
94 #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
95
96 /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
97 of Unixware. The acl() call returns the access and default ACL both
98 at once. */
99 # ifdef ACE_GETACL
100 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
101 file systems (whereas the other ones are used in UFS file systems).
102 There is an API
103 pathconf (name, _PC_ACL_ENABLED)
104 fpathconf (desc, _PC_ACL_ENABLED)
105 that allows us to determine which of the two kinds of ACLs is supported
106 for the given file. But some file systems may implement this call
107 incorrectly, so better not use it.
108 When fetching the source ACL, we simply fetch both ACL types.
109 When setting the destination ACL, we try either ACL types, assuming
110 that the kernel will translate the ACL from one form to the other.
111 (See in <https://docs.oracle.com/cd/E86824_01/html/E54765/acl-2.html>
112 the description of ENOTSUP.) */
113 for (;;)
114 {
115 int ret;
116
117 if (desc != -1)
118 ret = facl (desc, ACE_GETACLCNT, 0, NULL);
119 else
120 ret = acl (name, ACE_GETACLCNT, 0, NULL);
121 if (ret < 0)
122 {
123 if (errno == ENOSYS || errno == EINVAL)
124 ret = 0;
125 else
126 return -1;
127 }
128 ctx->ace_count = ret;
129
130 if (ctx->ace_count == 0)
131 break;
132
133 ctx->ace_entries = (ace_t *) malloc (ctx->ace_count * sizeof (ace_t));
134 if (ctx->ace_entries == NULL)
135 {
136 errno = ENOMEM;
137 return -1;
138 }
139
140 if (desc != -1)
141 ret = facl (desc, ACE_GETACL, ctx->ace_count, ctx->ace_entries);
142 else
143 ret = acl (name, ACE_GETACL, ctx->ace_count, ctx->ace_entries);
144 if (ret < 0)
145 {
146 if (errno == ENOSYS || errno == EINVAL)
147 {
148 free (ctx->ace_entries);
149 ctx->ace_entries = NULL;
150 ctx->ace_count = 0;
151 break;
152 }
153 else
154 return -1;
155 }
156 if (ret <= ctx->ace_count)
157 {
158 ctx->ace_count = ret;
159 break;
160 }
161 /* Huh? The number of ACL entries has increased since the last call.
162 Repeat. */
163 free (ctx->ace_entries);
164 ctx->ace_entries = NULL;
165 }
166 # endif
167
168 for (;;)
169 {
170 int ret;
171
172 if (desc != -1)
173 ret = facl (desc, GETACLCNT, 0, NULL);
174 else
175 ret = acl (name, GETACLCNT, 0, NULL);
176 if (ret < 0)
177 {
178 if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
179 ret = 0;
180 else
181 return -1;
182 }
183 ctx->count = ret;
184
185 if (ctx->count == 0)
186 break;
187
188 ctx->entries = (aclent_t *) malloc (ctx->count * sizeof (aclent_t));
189 if (ctx->entries == NULL)
190 {
191 errno = ENOMEM;
192 return -1;
193 }
194
195 if (desc != -1)
196 ret = facl (desc, GETACL, ctx->count, ctx->entries);
197 else
198 ret = acl (name, GETACL, ctx->count, ctx->entries);
199 if (ret < 0)
200 {
201 if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
202 {
203 free (ctx->entries);
204 ctx->entries = NULL;
205 ctx->count = 0;
206 break;
207 }
208 else
209 return -1;
210 }
211 if (ret <= ctx->count)
212 {
213 ctx->count = ret;
214 break;
215 }
216 /* Huh? The number of ACL entries has increased since the last call.
217 Repeat. */
218 free (ctx->entries);
219 ctx->entries = NULL;
220 }
221
222 #elif USE_ACL && HAVE_GETACL /* HP-UX */
223
224 {
225 int ret;
226
227 if (desc != -1)
228 ret = fgetacl (desc, NACLENTRIES, ctx->entries);
229 else
230 ret = getacl (name, NACLENTRIES, ctx->entries);
231 if (ret < 0)
232 {
233 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
234 ret = 0;
235 else
236 return -1;
237 }
238 else if (ret > NACLENTRIES)
239 /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
240 abort ();
241 ctx->count = ret;
242
243 # if HAVE_ACLV_H
244 ret = acl ((char *) name, ACL_GET, NACLVENTRIES, ctx->aclv_entries);
245 if (ret < 0)
246 {
247 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
248 ret = 0;
249 else
250 return -2;
251 }
252 else if (ret > NACLVENTRIES)
253 /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */
254 abort ();
255 ctx->aclv_count = ret;
256 # endif
257 }
258
259 #elif USE_ACL && HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */
260
261 /* TODO (see set_permissions). */
262
263 #elif USE_ACL && HAVE_STATACL /* older AIX */
264
265 {
266 int ret;
267 if (desc != -1)
268 ret = fstatacl (desc, STX_NORMAL, &ctx->u.a, sizeof ctx->u);
269 else
270 ret = statacl ((char *) name, STX_NORMAL, &ctx->u.a, sizeof ctx->u);
271 if (ret == 0)
272 ctx->have_u = true;
273 }
274
275 #elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
276
277 {
278 int ret = acl ((char *) name, ACL_GET, NACLENTRIES, ctx->entries);
279 if (ret < 0)
280 return -1;
281 else if (ret > NACLENTRIES)
282 /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
283 abort ();
284 ctx->count = ret;
285 }
286
287 #endif
288
289 return 0;
290
291 }
292