1 /* acl.c - access control lists
3 Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
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 2, or (at your option)
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.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 Written by Paul Eggert and Andreas Gruenbacher. */
29 # define S_ISLNK(Mode) 0
32 #ifdef HAVE_ACL_LIBACL_H
33 # include <acl/libacl.h>
49 # define _(Text) gettext (Text)
55 # define HAVE_FCHMOD false
56 # define fchmod(fd, mode) (-1)
59 /* POSIX 1003.1e (draft 17) */
60 #ifndef HAVE_ACL_GET_FD
61 # define HAVE_ACL_GET_FD false
62 # define acl_get_fd(fd) (NULL)
65 /* POSIX 1003.1e (draft 17) */
66 #ifndef HAVE_ACL_SET_FD
67 # define HAVE_ACL_SET_FD false
68 # define acl_set_fd(fd, acl) (-1)
72 #ifndef HAVE_ACL_EXTENDED_FILE
73 # define HAVE_ACL_EXTENDED_FILE false
74 # define acl_extended_file(name) (-1)
78 #ifndef HAVE_ACL_FROM_MODE
79 # define HAVE_ACL_FROM_MODE false
80 # define acl_from_mode(mode) (NULL)
83 /* We detect the presence of POSIX 1003.1e (draft 17 -- abandoned) support
84 by checking for HAVE_ACL_GET_FILE, HAVE_ACL_SET_FILE, and HAVE_ACL_FREE.
85 Systems that have acl_get_file, acl_set_file, and acl_free must also
86 have acl_to_text, acl_from_text, and acl_delete_def_file (all defined
87 in the draft); systems that don't would hit #error statements here. */
89 #if USE_ACL && HAVE_ACL_GET_FILE && !HAVE_ACL_ENTRIES
90 # ifndef HAVE_ACL_TO_TEXT
91 # error Must have acl_to_text (see POSIX 1003.1e draft 17).
94 /* Return the number of entries in ACL. Linux implements acl_entries
95 as a more efficient extension than using this workaround. */
98 acl_entries (acl_t acl)
100 char *text = acl_to_text (acl, NULL), *t;
104 for (entries = 0, t = text; ; t++, entries++) {
105 t = strchr (t, '\n');
114 /* If DESC is a valid file descriptor use fchmod to change the
115 file's mode to MODE on systems that have fchown. On systems
116 that don't have fchown and if DESC is invalid, use chown on
120 chmod_or_fchmod (const char *name, int desc, mode_t mode)
122 if (HAVE_FCHMOD && desc != -1)
123 return fchmod (desc, mode);
125 return chmod (name, mode);
128 /* Return 1 if NAME has a nontrivial access control list, 0 if
129 NAME only has no or a base access control list, and -1 on
130 error. SB must be set to the stat buffer of FILE. */
133 file_has_acl (char const *name, struct stat const *sb)
135 #if USE_ACL && HAVE_ACL && defined GETACLCNT
136 /* This implementation should work on recent-enough versions of HP-UX,
137 Solaris, and Unixware. */
139 # ifndef MIN_ACL_ENTRIES
140 # define MIN_ACL_ENTRIES 4
143 if (! S_ISLNK (sb->st_mode))
145 int n = acl (name, GETACLCNT, 0, NULL);
146 return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n);
148 #elif USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE
149 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
151 if (! S_ISLNK (sb->st_mode))
155 if (HAVE_ACL_EXTENDED_FILE)
156 ret = acl_extended_file (name);
159 acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
162 ret = (3 < acl_entries (acl));
164 if (ret == 0 && S_ISDIR (sb->st_mode))
166 acl = acl_get_file (name, ACL_TYPE_DEFAULT);
169 ret = (0 < acl_entries (acl));
180 return (errno == ENOSYS || errno == ENOTSUP) ? 0 : -1;
185 /* FIXME: Add support for AIX, Irix, and Tru64. Please see Samba's
186 source/lib/sysacls.c file for fix-related ideas. */
191 /* Copy access control lists from one file to another. If SOURCE_DESC is
192 a valid file descriptor, use file descriptor operations, else use
193 filename based operations on SRC_NAME. Likewise for DEST_DESC and
195 If access control lists are not available, fchmod the target file to
196 MODE. Also sets the non-permission bits of the destination file
197 (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
198 System call return value semantics. */
201 copy_acl (const char *src_name, int source_desc, const char *dst_name,
202 int dest_desc, mode_t mode)
206 #if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
207 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
210 if (HAVE_ACL_GET_FD && source_desc != -1)
211 acl = acl_get_fd (source_desc);
213 acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
216 if (errno == ENOSYS || errno == ENOTSUP)
217 return set_acl (dst_name, dest_desc, mode);
220 error (0, errno, "%s", quote (src_name));
225 if (HAVE_ACL_SET_FD && dest_desc != -1)
226 ret = acl_set_fd (dest_desc, acl);
228 ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
231 int saved_errno = errno;
233 if (errno == ENOSYS || errno == ENOTSUP)
235 int n = acl_entries (acl);
240 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
246 chmod_or_fchmod (dst_name, dest_desc, mode);
251 chmod_or_fchmod (dst_name, dest_desc, mode);
253 error (0, saved_errno, _("preserving permissions for %s"),
260 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
262 /* We did not call chmod so far, so the special bits have not yet
265 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
267 error (0, errno, _("preserving permissions for %s"),
275 acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
278 error (0, errno, "%s", quote (src_name));
282 if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
284 error (0, errno, _("preserving permissions for %s"),
294 ret = chmod_or_fchmod (dst_name, dest_desc, mode);
296 error (0, errno, _("preserving permissions for %s"), quote (dst_name));
301 /* Set the access control lists of a file. If DESC is a valid file
302 descriptor, use file descriptor operations where available, else use
303 filename based operations on NAME. If access control lists are not
304 available, fchmod the target file to MODE. Also sets the
305 non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
306 to those from MODE if any are set. System call return value
310 set_acl (char const *name, int desc, mode_t mode)
312 #if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
313 /* POSIX 1003.1e draft 17 (abandoned) specific version. */
315 /* We must also have have_acl_from_text and acl_delete_def_file.
316 (acl_delete_def_file could be emulated with acl_init followed
317 by acl_set_file, but acl_set_file with an empty acl is
320 # ifndef HAVE_ACL_FROM_TEXT
321 # error Must have acl_from_text (see POSIX 1003.1e draft 17).
323 # ifndef HAVE_ACL_DELETE_DEF_FILE
324 # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
330 if (HAVE_ACL_FROM_MODE)
332 acl = acl_from_mode (mode);
335 error (0, errno, "%s", quote (name));
341 char acl_text[] = "u::---,g::---,o::---";
343 if (mode & S_IRUSR) acl_text[ 3] = 'r';
344 if (mode & S_IWUSR) acl_text[ 4] = 'w';
345 if (mode & S_IXUSR) acl_text[ 5] = 'x';
346 if (mode & S_IRGRP) acl_text[10] = 'r';
347 if (mode & S_IWGRP) acl_text[11] = 'w';
348 if (mode & S_IXGRP) acl_text[12] = 'x';
349 if (mode & S_IROTH) acl_text[17] = 'r';
350 if (mode & S_IWOTH) acl_text[18] = 'w';
351 if (mode & S_IXOTH) acl_text[19] = 'x';
353 acl = acl_from_text (acl_text);
356 error (0, errno, "%s", quote (name));
360 if (HAVE_ACL_SET_FD && desc != -1)
361 ret = acl_set_fd (desc, acl);
363 ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
366 int saved_errno = errno;
369 if (errno == ENOTSUP || errno == ENOSYS)
371 if (chmod_or_fchmod (name, desc, mode) != 0)
376 error (0, saved_errno, _("setting permissions for %s"), quote (name));
382 if (S_ISDIR (mode) && acl_delete_def_file (name))
384 error (0, errno, _("setting permissions for %s"), quote (name));
388 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
390 /* We did not call chmod so far, so the special bits have not yet
393 if (chmod_or_fchmod (name, desc, mode))
395 error (0, errno, _("preserving permissions for %s"), quote (name));
401 int ret = chmod_or_fchmod (name, desc, mode);
403 error (0, errno, _("setting permissions for %s"), quote (name));