Don't include <config.h> twice; this doesn't work in some cases,
[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 <stdbool.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/stat.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 "acl.h"
37 #include "error.h"
38 #include "quote.h"
39
40 #include <errno.h>
41 #ifndef ENOSYS
42 # define ENOSYS (-1)
43 #endif
44 #ifndef ENOTSUP
45 # define ENOTSUP (-1)
46 #endif
47
48 #if ENABLE_NLS
49 # include <libintl.h>
50 # define _(Text) gettext (Text)
51 #else
52 # define _(Text) Text
53 #endif
54
55 #ifndef HAVE_FCHMOD
56 # define HAVE_FCHMOD false
57 # define fchmod(fd, mode) (-1)
58 #endif
59
60 /* POSIX 1003.1e (draft 17) */
61 #ifndef HAVE_ACL_GET_FD
62 # define HAVE_ACL_GET_FD false
63 # define acl_get_fd(fd) (NULL)
64 #endif
65
66 /* POSIX 1003.1e (draft 17) */
67 #ifndef HAVE_ACL_SET_FD
68 # define HAVE_ACL_SET_FD false
69 # define acl_set_fd(fd, acl) (-1)
70 #endif
71
72 /* Linux-specific */
73 #ifndef HAVE_ACL_EXTENDED_FILE
74 # define HAVE_ACL_EXTENDED_FILE false
75 # define acl_extended_file(name) (-1)
76 #endif
77
78 /* Linux-specific */
79 #ifndef HAVE_ACL_FROM_MODE
80 # define HAVE_ACL_FROM_MODE false
81 # define acl_from_mode(mode) (NULL)
82 #endif
83
84 /* We detect the presence of POSIX 1003.1e (draft 17 -- abandoned) support
85    by checking for HAVE_ACL_GET_FILE, HAVE_ACL_SET_FILE, and HAVE_ACL_FREE.
86    Systems that have acl_get_file, acl_set_file, and acl_free must also
87    have acl_to_text, acl_from_text, and acl_delete_def_file (all defined
88    in the draft); systems that don't would hit #error statements here.  */
89
90 #if USE_ACL && HAVE_ACL_GET_FILE && !HAVE_ACL_ENTRIES
91 # ifndef HAVE_ACL_TO_TEXT
92 #  error Must have acl_to_text (see POSIX 1003.1e draft 17).
93 # endif
94
95 /* Return the number of entries in ACL. Linux implements acl_entries
96    as a more efficient extension than using this workaround.  */
97
98 static int
99 acl_entries (acl_t acl)
100 {
101   char *text = acl_to_text (acl, NULL), *t;
102   int entries;
103   if (text == NULL)
104     return -1;
105   for (entries = 0, t = text; ; t++, entries++) {
106     t = strchr (t, '\n');
107     if (t == NULL)
108       break;
109   }
110   acl_free (text);
111   return entries;
112 }
113 #endif
114
115 /* If DESC is a valid file descriptor use fchmod to change the
116    file's mode to MODE on systems that have fchown. On systems
117    that don't have fchown and if DESC is invalid, use chown on
118    NAME instead.  */
119
120 int
121 chmod_or_fchmod (const char *name, int desc, mode_t mode)
122 {
123   if (HAVE_FCHMOD && desc != -1)
124     return fchmod (desc, mode);
125   else
126     return chmod (name, mode);
127 }
128
129 /* Return 1 if NAME has a nontrivial access control list, 0 if
130    NAME only has no or a base access control list, and -1 on
131    error.  SB must be set to the stat buffer of FILE.  */
132
133 int
134 file_has_acl (char const *name, struct stat const *sb)
135 {
136 #if USE_ACL && HAVE_ACL && defined GETACLCNT
137   /* This implementation should work on recent-enough versions of HP-UX,
138      Solaris, and Unixware.  */
139
140 # ifndef MIN_ACL_ENTRIES
141 #  define MIN_ACL_ENTRIES 4
142 # endif
143
144   if (! S_ISLNK (sb->st_mode))
145     {
146       int n = acl (name, GETACLCNT, 0, NULL);
147       return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n);
148     }
149 #elif USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE
150   /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
151
152   if (! S_ISLNK (sb->st_mode))
153     {
154       int ret;
155
156       if (HAVE_ACL_EXTENDED_FILE)
157         ret = acl_extended_file (name);
158       else
159         {
160           acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
161           if (acl)
162             {
163               ret = (3 < acl_entries (acl));
164               acl_free (acl);
165               if (ret == 0 && S_ISDIR (sb->st_mode))
166                 {
167                   acl = acl_get_file (name, ACL_TYPE_DEFAULT);
168                   if (acl)
169                     {
170                       ret = (0 < acl_entries (acl));
171                       acl_free (acl);
172                     }
173                   else
174                     ret = -1;
175                 }
176             }
177           else
178             ret = -1;
179         }
180       if (ret < 0)
181         return (errno == ENOSYS || errno == ENOTSUP) ? 0 : -1;
182       return ret;
183     }
184 #endif
185
186   /* FIXME: Add support for AIX, Irix, and Tru64.  Please see Samba's
187      source/lib/sysacls.c file for fix-related ideas.  */
188
189   return 0;
190 }
191
192 /* Copy access control lists from one file to another. If SOURCE_DESC is
193    a valid file descriptor, use file descriptor operations, else use
194    filename based operations on SRC_NAME. Likewise for DEST_DESC and
195    DEST_NAME.
196    If access control lists are not available, fchmod the target file to
197    MODE.  Also sets the non-permission bits of the destination file
198    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
199    System call return value semantics.  */
200
201 int
202 copy_acl (const char *src_name, int source_desc, const char *dst_name,
203           int dest_desc, mode_t mode)
204 {
205   int ret;
206
207 #if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
208   /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
209
210   acl_t acl;
211   if (HAVE_ACL_GET_FD && source_desc != -1)
212     acl = acl_get_fd (source_desc);
213   else
214     acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
215   if (acl == NULL)
216     {
217       if (errno == ENOSYS || errno == ENOTSUP)
218         return set_acl (dst_name, dest_desc, mode);
219       else
220         {
221           error (0, errno, "%s", quote (src_name));
222           return -1;
223         }
224     }
225
226   if (HAVE_ACL_SET_FD && dest_desc != -1)
227     ret = acl_set_fd (dest_desc, acl);
228   else
229     ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
230   if (ret != 0)
231     {
232       int saved_errno = errno;
233
234       if (errno == ENOSYS || errno == ENOTSUP)
235         {
236           int n = acl_entries (acl);
237
238           acl_free (acl);
239           if (n == 3)
240             {
241               if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
242                 saved_errno = errno;
243               else
244                 return 0;
245             }
246           else
247             chmod_or_fchmod (dst_name, dest_desc, mode);
248         }
249       else
250         {
251           acl_free (acl);
252           chmod_or_fchmod (dst_name, dest_desc, mode);
253         }
254       error (0, saved_errno, _("preserving permissions for %s"),
255              quote (dst_name));
256       return -1;
257     }
258   else
259     acl_free (acl);
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 (dst_name, dest_desc, mode) != 0)
267         {
268           error (0, errno, _("preserving permissions for %s"),
269                  quote (dst_name));
270           return -1;
271         }
272     }
273
274   if (S_ISDIR (mode))
275     {
276       acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
277       if (acl == NULL)
278         {
279           error (0, errno, "%s", quote (src_name));
280           return -1;
281         }
282
283       if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
284         {
285           error (0, errno, _("preserving permissions for %s"),
286                  quote (dst_name));
287           acl_free (acl);
288           return -1;
289         }
290       else
291         acl_free (acl);
292     }
293   return 0;
294 #else
295   ret = chmod_or_fchmod (dst_name, dest_desc, mode);
296   if (ret != 0)
297     error (0, errno, _("preserving permissions for %s"), quote (dst_name));
298   return ret;
299 #endif
300 }
301
302 /* Set the access control lists of a file. If DESC is a valid file
303    descriptor, use file descriptor operations where available, else use
304    filename based operations on NAME.  If access control lists are not
305    available, fchmod the target file to MODE.  Also sets the
306    non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
307    to those from MODE if any are set.  System call return value
308    semantics.  */
309
310 int
311 set_acl (char const *name, int desc, mode_t mode)
312 {
313 #if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
314   /* POSIX 1003.1e draft 17 (abandoned) specific version.  */
315
316   /* We must also have have_acl_from_text and acl_delete_def_file.
317      (acl_delete_def_file could be emulated with acl_init followed
318       by acl_set_file, but acl_set_file with an empty acl is
319       unspecified.)  */
320
321 # ifndef HAVE_ACL_FROM_TEXT
322 #  error Must have acl_from_text (see POSIX 1003.1e draft 17).
323 # endif
324 # ifndef HAVE_ACL_DELETE_DEF_FILE
325 #  error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
326 # endif
327
328   acl_t acl;
329   int ret;
330
331   if (HAVE_ACL_FROM_MODE)
332     {
333       acl = acl_from_mode (mode);
334       if (!acl)
335         {
336           error (0, errno, "%s", quote (name));
337           return -1;
338         }
339     }
340   else
341     {
342       char acl_text[] = "u::---,g::---,o::---";
343
344       if (mode & S_IRUSR) acl_text[ 3] = 'r';
345       if (mode & S_IWUSR) acl_text[ 4] = 'w';
346       if (mode & S_IXUSR) acl_text[ 5] = 'x';
347       if (mode & S_IRGRP) acl_text[10] = 'r';
348       if (mode & S_IWGRP) acl_text[11] = 'w';
349       if (mode & S_IXGRP) acl_text[12] = 'x';
350       if (mode & S_IROTH) acl_text[17] = 'r';
351       if (mode & S_IWOTH) acl_text[18] = 'w';
352       if (mode & S_IXOTH) acl_text[19] = 'x';
353
354       acl = acl_from_text (acl_text);
355       if (!acl)
356         {
357           error (0, errno, "%s", quote (name));
358           return -1;
359         }
360     }
361   if (HAVE_ACL_SET_FD && desc != -1)
362     ret = acl_set_fd (desc, acl);
363   else
364     ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
365   if (ret != 0)
366     {
367       int saved_errno = errno;
368       acl_free (acl);
369
370       if (errno == ENOTSUP || errno == ENOSYS)
371         {
372           if (chmod_or_fchmod (name, desc, mode) != 0)
373             saved_errno = errno;
374           else
375             return 0;
376         }
377       error (0, saved_errno, _("setting permissions for %s"), quote (name));
378       return -1;
379     }
380   else
381     acl_free (acl);
382
383   if (S_ISDIR (mode) && acl_delete_def_file (name))
384     {
385       error (0, errno, _("setting permissions for %s"), quote (name));
386       return -1;
387     }
388
389   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
390     {
391       /* We did not call chmod so far, so the special bits have not yet
392          been set.  */
393
394       if (chmod_or_fchmod (name, desc, mode))
395         {
396           error (0, errno, _("preserving permissions for %s"), quote (name));
397           return -1;
398         }
399     }
400   return 0;
401 #else
402    int ret = chmod_or_fchmod (name, desc, mode);
403    if (ret)
404      error (0, errno, _("setting permissions for %s"), quote (name));
405    return ret;
406 #endif
407 }