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