10499a0bbf70e77e12586f12c0dbbb75d4f718d7
[gnulib.git] / lib / acl.c
1 /* acl.c - access control lists
2
3    Copyright (C) 2002-2003, 2005-2008 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 chmod_or_fchmod (const char *name, int desc, mode_t mode)
33 {
34   if (HAVE_FCHMOD && desc != -1)
35     return fchmod (desc, mode);
36   else
37     return chmod (name, mode);
38 }
39
40 /* Set the access control lists of a file. If DESC is a valid file
41    descriptor, use file descriptor operations where available, else use
42    filename based operations on NAME.  If access control lists are not
43    available, fchmod the target file to MODE.  Also sets the
44    non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
45    to those from MODE if any are set.  System call return value
46    semantics.  */
47
48 int
49 qset_acl (char const *name, int desc, mode_t mode)
50 {
51 #if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
52   /* POSIX 1003.1e draft 17 (abandoned) specific version.  */
53
54   /* We must also have have_acl_from_text and acl_delete_def_file.
55      (acl_delete_def_file could be emulated with acl_init followed
56       by acl_set_file, but acl_set_file with an empty acl is
57       unspecified.)  */
58
59 # ifndef HAVE_ACL_FROM_TEXT
60 #  error Must have acl_from_text (see POSIX 1003.1e draft 17).
61 # endif
62 # ifndef HAVE_ACL_DELETE_DEF_FILE
63 #  error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
64 # endif
65
66   acl_t acl;
67   int ret;
68
69   if (HAVE_ACL_FROM_MODE)
70     {
71       acl = acl_from_mode (mode);
72       if (!acl)
73         return -1;
74     }
75   else
76     {
77       char acl_text[] = "u::---,g::---,o::---";
78
79       if (mode & S_IRUSR) acl_text[ 3] = 'r';
80       if (mode & S_IWUSR) acl_text[ 4] = 'w';
81       if (mode & S_IXUSR) acl_text[ 5] = 'x';
82       if (mode & S_IRGRP) acl_text[10] = 'r';
83       if (mode & S_IWGRP) acl_text[11] = 'w';
84       if (mode & S_IXGRP) acl_text[12] = 'x';
85       if (mode & S_IROTH) acl_text[17] = 'r';
86       if (mode & S_IWOTH) acl_text[18] = 'w';
87       if (mode & S_IXOTH) acl_text[19] = 'x';
88
89       acl = acl_from_text (acl_text);
90       if (!acl)
91         return -1;
92     }
93   if (HAVE_ACL_SET_FD && desc != -1)
94     ret = acl_set_fd (desc, acl);
95   else
96     ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
97   if (ret != 0)
98     {
99       int saved_errno = errno;
100       acl_free (acl);
101
102       if (ACL_NOT_WELL_SUPPORTED (errno))
103         {
104           if (chmod_or_fchmod (name, desc, mode) != 0)
105             saved_errno = errno;
106           else
107             return 0;
108         }
109       errno = saved_errno;
110       return -1;
111     }
112   else
113     acl_free (acl);
114
115   if (S_ISDIR (mode) && acl_delete_def_file (name))
116     return -1;
117
118   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
119     {
120       /* We did not call chmod so far, so the special bits have not yet
121          been set.  */
122
123       if (chmod_or_fchmod (name, desc, mode))
124         return -1;
125     }
126   return 0;
127 #else
128
129 # if USE_ACL && defined ACL_NO_TRIVIAL
130
131   /* Solaris 10, with NFSv4 ACLs.  */
132   acl_t *aclp;
133   char acl_text[] = "user::---,group::---,mask:---,other:---";
134
135   if (mode & S_IRUSR) acl_text[ 6] = 'r';
136   if (mode & S_IWUSR) acl_text[ 7] = 'w';
137   if (mode & S_IXUSR) acl_text[ 8] = 'x';
138   if (mode & S_IRGRP) acl_text[17] = acl_text[26] = 'r';
139   if (mode & S_IWGRP) acl_text[18] = acl_text[27] = 'w';
140   if (mode & S_IXGRP) acl_text[19] = acl_text[28] = 'x';
141   if (mode & S_IROTH) acl_text[36] = 'r';
142   if (mode & S_IWOTH) acl_text[37] = 'w';
143   if (mode & S_IXOTH) acl_text[38] = 'x';
144
145   if (acl_fromtext (acl_text, &aclp) != 0)
146     {
147       errno = ENOMEM;
148       return -1;
149     }
150   else
151     {
152       int acl_result = (desc < 0 ? acl_set (name, aclp) : facl_set (desc, aclp));
153       int acl_errno = errno;
154       acl_free (aclp);
155       if (acl_result == 0 || acl_errno != ENOSYS)
156         {
157           errno = acl_errno;
158           return acl_result;
159         }
160     }
161 # endif
162
163   return chmod_or_fchmod (name, desc, mode);
164
165 #endif
166 }
167
168 /* As with qset_acl, but also output a diagnostic on failure.  */
169
170 int
171 set_acl (char const *name, int desc, mode_t mode)
172 {
173   int r = qset_acl (name, desc, mode);
174   if (r != 0)
175     error (0, errno, _("setting permissions for %s"), quote (name));
176   return r;
177 }