]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
Make sure it compiles and moved the berrno as a local variable of the function and...
[bacula/bacula] / bacula / src / filed / acl.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU 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    General Public License for more details.
17
18    You should have received a copy of the GNU 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    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Functions to handle ACLs for bacula.
30  *
31  * We handle two different types of ACLs: access and default ACLS.
32  * On most systems that support default ACLs they only apply to directories.
33  *
34  * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs
35  * independently, while others (eg. Solaris) provide both in one call.
36  *
37  * The Filed saves ACLs in their native format and uses different streams
38  * for all different platforms. Currently we only allow ACLs to be restored
39  * which were saved in the native format of the platform they are extracted
40  * on. Later on we might add conversion functions for mapping from one
41  * platform to an other or allow restores of systems that use the same
42  * native format.
43  *
44  * Its also interesting to see what the exact format of acl text is on
45  * certain platforms and if they use they same encoding we might allow
46  * different platform streams to be decoded on an other similar platform.
47  *
48  *   Original written by Preben 'Peppe' Guldberg, December MMIV
49  *   Major rewrite by Marco van Wieringen, November MMVIII
50  *
51  *   Version $Id$
52  */
53   
54 #include "bacula.h"
55 #include "filed.h"
56 #include "acl.h"
57   
58 #if !defined(HAVE_ACL)
59 /*
60  * Entry points when compiled without support for ACLs or on an unsupported platform.
61  */
62 bsub_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
63 {
64    return bsub_exit_fatal;
65 }
66
67 bsub_exit_code parse_acl_stream(JCR *jcr, int stream)
68 {
69    return bsub_exit_fatal;
70 }
71 #else
72 /*
73  * Send an ACL stream to the SD.
74  */
75 static bsub_exit_code send_acl_stream(JCR *jcr, int stream)
76 {
77    BSOCK *sd = jcr->store_bsock;
78    POOLMEM *msgsave;
79 #ifdef FD_NO_SEND_TEST
80    return bsub_exit_ok;
81 #endif
82
83    /*
84     * Sanity check
85     */
86    if (jcr->acl_data_len <= 0)
87       return bsub_exit_ok;
88
89    /*
90     * Send header
91     */
92    if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
93       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
94             sd->bstrerror());
95       return bsub_exit_fatal;
96    }
97
98    /*
99     * Send the buffer to the storage deamon
100     */
101    Dmsg1(400, "Backing up ACL <%s>\n", jcr->acl_data);
102    msgsave = sd->msg;
103    sd->msg = jcr->acl_data;
104    sd->msglen = jcr->acl_data_len + 1;
105    if (!sd->send()) {
106       sd->msg = msgsave;
107       sd->msglen = 0;
108       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
109             sd->bstrerror());
110       return bsub_exit_fatal;
111    }
112
113    jcr->JobBytes += sd->msglen;
114    sd->msg = msgsave;
115    if (!sd->signal(BNET_EOD)) {
116       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
117             sd->bstrerror());
118       return bsub_exit_fatal;
119    }
120
121    Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
122    return bsub_exit_ok;
123 }
124
125 #if defined(HAVE_AIX_OS)
126
127 #include <sys/access.h>
128
129 /*
130  * Define the supported ACL streams for this OS
131  */
132 static int os_access_acl_streams[1] = { STREAM_ACL_AIX_TEXT };
133 static int os_default_acl_streams[1] = { -1 };
134
135 static bsub_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
136 {
137    char *acl_text;
138
139    if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
140       jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
141       actuallyfree(acl_text);
142       return send_acl_stream(jcr, STREAM_ACL_AIX_TEXT);
143    }
144    return bsub_exit_nok;
145 }
146
147 static bsub_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
148 {
149    if (acl_put(jcr->last_fname, jcr->acl_data, 0) != 0) {
150       return bsub_exit_nok;
151    }
152    return bsub_exit_ok;
153 }
154
155 /*
156  * For this OS setup the build and parse function pointer to the OS specific functions.
157  */
158 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = aix_build_acl_streams;
159 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = aix_parse_acl_streams;
160
161 #elif defined(HAVE_DARWIN_OS) \
162    || defined(HAVE_FREEBSD_OS) \
163    || defined(HAVE_IRIX_OS) \
164    || defined(HAVE_OSF1_OS) \
165    || defined(HAVE_LINUX_OS)
166
167 #include <sys/types.h>
168
169 #ifdef HAVE_SYS_ACL_H
170 #include <sys/acl.h>
171 #else
172 #error "configure failed to detect availability of sys/acl.h"
173 #endif
174
175 /* On IRIX we can get shortened ACLs */
176 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
177 #define acl_to_text(acl,len)     acl_to_short_text((acl), (len))
178 #endif
179
180 /* In Linux we can get numeric and/or shorted ACLs */
181 #if defined(HAVE_LINUX_OS)
182 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
183 #define BACL_ALTERNATE_TEXT            (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
184 #elif defined(BACL_WANT_SHORT_ACLS)
185 #define BACL_ALTERNATE_TEXT            TEXT_ABBREVIATE
186 #elif defined(BACL_WANT_NUMERIC_IDS)
187 #define BACL_ALTERNATE_TEXT            TEXT_NUMERIC_IDS
188 #endif
189 #ifdef BACL_ALTERNATE_TEXT
190 #include <acl/libacl.h>
191 #define acl_to_text(acl,len)     (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
192 #endif
193 #endif
194
195 /*
196  * Some generic functions used by multiple OSes.
197  */
198 static acl_type_t bac_to_os_acltype(bacl_type acltype)
199 {
200    acl_type_t ostype;
201
202    switch (acltype) {
203    case BACL_TYPE_ACCESS:
204       ostype = ACL_TYPE_ACCESS;
205       break;
206    case BACL_TYPE_DEFAULT:
207       ostype = ACL_TYPE_DEFAULT;
208       break;
209
210 #ifdef ACL_TYPE_DEFAULT_DIR
211    case BACL_TYPE_DEFAULT_DIR:
212       /*
213        * OSF1 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
214        */
215       ostype = ACL_TYPE_DEFAULT_DIR;
216       break;
217 #endif
218 #ifdef ACL_TYPE_EXTENDED
219    case BACL_TYPE_EXTENDED:
220       /*
221        * MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
222        */
223       ostype = ACL_TYPE_EXTENDED;
224       break;
225 #endif
226    default:
227       /*
228        * This should never happen, as the per OS version function only tries acl
229        * types supported on a certain platform.
230        */
231       ostype = (acl_type_t)ACL_TYPE_NONE;
232       break;
233    }
234    return ostype;
235 }
236
237 #if !defined(HAVE_DARWIN_OS)
238 /*
239  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
240  * There is no need to store those acls as we already store the stat bits too.
241  */
242 static bool acl_is_trivial(acl_t acl)
243 {
244   /*
245    * acl is trivial if it has only the following entries:
246    * "user::",
247    * "group::",
248    * "other::"
249    */
250    acl_entry_t ace;
251    acl_tag_t tag;
252 #if defined(HAVE_FREEBSD_OS) || defined(HAVE_LINUX_OS)
253    int entry_available;
254
255    entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
256    while (entry_available == 1) {
257       /*
258        * Get the tag type of this acl entry.
259        * If we fail to get the tagtype we call the acl non-trivial.
260        */
261       if (acl_get_tag_type(ace, &tag) < 0)
262          return true;
263       /*
264        * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
265        */
266       if (tag != ACL_USER_OBJ &&
267           tag != ACL_GROUP_OBJ &&
268           tag != ACL_OTHER)
269          return false;
270       entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
271    }
272    return true;
273 #elif defined(HAVE_IRIX_OS)
274    int n;
275
276    for (n = 0; n < acl->acl_cnt; n++) {
277       ace = &acl->acl_entry[n];
278       tag = ace->ae_tag;
279
280       /*
281        * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
282        */
283       if (tag != ACL_USER_OBJ &&
284           tag != ACL_GROUP_OBJ &&
285           tag != ACL_OTHER_OBJ)
286          return false;
287    }
288    return true;
289 #elif defined(HAVE_OSF1_OS)
290    int count;
291
292    ace = acl->acl_first;
293    count = acl->acl_num;
294
295    while (count > 0) {
296       tag = ace->entry->acl_type;
297       /*
298        * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
299        */
300       if (tag != ACL_USER_OBJ &&
301           tag != ACL_GROUP_OBJ &&
302           tag != ACL_OTHER)
303          return false;
304       /*
305        * On Tru64, perm can also contain non-standard bits such as
306        * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
307        */
308       if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)))
309          return false;
310       ace = ace->next;
311       count--;
312    }
313    return true;
314 #endif
315 }
316 #endif
317
318 /*
319  * Generic wrapper around acl_get_file call.
320  */
321 static bsub_exit_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype)
322 {
323    acl_t acl;
324    acl_type_t ostype;
325    char *acl_text;
326    berrno be;
327
328    ostype = bac_to_os_acltype(acltype);
329    acl = acl_get_file(jcr->last_fname, ostype);
330    if (acl) {
331 #if defined(HAVE_IRIX_OS)
332       /* 
333        * From observation, IRIX's acl_get_file() seems to return a
334        * non-NULL acl with a count field of -1 when a file has no ACL
335        * defined, while IRIX's acl_to_text() returns NULL when presented
336        * with such an ACL. 
337        *
338        * Checking the count in the acl structure before calling
339        * acl_to_text() lets us avoid error messages about files
340        * with no ACLs, without modifying the flow of the code used for 
341        * other operating systems, and it saves making some calls
342        * to acl_to_text() besides.
343        */
344       if (acl->acl_cnt <= 0) {
345          pm_strcpy(jcr->acl_data, "");
346          jcr->acl_data_len = 0;
347          acl_free(acl);
348          return bsub_exit_ok;
349       }
350 #endif
351
352 #if !defined(HAVE_DARWIN_OS)
353       /*
354        * Make sure this is not just a trivial ACL.
355        */
356       if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
357          /*
358           * The ACLs simply reflect the (already known) standard permissions
359           * So we don't send an ACL stream to the SD.
360           */
361          pm_strcpy(jcr->acl_data, "");
362          jcr->acl_data_len = 0;
363          acl_free(acl);
364          return bsub_exit_ok;
365       }
366 #endif
367
368       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
369          jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
370          acl_free(acl);
371          acl_free(acl_text);
372          return bsub_exit_ok;
373       }
374
375       Jmsg2(jcr, M_ERROR, 0, _("acl_to_text error on file \"%s\": ERR=%s\n"),
376          jcr->last_fname, be.bstrerror());
377       Dmsg2(100, "acl_to_text error file=%s ERR=%s\n",  
378          jcr->last_fname, be.bstrerror());
379
380       pm_strcpy(jcr->acl_data, "");
381       jcr->acl_data_len = 0;
382       acl_free(acl);
383       return bsub_exit_nok;
384    }
385
386    /*
387     * Handle errors gracefully.
388     */
389    if (acl == (acl_t)NULL) {
390       switch (errno) {
391 #if defined(BACL_ENOTSUP)
392       case BACL_ENOTSUP:
393          break;                       /* not supported */
394 #endif
395       case ENOENT:
396          pm_strcpy(jcr->acl_data, "");
397          jcr->acl_data_len = 0;
398          return bsub_exit_ok;
399       default:
400          /* Some real error */
401          Jmsg2(jcr, M_ERROR, 0, _("acl_get_file error on file \"%s\": ERR=%s\n"),
402             jcr->last_fname, be.bstrerror());
403          Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",  
404             jcr->last_fname, be.bstrerror());
405
406          pm_strcpy(jcr->acl_data, "");
407          jcr->acl_data_len = 0;
408          return bsub_exit_nok;
409       }
410    }
411    /*
412     * Not supported, just pretend there is nothing to see
413     */
414    pm_strcpy(jcr->acl_data, "");
415    jcr->acl_data_len = 0;
416    return bsub_exit_ok;
417 }
418
419 /*
420  * Generic wrapper around acl_set_file call.
421  */
422 static bsub_exit_code generic_set_acl_on_os(JCR *jcr, bacl_type acltype)
423 {
424    acl_t acl;
425    acl_type_t ostype;
426    berrno be;
427
428    /*
429     * If we get empty default ACLs, clear ACLs now
430     */
431    ostype = bac_to_os_acltype(acltype);
432    if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_data) == 0) {
433       if (acl_delete_def_file(jcr->last_fname) == 0) {
434          return bsub_exit_ok;
435       }
436       switch (errno) {
437       case ENOENT:
438          return bsub_exit_ok;
439       default:
440          Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
441             jcr->last_fname, be.bstrerror());
442          return bsub_exit_nok;
443       }
444    }
445
446    acl = acl_from_text(jcr->acl_data);
447    if (acl == NULL) {
448       Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
449          jcr->last_fname, be.bstrerror());
450       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",  
451          jcr->acl_data, jcr->last_fname, be.bstrerror());
452       return bsub_exit_nok;
453    }
454
455 #ifndef HAVE_FREEBSD_OS
456    /*
457     * FreeBSD always fails acl_valid() - at least on valid input...
458     * As it does the right thing, given valid input, just ignore acl_valid().
459     */
460    if (acl_valid(acl) != 0) {
461       Jmsg2(jcr, M_ERROR, 0, _("acl_valid error on file \"%s\": ERR=%s\n"),
462          jcr->last_fname, be.bstrerror());
463       Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",  
464          jcr->acl_data, jcr->last_fname, be.bstrerror());
465       acl_free(acl);
466       return bsub_exit_nok;
467    }
468 #endif
469
470    /*
471     * Restore the ACLs, but don't complain about links which really should
472     * not have attributes, and the file it is linked to may not yet be restored.
473     * This is only true for the old acl streams as in the new implementation we
474     * don't save acls of symlinks (which cannot have acls anyhow)
475     */
476    if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
477       switch (errno) {
478       case ENOENT:
479          acl_free(acl);
480          return bsub_exit_nok;
481       default:
482          Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
483             jcr->last_fname, be.bstrerror());
484          Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
485             jcr->acl_data, jcr->last_fname, be.bstrerror());
486          acl_free(acl);
487          return bsub_exit_nok;
488       }
489    }
490    acl_free(acl);
491    return bsub_exit_ok;
492 }
493
494 /*
495  * OS specific functions for handling different types of acl streams.
496  */
497 #if defined(HAVE_DARWIN_OS)
498 /*
499  * Define the supported ACL streams for this OS
500  */
501 static int os_access_acl_streams[1] = { STREAM_ACL_DARWIN_ACCESS_ACL };
502 static int os_default_acl_streams[1] = { -1 };
503
504 static bsub_exit_code darwin_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
505 {
506 #if defined(ACL_TYPE_EXTENDED)
507    /*
508     * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
509     * and acl_get_file (name, ACL_TYPE_DEFAULT)
510     * always return NULL / EINVAL.  There is no point in making
511     * these two useless calls.  The real ACL is retrieved through
512     * acl_get_file (name, ACL_TYPE_EXTENDED).
513     *
514     * Read access ACLs for files, dirs and links
515     */
516    if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bsub_exit_fatal)
517       return bsub_exit_fatal;
518 #else
519    /*
520     * Read access ACLs for files, dirs and links
521     */
522    if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bsub_exit_fatal)
523       return bsub_exit_fatal;
524 #endif
525
526    if (jcr->acl_data_len > 0) {
527       return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS_ACL);
528    }
529    return bsub_exit_ok;
530 }
531
532 static bsub_exit_code darwin_parse_acl_streams(JCR *jcr, int stream)
533 {
534 #if defined(ACL_TYPE_EXTENDED)
535       return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED);
536 #else
537       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
538 #endif
539 }
540
541 /*
542  * For this OS setup the build and parse function pointer to the OS specific functions.
543  */
544 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
545 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = darwin_parse_acl_streams;
546
547 #elif defined(HAVE_FREEBSD_OS) || defined(HAVE_IRIX_OS) || defined(HAVE_LINUX_OS)
548
549 /*
550  * Define the supported ACL streams for these OSes
551  */
552 #if defined(HAVE_FREEBSD_OS)
553 static int os_access_acl_streams[1] = { STREAM_ACL_FREEBSD_ACCESS_ACL };
554 static int os_default_acl_streams[1] = { STREAM_ACL_FREEBSD_DEFAULT_ACL };
555 #elif defined(HAVE_IRIX_OS)
556 static int os_access_acl_streams[1] = { STREAM_ACL_IRIX_ACCESS_ACL };
557 static int os_default_acl_streams[1] = { STREAM_ACL_IRIX_DEFAULT_ACL };
558 #elif defined(HAVE_LINUX_OS)
559 static int os_access_acl_streams[1] = { STREAM_ACL_LINUX_ACCESS_ACL };
560 static int os_default_acl_streams[1] = { STREAM_ACL_LINUX_DEFAULT_ACL };
561 #endif
562
563 static bsub_exit_code generic_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
564 {
565    /*
566     * Read access ACLs for files, dirs and links
567     */
568    if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bsub_exit_fatal)
569       return bsub_exit_fatal;
570
571    if (jcr->acl_data_len > 0) {
572       if (send_acl_stream(jcr, os_access_acl_streams[0]) == bsub_exit_fatal)
573          return bsub_exit_fatal;
574    }
575
576    /*
577     * Directories can have default ACLs too
578     */
579    if (ff_pkt->type == FT_DIREND) {
580       if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bsub_exit_fatal)
581          return bsub_exit_fatal;
582       if (jcr->acl_data_len > 0) {
583          if (send_acl_stream(jcr, os_default_acl_streams[0]) == bsub_exit_fatal)
584             return bsub_exit_fatal;
585       }
586    }
587    return bsub_exit_ok;
588 }
589
590 static bsub_exit_code generic_parse_acl_streams(JCR *jcr, int stream)
591 {
592    int cnt;
593
594    switch (stream) {
595    case STREAM_UNIX_ACCESS_ACL:
596       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
597    case STREAM_UNIX_DEFAULT_ACL:
598       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
599    default:
600       /*
601        * See what type of acl it is.
602        */
603       for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
604          if (os_access_acl_streams[cnt] == stream) {
605             return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
606          }
607       }
608       for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
609          if (os_default_acl_streams[cnt] == stream) {
610             return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
611          }
612       }
613       break;
614    }
615    return bsub_exit_nok;
616 }
617
618 /*
619  * For this OSes setup the build and parse function pointer to the OS specific functions.
620  */
621 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
622 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = generic_parse_acl_streams;
623
624 #elif defined(HAVE_OSF1_OS)
625
626 /*
627  * Define the supported ACL streams for this OS
628  */
629 static int os_access_acl_streams[1] = { STREAM_ACL_TRU64_ACCESS_ACL };
630 static int os_default_acl_streams[2] = { STREAM_ACL_TRU64_DEFAULT_ACL, STREAM_ACL_TRU64_DEFAULT_DIR_ACL };
631
632 static bsub_exit_code tru64_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
633 {
634    /*
635     * Read access ACLs for files, dirs and links
636     */
637    if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS)) < 0)
638       return bsub_exit_nok;
639    if (jcr->acl_data_len > 0) {
640       if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
641          return bsub_exit_nok;
642    }
643    /*
644     * Directories can have default ACLs too
645     */
646    if (ff_pkt->type == FT_DIREND) {
647       if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT)) < 0)
648          return bsub_exit_nok;
649       if (jcr->acl_data_len > 0) {
650          if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
651             return bsub_exit_nok;
652       }
653       /*
654        * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
655        * This is an inherited acl for all subdirs.
656        * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
657        * Section 21.5 Default ACLs 
658        */
659       if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR)) < 0)
660          return bsub_exit_nok;
661       if (jcr->acl_data_len > 0) {
662          if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
663             return bsub_exit_nok;
664       }
665    }
666    return bsub_exit_ok;
667 }
668
669 static bsub_exit_code tru64_parse_acl_streams(JCR *jcr, int stream)
670 {
671    switch (stream) {
672    case STREAM_UNIX_ACCESS_ACL:
673    case STREAM_ACL_TRU64_ACCESS_ACL:
674       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
675    case STREAM_UNIX_DEFAULT_ACL:
676    case STREAM_ACL_TRU64_DEFAULT_ACL:
677       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
678    case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
679       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR);
680 }
681
682 /*
683  * For this OS setup the build and parse function pointer to the OS specific functions.
684  */
685 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
686 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = tru64_parse_acl_streams;
687
688 #endif
689
690 #elif defined(HAVE_HPUX_OS)
691 #ifdef HAVE_SYS_ACL_H
692 #include <sys/acl.h>
693 #else
694 #error "configure failed to detect availability of sys/acl.h"
695 #endif
696
697 #include <acllib.h>
698
699 /*
700  * Define the supported ACL streams for this OS
701  */
702 static int os_access_acl_streams[1] = { STREAM_ACL_HPUX_ACL_ENTRY };
703 static int os_default_acl_streams[1] = { -1 };
704
705 /*
706  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
707  * There is no need to store those acls as we already store the stat bits too.
708  */
709 static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb)
710 {
711    int n;
712    struct acl_entry ace
713
714    for (n = 0; n < count; n++) {
715       ace = entries[n];
716       /*
717        * See if this acl just is the stat mode in acl form.
718        */
719       if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
720             (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
721             (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
722          return false;
723    }
724    return true;
725 }
726
727 /*
728  * OS specific functions for handling different types of acl streams.
729  */
730 static bsub_exit_code hpux_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
731 {
732    int n;
733    struct acl_entry acls[NACLENTRIES];
734    char *acl_text;
735    berrno be;
736
737    if ((n = getacl(jcr->last_fname, 0, acls)) < 0) {
738       switch (errno) {
739 #if defined(BACL_ENOTSUP)
740       case BACL_ENOTSUP:
741          /*
742           * Not supported, just pretend there is nothing to see
743           */
744          pm_strcpy(jcr->acl_data, "");
745          jcr->acl_data_len = 0;
746          return bsub_exit_ok;
747 #endif
748       case ENOENT:
749          pm_strcpy(jcr->acl_data, "");
750          jcr->acl_data_len = 0;
751          return bsub_exit_ok;
752       default:
753          Jmsg2(jcr, M_ERROR, 0, _("getacl error on file \"%s\": ERR=%s\n"),
754             jcr->last_fname, be.bstrerror());
755          Dmsg2(100, "getacl error file=%s ERR=%s\n",  
756             jcr->last_fname, be.bstrerror());
757
758          pm_strcpy(jcr->acl_data, "");
759          jcr->acl_data_len = 0;
760          return bsub_exit_nok;
761       }
762    }
763    if (n == 0) {
764       pm_strcpy(jcr->acl_data, "");
765       jcr->acl_data_len = 0;
766       return bsub_exit_ok;
767    }
768    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
769       if (acl_is_trivial(n, acls, ff_pkt->statp)) {
770          /*
771           * The ACLs simply reflect the (already known) standard permissions
772           * So we don't send an ACL stream to the SD.
773           */
774          pm_strcpy(jcr->acl_data, "");
775          jcr->acl_data_len = 0;
776          return bsub_exit_ok;
777       }
778       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
779          jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
780          actuallyfree(acl_text);
781
782          return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY);
783       }
784       Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
785          jcr->last_fname, be.bstrerror());
786       Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",  
787          jcr->acl_data, jcr->last_fname, be.bstrerror());
788       return bsub_exit_nok;
789    }
790    return bsub_exit_nok;
791 }
792
793 static bsub_exit_code hpux_parse_acl_streams(JCR *jcr, int stream)
794 {
795    int n, stat;
796    struct acl_entry acls[NACLENTRIES];
797    berrno be;
798
799    n = strtoacl(jcr->acl_data, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
800    if (n <= 0) {
801       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
802          jcr->last_fname, be.bstrerror());
803       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
804          jcr->acl_data, jcr->last_fname, be.bstrerror());
805       return bsub_exit_nok;
806    }
807    if (strtoacl(jcr->acl_data, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
808       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
809          jcr->last_fname, be.bstrerror());
810       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
811          jcr->acl_data, jcr->last_fname, be.bstrerror());
812
813       return bsub_exit_nok;
814    }
815    /*
816     * Restore the ACLs, but don't complain about links which really should
817     * not have attributes, and the file it is linked to may not yet be restored.
818     * This is only true for the old acl streams as in the new implementation we
819     * don't save acls of symlinks (which cannot have acls anyhow)
820     */
821    if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
822       switch (errno) {
823       case ENOENT:
824          return bsub_exit_nok;
825       default:
826          Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
827             jcr->last_fname, be.bstrerror());
828          Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
829             jcr->acl_data, jcr->last_fname, be.bstrerror());
830          return bsub_exit_nok;
831       }
832    }
833    return bsub_exit_ok;
834 }
835
836 /*
837  * For this OS setup the build and parse function pointer to the OS specific functions.
838  */
839 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
840 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = hpux_parse_acl_streams;
841
842 #elif defined(HAVE_SUN_OS)
843 #ifdef HAVE_SYS_ACL_H
844 #include <sys/acl.h>
845 #else
846 #error "configure failed to detect availability of sys/acl.h"
847 #endif
848
849 #if defined(HAVE_EXTENDED_ACL)
850 /*
851  * We define some internals of the Solaris acl libs here as those
852  * are not exposed yet. Probably because they want us to see the
853  * acls as opague data. But as we need to support different platforms
854  * and versions of Solaris we need to expose some data to be able
855  * to determine the type of acl used to stuff it into the correct
856  * data stream. I know this is far from portable, but maybe the
857  * proper interface is exposed later on and we can get ride of
858  * this kludge. Newer versions of Solaris include sys/acl_impl.h
859  * which has implementation details of acls, if thats included we
860  * don't have to define it ourself.
861  */
862 #if !defined(_SYS_ACL_IMPL_H)
863 typedef enum acl_type {
864    ACLENT_T = 0,
865    ACE_T = 1
866 } acl_type_t;
867 #endif
868
869 /*
870  * Two external references to functions in the libsec library function not in current include files.
871  */
872 extern "C" {
873 int acl_type(acl_t *);
874 char *acl_strerror(int);
875 }
876
877 /*
878  * Define the supported ACL streams for this OS
879  */
880 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT, STREAM_ACL_SOLARIS_ACE };
881 static int os_default_acl_streams[1] = { -1 };
882
883 /*
884  * As the new libsec interface with acl_totext and acl_fromtext also handles
885  * the old format from acltotext we can use the new functions even
886  * for acls retrieved and stored in the database with older fd versions. If the
887  * new interface is not defined (Solaris 9 and older we fall back to the old code)
888  */
889 static bsub_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
890 {
891    int acl_enabled, flags;
892    acl_t *aclp;
893    char *acl_text;
894    bsub_exit_code stream_status = bsub_exit_nok;
895    berrno be;
896
897    /*
898     * See if filesystem supports acls.
899     */
900    acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
901    switch (acl_enabled) {
902    case 0:
903       pm_strcpy(jcr->acl_data, "");
904       jcr->acl_data_len = 0;
905       return bsub_exit_ok;
906    case -1:
907       Jmsg2(jcr, M_ERROR, 0, _("pathconf error on file \"%s\": ERR=%s\n"),
908          jcr->last_fname, be.bstrerror());
909       Dmsg2(100, "pathconf error file=%s ERR=%s\n",  
910          jcr->last_fname, be.bstrerror());
911       return bsub_exit_nok;
912    default:
913       break;
914    }
915
916    /*
917     * Get ACL info: don't bother allocating space if there is only a trivial ACL.
918     */
919    if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
920       Jmsg2(jcr, M_ERROR, 0, _("acl_get error on file \"%s\": ERR=%s\n"),
921          jcr->last_fname, acl_strerror(errno));
922       Dmsg2(100, "acl_get error file=%s ERR=%s\n",  
923          jcr->last_fname, acl_strerror(errno));
924       return bsub_exit_nok;
925    }
926
927    if (!aclp) {
928       /*
929        * The ACLs simply reflect the (already known) standard permissions
930        * So we don't send an ACL stream to the SD.
931        */
932       pm_strcpy(jcr->acl_data, "");
933       jcr->acl_data_len = 0;
934       return bsub_exit_ok;
935    }
936
937 #if defined(ACL_SID_FMT)
938    /*
939     * New format flag added in newer Solaris versions.
940     */
941    flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
942 #else
943    flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
944 #endif /* ACL_SID_FMT */
945
946    if ((acl_text = acl_totext(aclp, flags)) != NULL) {
947       jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
948       actuallyfree(acl_text);
949
950       switch (acl_type(aclp)) {
951       case ACLENT_T:
952          stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
953          break;
954       case ACE_T:
955          stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACE);
956          break;
957       default:
958          break;
959       }
960
961       acl_free(aclp);
962    }
963    return stream_status;
964 }
965
966 static bsub_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
967 {
968    acl_t *aclp;
969    int acl_enabled, error;
970    berrno be;
971
972    switch (stream) {
973    case STREAM_UNIX_ACCESS_ACL:
974    case STREAM_ACL_SOLARIS_ACLENT:
975    case STREAM_ACL_SOLARIS_ACE:
976       /*
977        * First make sure the filesystem supports acls.
978        */
979       acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
980       switch (acl_enabled) {
981       case 0:
982          Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
983             jcr->last_fname);
984          return bsub_exit_nok;
985       case -1:
986          Jmsg2(jcr, M_ERROR, 0, _("pathconf error on file \"%s\": ERR=%s\n"),
987             jcr->last_fname, be.bstrerror());
988          Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",  
989             jcr->acl_data, jcr->last_fname, be.bstrerror());
990          return bsub_exit_nok;
991       default:
992          /*
993           * On a filesystem with ACL support make sure this particilar ACL type can be restored.
994           */
995          switch (stream) {
996          case STREAM_ACL_SOLARIS_ACLENT:
997             /*
998              * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
999              */
1000             if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1001                Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without aclent acl support\n"),
1002                   jcr->last_fname);
1003                return bsub_exit_nok;
1004             }
1005             break;
1006          case STREAM_ACL_SOLARIS_ACE:
1007             /*
1008              * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1009              */
1010             if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1011                Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without ace acl support\n"),
1012                   jcr->last_fname);
1013                return bsub_exit_nok;
1014             }
1015             break;
1016          default:
1017             /*
1018              * Stream id which doesn't describe the type of acl which is encoded.
1019              */
1020             break;
1021          }
1022          break;
1023       }
1024
1025       if ((error = acl_fromtext(jcr->acl_data, &aclp)) != 0) {
1026          Jmsg2(jcr, M_ERROR, 0, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1027             jcr->last_fname, acl_strerror(error));
1028          Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",  
1029             jcr->acl_data, jcr->last_fname, acl_strerror(error));
1030          return bsub_exit_nok;
1031       }
1032
1033       /*
1034        * Validate that the conversion gave us the correct acl type.
1035        */
1036       switch (stream) {
1037       case STREAM_ACL_SOLARIS_ACLENT:
1038          if (acl_type(aclp) != ACLENT_T) {
1039             Jmsg1(jcr, M_ERROR, 0, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1040                jcr->last_fname);
1041             return bsub_exit_nok;
1042          }
1043          break;
1044       case STREAM_ACL_SOLARIS_ACE:
1045          if (acl_type(aclp) != ACE_T) {
1046             Jmsg1(jcr, M_ERROR, 0, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1047                jcr->last_fname);
1048             return bsub_exit_nok;
1049          }
1050          break;
1051       default:
1052          /*
1053           * Stream id which doesn't describe the type of acl which is encoded.
1054           */
1055          break;
1056       }
1057
1058       /*
1059        * Restore the ACLs, but don't complain about links which really should
1060        * not have attributes, and the file it is linked to may not yet be restored.
1061        * This is only true for the old acl streams as in the new implementation we
1062        * don't save acls of symlinks (which cannot have acls anyhow)
1063        */
1064       if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
1065          Jmsg2(jcr, M_ERROR, 0, _("acl_set error on file \"%s\": ERR=%s\n"),
1066             jcr->last_fname, acl_strerror(error));
1067          Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",  
1068             jcr->acl_data, jcr->last_fname, acl_strerror(error));
1069
1070          acl_free(aclp);
1071          return bsub_exit_nok;
1072       }
1073
1074       acl_free(aclp);
1075       return bsub_exit_ok;
1076    default:
1077       return bsub_exit_nok;
1078    } /* end switch (stream) */
1079 }
1080
1081 #else /* HAVE_EXTENDED_ACL */
1082
1083 /*
1084  * Define the supported ACL streams for this OS
1085  */
1086 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
1087 static int os_default_acl_streams[1] = { -1 };
1088
1089 /*
1090  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1091  * There is no need to store those acls as we already store the stat bits too.
1092  */
1093 static bool acl_is_trivial(int count, aclent_t *entries)
1094 {
1095    int n;
1096    aclent_t *ace;
1097
1098    for (n = 0; n < count; n++) {
1099       ace = &entries[n];
1100
1101       if (!(ace->a_type == USER_OBJ ||
1102             ace->a_type == GROUP_OBJ ||
1103             ace->a_type == OTHER_OBJ ||
1104             ace->a_type == CLASS_OBJ))
1105         return false;
1106    }
1107    return true;
1108 }
1109
1110 /*
1111  * OS specific functions for handling different types of acl streams.
1112  */
1113 static bsub_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1114 {
1115    int n;
1116    aclent_t *acls;
1117    char *acl_text;
1118    berrno be;
1119
1120    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
1121    if (n < MIN_ACL_ENTRIES)
1122       return bsub_exit_nok;
1123
1124    acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1125    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
1126       if (acl_is_trivial(n, acls)) {
1127          /*
1128           * The ACLs simply reflect the (already known) standard permissions
1129           * So we don't send an ACL stream to the SD.
1130           */
1131          free(acls);
1132          pm_strcpy(jcr->acl_data, "");
1133          jcr->acl_data_len = 0;
1134          return bsub_exit_ok;
1135       }
1136
1137       if ((acl_text = acltotext(acls, n)) != NULL) {
1138          jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
1139          actuallyfree(acl_text);
1140          free(acls);
1141          return send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1142       }
1143
1144       Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
1145          jcr->last_fname, be.bstrerror());
1146       Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",  
1147          jcr->acl_data, jcr->last_fname, be.bstrerror());
1148    }
1149
1150    free(acls);
1151    return bsub_exit_nok;
1152 }
1153
1154 static bsub_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1155 {
1156    int n;
1157    aclent_t *acls;
1158    berrno be;
1159
1160    acls = aclfromtext(jcr->acl_data, &n);
1161    if (!acls) {
1162       Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
1163          jcr->last_fname, be.bstrerror());
1164       Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",  
1165          jcr->acl_data, jcr->last_fname, be.bstrerror());
1166       return bsub_exit_nok;
1167    }
1168
1169    /*
1170     * Restore the ACLs, but don't complain about links which really should
1171     * not have attributes, and the file it is linked to may not yet be restored.
1172     */
1173    if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
1174       switch (errno) {
1175       case ENOENT:
1176          actuallyfree(acls);
1177          return bsub_exit_nok;
1178       default:
1179          Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
1180             jcr->last_fname, be.bstrerror());
1181          Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
1182             jcr->acl_data, jcr->last_fname, be.bstrerror());
1183          actuallyfree(acls);
1184          return bsub_exit_nok;
1185       }
1186    }
1187    actuallyfree(acls);
1188    return bsub_exit_ok;
1189 }
1190 #endif /* HAVE_EXTENDED_ACL */
1191
1192 /*
1193  * For this OS setup the build and parse function pointer to the OS specific functions.
1194  */
1195 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
1196 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
1197
1198 #endif /* HAVE_SUN_OS */
1199
1200 /*
1201  * Entry points when compiled with support for ACLs on a supported platform.
1202  */
1203
1204 /*
1205  * Read and send an ACL for the last encountered file.
1206  */
1207 bsub_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1208 {
1209    /*
1210     * Call the appropriate function.
1211     */
1212    if (os_build_acl_streams) {
1213       return (*os_build_acl_streams)(jcr, ff_pkt);
1214    }
1215    return bsub_exit_nok;
1216 }
1217
1218 bsub_exit_code parse_acl_streams(JCR *jcr, int stream)
1219 {
1220    int cnt;
1221
1222    switch (stream) {
1223    case STREAM_UNIX_ACCESS_ACL:
1224    case STREAM_UNIX_DEFAULT_ACL:
1225       /*
1226        * Handle legacy ACL streams.
1227        */
1228       if (os_parse_acl_streams) {
1229          return (*os_parse_acl_streams)(jcr, stream);
1230       }
1231       break;
1232    default:
1233       if (os_parse_acl_streams) {
1234          /*
1235           * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
1236           */
1237          for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1238             if (os_access_acl_streams[cnt] == stream) {
1239                return (*os_parse_acl_streams)(jcr, stream);
1240             }
1241          }
1242          /*
1243           * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
1244           */
1245          for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1246             if (os_default_acl_streams[cnt] == stream) {
1247                return (*os_parse_acl_streams)(jcr, stream);
1248             }
1249          }
1250       }
1251       break;
1252    }
1253    Qmsg2(jcr, M_WARNING, 0,
1254       _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
1255       jcr->last_fname, stream);
1256    return bsub_exit_nok;
1257 }
1258 #endif