1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2004-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2016 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  *   Original written by Preben 'Peppe' Guldberg, December 2004
25  *   Major rewrite by Marco van Wieringen, November 2008
26  *   Major overhaul by Marco van Wieringen, January 2012
27  *   Moved into findlib so it can be used from other programs, May 2012
28  */
29 /**
30  * @file
31  * Functions to handle ACLs for bareos.
32  *
33  * Currently we support the following OSes:
34  *   - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface)
35  *   - Darwin
36  *   - FreeBSD (POSIX and NFSv4/ZFS acls)
37  *   - GNU Hurd
38  *   - HPUX
39  *   - IRIX
40  *   - Linux
41  *   - Solaris (POSIX and NFSv4/ZFS acls)
42  *   - Tru64
43  *
44  * Next to OS specific acls we support AFS acls using the pioctl interface.
45  *
46  * We handle two different types of ACLs: access and default ACLS.
47  * On most systems that support default ACLs they only apply to directories.
48  *
49  * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs
50  * independently, while others (eg. Solaris) provide both in one call.
51  *
52  * The Filed saves ACLs in their native format and uses different streams
53  * for all different platforms. Currently we only allow ACLs to be restored
54  * which were saved in the native format of the platform they are extracted
55  * on. Later on we might add conversion functions for mapping from one
56  * platform to another or allow restores of systems that use the same
57  * native format.
58  *
59  * Its also interesting to see what the exact format of acl text is on
60  * certain platforms and if they use they same encoding we might allow
61  * different platform streams to be decoded on another similar platform.
62  */
63 
64 #include "include/bareos.h"
65 #include "include/jcr.h"
66 #include "find.h"
67 
68 #if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL)
69 /**
70  * Entry points when compiled without support for ACLs or on an unsupported platform.
71  */
BuildAclStreams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)72 bacl_exit_code BuildAclStreams(JobControlRecord *jcr,
73                                  acl_data_t *acl_data,
74                                  FindFilesPacket *ff_pkt)
75 {
76    return bacl_exit_fatal;
77 }
78 
parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)79 bacl_exit_code parse_acl_streams(JobControlRecord *jcr,
80                                  acl_data_t *acl_data,
81                                  int stream,
82                                  char *content,
83                                  uint32_t content_length)
84 {
85    return bacl_exit_fatal;
86 }
87 #else
88 /**
89  * Send an ACL stream to the SD.
90  */
SendAclStream(JobControlRecord * jcr,acl_data_t * acl_data,int stream)91 bacl_exit_code SendAclStream(JobControlRecord *jcr, acl_data_t *acl_data, int stream)
92 {
93    BareosSocket *sd = jcr->store_bsock;
94    POOLMEM *msgsave;
95 #ifdef FD_NO_SEND_TEST
96    return bacl_exit_ok;
97 #endif
98 
99    /*
100     * Sanity check
101     */
102    if (acl_data->u.build->content_length <= 0) {
103       return bacl_exit_ok;
104    }
105 
106    /*
107     * Send header
108     */
109    if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
110       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
111             sd->bstrerror());
112       return bacl_exit_fatal;
113    }
114 
115    /*
116     * Send the buffer to the storage deamon
117     */
118    Dmsg1(400, "Backing up ACL <%s>\n", acl_data->u.build->content);
119    msgsave = sd->msg;
120    sd->msg = acl_data->u.build->content;
121    sd->message_length = acl_data->u.build->content_length + 1;
122    if (!sd->send()) {
123       sd->msg = msgsave;
124       sd->message_length = 0;
125       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
126             sd->bstrerror());
127       return bacl_exit_fatal;
128    }
129 
130    jcr->JobBytes += sd->message_length;
131    sd->msg = msgsave;
132    if (!sd->signal(BNET_EOD)) {
133       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
134             sd->bstrerror());
135       return bacl_exit_fatal;
136    }
137 
138    Dmsg1(200, "ACL of file: %s successfully backed up!\n", acl_data->last_fname);
139    return bacl_exit_ok;
140 }
141 
142 /**
143  * First the native ACLs.
144  */
145 #if defined(HAVE_ACL)
146 #if defined(HAVE_AIX_OS)
147 
148 #if defined(HAVE_EXTENDED_ACL)
149 
150 #include <sys/access.h>
151 #include <sys/acl.h>
152 
AclIsTrivial(struct acl * acl)153 static bool AclIsTrivial(struct acl *acl)
154 {
155    return (acl_last(acl) != acl->acl_ext ? false : true);
156 }
157 
acl_nfs4_is_trivial(nfs4_acl_int_t * acl)158 static bool acl_nfs4_is_trivial(nfs4_acl_int_t *acl)
159 {
160 #if 0
161    return (acl->aclEntryN > 0 ? false : true);
162 #else
163    int i;
164    int count = acl->aclEntryN;
165    nfs4_ace_int_t *ace;
166 
167    for (i = 0; i < count; i++) {
168       ace = &acl->aclEntry[i];
169       if (!((ace->flags & ACE4_ID_SPECIAL) != 0 &&
170             (ace->aceWho.special_whoid == ACE4_WHO_OWNER ||
171              ace->aceWho.special_whoid == ACE4_WHO_GROUP ||
172              ace->aceWho.special_whoid == ACE4_WHO_EVERYONE) &&
173              ace->aceType == ACE4_ACCESS_ALLOWED_ACE_TYPE &&
174              ace->aceFlags == 0 &&
175             (ace->aceMask & ~(ACE4_READ_DATA |
176                               ACE4_LIST_DIRECTORY |
177                               ACE4_WRITE_DATA |
178                               ACE4_ADD_FILE |
179                               ACE4_EXECUTE)) == 0)) {
180          return false;
181       }
182    }
183    return true;
184 #endif
185 }
186 
187 /**
188  * Define the supported ACL streams for this OS
189  */
190 static int os_access_acl_streams[3] = {
191    STREAM_ACL_AIX_TEXT,
192    STREAM_ACL_AIX_AIXC,
193    STREAM_ACL_AIX_NFS4
194 };
195 static int os_default_acl_streams[1] = {
196    -1
197 };
198 
aix_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)199 static bacl_exit_code aix_build_acl_streams(JobControlRecord *jcr,
200                                             acl_data_t *acl_data,
201                                             FindFilesPacket *ff_pkt)
202 {
203    mode_t mode;
204    acl_type_t type;
205    size_t aclsize, acltxtsize;
206    bacl_exit_code retval = bacl_exit_error;
207    POOLMEM *aclbuf = GetPoolMemory(PM_MESSAGE);
208 
209    /*
210     * First see how big the buffers should be.
211     */
212    memset(&type, 0, sizeof(acl_type_t));
213    type.u64 = ACL_ANY;
214    if (aclx_get(acl_data->last_fname,
215 #if defined(GET_ACL_FOR_LINK)
216                 GET_ACLINFO_ONLY | GET_ACL_FOR_LINK,
217 #else
218                 GET_ACLINFO_ONLY,
219 #endif
220                 &type, NULL, &aclsize, &mode) < 0) {
221       BErrNo be;
222 
223       switch (errno) {
224       case ENOENT:
225          retval = bacl_exit_ok;
226          goto bail_out;
227       case ENOSYS:
228          /*
229           * If the filesystem reports it doesn't support ACLs we clear the
230           * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
231           * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
232           * when we change from one filesystem to another.
233           */
234          acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
235          retval = bacl_exit_ok;
236          goto bail_out;
237       default:
238          Mmsg2(jcr->errmsg,
239                _("aclx_get error on file \"%s\": ERR=%s\n"),
240                acl_data->last_fname, be.bstrerror());
241          Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
242                acl_data->last_fname, be.bstrerror());
243          goto bail_out;
244       }
245    }
246 
247    /*
248     * Make sure the buffers are big enough.
249     */
250    aclbuf = CheckPoolMemorySize(aclbuf, aclsize + 1);
251 
252    /*
253     * Retrieve the ACL info.
254     */
255    if (aclx_get(acl_data->last_fname,
256 #if defined(GET_ACL_FOR_LINK)
257                 GET_ACL_FOR_LINK,
258 #else
259                 0,
260 #endif
261                 &type, aclbuf, &aclsize, &mode) < 0) {
262       BErrNo be;
263 
264       switch (errno) {
265       case ENOENT:
266          retval = bacl_exit_ok;
267          goto bail_out;
268       default:
269          Mmsg2(jcr->errmsg,
270                _("aclx_get error on file \"%s\": ERR=%s\n"),
271                acl_data->last_fname, be.bstrerror());
272          Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
273                acl_data->last_fname, be.bstrerror());
274          goto bail_out;
275       }
276    }
277 
278    /*
279     * See if the acl is non trivial.
280     */
281    switch (type.u64) {
282    case ACL_AIXC:
283       if (AclIsTrivial((struct acl *)aclbuf)) {
284          retval = bacl_exit_ok;
285          goto bail_out;
286       }
287       break;
288    case ACL_NFS4:
289       if (acl_nfs4_is_trivial((nfs4_acl_int_t *)aclbuf)) {
290          retval = bacl_exit_ok;
291          goto bail_out;
292       }
293       break;
294    default:
295       Mmsg2(jcr->errmsg,
296             _("Unknown acl type encountered on file \"%s\": %ld\n"),
297             acl_data->last_fname, type.u64);
298       Dmsg2(100, "Unknown acl type encountered on file \"%s\": %ld\n",
299             acl_data->last_fname, type.u64);
300       goto bail_out;
301    }
302 
303    /*
304     * We have a non-trivial acl lets convert it into some ASCII form.
305     */
306    acltxtsize = SizeofPoolMemory(acl_data->u.build->content);
307    if (aclx_printStr(acl_data->u.build->content, &acltxtsize, aclbuf,
308                      aclsize, type, acl_data->last_fname, 0) < 0) {
309       switch (errno) {
310       case ENOSPC:
311          /*
312           * Our buffer is not big enough, acltxtsize should be updated with the value
313           * the aclx_printStr really need. So we increase the buffer and try again.
314           */
315          acl_data->u.build->content =
316          CheckPoolMemorySize(acl_data->u.build->content, acltxtsize + 1);
317          if (aclx_printStr(acl_data->u.build->content, &acltxtsize, aclbuf,
318                            aclsize, type, acl_data->last_fname, 0) < 0) {
319             Mmsg1(jcr->errmsg,
320                   _("Failed to convert acl into text on file \"%s\"\n"),
321                   acl_data->last_fname);
322             Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
323                   acl_data->last_fname, type.u64);
324             goto bail_out;
325          }
326          break;
327       default:
328          Mmsg1(jcr->errmsg,
329                _("Failed to convert acl into text on file \"%s\"\n"),
330                acl_data->last_fname);
331          Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
332                acl_data->last_fname, type.u64);
333          goto bail_out;
334       }
335    }
336 
337    acl_data->u.build->content_length = strlen(acl_data->u.build->content) + 1;
338    switch (type.u64) {
339    case ACL_AIXC:
340       retval = SendAclStream(jcr, acl_data, STREAM_ACL_AIX_AIXC);
341       break;
342    case ACL_NFS4:
343       retval = SendAclStream(jcr, acl_data, STREAM_ACL_AIX_NFS4);
344       break;
345    }
346 
347 bail_out:
348    FreePoolMemory(aclbuf);
349 
350    return retval;
351 }
352 
353 /**
354  * See if a specific type of ACLs are supported on the filesystem
355  * the file is located on.
356  */
aix_query_acl_support(JobControlRecord * jcr,acl_data_t * acl_data,uint64_t aclType,acl_type_t * pacl_type_info)357 static inline bool aix_query_acl_support(JobControlRecord *jcr,
358                                          acl_data_t *acl_data,
359                                          uint64_t aclType,
360                                          acl_type_t *pacl_type_info)
361 {
362    unsigned int i;
363    acl_types_list_t acl_type_list;
364    size_t acl_type_list_len = sizeof(acl_types_list_t);
365 
366    memset(&acl_type_list, 0, sizeof(acl_type_list));
367    if (aclx_gettypes(acl_data->last_fname, &acl_type_list, &acl_type_list_len)) {
368       return false;
369    }
370 
371    for (i = 0; i < acl_type_list.num_entries; i++) {
372       if (acl_type_list.entries[i].u64 == aclType) {
373          memcpy(pacl_type_info, acl_type_list.entries + i, sizeof(acl_type_t));
374          return true;
375       }
376    }
377    return false;
378 }
379 
aix_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)380 static bacl_exit_code aix_parse_acl_streams(JobControlRecord *jcr,
381                                             acl_data_t *acl_data,
382                                             int stream,
383                                             char *content,
384                                             uint32_t content_length)
385 {
386    int cnt;
387    acl_type_t type;
388    size_t aclsize;
389    bacl_exit_code retval = bacl_exit_error;
390    POOLMEM *aclbuf = GetPoolMemory(PM_MESSAGE);
391 
392    switch (stream) {
393    case STREAM_ACL_AIX_TEXT:
394       /*
395        * Handle the old stream using the old system call for now.
396        */
397       if (acl_put(acl_data->last_fname, content, 0) != 0) {
398          retval = bacl_exit_error;
399          goto bail_out;
400       }
401       retval = bacl_exit_ok;
402       goto bail_out;
403    case STREAM_ACL_AIX_AIXC:
404       if (!aix_query_acl_support(jcr, acl_data, ACL_AIXC, &type)) {
405          Mmsg1(jcr->errmsg,
406                _("Trying to restore POSIX acl on file \"%s\" on filesystem without AIXC acl support\n"),
407                acl_data->last_fname);
408          goto bail_out;
409       }
410       break;
411    case STREAM_ACL_AIX_NFS4:
412       if (!aix_query_acl_support(jcr, acl_data, ACL_NFS4, &type)) {
413          Mmsg1(jcr->errmsg,
414                _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without NFS4 acl support\n"),
415                acl_data->last_fname);
416          goto bail_out;
417       }
418       break;
419    default:
420       goto bail_out;
421    } /* end switch (stream) */
422 
423    /*
424     * Set the acl buffer to an initial size. For now we set it
425     * to the same size as the ASCII representation.
426     */
427    aclbuf = CheckPoolMemorySize(aclbuf, content_length);
428    aclsize = content_length;
429    if (aclx_scanStr(content, aclbuf, &aclsize, type) < 0) {
430       BErrNo be;
431 
432       switch (errno) {
433       case ENOSPC:
434          /*
435           * The buffer isn't big enough. The man page doesn't say that aclsize
436           * is updated to the needed size as what is done with aclx_printStr.
437           * So for now we try to increase the buffer a maximum of 3 times
438           * and retry the conversion.
439           */
440          for (cnt = 0; cnt < 3; cnt++) {
441             aclsize = 2 * aclsize;
442             aclbuf = CheckPoolMemorySize(aclbuf, aclsize);
443 
444             if (aclx_scanStr(content, aclbuf, &aclsize, type) == 0) {
445                break;
446             }
447 
448             /*
449              * See why we failed this time, ENOSPC retry if max retries not met,
450              * otherwise abort.
451              */
452             switch (errno) {
453             case ENOSPC:
454                if (cnt < 3) {
455                   continue;
456                }
457                /*
458                 * FALLTHROUGH
459                 */
460             default:
461                Mmsg2(jcr->errmsg,
462                      _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
463                      acl_data->last_fname, be.bstrerror(errno));
464                Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
465                      acl_data->last_fname, be.bstrerror());
466                goto bail_out;
467             }
468          }
469          break;
470       default:
471          Mmsg2(jcr->errmsg,
472                _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
473                acl_data->last_fname, be.bstrerror());
474          Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
475                acl_data->last_fname, be.bstrerror());
476       }
477    }
478 
479    if (aclx_put(acl_data->last_fname, SET_ACL, type, aclbuf, aclsize, 0) < 0) {
480       BErrNo be;
481 
482       switch (errno) {
483       case ENOENT:
484          retval = bacl_exit_ok;
485          goto bail_out;
486       case ENOSYS:
487          /*
488           * If the filesystem reports it doesn't support ACLs we clear the
489           * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
490           * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
491           * when we change from one filesystem to another.
492           */
493          acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
494          retval = bacl_exit_ok;
495          goto bail_out;
496       default:
497          Mmsg2(jcr->errmsg,
498                _("aclx_put error on file \"%s\": ERR=%s\n"),
499                acl_data->last_fname, be.bstrerror());
500          Dmsg2(100, "aclx_put error file=%s ERR=%s\n",
501                acl_data->last_fname, be.bstrerror());
502          goto bail_out;
503       }
504    }
505 
506    retval = bacl_exit_ok;
507 
508 bail_out:
509    FreePoolMemory(aclbuf);
510 
511    return retval;
512 }
513 
514 #else /* HAVE_EXTENDED_ACL */
515 
516 #include <sys/access.h>
517 
518 /**
519  * Define the supported ACL streams for this OS
520  */
521 static int os_access_acl_streams[1] = {
522    STREAM_ACL_AIX_TEXT
523 };
524 static int os_default_acl_streams[1] = {
525    -1
526 };
527 
aix_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)528 static bacl_exit_code aix_build_acl_streams(JobControlRecord *jcr,
529                                             acl_data_t *acl_data,
530                                             FindFilesPacket *ff_pkt)
531 {
532    char *acl_text;
533 
534    if ((acl_text = acl_get(acl_data->last_fname)) != NULL) {
535       acl_data->u.build->content_length =
536       PmStrcpy(acl_data->u.build->content, acl_text);
537       Actuallyfree(acl_text);
538       return SendAclStream(jcr, acl_data, STREAM_ACL_AIX_TEXT);
539    }
540    return bacl_exit_error;
541 }
542 
aix_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)543 static bacl_exit_code aix_parse_acl_streams(JobControlRecord *jcr,
544                                             acl_data_t *acl_data,
545                                             int stream,
546                                             char *content,
547                                             uint32_t content_length)
548 {
549    if (acl_put(acl_data->last_fname, content, 0) != 0) {
550       return bacl_exit_error;
551    }
552    return bacl_exit_ok;
553 }
554 #endif /* HAVE_EXTENDED_ACL */
555 
556 /**
557  * For this OS setup the build and parse function pointer to the OS specific functions.
558  */
559 static bacl_exit_code (*os_build_acl_streams)
560                       (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
561                       aix_build_acl_streams;
562 static bacl_exit_code (*os_parse_acl_streams)
563                       (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
564                       aix_parse_acl_streams;
565 
566 #elif defined(HAVE_DARWIN_OS) || \
567       defined(HAVE_FREEBSD_OS) || \
568       defined(HAVE_IRIX_OS) || \
569       defined(HAVE_OSF1_OS) || \
570       defined(HAVE_LINUX_OS) || \
571       defined(HAVE_HURD_OS)
572 
573 #include <sys/types.h>
574 
575 #ifdef HAVE_SYS_ACL_H
576 #include <sys/acl.h>
577 #else
578 #error "configure failed to detect availability of sys/acl.h"
579 #endif
580 
581 /**
582  * On IRIX we can get shortened ACLs
583  */
584 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
585 #define acl_to_text(acl,len)     acl_to_short_text((acl), (len))
586 #endif
587 
588 /**
589  * On Linux we can get numeric and/or shorted ACLs
590  */
591 #if defined(HAVE_LINUX_OS)
592 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
593 #define BACL_ALTERNATE_TEXT            (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
594 #elif defined(BACL_WANT_SHORT_ACLS)
595 #define BACL_ALTERNATE_TEXT            TEXT_ABBREVIATE
596 #elif defined(BACL_WANT_NUMERIC_IDS)
597 #define BACL_ALTERNATE_TEXT            TEXT_NUMERIC_IDS
598 #endif
599 #ifdef BACL_ALTERNATE_TEXT
600 #include <acl/libacl.h>
601 #define acl_to_text(acl,len)     (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
602 #endif
603 #endif
604 
605 /**
606  * On FreeBSD we can get numeric ACLs
607  */
608 #if defined(HAVE_FREEBSD_OS)
609 #if defined(BACL_WANT_NUMERIC_IDS)
610 #define BACL_ALTERNATE_TEXT            ACL_TEXT_NUMERIC_IDS
611 #endif
612 #ifdef BACL_ALTERNATE_TEXT
613 #define acl_to_text(acl,len)     (acl_to_text_np((acl), (len), BACL_ALTERNATE_TEXT))
614 #endif
615 #endif
616 
617 /**
618  * Some generic functions used by multiple OSes.
619  */
BacToOsAcltype(bacl_type acltype)620 static acl_type_t BacToOsAcltype(bacl_type acltype)
621 {
622    acl_type_t ostype;
623 
624    switch (acltype) {
625    case BACL_TYPE_ACCESS:
626       ostype = ACL_TYPE_ACCESS;
627       break;
628    case BACL_TYPE_DEFAULT:
629       ostype = ACL_TYPE_DEFAULT;
630       break;
631 #ifdef HAVE_ACL_TYPE_NFS4
632       /*
633        * FreeBSD has an additional acl type named ACL_TYPE_NFS4.
634        */
635    case BACL_TYPE_NFS4:
636       ostype = ACL_TYPE_NFS4;
637       break;
638 #endif
639 #ifdef HAVE_ACL_TYPE_DEFAULT_DIR
640    case BACL_TYPE_DEFAULT_DIR:
641       /*
642        * TRU64 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
643        */
644       ostype = ACL_TYPE_DEFAULT_DIR;
645       break;
646 #endif
647 #ifdef HAVE_ACL_TYPE_EXTENDED
648    case BACL_TYPE_EXTENDED:
649       /*
650        * MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
651        */
652       ostype = ACL_TYPE_EXTENDED;
653       break;
654 #endif
655    default:
656       /*
657        * This should never happen, as the per OS version function only tries acl
658        * types supported on a certain platform.
659        */
660       ostype = (acl_type_t)ACL_TYPE_NONE;
661       break;
662    }
663    return ostype;
664 }
665 
AclCountEntries(acl_t acl)666 static int AclCountEntries(acl_t acl)
667 {
668    int count = 0;
669 #if defined(HAVE_FREEBSD_OS) || \
670     defined(HAVE_LINUX_OS) || \
671     defined(HAVE_HURD_OS)
672    acl_entry_t ace;
673    int entry_available;
674 
675    entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
676    while (entry_available == 1) {
677       count++;
678       entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
679    }
680 #elif defined(HAVE_IRIX_OS)
681    count = acl->acl_cnt;
682 #elif defined(HAVE_OSF1_OS)
683    count = acl->acl_num;
684 #elif defined(HAVE_DARWIN_OS)
685    acl_entry_t ace;
686    int entry_available;
687 
688    entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
689    while (entry_available == 0) {
690       count++;
691       entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
692    }
693 #endif
694    return count;
695 }
696 
697 #if !defined(HAVE_DARWIN_OS)
698 /**
699  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
700  * There is no need to store those acls as we already store the stat bits too.
701  */
AclIsTrivial(acl_t acl)702 static bool AclIsTrivial(acl_t acl)
703 {
704   /*
705    * acl is trivial if it has only the following entries:
706    * "user::",
707    * "group::",
708    * "other::"
709    */
710    acl_entry_t ace;
711    acl_tag_t tag;
712 #if defined(HAVE_FREEBSD_OS) || \
713     defined(HAVE_LINUX_OS) || \
714     defined(HAVE_HURD_OS)
715    int entry_available;
716 
717    entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
718    while (entry_available == 1) {
719       /*
720        * Get the tag type of this acl entry.
721        * If we fail to get the tagtype we call the acl non-trivial.
722        */
723       if (acl_get_tag_type(ace, &tag) < 0)
724          return true;
725       /*
726        * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
727        */
728       if (tag != ACL_USER_OBJ &&
729           tag != ACL_GROUP_OBJ &&
730           tag != ACL_OTHER)
731          return false;
732       entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
733    }
734    return true;
735 #elif defined(HAVE_IRIX_OS)
736    int n;
737 
738    for (n = 0; n < acl->acl_cnt; n++) {
739       ace = &acl->acl_entry[n];
740       tag = ace->ae_tag;
741 
742       /*
743        * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
744        */
745       if (tag != ACL_USER_OBJ &&
746           tag != ACL_GROUP_OBJ &&
747           tag != ACL_OTHER_OBJ)
748          return false;
749    }
750    return true;
751 #elif defined(HAVE_OSF1_OS)
752    int count;
753 
754    ace = acl->acl_first;
755    count = acl->acl_num;
756 
757    while (count > 0) {
758       tag = ace->entry->acl_type;
759       /*
760        * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
761        */
762       if (tag != ACL_USER_OBJ &&
763           tag != ACL_GROUP_OBJ &&
764           tag != ACL_OTHER)
765          return false;
766       /*
767        * On Tru64, perm can also contain non-standard bits such as
768        * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
769        */
770       if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)))
771          return false;
772       ace = ace->next;
773       count--;
774    }
775    return true;
776 #endif
777 }
778 #endif
779 
780 /**
781  * Generic wrapper around acl_get_file call.
782  */
generic_get_acl_from_os(JobControlRecord * jcr,acl_data_t * acl_data,bacl_type acltype)783 static bacl_exit_code generic_get_acl_from_os(JobControlRecord *jcr,
784                                               acl_data_t *acl_data,
785                                               bacl_type acltype)
786 {
787    acl_t acl;
788    acl_type_t ostype;
789    char *acl_text;
790    bacl_exit_code retval = bacl_exit_ok;
791 
792    ostype = BacToOsAcltype(acltype);
793    acl = acl_get_file(acl_data->last_fname, ostype);
794    if (acl) {
795       /**
796        * From observation, IRIX's acl_get_file() seems to return a
797        * non-NULL acl with a count field of -1 when a file has no ACL
798        * defined, while IRIX's acl_to_text() returns NULL when presented
799        * with such an ACL.
800        *
801        * For all other implmentations we check if there are more then
802        * zero entries in the acl returned.
803        */
804       if (AclCountEntries(acl) <= 0) {
805          goto bail_out;
806       }
807 
808       /*
809        * Make sure this is not just a trivial ACL.
810        */
811 #if !defined(HAVE_DARWIN_OS)
812       if (acltype == BACL_TYPE_ACCESS && AclIsTrivial(acl)) {
813          /*
814           * The ACLs simply reflect the (already known) standard permissions
815           * So we don't send an ACL stream to the SD.
816           */
817          goto bail_out;
818       }
819 #endif
820 #if defined(HAVE_FREEBSD_OS) && defined(_PC_ACL_NFS4)
821       if (acltype == BACL_TYPE_NFS4) {
822          int trivial;
823          if (acl_is_trivial_np(acl, &trivial) == 0) {
824             if (trivial == 1) {
825                /*
826                 * The ACLs simply reflect the (already known) standard permissions
827                 * So we don't send an ACL stream to the SD.
828                 */
829                goto bail_out;
830             }
831          }
832       }
833 #endif
834 
835       /*
836        * Convert the internal acl representation into a text representation.
837        */
838       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
839          acl_data->u.build->content_length =
840          PmStrcpy(acl_data->u.build->content, acl_text);
841          acl_free(acl);
842          acl_free(acl_text);
843          return bacl_exit_ok;
844       }
845 
846       BErrNo be;
847       Mmsg2(jcr->errmsg,
848             _("acl_to_text error on file \"%s\": ERR=%s\n"),
849             acl_data->last_fname, be.bstrerror());
850       Dmsg2(100, "acl_to_text error file=%s ERR=%s\n",
851             acl_data->last_fname, be.bstrerror());
852 
853       retval = bacl_exit_error;
854       goto bail_out;
855    } else {
856       BErrNo be;
857 
858       /*
859        * Handle errors gracefully.
860        */
861       switch (errno) {
862 #if defined(BACL_ENOTSUP)
863       case BACL_ENOTSUP:
864          /*
865           * If the filesystem reports it doesn't support ACLs we clear the
866           * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
867           * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
868           * when we change from one filesystem to another.
869           */
870          acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
871          goto bail_out;
872 #endif
873       case ENOENT:
874          goto bail_out;
875       default:
876          /* Some real error */
877          Mmsg2(jcr->errmsg,
878                _("acl_get_file error on file \"%s\": ERR=%s\n"),
879                acl_data->last_fname, be.bstrerror());
880          Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",
881                acl_data->last_fname, be.bstrerror());
882 
883          retval = bacl_exit_error;
884          goto bail_out;
885       }
886    }
887 
888 bail_out:
889    if (acl) {
890       acl_free(acl);
891    }
892    PmStrcpy(acl_data->u.build->content, "");
893    acl_data->u.build->content_length = 0;
894    return retval;
895 }
896 
897 /**
898  * Generic wrapper around acl_set_file call.
899  */
generic_set_acl_on_os(JobControlRecord * jcr,acl_data_t * acl_data,bacl_type acltype,char * content,uint32_t content_length)900 static bacl_exit_code generic_set_acl_on_os(JobControlRecord *jcr,
901                                             acl_data_t *acl_data,
902                                             bacl_type acltype,
903                                             char *content,
904                                             uint32_t content_length)
905 {
906    acl_t acl;
907    acl_type_t ostype;
908 
909    /*
910     * If we get empty default ACLs, clear ACLs now
911     */
912    ostype = BacToOsAcltype(acltype);
913    if (ostype == ACL_TYPE_DEFAULT && strlen(content) == 0) {
914       if (acl_delete_def_file(acl_data->last_fname) == 0) {
915          return bacl_exit_ok;
916       }
917       BErrNo be;
918 
919       switch (errno) {
920       case ENOENT:
921          return bacl_exit_ok;
922 #if defined(BACL_ENOTSUP)
923       case BACL_ENOTSUP:
924          /*
925           * If the filesystem reports it doesn't support ACLs we clear the
926           * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
927           * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
928           * when we change from one filesystem to another.
929           */
930          acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
931          Mmsg1(jcr->errmsg,
932                _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"),
933                acl_data->last_fname);
934          return bacl_exit_error;
935 #endif
936       default:
937          Mmsg2(jcr->errmsg,
938                _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
939                acl_data->last_fname, be.bstrerror());
940          return bacl_exit_error;
941       }
942    }
943 
944    acl = acl_from_text(content);
945    if (acl == NULL) {
946       BErrNo be;
947 
948       Mmsg2(jcr->errmsg,
949             _("acl_from_text error on file \"%s\": ERR=%s\n"),
950             acl_data->last_fname, be.bstrerror());
951       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
952             content, acl_data->last_fname, be.bstrerror());
953       return bacl_exit_error;
954    }
955 
956    /**
957     * Only validate POSIX acls the acl_valid interface is only implemented
958     * for checking POSIX acls on most platforms.
959     */
960    switch (acltype) {
961    case BACL_TYPE_NFS4:
962       /*
963        * Skip acl_valid tests on NFSv4 acls.
964        */
965       break;
966    default:
967       if (acl_valid(acl) != 0) {
968          BErrNo be;
969 
970          Mmsg2(jcr->errmsg, _("acl_valid error on file \"%s\": ERR=%s\n"),
971                acl_data->last_fname, be.bstrerror());
972          Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",
973                content, acl_data->last_fname, be.bstrerror());
974          acl_free(acl);
975          return bacl_exit_error;
976       }
977       break;
978    }
979 
980    /**
981     * Restore the ACLs, but don't complain about links which really should
982     * not have attributes, and the file it is linked to may not yet be restored.
983     * This is only true for the old acl streams as in the new implementation we
984     * don't save acls of symlinks (which cannot have acls anyhow)
985     */
986    if (acl_set_file(acl_data->last_fname, ostype, acl) != 0 && acl_data->filetype != FT_LNK) {
987       BErrNo be;
988 
989       switch (errno) {
990       case ENOENT:
991          acl_free(acl);
992          return bacl_exit_ok;
993 #if defined(BACL_ENOTSUP)
994       case BACL_ENOTSUP:
995          /*
996           * If the filesystem reports it doesn't support ACLs we clear the
997           * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
998           * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
999           * when we change from one filesystem to another.
1000           */
1001          acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
1002          Mmsg1(jcr->errmsg,
1003                _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"),
1004                acl_data->last_fname);
1005          Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n",
1006                content, acl_data->last_fname);
1007          acl_free(acl);
1008          return bacl_exit_error;
1009 #endif
1010       default:
1011          Mmsg2(jcr->errmsg,
1012                _("acl_set_file error on file \"%s\": ERR=%s\n"),
1013                acl_data->last_fname, be.bstrerror());
1014          Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
1015                content, acl_data->last_fname, be.bstrerror());
1016          acl_free(acl);
1017          return bacl_exit_error;
1018       }
1019    }
1020    acl_free(acl);
1021    return bacl_exit_ok;
1022 }
1023 
1024 /**
1025  * OS specific functions for handling different types of acl streams.
1026  */
1027 #if defined(HAVE_DARWIN_OS)
1028 /**
1029  * Define the supported ACL streams for this OS
1030  */
1031 static int os_access_acl_streams[1] = {
1032    STREAM_ACL_DARWIN_ACCESS_ACL
1033 };
1034 static int os_default_acl_streams[1] = {
1035    -1
1036 };
1037 
darwin_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)1038 static bacl_exit_code darwin_build_acl_streams(JobControlRecord *jcr,
1039                                                acl_data_t *acl_data,
1040                                                FindFilesPacket *ff_pkt)
1041 {
1042 #if defined(HAVE_ACL_TYPE_EXTENDED)
1043    /**
1044     * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
1045     * and acl_get_file (name, ACL_TYPE_DEFAULT)
1046     * always return NULL / EINVAL.  There is no point in making
1047     * these two useless calls.  The real ACL is retrieved through
1048     * acl_get_file (name, ACL_TYPE_EXTENDED).
1049     *
1050     * Read access ACLs for files, dirs and links
1051     */
1052    if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_EXTENDED) == bacl_exit_fatal)
1053       return bacl_exit_fatal;
1054 #else
1055    /**
1056     * Read access ACLs for files, dirs and links
1057     */
1058    if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_ACCESS) == bacl_exit_fatal)
1059       return bacl_exit_fatal;
1060 #endif
1061 
1062    if (acl_data->u.build->content_length > 0) {
1063       return SendAclStream(jcr, acl_data, STREAM_ACL_DARWIN_ACCESS_ACL);
1064    }
1065    return bacl_exit_ok;
1066 }
1067 
darwin_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)1068 static bacl_exit_code darwin_parse_acl_streams(JobControlRecord *jcr,
1069                                                acl_data_t *acl_data,
1070                                                int stream,
1071                                                char *content,
1072                                                uint32_t content_length)
1073 {
1074 #if defined(HAVE_ACL_TYPE_EXTENDED)
1075       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_EXTENDED,
1076                                    content, content_length);
1077 #else
1078       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1079                                    content, content_length);
1080 #endif
1081 }
1082 
1083 /**
1084  * For this OS setup the build and parse function pointer to the OS specific functions.
1085  */
1086 static bacl_exit_code (*os_build_acl_streams)
1087                       (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1088                       darwin_build_acl_streams;
1089 static bacl_exit_code (*os_parse_acl_streams)
1090                       (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1091                       darwin_parse_acl_streams;
1092 
1093 #elif defined(HAVE_FREEBSD_OS)
1094 /**
1095  * Define the supported ACL streams for these OSes
1096  */
1097 static int os_access_acl_streams[2] = {
1098    STREAM_ACL_FREEBSD_ACCESS_ACL,
1099    STREAM_ACL_FREEBSD_NFS4_ACL
1100 };
1101 static int os_default_acl_streams[1] = {
1102    STREAM_ACL_FREEBSD_DEFAULT_ACL
1103 };
1104 
freebsd_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)1105 static bacl_exit_code freebsd_build_acl_streams(JobControlRecord *jcr,
1106                                                 acl_data_t *acl_data,
1107                                                 FindFilesPacket *ff_pkt)
1108 {
1109    int acl_enabled = 0;
1110    bacl_type acltype = BACL_TYPE_NONE;
1111 
1112 #if defined(_PC_ACL_NFS4)
1113    /*
1114     * See if filesystem supports NFS4 acls.
1115     */
1116    acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_NFS4);
1117    switch (acl_enabled) {
1118    case -1: {
1119       BErrNo be;
1120 
1121       switch (errno) {
1122       case ENOENT:
1123          return bacl_exit_ok;
1124       default:
1125          Mmsg2(jcr->errmsg,
1126                _("pathconf error on file \"%s\": ERR=%s\n"),
1127                acl_data->last_fname, be.bstrerror());
1128          Dmsg2(100, "pathconf error file=%s ERR=%s\n",
1129                acl_data->last_fname, be.bstrerror());
1130          return bacl_exit_error;
1131       }
1132    }
1133    case 0:
1134       break;
1135    default:
1136       acltype = BACL_TYPE_NFS4;
1137       break;
1138    }
1139 #endif
1140 
1141    if (acl_enabled == 0) {
1142       /*
1143        * See if filesystem supports POSIX acls.
1144        */
1145       acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_EXTENDED);
1146       switch (acl_enabled) {
1147       case -1: {
1148          BErrNo be;
1149 
1150          switch (errno) {
1151          case ENOENT:
1152             return bacl_exit_ok;
1153          default:
1154             Mmsg2(jcr->errmsg,
1155                   _("pathconf error on file \"%s\": ERR=%s\n"),
1156                   acl_data->last_fname, be.bstrerror());
1157             Dmsg2(100, "pathconf error file=%s ERR=%s\n",
1158                   acl_data->last_fname, be.bstrerror());
1159             return bacl_exit_error;
1160          }
1161       }
1162       case 0:
1163          break;
1164       default:
1165          acltype = BACL_TYPE_ACCESS;
1166          break;
1167       }
1168    }
1169 
1170    /*
1171     * If the filesystem reports it doesn't support ACLs we clear the
1172     * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1173     * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1174     * when we change from one filesystem to another.
1175     */
1176    if (acl_enabled == 0) {
1177       acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1178       PmStrcpy(acl_data->u.build->content, "");
1179       acl_data->u.build->content_length = 0;
1180       return bacl_exit_ok;
1181    }
1182 
1183    /*
1184     * Based on the supported ACLs retrieve and store them.
1185     */
1186    switch (acltype) {
1187    case BACL_TYPE_NFS4:
1188       /*
1189        * Read NFS4 ACLs for files, dirs and links
1190        */
1191       if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_NFS4) == bacl_exit_fatal)
1192          return bacl_exit_fatal;
1193 
1194       if (acl_data->u.build->content_length > 0) {
1195          if (SendAclStream(jcr, acl_data, STREAM_ACL_FREEBSD_NFS4_ACL) == bacl_exit_fatal)
1196             return bacl_exit_fatal;
1197       }
1198       break;
1199    case BACL_TYPE_ACCESS:
1200       /*
1201        * Read access ACLs for files, dirs and links
1202        */
1203       if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_ACCESS) == bacl_exit_fatal)
1204          return bacl_exit_fatal;
1205 
1206       if (acl_data->u.build->content_length > 0) {
1207          if (SendAclStream(jcr, acl_data, STREAM_ACL_FREEBSD_ACCESS_ACL) == bacl_exit_fatal)
1208             return bacl_exit_fatal;
1209       }
1210 
1211       /*
1212        * Directories can have default ACLs too
1213        */
1214       if (acl_data->filetype == FT_DIREND) {
1215          if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
1216             return bacl_exit_fatal;
1217          if (acl_data->u.build->content_length > 0) {
1218             if (SendAclStream(jcr, acl_data, STREAM_ACL_FREEBSD_DEFAULT_ACL) == bacl_exit_fatal)
1219                return bacl_exit_fatal;
1220          }
1221       }
1222       break;
1223    default:
1224       break;
1225    }
1226 
1227    return bacl_exit_ok;
1228 }
1229 
freebsd_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)1230 static bacl_exit_code freebsd_parse_acl_streams(JobControlRecord *jcr,
1231                                                 acl_data_t *acl_data,
1232                                                 int stream,
1233                                                 char *content,
1234                                                 uint32_t content_length)
1235 {
1236    int acl_enabled = 0;
1237    const char *acl_type_name;
1238 
1239    /*
1240     * First make sure the filesystem supports acls.
1241     */
1242    switch (stream) {
1243    case STREAM_UNIX_ACCESS_ACL:
1244    case STREAM_ACL_FREEBSD_ACCESS_ACL:
1245    case STREAM_UNIX_DEFAULT_ACL:
1246    case STREAM_ACL_FREEBSD_DEFAULT_ACL:
1247       acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_EXTENDED);
1248       acl_type_name = "POSIX";
1249       break;
1250    case STREAM_ACL_FREEBSD_NFS4_ACL:
1251 #if defined(_PC_ACL_NFS4)
1252       acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_NFS4);
1253 #endif
1254       acl_type_name = "NFS4";
1255       break;
1256    default:
1257       acl_type_name = "unknown";
1258       break;
1259    }
1260 
1261    switch (acl_enabled) {
1262    case -1: {
1263       BErrNo be;
1264 
1265       switch (errno) {
1266       case ENOENT:
1267          return bacl_exit_ok;
1268       default:
1269          Mmsg2(jcr->errmsg,
1270                _("pathconf error on file \"%s\": ERR=%s\n"),
1271                acl_data->last_fname, be.bstrerror());
1272          Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
1273                content, acl_data->last_fname, be.bstrerror());
1274          return bacl_exit_error;
1275       }
1276    }
1277    case 0:
1278       /*
1279        * If the filesystem reports it doesn't support ACLs we clear the
1280        * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
1281        * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
1282        * when we change from one filesystem to another.
1283        */
1284       acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1285       Mmsg2(jcr->errmsg,
1286             _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"),
1287             acl_data->last_fname, acl_type_name);
1288       return bacl_exit_error;
1289    default:
1290       break;
1291    }
1292 
1293    /*
1294     * Restore the ACLs.
1295     */
1296    switch (stream) {
1297    case STREAM_UNIX_ACCESS_ACL:
1298    case STREAM_ACL_FREEBSD_ACCESS_ACL:
1299       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1300                                    content, content_length);
1301    case STREAM_UNIX_DEFAULT_ACL:
1302    case STREAM_ACL_FREEBSD_DEFAULT_ACL:
1303       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT,
1304                                    content, content_length);
1305    case STREAM_ACL_FREEBSD_NFS4_ACL:
1306       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_NFS4,
1307                                    content, content_length);
1308    default:
1309       break;
1310    }
1311    return bacl_exit_error;
1312 }
1313 
1314 /**
1315  * For this OSes setup the build and parse function pointer to the OS specific functions.
1316  */
1317 static bacl_exit_code (*os_build_acl_streams)
1318                       (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1319                       freebsd_build_acl_streams;
1320 static bacl_exit_code (*os_parse_acl_streams)
1321                       (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1322                       freebsd_parse_acl_streams;
1323 
1324 #elif defined(HAVE_IRIX_OS) || \
1325       defined(HAVE_LINUX_OS) || \
1326       defined(HAVE_HURD_OS)
1327 /**
1328  * Define the supported ACL streams for these OSes
1329  */
1330 #if defined(HAVE_IRIX_OS)
1331 static int os_access_acl_streams[1] = {
1332    STREAM_ACL_IRIX_ACCESS_ACL
1333 };
1334 static int os_default_acl_streams[1] = {
1335    STREAM_ACL_IRIX_DEFAULT_ACL
1336 };
1337 #elif defined(HAVE_LINUX_OS)
1338 static int os_access_acl_streams[1] = {
1339    STREAM_ACL_LINUX_ACCESS_ACL
1340 };
1341 static int os_default_acl_streams[1] = {
1342    STREAM_ACL_LINUX_DEFAULT_ACL
1343 };
1344 #elif defined(HAVE_HURD_OS)
1345 static int os_access_acl_streams[1] = {
1346    STREAM_ACL_HURD_ACCESS_ACL
1347 };
1348 static int os_default_acl_streams[1] = {
1349    STREAM_ACL_HURD_DEFAULT_ACL
1350 };
1351 #endif
1352 
generic_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)1353 static bacl_exit_code generic_build_acl_streams(JobControlRecord *jcr,
1354                                                 acl_data_t *acl_data,
1355                                                 FindFilesPacket *ff_pkt)
1356 {
1357    /*
1358     * Read access ACLs for files, dirs and links
1359     */
1360    if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_ACCESS) == bacl_exit_fatal)
1361       return bacl_exit_fatal;
1362 
1363    if (acl_data->u.build->content_length > 0) {
1364       if (SendAclStream(jcr, acl_data, os_access_acl_streams[0]) == bacl_exit_fatal)
1365          return bacl_exit_fatal;
1366    }
1367 
1368    /*
1369     * Directories can have default ACLs too
1370     */
1371    if (acl_data->filetype == FT_DIREND) {
1372       if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
1373          return bacl_exit_fatal;
1374       if (acl_data->u.build->content_length > 0) {
1375          if (SendAclStream(jcr, acl_data, os_default_acl_streams[0]) == bacl_exit_fatal)
1376             return bacl_exit_fatal;
1377       }
1378    }
1379    return bacl_exit_ok;
1380 }
1381 
generic_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)1382 static bacl_exit_code generic_parse_acl_streams(JobControlRecord *jcr,
1383                                                 acl_data_t *acl_data,
1384                                                 int stream,
1385                                                 char *content,
1386                                                 uint32_t content_length)
1387 {
1388    unsigned int cnt;
1389 
1390    switch (stream) {
1391    case STREAM_UNIX_ACCESS_ACL:
1392       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1393                                    content, content_length);
1394    case STREAM_UNIX_DEFAULT_ACL:
1395       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT,
1396                                    content, content_length);
1397    default:
1398       /*
1399        * See what type of acl it is.
1400        */
1401       for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1402          if (os_access_acl_streams[cnt] == stream) {
1403             return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1404                                          content, content_length);
1405          }
1406       }
1407       for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1408          if (os_default_acl_streams[cnt] == stream) {
1409             return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT,
1410                                          content, content_length);
1411          }
1412       }
1413       break;
1414    }
1415    return bacl_exit_error;
1416 }
1417 
1418 /**
1419  * For this OSes setup the build and parse function pointer to the OS specific functions.
1420  */
1421 static bacl_exit_code (*os_build_acl_streams)
1422                       (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1423                       generic_build_acl_streams;
1424 static bacl_exit_code (*os_parse_acl_streams)
1425                       (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1426                       generic_parse_acl_streams;
1427 
1428 #elif defined(HAVE_OSF1_OS)
1429 
1430 /**
1431  * Define the supported ACL streams for this OS
1432  */
1433 static int os_access_acl_streams[1] = {
1434    STREAM_ACL_TRU64_ACCESS_ACL
1435 };
1436 static int os_default_acl_streams[2] = {
1437    STREAM_ACL_TRU64_DEFAULT_ACL,
1438    STREAM_ACL_TRU64_DEFAULT_DIR_ACL
1439 };
1440 
tru64_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)1441 static bacl_exit_code tru64_build_acl_streams(JobControlRecord *jcr,
1442                                               acl_data_t *acl_data,
1443                                               FindFilesPacket *ff_pkt)
1444 {
1445    /*
1446     * Read access ACLs for files, dirs and links
1447     */
1448    if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_ACCESS) == bacl_exit_fatal) {
1449       return bacl_exit_error;
1450    if (acl_data->u.build->content_length > 0) {
1451       if (!SendAclStream(jcr, acl_data, STREAM_ACL_TRU64_ACCESS_ACL))
1452          return bacl_exit_error;
1453    }
1454    /*
1455     * Directories can have default ACLs too
1456     */
1457    if (acl_data->filetype == FT_DIREND) {
1458       if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_DEFAULT) == bacl_exit_fatal) {
1459          return bacl_exit_error;
1460       if (acl_data->u.build->content_length > 0) {
1461          if (!SendAclStream(jcr, acl_data, STREAM_ACL_TRU64_DEFAULT_ACL))
1462             return bacl_exit_error;
1463       }
1464       /**
1465        * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
1466        * This is an inherited acl for all subdirs.
1467        */
1468       if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_DEFAULT_DIR) == bacl_exit_fatal) {
1469          return bacl_exit_error;
1470       if (acl_data->u.build->content_length > 0) {
1471          if (!SendAclStream(jcr, acl_data, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
1472             return bacl_exit_error;
1473       }
1474    }
1475    return bacl_exit_ok;
1476 }
1477 
1478 static bacl_exit_code tru64_parse_acl_streams(JobControlRecord *jcr,
1479                                               acl_data_t *acl_data,
1480                                               int stream,
1481                                               char *content,
1482                                               uint32_t content_length)
1483 {
1484    switch (stream) {
1485    case STREAM_UNIX_ACCESS_ACL:
1486    case STREAM_ACL_TRU64_ACCESS_ACL:
1487       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1488                                    content, content_length);
1489    case STREAM_UNIX_DEFAULT_ACL:
1490    case STREAM_ACL_TRU64_DEFAULT_ACL:
1491       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT,
1492                                    content, content_length);
1493    case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
1494       return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT_DIR,
1495                                    content, content_length);
1496 }
1497 
1498 /**
1499  * For this OS setup the build and parse function pointer to the OS specific functions.
1500  */
1501 static bacl_exit_code (*os_build_acl_streams)
1502                       (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1503                       tru64_build_acl_streams;
1504 static bacl_exit_code (*os_parse_acl_streams)
1505                       (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1506                       tru64_parse_acl_streams;
1507 
1508 #endif
1509 
1510 #elif defined(HAVE_HPUX_OS)
1511 #ifdef HAVE_SYS_ACL_H
1512 #include <sys/acl.h>
1513 #else
1514 #error "configure failed to detect availability of sys/acl.h"
1515 #endif
1516 
1517 #include <acllib.h>
1518 
1519 /**
1520  * Define the supported ACL streams for this OS
1521  */
1522 static int os_access_acl_streams[1] = {
1523    STREAM_ACL_HPUX_ACL_ENTRY
1524 };
1525 static int os_default_acl_streams[1] = {
1526    -1
1527 };
1528 
1529 /**
1530  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1531  * There is no need to store those acls as we already store the stat bits too.
1532  */
1533 static bool AclIsTrivial(int count, struct acl_entry *entries, struct stat sb)
1534 {
1535    int n;
1536    struct acl_entry ace
1537 
1538    for (n = 0; n < count; n++) {
1539       ace = entries[n];
1540       /*
1541        * See if this acl just is the stat mode in acl form.
1542        */
1543       if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
1544             (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
1545             (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
1546          return false;
1547    }
1548    return true;
1549 }
1550 
1551 /**
1552  * OS specific functions for handling different types of acl streams.
1553  */
1554 static bacl_exit_code hpux_build_acl_streams(JobControlRecord *jcr,
1555                                              acl_data_t *acl_data
1556                                              FindFilesPacket *ff_pkt)
1557 {
1558    int n;
1559    struct acl_entry acls[NACLENTRIES];
1560    char *acl_text;
1561 
1562    if ((n = getacl(acl_data->last_fname, 0, acls)) < 0) {
1563       BErrNo be;
1564 
1565       switch (errno) {
1566 #if defined(BACL_ENOTSUP)
1567       case BACL_ENOTSUP:
1568          /*
1569           * Not supported, just pretend there is nothing to see
1570           *
1571           * If the filesystem reports it doesn't support ACLs we clear the
1572           * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1573           * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1574           * when we change from one filesystem to another.
1575           */
1576          acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1577          PmStrcpy(acl_data->u.build->content, "");
1578          acl_data->u.build->content_length = 0;
1579          return bacl_exit_ok;
1580 #endif
1581       case ENOENT:
1582          PmStrcpy(acl_data->u.build->content, "");
1583          acl_data->u.build->content_length = 0;
1584          return bacl_exit_ok;
1585       default:
1586          Mmsg2(jcr->errmsg,
1587                _("getacl error on file \"%s\": ERR=%s\n"),
1588                acl_data->last_fname, be.bstrerror());
1589          Dmsg2(100, "getacl error file=%s ERR=%s\n",
1590                acl_data->last_fname, be.bstrerror());
1591 
1592          PmStrcpy(acl_data->u.build->content, "");
1593          acl_data->u.build->content_length = 0;
1594          return bacl_exit_error;
1595       }
1596    }
1597    if (n == 0) {
1598       PmStrcpy(acl_data->u.build->content, "");
1599       acl_data->u.build->content_length = 0;
1600       return bacl_exit_ok;
1601    }
1602    if ((n = getacl(acl_data->last_fname, n, acls)) > 0) {
1603       if (AclIsTrivial(n, acls, ff_pkt->statp)) {
1604          /*
1605           * The ACLs simply reflect the (already known) standard permissions
1606           * So we don't send an ACL stream to the SD.
1607           */
1608          PmStrcpy(acl_data->u.build->content, "");
1609          acl_data->u.build->content_length = 0;
1610          return bacl_exit_ok;
1611       }
1612       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
1613          acl_data->u.build->content_length =
1614          PmStrcpy(acl_data->u.build->content, acl_text);
1615          Actuallyfree(acl_text);
1616 
1617          return SendAclStream(jcr, acl_data, STREAM_ACL_HPUX_ACL_ENTRY);
1618       }
1619 
1620       BErrNo be;
1621       Mmsg2(jcr->errmsg,
1622             _("acltostr error on file \"%s\": ERR=%s\n"),
1623             acl_data->last_fname, be.bstrerror());
1624       Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",
1625             acl_data->u.build->content, acl_data->last_fname, be.bstrerror());
1626       return bacl_exit_error;
1627    }
1628    return bacl_exit_error;
1629 }
1630 
1631 static bacl_exit_code hpux_parse_acl_streams(JobControlRecord *jcr,
1632                                              acl_data_t *acl_data,
1633                                              int stream,
1634                                              char *content,
1635                                              uint32_t content_length)
1636 {
1637    int n;
1638    struct acl_entry acls[NACLENTRIES];
1639 
1640    n = strtoacl(content, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
1641    if (n <= 0) {
1642       BErrNo be;
1643 
1644       Mmsg2(jcr->errmsg,
1645             _("strtoacl error on file \"%s\": ERR=%s\n"),
1646             acl_data->last_fname, be.bstrerror());
1647       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
1648             content, acl_data->last_fname, be.bstrerror());
1649       return bacl_exit_error;
1650    }
1651    if (strtoacl(content, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
1652       BErrNo be;
1653 
1654       Mmsg2(jcr->errmsg,
1655             _("strtoacl error on file \"%s\": ERR=%s\n"),
1656             acl_data->last_fname, be.bstrerror());
1657       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
1658             content, acl_data->last_fname, be.bstrerror());
1659 
1660       return bacl_exit_error;
1661    }
1662    /**
1663     * Restore the ACLs, but don't complain about links which really should
1664     * not have attributes, and the file it is linked to may not yet be restored.
1665     * This is only true for the old acl streams as in the new implementation we
1666     * don't save acls of symlinks (which cannot have acls anyhow)
1667     */
1668    if (setacl(acl_data->last_fname, n, acls) != 0 && acl_data->filetype != FT_LNK) {
1669       BErrNo be;
1670 
1671       switch (errno) {
1672       case ENOENT:
1673          return bacl_exit_ok;
1674 #if defined(BACL_ENOTSUP)
1675       case BACL_ENOTSUP:
1676          /*
1677           * If the filesystem reports it doesn't support ACLs we clear the
1678           * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
1679           * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
1680           * when we change from one filesystem to another.
1681           */
1682          acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1683          Mmsg1(jcr->errmsg,
1684                _("setacl error on file \"%s\": filesystem doesn't support ACLs\n"),
1685                acl_data->last_fname);
1686          Dmsg2(100, "setacl error acl=%s file=%s filesystem doesn't support ACLs\n",
1687                content, acl_data->last_fname);
1688          return bacl_exit_error;
1689 #endif
1690       default:
1691          Mmsg2(jcr->errmsg,
1692                _("setacl error on file \"%s\": ERR=%s\n"),
1693                acl_data->last_fname, be.bstrerror());
1694          Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
1695                content, acl_data->last_fname, be.bstrerror());
1696          return bacl_exit_error;
1697       }
1698    }
1699    return bacl_exit_ok;
1700 }
1701 
1702 /**
1703  * For this OS setup the build and parse function pointer to the OS specific functions.
1704  */
1705 static bacl_exit_code (*os_build_acl_streams)
1706                       (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1707                       hpux_build_acl_streams;
1708 static bacl_exit_code (*os_parse_acl_streams)
1709                       (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1710                       hpux_parse_acl_streams;
1711 
1712 #elif defined(HAVE_SUN_OS)
1713 #ifdef HAVE_SYS_ACL_H
1714 #include <sys/acl.h>
1715 #else
1716 #error "configure failed to detect availability of sys/acl.h"
1717 #endif
1718 
1719 #if defined(HAVE_EXTENDED_ACL)
1720 /**
1721  * We define some internals of the Solaris acl libs here as those
1722  * are not exposed yet. Probably because they want us to see the
1723  * acls as opague data. But as we need to support different platforms
1724  * and versions of Solaris we need to expose some data to be able
1725  * to determine the type of acl used to stuff it into the correct
1726  * data stream. I know this is far from portable, but maybe the
1727  * proper interface is exposed later on and we can get ride of
1728  * this kludge. Newer versions of Solaris include sys/acl_impl.h
1729  * which has implementation details of acls, if thats included we
1730  * don't have to define it ourself.
1731  */
1732 #if !defined(_SYS_ACL_IMPL_H)
1733 typedef enum acl_type {
1734    ACLENT_T = 0,
1735    ACE_T = 1
1736 } acl_type_t;
1737 #endif
1738 
1739 /**
1740  * Two external references to functions in the libsec library function not in current include files.
1741  */
1742 extern "C" {
1743 int acl_type(acl_t *);
1744 char *acl_strerror(int);
1745 }
1746 
1747 /**
1748  * Define the supported ACL streams for this OS
1749  */
1750 static int os_access_acl_streams[2] = {
1751    STREAM_ACL_SOLARIS_ACLENT,
1752    STREAM_ACL_SOLARIS_ACE
1753 };
1754 static int os_default_acl_streams[1] = {
1755    -1
1756 };
1757 
1758 /**
1759  * As the new libsec interface with acl_totext and acl_fromtext also handles
1760  * the old format from acltotext we can use the new functions even
1761  * for acls retrieved and stored in the database with older fd versions. If the
1762  * new interface is not defined (Solaris 9 and older we fall back to the old code)
1763  */
1764 static bacl_exit_code solaris_build_acl_streams(JobControlRecord *jcr,
1765                                                 acl_data_t *acl_data,
1766                                                 FindFilesPacket *ff_pkt)
1767 {
1768    int acl_enabled, flags;
1769    acl_t *aclp;
1770    char *acl_text;
1771    bacl_exit_code stream_status = bacl_exit_error;
1772 
1773    /*
1774     * See if filesystem supports acls.
1775     */
1776    acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_ENABLED);
1777    switch (acl_enabled) {
1778    case 0:
1779       /*
1780        * If the filesystem reports it doesn't support ACLs we clear the
1781        * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1782        * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1783        * when we change from one filesystem to another.
1784        */
1785       acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1786       PmStrcpy(acl_data->u.build->content, "");
1787       acl_data->u.build->content_length = 0;
1788       return bacl_exit_ok;
1789    case -1: {
1790       BErrNo be;
1791 
1792       switch (errno) {
1793       case ENOENT:
1794          return bacl_exit_ok;
1795       default:
1796          Mmsg2(jcr->errmsg,
1797                _("pathconf error on file \"%s\": ERR=%s\n"),
1798                acl_data->last_fname, be.bstrerror());
1799          Dmsg2(100, "pathconf error file=%s ERR=%s\n",
1800                acl_data->last_fname, be.bstrerror());
1801          return bacl_exit_error;
1802       }
1803    }
1804    default:
1805       break;
1806    }
1807 
1808    /*
1809     * Get ACL info: don't bother allocating space if there is only a trivial ACL.
1810     */
1811    if (acl_get(acl_data->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
1812       BErrNo be;
1813 
1814       switch (errno) {
1815       case ENOENT:
1816          return bacl_exit_ok;
1817       default:
1818          Mmsg2(jcr->errmsg,
1819                _("acl_get error on file \"%s\": ERR=%s\n"),
1820                acl_data->last_fname, acl_strerror(errno));
1821          Dmsg2(100, "acl_get error file=%s ERR=%s\n",
1822               acl_data->last_fname, acl_strerror(errno));
1823          return bacl_exit_error;
1824       }
1825    }
1826 
1827    if (!aclp) {
1828       /*
1829        * The ACLs simply reflect the (already known) standard permissions
1830        * So we don't send an ACL stream to the SD.
1831        */
1832       PmStrcpy(acl_data->u.build->content, "");
1833       acl_data->u.build->content_length = 0;
1834       return bacl_exit_ok;
1835    }
1836 
1837 #if defined(ACL_SID_FMT)
1838    /*
1839     * New format flag added in newer Solaris versions.
1840     */
1841    flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1842 #else
1843    flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1844 #endif /* ACL_SID_FMT */
1845 
1846    if ((acl_text = acl_totext(aclp, flags)) != NULL) {
1847       acl_data->u.build->content_length =
1848       PmStrcpy(acl_data->u.build->content, acl_text);
1849       Actuallyfree(acl_text);
1850 
1851       switch (acl_type(aclp)) {
1852       case ACLENT_T:
1853          stream_status = SendAclStream(jcr, acl_data, STREAM_ACL_SOLARIS_ACLENT);
1854          break;
1855       case ACE_T:
1856          stream_status = SendAclStream(jcr, acl_data, STREAM_ACL_SOLARIS_ACE);
1857          break;
1858       default:
1859          break;
1860       }
1861 
1862       acl_free(aclp);
1863    }
1864    return stream_status;
1865 }
1866 
1867 static bacl_exit_code solaris_parse_acl_streams(JobControlRecord *jcr,
1868                                                 acl_data_t *acl_data,
1869                                                 int stream,
1870                                                 char *content,
1871                                                 uint32_t content_length)
1872 {
1873    acl_t *aclp;
1874    int acl_enabled, error;
1875 
1876    switch (stream) {
1877    case STREAM_UNIX_ACCESS_ACL:
1878    case STREAM_ACL_SOLARIS_ACLENT:
1879    case STREAM_ACL_SOLARIS_ACE:
1880       /*
1881        * First make sure the filesystem supports acls.
1882        */
1883       acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_ENABLED);
1884       switch (acl_enabled) {
1885       case 0:
1886          /*
1887           * If the filesystem reports it doesn't support ACLs we clear the
1888           * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
1889           * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
1890           * when we change from one filesystem to another.
1891           */
1892          acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
1893          Mmsg1(jcr->errmsg,
1894                _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
1895                acl_data->last_fname);
1896          return bacl_exit_error;
1897       case -1: {
1898          BErrNo be;
1899 
1900          switch (errno) {
1901          case ENOENT:
1902             return bacl_exit_ok;
1903          default:
1904             Mmsg2(jcr->errmsg,
1905                   _("pathconf error on file \"%s\": ERR=%s\n"),
1906                   acl_data->last_fname, be.bstrerror());
1907             Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
1908                   content, acl_data->last_fname, be.bstrerror());
1909             return bacl_exit_error;
1910          }
1911       }
1912       default:
1913          /*
1914           * On a filesystem with ACL support make sure this particular ACL type can be restored.
1915           */
1916          switch (stream) {
1917          case STREAM_ACL_SOLARIS_ACLENT:
1918             /*
1919              * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1920              */
1921             if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1922                Mmsg1(jcr->errmsg,
1923                      _("Trying to restore POSIX acl on file \"%s\" on filesystem without aclent acl support\n"),
1924                      acl_data->last_fname);
1925                return bacl_exit_error;
1926             }
1927             break;
1928          case STREAM_ACL_SOLARIS_ACE:
1929             /*
1930              * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1931              */
1932             if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1933                Mmsg1(jcr->errmsg,
1934                      _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without ace acl support\n"),
1935                      acl_data->last_fname);
1936                return bacl_exit_error;
1937             }
1938             break;
1939          default:
1940             /*
1941              * Stream id which doesn't describe the type of acl which is encoded.
1942              */
1943             break;
1944          }
1945          break;
1946       }
1947 
1948       if ((error = acl_fromtext(content, &aclp)) != 0) {
1949          Mmsg2(jcr->errmsg,
1950                _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1951                acl_data->last_fname, acl_strerror(error));
1952          Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",
1953                content, acl_data->last_fname, acl_strerror(error));
1954          return bacl_exit_error;
1955       }
1956 
1957       /*
1958        * Validate that the conversion gave us the correct acl type.
1959        */
1960       switch (stream) {
1961       case STREAM_ACL_SOLARIS_ACLENT:
1962          if (acl_type(aclp) != ACLENT_T) {
1963             Mmsg1(jcr->errmsg,
1964                   _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1965                   acl_data->last_fname);
1966             return bacl_exit_error;
1967          }
1968          break;
1969       case STREAM_ACL_SOLARIS_ACE:
1970          if (acl_type(aclp) != ACE_T) {
1971             Mmsg1(jcr->errmsg,
1972                   _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1973                   acl_data->last_fname);
1974             return bacl_exit_error;
1975          }
1976          break;
1977       default:
1978          /*
1979           * Stream id which doesn't describe the type of acl which is encoded.
1980           */
1981          break;
1982       }
1983 
1984       /**
1985        * Restore the ACLs, but don't complain about links which really should
1986        * not have attributes, and the file it is linked to may not yet be restored.
1987        * This is only true for the old acl streams as in the new implementation we
1988        * don't save acls of symlinks (which cannot have acls anyhow)
1989        */
1990       if ((error = acl_set(acl_data->last_fname, aclp)) == -1 && acl_data->filetype != FT_LNK) {
1991          switch (errno) {
1992          case ENOENT:
1993             acl_free(aclp);
1994             return bacl_exit_ok;
1995          default:
1996             Mmsg2(jcr->errmsg,
1997                   _("acl_set error on file \"%s\": ERR=%s\n"),
1998                   acl_data->last_fname, acl_strerror(error));
1999             Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",
2000                   content, acl_data->last_fname, acl_strerror(error));
2001             acl_free(aclp);
2002             return bacl_exit_error;
2003          }
2004       }
2005 
2006       acl_free(aclp);
2007       return bacl_exit_ok;
2008    default:
2009       return bacl_exit_error;
2010    } /* end switch (stream) */
2011 }
2012 
2013 #else /* HAVE_EXTENDED_ACL */
2014 
2015 /**
2016  * Define the supported ACL streams for this OS
2017  */
2018 static int os_access_acl_streams[1] = {
2019    STREAM_ACL_SOLARIS_ACLENT
2020 };
2021 static int os_default_acl_streams[1] = {
2022    -1
2023 };
2024 
2025 /**
2026  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
2027  * There is no need to store those acls as we already store the stat bits too.
2028  */
2029 static bool AclIsTrivial(int count, aclent_t *entries)
2030 {
2031    int n;
2032    aclent_t *ace;
2033 
2034    for (n = 0; n < count; n++) {
2035       ace = &entries[n];
2036 
2037       if (!(ace->a_type == USER_OBJ ||
2038             ace->a_type == GROUP_OBJ ||
2039             ace->a_type == OTHER_OBJ ||
2040             ace->a_type == CLASS_OBJ))
2041         return false;
2042    }
2043    return true;
2044 }
2045 
2046 /**
2047  * OS specific functions for handling different types of acl streams.
2048  */
2049 static bacl_exit_code solaris_build_acl_streams(JobControlRecord *jcr,
2050                                                 acl_data_t *acl_data,
2051                                                 FindFilesPacket *ff_pkt)
2052 {
2053    int n;
2054    aclent_t *acls;
2055    char *acl_text;
2056 
2057    n = acl(acl_data->last_fname, GETACLCNT, 0, NULL);
2058    if (n < MIN_ACL_ENTRIES) {
2059       return bacl_exit_error;
2060    }
2061 
2062    acls = (aclent_t *)malloc(n * sizeof(aclent_t));
2063    if (acl(acl_data->last_fname, GETACL, n, acls) == n) {
2064       if (AclIsTrivial(n, acls)) {
2065          /*
2066           * The ACLs simply reflect the (already known) standard permissions
2067           * So we don't send an ACL stream to the SD.
2068           */
2069          free(acls);
2070          PmStrcpy(acl_data->u.build->content, "");
2071          acl_data->u.build->content_length = 0;
2072          return bacl_exit_ok;
2073       }
2074 
2075       if ((acl_text = acltotext(acls, n)) != NULL) {
2076          acl_data->u.build->content_length =
2077          PmStrcpy(acl_data->u.build->content, acl_text);
2078          Actuallyfree(acl_text);
2079          free(acls);
2080          return SendAclStream(jcr, acl_data, STREAM_ACL_SOLARIS_ACLENT);
2081       }
2082 
2083       BErrNo be;
2084       Mmsg2(jcr->errmsg,
2085             _("acltotext error on file \"%s\": ERR=%s\n"),
2086             acl_data->last_fname, be.bstrerror());
2087       Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
2088             acl_data->u.build->content, acl_data->last_fname, be.bstrerror());
2089    }
2090 
2091    free(acls);
2092    return bacl_exit_error;
2093 }
2094 
2095 static bacl_exit_code solaris_parse_acl_streams(JobControlRecord *jcr,
2096                                                 acl_data_t *acl_data,
2097                                                 int stream,
2098                                                 char *content,
2099                                                 uint32_t content_length)
2100 {
2101    int n;
2102    aclent_t *acls;
2103 
2104    acls = aclfromtext(content, &n);
2105    if (!acls) {
2106       BErrNo be;
2107 
2108       Mmsg2(jcr->errmsg,
2109             _("aclfromtext error on file \"%s\": ERR=%s\n"),
2110             acl_data->last_fname, be.bstrerror());
2111       Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
2112             content, acl_data->last_fname, be.bstrerror());
2113       return bacl_exit_error;
2114    }
2115 
2116    /*
2117     * Restore the ACLs, but don't complain about links which really should
2118     * not have attributes, and the file it is linked to may not yet be restored.
2119     */
2120    if (acl(acl_data->last_fname, SETACL, n, acls) == -1 && acl_data->filetype != FT_LNK) {
2121       BErrNo be;
2122 
2123       switch (errno) {
2124       case ENOENT:
2125          Actuallyfree(acls);
2126          return bacl_exit_ok;
2127       default:
2128          Mmsg2(jcr->errmsg,
2129                _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
2130                acl_data->last_fname, be.bstrerror());
2131          Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
2132                content, acl_data->last_fname, be.bstrerror());
2133          Actuallyfree(acls);
2134          return bacl_exit_error;
2135       }
2136    }
2137    Actuallyfree(acls);
2138    return bacl_exit_ok;
2139 }
2140 #endif /* HAVE_EXTENDED_ACL */
2141 
2142 /**
2143  * For this OS setup the build and parse function pointer to the OS specific functions.
2144  */
2145 static bacl_exit_code (*os_build_acl_streams)
2146                       (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
2147                       solaris_build_acl_streams;
2148 static bacl_exit_code (*os_parse_acl_streams)
2149                       (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
2150                       solaris_parse_acl_streams;
2151 
2152 #endif /* HAVE_SUN_OS */
2153 #endif /* HAVE_ACL */
2154 
2155 #if defined(HAVE_AFS_ACL)
2156 
2157 #if defined(HAVE_AFS_AFSINT_H) && defined(HAVE_AFS_VENUS_H)
2158 #include <afs/afsint.h>
2159 #include <afs/venus.h>
2160 #else
2161 #error "configure failed to detect availability of afs/afsint.h and/or afs/venus.h"
2162 #endif
2163 
2164 /**
2165  * External references to functions in the libsys library function not in current include files.
2166  */
2167 extern "C" {
2168 long pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow);
2169 }
2170 
2171 static bacl_exit_code afs_build_acl_streams(JobControlRecord *jcr,
2172                                             acl_data_t *acl_data,
2173                                             FindFilesPacket *ff_pkt)
2174 {
2175    int error;
2176    struct ViceIoctl vip;
2177    char acl_text[BUFSIZ];
2178 
2179    /*
2180     * AFS ACLs can only be set on a directory, so no need to try to
2181     * request them for anything other then that.
2182     */
2183    if (ff_pkt->type != FT_DIREND) {
2184       return bacl_exit_ok;
2185    }
2186 
2187    vip.in = NULL;
2188    vip.in_size = 0;
2189    vip.out = acl_text;
2190    vip.out_size = sizeof(acl_text);
2191    memset((caddr_t)acl_text, 0, sizeof(acl_text));
2192 
2193    if ((error = pioctl(acl_data->last_fname, VIOCGETAL, &vip, 0)) < 0) {
2194       BErrNo be;
2195 
2196       Mmsg2(jcr->errmsg,
2197             _("pioctl VIOCGETAL error on file \"%s\": ERR=%s\n"),
2198             acl_data->last_fname, be.bstrerror());
2199       Dmsg2(100, "pioctl VIOCGETAL error file=%s ERR=%s\n",
2200             acl_data->last_fname, be.bstrerror());
2201       return bacl_exit_error;
2202    }
2203    acl_data->u.build->content_length =
2204    PmStrcpy(acl_data->u.build->content, acl_text);
2205    return SendAclStream(jcr, acl_data, STREAM_ACL_AFS_TEXT);
2206 }
2207 
2208 static bacl_exit_code afs_parse_acl_stream(JobControlRecord *jcr,
2209                                            acl_data_t *acl_data,
2210                                            int stream,
2211                                            char *content,
2212                                            uint32_t content_length)
2213 {
2214    int error;
2215    struct ViceIoctl vip;
2216 
2217    vip.in = content;
2218    vip.in_size = content_length;
2219    vip.out = NULL;
2220    vip.out_size = 0;
2221 
2222    if ((error = pioctl(acl_data->last_fname, VIOCSETAL, &vip, 0)) < 0) {
2223       BErrNo be;
2224 
2225       Mmsg2(jcr->errmsg,
2226             _("pioctl VIOCSETAL error on file \"%s\": ERR=%s\n"),
2227             acl_data->last_fname, be.bstrerror());
2228       Dmsg2(100, "pioctl VIOCSETAL error file=%s ERR=%s\n",
2229             acl_data->last_fname, be.bstrerror());
2230 
2231       return bacl_exit_error;
2232    }
2233    return bacl_exit_ok;
2234 }
2235 #endif /* HAVE_AFS_ACL */
2236 
2237 /**
2238  * Entry points when compiled with support for ACLs on a supported platform.
2239  */
2240 
2241 /**
2242  * Read and send an ACL for the last encountered file.
2243  */
2244 bacl_exit_code BuildAclStreams(JobControlRecord *jcr,
2245                                  acl_data_t *acl_data,
2246                                  FindFilesPacket *ff_pkt)
2247 {
2248    /*
2249     * See if we are changing from one device to another.
2250     * We save the current device we are scanning and compare
2251     * it with the current st_dev in the last stat performed on
2252     * the file we are currently storing.
2253     */
2254    if (acl_data->current_dev != ff_pkt->statp.st_dev) {
2255       /*
2256        * Reset the acl save flags.
2257        */
2258       acl_data->flags = 0;
2259 
2260 #if defined(HAVE_AFS_ACL)
2261       /*
2262        * AFS is a non OS specific filesystem so see if this path is on an AFS filesystem
2263        * Set the BACL_FLAG_SAVE_AFS flag if it is. If not set the BACL_FLAG_SAVE_NATIVE flag.
2264        */
2265       if (FstypeEquals(acl_data->last_fname, "afs")) {
2266          acl_data->flags |= BACL_FLAG_SAVE_AFS;
2267       } else {
2268          acl_data->flags |= BACL_FLAG_SAVE_NATIVE;
2269       }
2270 #else
2271       acl_data->flags |= BACL_FLAG_SAVE_NATIVE;
2272 #endif
2273 
2274       /*
2275        * Save that we started scanning a new filesystem.
2276        */
2277       acl_data->current_dev = ff_pkt->statp.st_dev;
2278    }
2279 
2280 #if defined(HAVE_AFS_ACL)
2281    /*
2282     * See if the BACL_FLAG_SAVE_AFS flag is set which lets us know if we should
2283     * save AFS ACLs.
2284     */
2285    if (acl_data->flags & BACL_FLAG_SAVE_AFS) {
2286       return afs_build_acl_streams(jcr, acl_data, ff_pkt);
2287    }
2288 #endif
2289 #if defined(HAVE_ACL)
2290    /*
2291     * See if the BACL_FLAG_SAVE_NATIVE flag is set which lets us know if we should
2292     * save native ACLs.
2293     */
2294    if (acl_data->flags & BACL_FLAG_SAVE_NATIVE) {
2295       /*
2296        * Call the appropriate function.
2297        */
2298       if (os_build_acl_streams) {
2299          return os_build_acl_streams(jcr, acl_data, ff_pkt);
2300       }
2301    } else {
2302       return bacl_exit_ok;
2303    }
2304 #endif
2305    return bacl_exit_error;
2306 }
2307 
2308 bacl_exit_code parse_acl_streams(JobControlRecord *jcr,
2309                                  acl_data_t *acl_data,
2310                                  int stream,
2311                                  char *content,
2312                                  uint32_t content_length)
2313 {
2314    int ret;
2315    struct stat st;
2316    unsigned int cnt;
2317 
2318    /*
2319     * See if we are changing from one device to another.
2320     * We save the current device we are restoring to and compare
2321     * it with the current st_dev in the last stat performed on
2322     * the file we are currently restoring.
2323     */
2324    ret = lstat(acl_data->last_fname, &st);
2325    switch (ret) {
2326    case -1: {
2327       BErrNo be;
2328 
2329       switch (errno) {
2330       case ENOENT:
2331          return bacl_exit_ok;
2332       default:
2333          Mmsg2(jcr->errmsg,
2334                _("Unable to stat file \"%s\": ERR=%s\n"),
2335                acl_data->last_fname, be.bstrerror());
2336          Dmsg2(100, "Unable to stat file \"%s\": ERR=%s\n",
2337                acl_data->last_fname, be.bstrerror());
2338          return bacl_exit_error;
2339       }
2340       break;
2341    }
2342    case 0:
2343       break;
2344    }
2345    if (acl_data->current_dev != st.st_dev) {
2346       /*
2347        * Reset the acl save flags.
2348        */
2349       acl_data->flags = 0;
2350 
2351 #if defined(HAVE_AFS_ACL)
2352       /*
2353        * AFS is a non OS specific filesystem so see if this path is on an AFS filesystem
2354        * Set the BACL_FLAG_RESTORE_AFS flag if it is. If not set the BACL_FLAG_RETORE_NATIVE flag.
2355        */
2356       if (FstypeEquals(acl_data->last_fname, "afs")) {
2357          acl_data->flags |= BACL_FLAG_RESTORE_AFS;
2358       } else {
2359          acl_data->flags |= BACL_FLAG_RESTORE_NATIVE;
2360       }
2361 #else
2362       acl_data->flags |= BACL_FLAG_RESTORE_NATIVE;
2363 #endif
2364 
2365       /*
2366        * Save that we started restoring to a new filesystem.
2367        */
2368       acl_data->current_dev = st.st_dev;
2369    }
2370 
2371    switch (stream) {
2372 #if defined(HAVE_AFS_ACL)
2373    case STREAM_ACL_AFS_TEXT:
2374       if (acl_data->flags & BACL_FLAG_RESTORE_AFS) {
2375          return afs_parse_acl_stream(jcr, acl_data, stream, content, content_length);
2376       } else {
2377          /*
2378           * Increment error count but don't log an error again for the same filesystem.
2379           */
2380          acl_data->u.parse->nr_errors++;
2381          return bacl_exit_ok;
2382       }
2383 #endif
2384 #if defined(HAVE_ACL)
2385    case STREAM_UNIX_ACCESS_ACL:
2386    case STREAM_UNIX_DEFAULT_ACL:
2387       /*
2388        * Handle legacy ACL streams.
2389        */
2390       if ((acl_data->flags & BACL_FLAG_RESTORE_NATIVE) && os_parse_acl_streams) {
2391          return os_parse_acl_streams(jcr, acl_data, stream, content, content_length);
2392       } else {
2393          /*
2394           * Increment error count but don't log an error again for the same filesystem.
2395           */
2396          acl_data->u.parse->nr_errors++;
2397          return bacl_exit_ok;
2398       }
2399       break;
2400    default:
2401       if ((acl_data->flags & BACL_FLAG_RESTORE_NATIVE) && os_parse_acl_streams) {
2402          /*
2403           * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
2404           */
2405          for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
2406             if (os_access_acl_streams[cnt] == stream) {
2407                return os_parse_acl_streams(jcr, acl_data, stream, content, content_length);
2408             }
2409          }
2410          /*
2411           * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
2412           */
2413          for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
2414             if (os_default_acl_streams[cnt] == stream) {
2415                return os_parse_acl_streams(jcr, acl_data, stream, content, content_length);
2416             }
2417          }
2418       } else {
2419          /*
2420           * Increment error count but don't log an error again for the same filesystem.
2421           */
2422          acl_data->u.parse->nr_errors++;
2423          return bacl_exit_ok;
2424       }
2425       break;
2426 #else
2427    default:
2428       break;
2429 #endif
2430    }
2431    Qmsg2(jcr, M_WARNING, 0,
2432       _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
2433       acl_data->last_fname, stream);
2434    return bacl_exit_error;
2435 }
2436 #endif
2437