Imported Upstream version 0.18.1.1
[platform/upstream/gettext.git] / gettext-tools / gnulib-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-2010 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, and Bruno Haible.  */
19
20 #include <config.h>
21
22 #include "acl.h"
23
24 #include "acl-internal.h"
25
26 #include "gettext.h"
27 #define _(msgid) gettext (msgid)
28
29
30 /* If DESC is a valid file descriptor use fchmod to change the
31    file's mode to MODE on systems that have fchown. On systems
32    that don't have fchown and if DESC is invalid, use chown on
33    NAME instead.
34    Return 0 if successful.  Return -1 and set errno upon failure.  */
35
36 int
37 chmod_or_fchmod (const char *name, int desc, mode_t mode)
38 {
39   if (HAVE_FCHMOD && desc != -1)
40     return fchmod (desc, mode);
41   else
42     return chmod (name, mode);
43 }
44
45 /* Set the access control lists of a file. If DESC is a valid file
46    descriptor, use file descriptor operations where available, else use
47    filename based operations on NAME.  If access control lists are not
48    available, fchmod the target file to MODE.  Also sets the
49    non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
50    to those from MODE if any are set.
51    Return 0 if successful.  Return -1 and set errno upon failure.  */
52
53 int
54 qset_acl (char const *name, int desc, mode_t mode)
55 {
56 #if USE_ACL
57 # if HAVE_ACL_GET_FILE
58   /* POSIX 1003.1e draft 17 (abandoned) specific version.  */
59   /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
60 #  if MODE_INSIDE_ACL
61   /* Linux, FreeBSD, IRIX, Tru64 */
62
63   /* We must also have acl_from_text and acl_delete_def_file.
64      (acl_delete_def_file could be emulated with acl_init followed
65       by acl_set_file, but acl_set_file with an empty acl is
66       unspecified.)  */
67
68 #   ifndef HAVE_ACL_FROM_TEXT
69 #    error Must have acl_from_text (see POSIX 1003.1e draft 17).
70 #   endif
71 #   ifndef HAVE_ACL_DELETE_DEF_FILE
72 #    error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
73 #   endif
74
75   acl_t acl;
76   int ret;
77
78   if (HAVE_ACL_FROM_MODE) /* Linux */
79     {
80       acl = acl_from_mode (mode);
81       if (!acl)
82         return -1;
83     }
84   else /* FreeBSD, IRIX, Tru64 */
85     {
86       /* If we were to create the ACL using the functions acl_init(),
87          acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(),
88          acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we
89          would need to create a qualifier.  I don't know how to do this.
90          So create it using acl_from_text().  */
91
92 #   if HAVE_ACL_FREE_TEXT /* Tru64 */
93       char acl_text[] = "u::---,g::---,o::---,";
94 #   else /* FreeBSD, IRIX */
95       char acl_text[] = "u::---,g::---,o::---";
96 #   endif
97
98       if (mode & S_IRUSR) acl_text[ 3] = 'r';
99       if (mode & S_IWUSR) acl_text[ 4] = 'w';
100       if (mode & S_IXUSR) acl_text[ 5] = 'x';
101       if (mode & S_IRGRP) acl_text[10] = 'r';
102       if (mode & S_IWGRP) acl_text[11] = 'w';
103       if (mode & S_IXGRP) acl_text[12] = 'x';
104       if (mode & S_IROTH) acl_text[17] = 'r';
105       if (mode & S_IWOTH) acl_text[18] = 'w';
106       if (mode & S_IXOTH) acl_text[19] = 'x';
107
108       acl = acl_from_text (acl_text);
109       if (!acl)
110         return -1;
111     }
112   if (HAVE_ACL_SET_FD && desc != -1)
113     ret = acl_set_fd (desc, acl);
114   else
115     ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
116   if (ret != 0)
117     {
118       int saved_errno = errno;
119       acl_free (acl);
120
121       if (ACL_NOT_WELL_SUPPORTED (errno))
122         return chmod_or_fchmod (name, desc, mode);
123       else
124         {
125           errno = saved_errno;
126           return -1;
127         }
128     }
129   else
130     acl_free (acl);
131
132   if (S_ISDIR (mode) && acl_delete_def_file (name))
133     return -1;
134
135   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
136     {
137       /* We did not call chmod so far, so the special bits have not yet
138          been set.  */
139       return chmod_or_fchmod (name, desc, mode);
140     }
141   return 0;
142
143 #  else /* !MODE_INSIDE_ACL */
144   /* MacOS X */
145
146 #   if !HAVE_ACL_TYPE_EXTENDED
147 #    error Must have ACL_TYPE_EXTENDED
148 #   endif
149
150   /* On MacOS X,  acl_get_file (name, ACL_TYPE_ACCESS)
151      and          acl_get_file (name, ACL_TYPE_DEFAULT)
152      always return NULL / EINVAL.  You have to use
153                   acl_get_file (name, ACL_TYPE_EXTENDED)
154      or           acl_get_fd (open (name, ...))
155      to retrieve an ACL.
156      On the other hand,
157                   acl_set_file (name, ACL_TYPE_ACCESS, acl)
158      and          acl_set_file (name, ACL_TYPE_DEFAULT, acl)
159      have the same effect as
160                   acl_set_file (name, ACL_TYPE_EXTENDED, acl):
161      Each of these calls sets the file's ACL.  */
162
163   acl_t acl;
164   int ret;
165
166   /* Remove the ACL if the file has ACLs.  */
167   if (HAVE_ACL_GET_FD && desc != -1)
168     acl = acl_get_fd (desc);
169   else
170     acl = acl_get_file (name, ACL_TYPE_EXTENDED);
171   if (acl)
172     {
173       acl_free (acl);
174
175       acl = acl_init (0);
176       if (acl)
177         {
178           if (HAVE_ACL_SET_FD && desc != -1)
179             ret = acl_set_fd (desc, acl);
180           else
181             ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
182           if (ret != 0)
183             {
184               int saved_errno = errno;
185
186               acl_free (acl);
187
188               if (ACL_NOT_WELL_SUPPORTED (saved_errno))
189                 return chmod_or_fchmod (name, desc, mode);
190               else
191                 {
192                   errno = saved_errno;
193                   return -1;
194                 }
195             }
196           acl_free (acl);
197         }
198     }
199
200   /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly.  */
201   return chmod_or_fchmod (name, desc, mode);
202 #  endif
203
204 # elif HAVE_ACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
205
206 #  if defined ACL_NO_TRIVIAL
207   /* Solaris 10 (newer version), which has additional API declared in
208      <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
209      acl_fromtext, ...).  */
210
211   acl_t *aclp;
212   char acl_text[] = "user::---,group::---,mask:---,other:---";
213   int ret;
214   int saved_errno;
215
216   if (mode & S_IRUSR) acl_text[ 6] = 'r';
217   if (mode & S_IWUSR) acl_text[ 7] = 'w';
218   if (mode & S_IXUSR) acl_text[ 8] = 'x';
219   if (mode & S_IRGRP) acl_text[17] = acl_text[26] = 'r';
220   if (mode & S_IWGRP) acl_text[18] = acl_text[27] = 'w';
221   if (mode & S_IXGRP) acl_text[19] = acl_text[28] = 'x';
222   if (mode & S_IROTH) acl_text[36] = 'r';
223   if (mode & S_IWOTH) acl_text[37] = 'w';
224   if (mode & S_IXOTH) acl_text[38] = 'x';
225
226   if (acl_fromtext (acl_text, &aclp) != 0)
227     {
228       errno = ENOMEM;
229       return -1;
230     }
231
232   ret = (desc < 0 ? acl_set (name, aclp) : facl_set (desc, aclp));
233   saved_errno = errno;
234   acl_free (aclp);
235   if (ret < 0)
236     {
237       if (saved_errno == ENOSYS || saved_errno == EOPNOTSUPP)
238         return chmod_or_fchmod (name, desc, mode);
239       errno = saved_errno;
240       return -1;
241     }
242
243   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
244     {
245       /* We did not call chmod so far, so the special bits have not yet
246          been set.  */
247       return chmod_or_fchmod (name, desc, mode);
248     }
249   return 0;
250
251 #  else /* Solaris, Cygwin, general case */
252
253 #   ifdef ACE_GETACL
254   /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
255      file systems (whereas the other ones are used in UFS file systems).  */
256
257   /* The flags in the ace_t structure changed in a binary incompatible way
258      when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
259      How to distinguish the two conventions at runtime?
260      We fetch the existing ACL.  In the old convention, usually three ACEs have
261      a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
262      In the new convention, these values are not used.  */
263   int convention;
264
265   {
266     int count;
267     ace_t *entries;
268
269     for (;;)
270       {
271         if (desc != -1)
272           count = facl (desc, ACE_GETACLCNT, 0, NULL);
273         else
274           count = acl (name, ACE_GETACLCNT, 0, NULL);
275         if (count <= 0)
276           {
277             convention = -1;
278             break;
279           }
280         entries = (ace_t *) malloc (count * sizeof (ace_t));
281         if (entries == NULL)
282           {
283             errno = ENOMEM;
284             return -1;
285           }
286         if ((desc != -1
287              ? facl (desc, ACE_GETACL, count, entries)
288              : acl (name, ACE_GETACL, count, entries))
289             == count)
290           {
291             int i;
292
293             convention = 0;
294             for (i = 0; i < count; i++)
295               if (entries[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_OTHER))
296                 {
297                   convention = 1;
298                   break;
299                 }
300             free (entries);
301             break;
302           }
303         /* Huh? The number of ACL entries changed since the last call.
304            Repeat.  */
305         free (entries);
306       }
307   }
308
309   if (convention >= 0)
310     {
311       ace_t entries[3];
312       int ret;
313
314       if (convention)
315         {
316           /* Running on Solaris 10.  */
317           entries[0].a_type = ALLOW;
318           entries[0].a_flags = ACE_OWNER;
319           entries[0].a_who = 0; /* irrelevant */
320           entries[0].a_access_mask = (mode >> 6) & 7;
321           entries[1].a_type = ALLOW;
322           entries[1].a_flags = ACE_GROUP;
323           entries[1].a_who = 0; /* irrelevant */
324           entries[1].a_access_mask = (mode >> 3) & 7;
325           entries[2].a_type = ALLOW;
326           entries[2].a_flags = ACE_OTHER;
327           entries[2].a_who = 0;
328           entries[2].a_access_mask = mode & 7;
329         }
330       else
331         {
332           /* Running on Solaris 10 (newer version) or Solaris 11.  */
333           entries[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
334           entries[0].a_flags = NEW_ACE_OWNER;
335           entries[0].a_who = 0; /* irrelevant */
336           entries[0].a_access_mask =
337             (mode & 0400 ? NEW_ACE_READ_DATA : 0)
338             | (mode & 0200 ? NEW_ACE_WRITE_DATA : 0)
339             | (mode & 0100 ? NEW_ACE_EXECUTE : 0);
340           entries[1].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
341           entries[1].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
342           entries[1].a_who = 0; /* irrelevant */
343           entries[1].a_access_mask =
344             (mode & 0040 ? NEW_ACE_READ_DATA : 0)
345             | (mode & 0020 ? NEW_ACE_WRITE_DATA : 0)
346             | (mode & 0010 ? NEW_ACE_EXECUTE : 0);
347           entries[2].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
348           entries[2].a_flags = ACE_EVERYONE;
349           entries[2].a_who = 0;
350           entries[2].a_access_mask =
351             (mode & 0004 ? NEW_ACE_READ_DATA : 0)
352             | (mode & 0002 ? NEW_ACE_WRITE_DATA : 0)
353             | (mode & 0001 ? NEW_ACE_EXECUTE : 0);
354         }
355       if (desc != -1)
356         ret = facl (desc, ACE_SETACL,
357                     sizeof (entries) / sizeof (ace_t), entries);
358       else
359         ret = acl (name, ACE_SETACL,
360                    sizeof (entries) / sizeof (ace_t), entries);
361       if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
362         {
363           if (errno == ENOSYS)
364             return chmod_or_fchmod (name, desc, mode);
365           return -1;
366         }
367     }
368 #   endif
369
370   {
371     aclent_t entries[3];
372     int ret;
373
374     entries[0].a_type = USER_OBJ;
375     entries[0].a_id = 0; /* irrelevant */
376     entries[0].a_perm = (mode >> 6) & 7;
377     entries[1].a_type = GROUP_OBJ;
378     entries[1].a_id = 0; /* irrelevant */
379     entries[1].a_perm = (mode >> 3) & 7;
380     entries[2].a_type = OTHER_OBJ;
381     entries[2].a_id = 0;
382     entries[2].a_perm = mode & 7;
383
384     if (desc != -1)
385       ret = facl (desc, SETACL, sizeof (entries) / sizeof (aclent_t), entries);
386     else
387       ret = acl (name, SETACL, sizeof (entries) / sizeof (aclent_t), entries);
388     if (ret < 0)
389       {
390         if (errno == ENOSYS || errno == EOPNOTSUPP)
391           return chmod_or_fchmod (name, desc, mode);
392         return -1;
393       }
394   }
395
396   if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
397     {
398       /* We did not call chmod so far, so the special bits have not yet
399          been set.  */
400       return chmod_or_fchmod (name, desc, mode);
401     }
402   return 0;
403
404 #  endif
405
406 # elif HAVE_GETACL /* HP-UX */
407
408   struct stat statbuf;
409   struct acl_entry entries[3];
410   int ret;
411
412   if (desc != -1)
413     ret = fstat (desc, &statbuf);
414   else
415     ret = stat (name, &statbuf);
416   if (ret < 0)
417     return -1;
418
419   entries[0].uid = statbuf.st_uid;
420   entries[0].gid = ACL_NSGROUP;
421   entries[0].mode = (mode >> 6) & 7;
422   entries[1].uid = ACL_NSUSER;
423   entries[1].gid = statbuf.st_gid;
424   entries[1].mode = (mode >> 3) & 7;
425   entries[2].uid = ACL_NSUSER;
426   entries[2].gid = ACL_NSGROUP;
427   entries[2].mode = mode & 7;
428
429   if (desc != -1)
430     ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
431   else
432     ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
433   if (ret < 0)
434     {
435       if (errno == ENOSYS || errno == EOPNOTSUPP)
436         return chmod_or_fchmod (name, desc, mode);
437       return -1;
438     }
439
440   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
441     {
442       /* We did not call chmod so far, so the special bits have not yet
443          been set.  */
444       return chmod_or_fchmod (name, desc, mode);
445     }
446   return 0;
447
448 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
449
450   acl_type_list_t types;
451   size_t types_size = sizeof (types);
452   acl_type_t type;
453
454   if (aclx_gettypes (name, &types, &types_size) < 0
455       || types.num_entries == 0)
456     return chmod_or_fchmod (name, desc, mode);
457
458   /* XXX Do we need to clear all types of ACLs for the given file, or is it
459      sufficient to clear the first one?  */
460   type = types.entries[0];
461   if (type.u64 == ACL_AIXC)
462     {
463       union { struct acl a; char room[128]; } u;
464       int ret;
465
466       u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
467       u.a.acl_mode = mode & ~(S_IXACL | 0777);
468       u.a.u_access = (mode >> 6) & 7;
469       u.a.g_access = (mode >> 3) & 7;
470       u.a.o_access = mode & 7;
471
472       if (desc != -1)
473         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
474                          type, &u.a, u.a.acl_len, mode);
475       else
476         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
477                         type, &u.a, u.a.acl_len, mode);
478       if (!(ret < 0 && errno == ENOSYS))
479         return ret;
480     }
481   else if (type.u64 == ACL_NFS4)
482     {
483       union { nfs4_acl_int_t a; char room[128]; } u;
484       nfs4_ace_int_t *ace;
485       int ret;
486
487       u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
488       u.a.aclEntryN = 0;
489       ace = &u.a.aclEntry[0];
490       {
491         ace->flags = ACE4_ID_SPECIAL;
492         ace->aceWho.special_whoid = ACE4_WHO_OWNER;
493         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
494         ace->aceFlags = 0;
495         ace->aceMask =
496           (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
497           | (mode & 0200
498              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
499                | ACE4_ADD_SUBDIRECTORY
500              : 0)
501           | (mode & 0100 ? ACE4_EXECUTE : 0);
502         ace->aceWhoString[0] = '\0';
503         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
504         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
505         u.a.aclEntryN++;
506       }
507       {
508         ace->flags = ACE4_ID_SPECIAL;
509         ace->aceWho.special_whoid = ACE4_WHO_GROUP;
510         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
511         ace->aceFlags = 0;
512         ace->aceMask =
513           (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
514           | (mode & 0020
515              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
516                | ACE4_ADD_SUBDIRECTORY
517              : 0)
518           | (mode & 0010 ? ACE4_EXECUTE : 0);
519         ace->aceWhoString[0] = '\0';
520         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
521         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
522         u.a.aclEntryN++;
523       }
524       {
525         ace->flags = ACE4_ID_SPECIAL;
526         ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
527         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
528         ace->aceFlags = 0;
529         ace->aceMask =
530           (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
531           | (mode & 0002
532              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
533                | ACE4_ADD_SUBDIRECTORY
534              : 0)
535           | (mode & 0001 ? ACE4_EXECUTE : 0);
536         ace->aceWhoString[0] = '\0';
537         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
538         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
539         u.a.aclEntryN++;
540       }
541       u.a.aclLength = (char *) ace - (char *) &u.a;
542
543       if (desc != -1)
544         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
545                          type, &u.a, u.a.aclLength, mode);
546       else
547         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
548                         type, &u.a, u.a.aclLength, mode);
549       if (!(ret < 0 && errno == ENOSYS))
550         return ret;
551     }
552
553   return chmod_or_fchmod (name, desc, mode);
554
555 # elif HAVE_STATACL /* older AIX */
556
557   union { struct acl a; char room[128]; } u;
558   int ret;
559
560   u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
561   u.a.acl_mode = mode & ~(S_IXACL | 0777);
562   u.a.u_access = (mode >> 6) & 7;
563   u.a.g_access = (mode >> 3) & 7;
564   u.a.o_access = mode & 7;
565
566   if (desc != -1)
567     ret = fchacl (desc, &u.a, u.a.acl_len);
568   else
569     ret = chacl (name, &u.a, u.a.acl_len);
570
571   if (ret < 0 && errno == ENOSYS)
572     return chmod_or_fchmod (name, desc, mode);
573
574   return ret;
575
576 # else /* Unknown flavor of ACLs */
577   return chmod_or_fchmod (name, desc, mode);
578 # endif
579 #else /* !USE_ACL */
580   return chmod_or_fchmod (name, desc, mode);
581 #endif
582 }
583
584 /* As with qset_acl, but also output a diagnostic on failure.  */
585
586 int
587 set_acl (char const *name, int desc, mode_t mode)
588 {
589   int r = qset_acl (name, desc, mode);
590   if (r != 0)
591     error (0, errno, _("setting permissions for %s"), quote (name));
592   return r;
593 }