New module 'striconveh'.
[gnulib.git] / lib / acl.c
1 /* acl.c - access control lists
2
3    Copyright (C) 2002, 2003, 2005, 2006 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 2, or (at your option)
8    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, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19    Written by Paul Eggert and Andreas Gruenbacher.  */
20
21 #include <config.h>
22
23 #include "acl.h"
24
25 #include <stdbool.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #ifndef S_ISLNK
29 # define S_ISLNK(Mode) 0
30 #endif
31
32 #ifdef HAVE_ACL_LIBACL_H
33 # include <acl/libacl.h>
34 #endif
35
36 #include "error.h"
37 #include "quote.h"
38
39 #include <errno.h>
40 #ifndef ENOSYS
41 # define ENOSYS (-1)
42 #endif
43 #ifndef ENOTSUP
44 # define ENOTSUP (-1)
45 #endif
46
47 #if ENABLE_NLS
48 # include <libintl.h>
49 # define _(Text) gettext (Text)
50 #else
51 # define _(Text) Text
52 #endif
53
54 #ifndef HAVE_FCHMOD
55 # define HAVE_FCHMOD false
56 # define fchmod(fd, mode) (-1)
57 #endif
58
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)
63 #endif
64
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)
69 #endif
70
71 /* Linux-specific */
72 #ifndef HAVE_ACL_EXTENDED_FILE
73 # define HAVE_ACL_EXTENDED_FILE false
74 # define acl_extended_file(name) (-1)
75 #endif
76
77 /* Linux-specific */
78 #ifndef HAVE_ACL_FROM_MODE
79 # define HAVE_ACL_FROM_MODE false
80 # define acl_from_mode(mode) (NULL)
81 #endif
82
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.  */
88
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).
92 # endif
93
94 /* Return the number of entries in ACL. Linux implements acl_entries
95    as a more efficient extension than using this workaround.  */
96
97 static int
98 acl_entries (acl_t acl)
99 {
100   char *text = acl_to_text (acl, NULL), *t;
101   int entries;
102   if (text == NULL)
103     return -1;
104   for (entries = 0, t = text; ; t++, entries++) {
105     t = strchr (t, '\n');
106     if (t == NULL)
107       break;
108   }
109   acl_free (text);
110   return entries;
111 }
112 #endif
113
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
117    NAME instead.  */
118
119 int
120 chmod_or_fchmod (const char *name, int desc, mode_t mode)
121 {
122   if (HAVE_FCHMOD && desc != -1)
123     return fchmod (desc, mode);
124   else
125     return chmod (name, mode);
126 }
127
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.  */
131
132 int
133 file_has_acl (char const *name, struct stat const *sb)
134 {
135 #if USE_ACL && HAVE_ACL && defined GETACLCNT
136   /* This implementation should work on recent-enough versions of HP-UX,
137      Solaris, and Unixware.  */
138
139 # ifndef MIN_ACL_ENTRIES
140 #  define MIN_ACL_ENTRIES 4
141 # endif
142
143   if (! S_ISLNK (sb->st_mode))
144     {
145       int n = acl (name, GETACLCNT, 0, NULL);
146       return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n);
147     }
148 #elif USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE
149   /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
150
151   if (! S_ISLNK (sb->st_mode))
152     {
153       int ret;
154
155       if (HAVE_ACL_EXTENDED_FILE)
156         ret = acl_extended_file (name);
157       else
158         {
159           acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
160           if (acl)
161             {
162               ret = (3 < acl_entries (acl));
163               acl_free (acl);
164               if (ret == 0 && S_ISDIR (sb->st_mode))
165                 {
166                   acl = acl_get_file (name, ACL_TYPE_DEFAULT);
167                   if (acl)
168                     {
169                       ret = (0 < acl_entries (acl));
170                       acl_free (acl);
171                     }
172                   else
173                     ret = -1;
174                 }
175             }
176           else
177             ret = -1;
178         }
179       if (ret < 0)
180         return (errno == ENOSYS || errno == ENOTSUP) ? 0 : -1;
181       return ret;
182     }
183 #endif
184
185   /* FIXME: Add support for AIX, Irix, and Tru64.  Please see Samba's
186      source/lib/sysacls.c file for fix-related ideas.  */
187
188   return 0;
189 }
190
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
194    DEST_NAME.
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.  */
199
200 int
201 copy_acl (const char *src_name, int source_desc, const char *dst_name,
202           int dest_desc, mode_t mode)
203 {
204   int ret;
205
206 #if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
207   /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
208
209   acl_t acl;
210   if (HAVE_ACL_GET_FD && source_desc != -1)
211     acl = acl_get_fd (source_desc);
212   else
213     acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
214   if (acl == NULL)
215     {
216       if (errno == ENOSYS || errno == ENOTSUP)
217         return set_acl (dst_name, dest_desc, mode);
218       else
219         {
220           error (0, errno, "%s", quote (src_name));
221           return -1;
222         }
223     }
224
225   if (HAVE_ACL_SET_FD && dest_desc != -1)
226     ret = acl_set_fd (dest_desc, acl);
227   else
228     ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
229   if (ret != 0)
230     {
231       int saved_errno = errno;
232
233       if (errno == ENOSYS || errno == ENOTSUP)
234         {
235           int n = acl_entries (acl);
236
237           acl_free (acl);
238           if (n == 3)
239             {
240               if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
241                 saved_errno = errno;
242               else
243                 return 0;
244             }
245           else
246             chmod_or_fchmod (dst_name, dest_desc, mode);
247         }
248       else
249         {
250           acl_free (acl);
251           chmod_or_fchmod (dst_name, dest_desc, mode);
252         }
253       error (0, saved_errno, _("preserving permissions for %s"),
254              quote (dst_name));
255       return -1;
256     }
257   else
258     acl_free (acl);
259
260   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
261     {
262       /* We did not call chmod so far, so the special bits have not yet
263          been set.  */
264
265       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
266         {
267           error (0, errno, _("preserving permissions for %s"),
268                  quote (dst_name));
269           return -1;
270         }
271     }
272
273   if (S_ISDIR (mode))
274     {
275       acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
276       if (acl == NULL)
277         {
278           error (0, errno, "%s", quote (src_name));
279           return -1;
280         }
281
282       if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
283         {
284           error (0, errno, _("preserving permissions for %s"),
285                  quote (dst_name));
286           acl_free (acl);
287           return -1;
288         }
289       else
290         acl_free (acl);
291     }
292   return 0;
293 #else
294   ret = chmod_or_fchmod (dst_name, dest_desc, mode);
295   if (ret != 0)
296     error (0, errno, _("preserving permissions for %s"), quote (dst_name));
297   return ret;
298 #endif
299 }
300
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
307    semantics.  */
308
309 int
310 set_acl (char const *name, int desc, mode_t mode)
311 {
312 #if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
313   /* POSIX 1003.1e draft 17 (abandoned) specific version.  */
314
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
318       unspecified.)  */
319
320 # ifndef HAVE_ACL_FROM_TEXT
321 #  error Must have acl_from_text (see POSIX 1003.1e draft 17).
322 # endif
323 # ifndef HAVE_ACL_DELETE_DEF_FILE
324 #  error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
325 # endif
326
327   acl_t acl;
328   int ret;
329
330   if (HAVE_ACL_FROM_MODE)
331     {
332       acl = acl_from_mode (mode);
333       if (!acl)
334         {
335           error (0, errno, "%s", quote (name));
336           return -1;
337         }
338     }
339   else
340     {
341       char acl_text[] = "u::---,g::---,o::---";
342
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';
352
353       acl = acl_from_text (acl_text);
354       if (!acl)
355         {
356           error (0, errno, "%s", quote (name));
357           return -1;
358         }
359     }
360   if (HAVE_ACL_SET_FD && desc != -1)
361     ret = acl_set_fd (desc, acl);
362   else
363     ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
364   if (ret != 0)
365     {
366       int saved_errno = errno;
367       acl_free (acl);
368
369       if (errno == ENOTSUP || errno == ENOSYS)
370         {
371           if (chmod_or_fchmod (name, desc, mode) != 0)
372             saved_errno = errno;
373           else
374             return 0;
375         }
376       error (0, saved_errno, _("setting permissions for %s"), quote (name));
377       return -1;
378     }
379   else
380     acl_free (acl);
381
382   if (S_ISDIR (mode) && acl_delete_def_file (name))
383     {
384       error (0, errno, _("setting permissions for %s"), quote (name));
385       return -1;
386     }
387
388   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
389     {
390       /* We did not call chmod so far, so the special bits have not yet
391          been set.  */
392
393       if (chmod_or_fchmod (name, desc, mode))
394         {
395           error (0, errno, _("preserving permissions for %s"), quote (name));
396           return -1;
397         }
398     }
399   return 0;
400 #else
401    int ret = chmod_or_fchmod (name, desc, mode);
402    if (ret)
403      error (0, errno, _("setting permissions for %s"), quote (name));
404    return ret;
405 #endif
406 }