Line data Source code
1 : /* acl.c - access control lists
2 :
3 : Copyright (C) 2002, 2003, 2005, 2006, 2007 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 <http://www.gnu.org/licenses/>.
17 :
18 : Written by Paul Eggert and Andreas Gruenbacher. */
19 :
20 : #include <config.h>
21 :
22 : #include "acl.h"
23 :
24 : #include "acl-internal.h"
25 :
26 : /* If DESC is a valid file descriptor use fchmod to change the
27 : file's mode to MODE on systems that have fchown. On systems
28 : that don't have fchown and if DESC is invalid, use chown on
29 : NAME instead. */
30 :
31 : int
32 15 : chmod_or_fchmod (const char *name, int desc, mode_t mode)
33 : {
34 15 : if (HAVE_FCHMOD && desc != -1)
35 15 : return fchmod (desc, mode);
36 : else
37 0 : return chmod (name, mode);
38 : }
39 :
40 : /* Copy access control lists from one file to another. If SOURCE_DESC is
41 : a valid file descriptor, use file descriptor operations, else use
42 : filename based operations on SRC_NAME. Likewise for DEST_DESC and
43 : DST_NAME.
44 : If access control lists are not available, fchmod the target file to
45 : MODE. Also sets the non-permission bits of the destination file
46 : (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
47 : Return 0 if successful, otherwise output a diagnostic and return -1. */
48 :
49 : int
50 0 : copy_acl (const char *src_name, int source_desc, const char *dst_name,
51 : int dest_desc, mode_t mode)
52 : {
53 : int ret;
54 :
55 : #if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
56 : /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
57 :
58 : acl_t acl;
59 : if (HAVE_ACL_GET_FD && source_desc != -1)
60 : acl = acl_get_fd (source_desc);
61 : else
62 : acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
63 : if (acl == NULL)
64 : {
65 : if (ACL_NOT_WELL_SUPPORTED (errno))
66 : return set_acl (dst_name, dest_desc, mode);
67 : else
68 : {
69 : error (0, errno, "%s", quote (src_name));
70 : return -1;
71 : }
72 : }
73 :
74 : if (HAVE_ACL_SET_FD && dest_desc != -1)
75 : ret = acl_set_fd (dest_desc, acl);
76 : else
77 : ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
78 : if (ret != 0)
79 : {
80 : int saved_errno = errno;
81 :
82 : if (ACL_NOT_WELL_SUPPORTED (errno))
83 : {
84 : int n = acl_entries (acl);
85 :
86 : acl_free (acl);
87 : /* On most hosts an ACL is trivial if n == 3, and it cannot be
88 : less than 3. On IRIX 6.5 it is also trivial if n == -1.
89 : For simplicity and safety, assume the ACL is trivial if n <= 3.
90 : Also see file-has-acl.c for some of the other possibilities;
91 : it's not clear whether that complexity is needed here. */
92 : if (n <= 3)
93 : {
94 : if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
95 : saved_errno = errno;
96 : else
97 : return 0;
98 : }
99 : else
100 : chmod_or_fchmod (dst_name, dest_desc, mode);
101 : }
102 : else
103 : {
104 : acl_free (acl);
105 : chmod_or_fchmod (dst_name, dest_desc, mode);
106 : }
107 : error (0, saved_errno, _("preserving permissions for %s"),
108 : quote (dst_name));
109 : return -1;
110 : }
111 : else
112 : acl_free (acl);
113 :
114 : if (mode & (S_ISUID | S_ISGID | S_ISVTX))
115 : {
116 : /* We did not call chmod so far, so the special bits have not yet
117 : been set. */
118 :
119 : if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
120 : {
121 : error (0, errno, _("preserving permissions for %s"),
122 : quote (dst_name));
123 : return -1;
124 : }
125 : }
126 :
127 : if (S_ISDIR (mode))
128 : {
129 : acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
130 : if (acl == NULL)
131 : {
132 : error (0, errno, "%s", quote (src_name));
133 : return -1;
134 : }
135 :
136 : if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
137 : {
138 : error (0, errno, _("preserving permissions for %s"),
139 : quote (dst_name));
140 : acl_free (acl);
141 : return -1;
142 : }
143 : else
144 : acl_free (acl);
145 : }
146 : return 0;
147 :
148 : #else
149 :
150 : # if USE_ACL && defined ACL_NO_TRIVIAL
151 : /* Solaris 10 NFSv4 ACLs. */
152 : acl_t *aclp = NULL;
153 : ret = (source_desc < 0
154 : ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp)
155 : : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp));
156 : if (ret != 0 && errno != ENOSYS)
157 : {
158 : error (0, errno, "%s", quote (src_name));
159 : return ret;
160 : }
161 : # endif
162 :
163 0 : ret = qset_acl (dst_name, dest_desc, mode);
164 0 : if (ret != 0)
165 0 : error (0, errno, _("preserving permissions for %s"), quote (dst_name));
166 :
167 : # if USE_ACL && defined ACL_NO_TRIVIAL
168 : if (ret == 0 && aclp)
169 : {
170 : ret = (dest_desc < 0
171 : ? acl_set (dst_name, aclp)
172 : : facl_set (dest_desc, aclp));
173 : if (ret != 0)
174 : error (0, errno, _("preserving permissions for %s"), quote (dst_name));
175 : acl_free (aclp);
176 : }
177 : # endif
178 :
179 0 : return ret;
180 : #endif
181 : }
182 :
183 : /* Set the access control lists of a file. If DESC is a valid file
184 : descriptor, use file descriptor operations where available, else use
185 : filename based operations on NAME. If access control lists are not
186 : available, fchmod the target file to MODE. Also sets the
187 : non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
188 : to those from MODE if any are set. System call return value
189 : semantics. */
190 :
191 : int
192 15 : qset_acl (char const *name, int desc, mode_t mode)
193 : {
194 : #if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
195 : /* POSIX 1003.1e draft 17 (abandoned) specific version. */
196 :
197 : /* We must also have have_acl_from_text and acl_delete_def_file.
198 : (acl_delete_def_file could be emulated with acl_init followed
199 : by acl_set_file, but acl_set_file with an empty acl is
200 : unspecified.) */
201 :
202 : # ifndef HAVE_ACL_FROM_TEXT
203 : # error Must have acl_from_text (see POSIX 1003.1e draft 17).
204 : # endif
205 : # ifndef HAVE_ACL_DELETE_DEF_FILE
206 : # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
207 : # endif
208 :
209 : acl_t acl;
210 : int ret;
211 :
212 : if (HAVE_ACL_FROM_MODE)
213 : {
214 : acl = acl_from_mode (mode);
215 : if (!acl)
216 : return -1;
217 : }
218 : else
219 : {
220 : char acl_text[] = "u::---,g::---,o::---";
221 :
222 : if (mode & S_IRUSR) acl_text[ 3] = 'r';
223 : if (mode & S_IWUSR) acl_text[ 4] = 'w';
224 : if (mode & S_IXUSR) acl_text[ 5] = 'x';
225 : if (mode & S_IRGRP) acl_text[10] = 'r';
226 : if (mode & S_IWGRP) acl_text[11] = 'w';
227 : if (mode & S_IXGRP) acl_text[12] = 'x';
228 : if (mode & S_IROTH) acl_text[17] = 'r';
229 : if (mode & S_IWOTH) acl_text[18] = 'w';
230 : if (mode & S_IXOTH) acl_text[19] = 'x';
231 :
232 : acl = acl_from_text (acl_text);
233 : if (!acl)
234 : return -1;
235 : }
236 : if (HAVE_ACL_SET_FD && desc != -1)
237 : ret = acl_set_fd (desc, acl);
238 : else
239 : ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
240 : if (ret != 0)
241 : {
242 : int saved_errno = errno;
243 : acl_free (acl);
244 :
245 : if (ACL_NOT_WELL_SUPPORTED (errno))
246 : {
247 : if (chmod_or_fchmod (name, desc, mode) != 0)
248 : saved_errno = errno;
249 : else
250 : return 0;
251 : }
252 : errno = saved_errno;
253 : return -1;
254 : }
255 : else
256 : acl_free (acl);
257 :
258 : if (S_ISDIR (mode) && acl_delete_def_file (name))
259 : return -1;
260 :
261 : if (mode & (S_ISUID | S_ISGID | S_ISVTX))
262 : {
263 : /* We did not call chmod so far, so the special bits have not yet
264 : been set. */
265 :
266 : if (chmod_or_fchmod (name, desc, mode))
267 : return -1;
268 : }
269 : return 0;
270 : #else
271 :
272 : # if USE_ACL && defined ACL_NO_TRIVIAL
273 :
274 : /* Solaris 10, with NFSv4 ACLs. */
275 : acl_t *aclp;
276 : char acl_text[] = "user::---,group::---,mask:---,other:---";
277 :
278 : if (mode & S_IRUSR) acl_text[ 6] = 'r';
279 : if (mode & S_IWUSR) acl_text[ 7] = 'w';
280 : if (mode & S_IXUSR) acl_text[ 8] = 'x';
281 : if (mode & S_IRGRP) acl_text[17] = acl_text[26] = 'r';
282 : if (mode & S_IWGRP) acl_text[18] = acl_text[27] = 'w';
283 : if (mode & S_IXGRP) acl_text[19] = acl_text[28] = 'x';
284 : if (mode & S_IROTH) acl_text[36] = 'r';
285 : if (mode & S_IWOTH) acl_text[37] = 'w';
286 : if (mode & S_IXOTH) acl_text[38] = 'x';
287 :
288 : if (acl_fromtext (acl_text, &aclp) != 0)
289 : {
290 : errno = ENOMEM;
291 : return -1;
292 : }
293 : else
294 : {
295 : int acl_result = (desc < 0 ? acl_set (name, aclp) : facl_set (desc, aclp));
296 : int acl_errno = errno;
297 : acl_free (aclp);
298 : if (acl_result == 0 || acl_errno != ENOSYS)
299 : {
300 : errno = acl_errno;
301 : return acl_result;
302 : }
303 : }
304 : # endif
305 :
306 15 : return chmod_or_fchmod (name, desc, mode);
307 :
308 : #endif
309 : }
310 :
311 : /* As with qset_acl, but also output a diagnostic on failure. */
312 :
313 : int
314 15 : set_acl (char const *name, int desc, mode_t mode)
315 : {
316 15 : int r = qset_acl (name, desc, mode);
317 15 : if (r != 0)
318 0 : error (0, errno, _("setting permissions for %s"), quote (name));
319 15 : return r;
320 : }
|