Trivial stylistic improvements.
[gnulib.git] / lib / set-mode-acl.c
1 /* set-mode-acl.c - set access control list equivalent to a mode
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.
46    Return 0 if successful.  Return -1 and set errno upon failure.  */
47
48 int
49 qset_acl (char const *name, int desc, mode_t mode)
50 {
51 #if USE_ACL
52 # if HAVE_ACL_GET_FILE
53   /* POSIX 1003.1e draft 17 (abandoned) specific version.  */
54   /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
55 #  if MODE_INSIDE_ACL
56   /* Linux, FreeBSD, IRIX, Tru64 */
57
58   /* We must also have acl_from_text and acl_delete_def_file.
59      (acl_delete_def_file could be emulated with acl_init followed
60       by acl_set_file, but acl_set_file with an empty acl is
61       unspecified.)  */
62
63 #   ifndef HAVE_ACL_FROM_TEXT
64 #    error Must have acl_from_text (see POSIX 1003.1e draft 17).
65 #   endif
66 #   ifndef HAVE_ACL_DELETE_DEF_FILE
67 #    error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
68 #   endif
69
70   acl_t acl;
71   int ret;
72
73   if (HAVE_ACL_FROM_MODE) /* Linux */
74     {
75       acl = acl_from_mode (mode);
76       if (!acl)
77         return -1;
78     }
79   else /* FreeBSD, IRIX, Tru64 */
80     {
81       /* If we were to create the ACL using the functions acl_init(),
82          acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(),
83          acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we
84          would need to create a qualifier.  I don't know how to do this.
85          So create it using acl_from_text().  */
86
87 #   if HAVE_ACL_FREE_TEXT /* Tru64 */
88       char acl_text[] = "u::---,g::---,o::---,";
89 #   else /* FreeBSD, IRIX */
90       char acl_text[] = "u::---,g::---,o::---";
91 #   endif
92
93       if (mode & S_IRUSR) acl_text[ 3] = 'r';
94       if (mode & S_IWUSR) acl_text[ 4] = 'w';
95       if (mode & S_IXUSR) acl_text[ 5] = 'x';
96       if (mode & S_IRGRP) acl_text[10] = 'r';
97       if (mode & S_IWGRP) acl_text[11] = 'w';
98       if (mode & S_IXGRP) acl_text[12] = 'x';
99       if (mode & S_IROTH) acl_text[17] = 'r';
100       if (mode & S_IWOTH) acl_text[18] = 'w';
101       if (mode & S_IXOTH) acl_text[19] = 'x';
102
103       acl = acl_from_text (acl_text);
104       if (!acl)
105         return -1;
106     }
107   if (HAVE_ACL_SET_FD && desc != -1)
108     ret = acl_set_fd (desc, acl);
109   else
110     ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
111   if (ret != 0)
112     {
113       int saved_errno = errno;
114       acl_free (acl);
115
116       if (ACL_NOT_WELL_SUPPORTED (errno))
117         {
118           if (chmod_or_fchmod (name, desc, mode) != 0)
119             saved_errno = errno;
120           else
121             return 0;
122         }
123       errno = saved_errno;
124       return -1;
125     }
126   else
127     acl_free (acl);
128
129   if (S_ISDIR (mode) && acl_delete_def_file (name))
130     return -1;
131
132   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
133     {
134       /* We did not call chmod so far, so the special bits have not yet
135          been set.  */
136
137       if (chmod_or_fchmod (name, desc, mode))
138         return -1;
139     }
140   return 0;
141
142 #  else /* !MODE_INSIDE_ACL */
143   /* MacOS X */
144
145   acl_t acl;
146   int ret;
147
148   /* Remove the ACL if the file has ACLs.  */
149   if (HAVE_ACL_GET_FD && desc != -1)
150     acl = acl_get_fd (desc);
151   else
152     acl = acl_get_file (name, ACL_TYPE_ACCESS);
153   if (acl)
154     {
155 #   if HAVE_ACL_COPY_EXT_NATIVE && HAVE_ACL_CREATE_ENTRY_NP /* MacOS X */
156       static const char empty_acl_text[] = "!#acl 1\n";
157 #   else /* Unknown flavor of POSIX-like ACLs */
158 #    error Unknown flavor of POSIX-like ACLs - add support for your platform.
159 #   endif
160
161       acl = acl_from_text (empty_acl_text);
162       if (acl)
163         {
164           if (HAVE_ACL_SET_FD && desc != -1)
165             ret = acl_set_fd (desc, acl);
166           else
167             ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
168           if (ret != 0)
169             {
170               int saved_errno = errno;
171
172               acl_free (acl);
173
174               if (ACL_NOT_WELL_SUPPORTED (saved_errno))
175                 {
176                   if (chmod_or_fchmod (name, desc, mode) != 0)
177                     saved_errno = errno;
178                   else
179                     return 0;
180                 }
181               errno = saved_errno;
182               return -1;
183             }
184         }
185     }
186
187   return chmod_or_fchmod (name, desc, mode);
188 #  endif
189
190 # elif defined ACL_NO_TRIVIAL
191   /* Solaris 10, with NFSv4 ACLs.  */
192   acl_t *aclp;
193   char acl_text[] = "user::---,group::---,mask:---,other:---";
194
195   if (mode & S_IRUSR) acl_text[ 6] = 'r';
196   if (mode & S_IWUSR) acl_text[ 7] = 'w';
197   if (mode & S_IXUSR) acl_text[ 8] = 'x';
198   if (mode & S_IRGRP) acl_text[17] = acl_text[26] = 'r';
199   if (mode & S_IWGRP) acl_text[18] = acl_text[27] = 'w';
200   if (mode & S_IXGRP) acl_text[19] = acl_text[28] = 'x';
201   if (mode & S_IROTH) acl_text[36] = 'r';
202   if (mode & S_IWOTH) acl_text[37] = 'w';
203   if (mode & S_IXOTH) acl_text[38] = 'x';
204
205   if (acl_fromtext (acl_text, &aclp) != 0)
206     {
207       errno = ENOMEM;
208       return -1;
209     }
210   else
211     {
212       int ret = (desc < 0 ? acl_set (name, aclp) : facl_set (desc, aclp));
213       int saved_errno = errno;
214       acl_free (aclp);
215       if (ret == 0 || saved_errno != ENOSYS)
216         {
217           errno = saved_errno;
218           return ret;
219         }
220     }
221
222   return chmod_or_fchmod (name, desc, mode);
223
224 # else /* Unknown flavor of ACLs */
225   return chmod_or_fchmod (name, desc, mode);
226 # endif
227 #else /* !USE_ACL */
228   return chmod_or_fchmod (name, desc, mode);
229 #endif
230 }
231
232 /* As with qset_acl, but also output a diagnostic on failure.  */
233
234 int
235 set_acl (char const *name, int desc, mode_t mode)
236 {
237   int r = qset_acl (name, desc, mode);
238   if (r != 0)
239     error (0, errno, _("setting permissions for %s"), quote (name));
240   return r;
241 }