]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
79fdbd6d57a268c48fae160853396953483c7718
[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
327    ostype = bac_to_os_acltype(acltype);
328    acl = acl_get_file(jcr->last_fname, ostype);
329    if (acl) {
330 #if defined(HAVE_IRIX_OS)
331       /* 
332        * From observation, IRIX's acl_get_file() seems to return a
333        * non-NULL acl with a count field of -1 when a file has no ACL
334        * defined, while IRIX's acl_to_text() returns NULL when presented
335        * with such an ACL. 
336        *
337        * Checking the count in the acl structure before calling
338        * acl_to_text() lets us avoid error messages about files
339        * with no ACLs, without modifying the flow of the code used for 
340        * other operating systems, and it saves making some calls
341        * to acl_to_text() besides.
342        */
343       if (acl->acl_cnt <= 0) {
344          pm_strcpy(jcr->acl_data, "");
345          jcr->acl_data_len = 0;
346          acl_free(acl);
347          return bsub_exit_ok;
348       }
349 #endif
350
351 #if !defined(HAVE_DARWIN_OS)
352       /*
353        * Make sure this is not just a trivial ACL.
354        */
355       if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
356          /*
357           * The ACLs simply reflect the (already known) standard permissions
358           * So we don't send an ACL stream to the SD.
359           */
360          pm_strcpy(jcr->acl_data, "");
361          jcr->acl_data_len = 0;
362          acl_free(acl);
363          return bsub_exit_ok;
364       }
365 #endif
366
367       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
368          jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
369          acl_free(acl);
370          acl_free(acl_text);
371          return bsub_exit_ok;
372       }
373
374       berrno be;
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          berrno be;
401          /* Some real error */
402          Jmsg2(jcr, M_ERROR, 0, _("acl_get_file error on file \"%s\": ERR=%s\n"),
403             jcr->last_fname, be.bstrerror());
404          Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",  
405             jcr->last_fname, be.bstrerror());
406
407          pm_strcpy(jcr->acl_data, "");
408          jcr->acl_data_len = 0;
409          return bsub_exit_nok;
410       }
411    }
412    /*
413     * Not supported, just pretend there is nothing to see
414     */
415    pm_strcpy(jcr->acl_data, "");
416    jcr->acl_data_len = 0;
417    return bsub_exit_ok;
418 }
419
420 /*
421  * Generic wrapper around acl_set_file call.
422  */
423 static bsub_exit_code generic_set_acl_on_os(JCR *jcr, bacl_type acltype)
424 {
425    acl_t acl;
426    acl_type_t ostype;
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          berrno be;
441          Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
442             jcr->last_fname, be.bstrerror());
443          return bsub_exit_nok;
444       }
445    }
446
447    acl = acl_from_text(jcr->acl_data);
448    if (acl == NULL) {
449       berrno be;
450       Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
451          jcr->last_fname, be.bstrerror());
452       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",  
453          jcr->acl_data, jcr->last_fname, be.bstrerror());
454       return bsub_exit_nok;
455    }
456
457 #ifndef HAVE_FREEBSD_OS
458    /*
459     * FreeBSD always fails acl_valid() - at least on valid input...
460     * As it does the right thing, given valid input, just ignore acl_valid().
461     */
462    if (acl_valid(acl) != 0) {
463       berrno be;
464       Jmsg2(jcr, M_ERROR, 0, _("ac_valid error on file \"%s\": ERR=%s\n"),
465          jcr->last_fname, be.bstrerror());
466       Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",  
467          jcr->acl_data, jcr->last_fname, be.bstrerror());
468       acl_free(acl);
469       return bsub_exit_nok;
470    }
471 #endif
472
473    /*
474     * Restore the ACLs, but don't complain about links which really should
475     * not have attributes, and the file it is linked to may not yet be restored.
476     * This is only true for the old acl streams as in the new implementation we
477     * don't save acls of symlinks (which cannot have acls anyhow)
478     */
479    if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
480       switch (errno) {
481       case ENOENT:
482          acl_free(acl);
483          return bsub_exit_nok;
484       default:
485          berrno be;
486          Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
487             jcr->last_fname, be.bstrerror());
488          Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
489             jcr->acl_data, jcr->last_fname, be.bstrerror());
490          acl_free(acl);
491          return bsub_exit_nok;
492       }
493    }
494    acl_free(acl);
495    return bsub_exit_ok;
496 }
497
498 /*
499  * OS specific functions for handling different types of acl streams.
500  */
501 #if defined(HAVE_DARWIN_OS)
502 /*
503  * Define the supported ACL streams for this OS
504  */
505 static int os_access_acl_streams[1] = { STREAM_ACL_DARWIN_ACCESS_ACL };
506 static int os_default_acl_streams[1] = { -1 };
507
508 static bsub_exit_code darwin_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
509 {
510 #if defined(ACL_TYPE_EXTENDED)
511    /*
512     * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
513     * and acl_get_file (name, ACL_TYPE_DEFAULT)
514     * always return NULL / EINVAL.  There is no point in making
515     * these two useless calls.  The real ACL is retrieved through
516     * acl_get_file (name, ACL_TYPE_EXTENDED).
517     *
518     * Read access ACLs for files, dirs and links
519     */
520    if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bsub_exit_fatal)
521       return bsub_exit_fatal;
522 #else
523    /*
524     * Read access ACLs for files, dirs and links
525     */
526    if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bsub_exit_fatal)
527       return bsub_exit_fatal;
528 #endif
529
530    if (jcr->acl_data_len > 0) {
531       return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS_ACL);
532    }
533    return bsub_exit_ok;
534 }
535
536 static bsub_exit_code darwin_parse_acl_streams(JCR *jcr, int stream)
537 {
538 #if defined(ACL_TYPE_EXTENDED)
539       return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED);
540 #else
541       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
542 #endif
543 }
544
545 /*
546  * For this OS setup the build and parse function pointer to the OS specific functions.
547  */
548 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
549 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = darwin_parse_acl_streams;
550
551 #elif defined(HAVE_FREEBSD_OS) || defined(HAVE_IRIX_OS) || defined(HAVE_LINUX_OS)
552
553 /*
554  * Define the supported ACL streams for these OSes
555  */
556 #if defined(HAVE_FREEBSD_OS)
557 static int os_access_acl_streams[1] = { STREAM_ACL_FREEBSD_ACCESS_ACL };
558 static int os_default_acl_streams[1] = { STREAM_ACL_FREEBSD_DEFAULT_ACL };
559 #elif defined(HAVE_IRIX_OS)
560 static int os_access_acl_streams[1] = { STREAM_ACL_IRIX_ACCESS_ACL };
561 static int os_default_acl_streams[1] = { STREAM_ACL_IRIX_DEFAULT_ACL };
562 #elif defined(HAVE_LINUX_OS)
563 static int os_access_acl_streams[1] = { STREAM_ACL_LINUX_ACCESS_ACL };
564 static int os_default_acl_streams[1] = { STREAM_ACL_LINUX_DEFAULT_ACL };
565 #endif
566
567 static bsub_exit_code generic_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
568 {
569    /*
570     * Read access ACLs for files, dirs and links
571     */
572    if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bsub_exit_fatal)
573       return bsub_exit_fatal;
574
575    if (jcr->acl_data_len > 0) {
576       if (send_acl_stream(jcr, os_access_acl_streams[0]) == bsub_exit_fatal)
577          return bsub_exit_fatal;
578    }
579
580    /*
581     * Directories can have default ACLs too
582     */
583    if (ff_pkt->type == FT_DIREND) {
584       if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bsub_exit_fatal)
585          return bsub_exit_fatal;
586       if (jcr->acl_data_len > 0) {
587          if (send_acl_stream(jcr, os_default_acl_streams[0]) == bsub_exit_fatal)
588             return bsub_exit_fatal;
589       }
590    }
591    return bsub_exit_ok;
592 }
593
594 static bsub_exit_code generic_parse_acl_streams(JCR *jcr, int stream)
595 {
596    int cnt;
597
598    switch (stream) {
599    case STREAM_UNIX_ACCESS_ACL:
600       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
601    case STREAM_UNIX_DEFAULT_ACL:
602       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
603    default:
604       /*
605        * See what type of acl it is.
606        */
607       for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
608          if (os_access_acl_streams[cnt] == stream) {
609             return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
610          }
611       }
612       for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
613          if (os_default_acl_streams[cnt] == stream) {
614             return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
615          }
616       }
617       break;
618    }
619    return bsub_exit_nok;
620 }
621
622 /*
623  * For this OSes setup the build and parse function pointer to the OS specific functions.
624  */
625 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
626 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = generic_parse_acl_streams;
627
628 #elif defined(HAVE_OSF1_OS)
629
630 /*
631  * Define the supported ACL streams for this OS
632  */
633 static int os_access_acl_streams[1] = { STREAM_ACL_TRU64_ACCESS_ACL };
634 static int os_default_acl_streams[2] = { STREAM_ACL_TRU64_DEFAULT_ACL, STREAM_ACL_TRU64_DEFAULT_DIR_ACL };
635
636 static bsub_exit_code tru64_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
637 {
638    /*
639     * Read access ACLs for files, dirs and links
640     */
641    if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS)) < 0)
642       return bsub_exit_nok;
643    if (jcr->acl_data_len > 0) {
644       if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
645          return bsub_exit_nok;
646    }
647    /*
648     * Directories can have default ACLs too
649     */
650    if (ff_pkt->type == FT_DIREND) {
651       if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT)) < 0)
652          return bsub_exit_nok;
653       if (jcr->acl_data_len > 0) {
654          if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
655             return bsub_exit_nok;
656       }
657       /*
658        * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
659        * This is an inherited acl for all subdirs.
660        * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
661        * Section 21.5 Default ACLs 
662        */
663       if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR)) < 0)
664          return bsub_exit_nok;
665       if (jcr->acl_data_len > 0) {
666          if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
667             return bsub_exit_nok;
668       }
669    }
670    return bsub_exit_ok;
671 }
672
673 static bsub_exit_code tru64_parse_acl_streams(JCR *jcr, int stream)
674 {
675    switch (stream) {
676    case STREAM_UNIX_ACCESS_ACL:
677    case STREAM_ACL_TRU64_ACCESS_ACL:
678       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
679    case STREAM_UNIX_DEFAULT_ACL:
680    case STREAM_ACL_TRU64_DEFAULT_ACL:
681       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
682    case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
683       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR);
684 }
685
686 /*
687  * For this OS setup the build and parse function pointer to the OS specific functions.
688  */
689 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
690 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = tru64_parse_acl_streams;
691
692 #endif
693
694 #elif defined(HAVE_HPUX_OS)
695 #ifdef HAVE_SYS_ACL_H
696 #include <sys/acl.h>
697 #else
698 #error "configure failed to detect availability of sys/acl.h"
699 #endif
700
701 #include <acllib.h>
702
703 /*
704  * Define the supported ACL streams for this OS
705  */
706 static int os_access_acl_streams[1] = { STREAM_ACL_HPUX_ACL_ENTRY };
707 static int os_default_acl_streams[1] = { -1 };
708
709 /*
710  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
711  * There is no need to store those acls as we already store the stat bits too.
712  */
713 static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb)
714 {
715    int n;
716    struct acl_entry ace
717
718    for (n = 0; n < count; n++) {
719       ace = entries[n];
720       /*
721        * See if this acl just is the stat mode in acl form.
722        */
723       if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
724             (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
725             (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
726          return false;
727    }
728    return true;
729 }
730
731 /*
732  * OS specific functions for handling different types of acl streams.
733  */
734 static bsub_exit_code hpux_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
735 {
736    int n;
737    struct acl_entry acls[NACLENTRIES];
738    char *acl_text;
739
740    if ((n = getacl(jcr->last_fname, 0, acls)) < 0) {
741       switch (errno) {
742 #if defined(BACL_ENOTSUP)
743       case BACL_ENOTSUP:
744          /*
745           * Not supported, just pretend there is nothing to see
746           */
747          pm_strcpy(jcr->acl_data, "");
748          jcr->acl_data_len = 0;
749          return bsub_exit_ok;
750 #endif
751       case ENOENT:
752          pm_strcpy(jcr->acl_data, "");
753          jcr->acl_data_len = 0;
754          return bsub_exit_ok;
755       default:
756          berrno be;
757          Jmsg2(jcr, M_ERROR, 0, _("getacl error on file \"%s\": ERR=%s\n"),
758             jcr->last_fname, be.bstrerror());
759          Dmsg2(100, "getacl error file=%s ERR=%s\n",  
760             jcr->last_fname, be.bstrerror());
761
762          pm_strcpy(jcr->acl_data, "");
763          jcr->acl_data_len = 0;
764          return bsub_exit_nok;
765       }
766    }
767    if (n == 0) {
768       pm_strcpy(jcr->acl_data, "");
769       jcr->acl_data_len = 0;
770       return bsub_exit_ok;
771    }
772    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
773       if (acl_is_trivial(n, acls, ff_pkt->statp)) {
774          /*
775           * The ACLs simply reflect the (already known) standard permissions
776           * So we don't send an ACL stream to the SD.
777           */
778          pm_strcpy(jcr->acl_data, "");
779          jcr->acl_data_len = 0;
780          return bsub_exit_ok;
781       }
782       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
783          jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
784          actuallyfree(acl_text);
785
786          return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY);
787       }
788       berrno be;
789       Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
790          jcr->last_fname, be.bstrerror());
791       Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",  
792          jcr->acl_data, jcr->last_fname, be.bstrerror());
793       return bsub_exit_nok;
794    }
795    return bsub_exit_nok;
796 }
797
798 static bsub_exit_code hpux_parse_acl_streams(JCR *jcr, int stream)
799 {
800    int n, stat;
801    struct acl_entry acls[NACLENTRIES];
802
803    n = strtoacl(jcr->acl_data, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
804    if (n <= 0) {
805       berrno be;
806       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
807          jcr->last_fname, be.bstrerror());
808       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
809          jcr->acl_data, jcr->last_fname, be.bstrerror());
810       return bsub_exit_nok;
811    }
812    if (strtoacl(jcr->acl_data, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
813       berrno be;
814       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
815          jcr->last_fname, be.bstrerror());
816       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
817          jcr->acl_data, jcr->last_fname, be.bstrerror());
818
819       return bsub_exit_nok;
820    }
821    /*
822     * Restore the ACLs, but don't complain about links which really should
823     * not have attributes, and the file it is linked to may not yet be restored.
824     * This is only true for the old acl streams as in the new implementation we
825     * don't save acls of symlinks (which cannot have acls anyhow)
826     */
827    if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
828       switch (errno) {
829       case ENOENT:
830          return bsub_exit_nok;
831       default:
832          berrno be;
833          Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
834             jcr->last_fname, be.bstrerror());
835          Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
836             jcr->acl_data, jcr->last_fname, be.bstrerror());
837          return bsub_exit_nok;
838       }
839    }
840    return bsub_exit_ok;
841 }
842
843 /*
844  * For this OS setup the build and parse function pointer to the OS specific functions.
845  */
846 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
847 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = hpux_parse_acl_streams;
848
849 #elif defined(HAVE_SUN_OS)
850 #ifdef HAVE_SYS_ACL_H
851 #include <sys/acl.h>
852 #else
853 #error "configure failed to detect availability of sys/acl.h"
854 #endif
855
856 #if defined(HAVE_EXTENDED_ACL)
857 /*
858  * We define some internals of the Solaris acl libs here as those
859  * are not exposed yet. Probably because they want us to see the
860  * acls as opague data. But as we need to support different platforms
861  * and versions of Solaris we need to expose some data to be able
862  * to determine the type of acl used to stuff it into the correct
863  * data stream. I know this is far from portable, but maybe the
864  * proper interface is exposed later on and we can get ride of
865  * this kludge. Newer versions of Solaris include sys/acl_impl.h
866  * which has implementation details of acls, if thats included we
867  * don't have to define it ourself.
868  */
869 #if !defined(_SYS_ACL_IMPL_H)
870 typedef enum acl_type {
871    ACLENT_T = 0,
872    ACE_T = 1
873 } acl_type_t;
874 #endif
875
876 /*
877  * Two external references to functions in the libsec library function not in current include files.
878  */
879 extern "C" {
880 int acl_type(acl_t *);
881 char *acl_strerror(int);
882 }
883
884 /*
885  * Define the supported ACL streams for this OS
886  */
887 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT, STREAM_ACL_SOLARIS_ACE };
888 static int os_default_acl_streams[1] = { -1 };
889
890 /*
891  * As the new libsec interface with acl_totext and acl_fromtext also handles
892  * the old format from acltotext we can use the new functions even
893  * for acls retrieved and stored in the database with older fd versions. If the
894  * new interface is not defined (Solaris 9 and older we fall back to the old code)
895  */
896 static bsub_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
897 {
898    int acl_enabled, flags;
899    acl_t *aclp;
900    char *acl_text;
901    bsub_exit_code stream_status = bsub_exit_nok;
902
903    /*
904     * See if filesystem supports acls.
905     */
906    acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
907    switch (acl_enabled) {
908    case 0:
909       pm_strcpy(jcr->acl_data, "");
910       jcr->acl_data_len = 0;
911       return bsub_exit_ok;
912    case -1:
913       berrno be;
914       Jmsg2(jcr, M_ERROR, 0, _("pathconf error on file \"%s\": ERR=%s\n"),
915          jcr->last_fname, be.bstrerror());
916       Dmsg2(100, "pathconf error file=%s ERR=%s\n",  
917          jcr->last_fname, be.bstrerror());
918       return bsub_exit_nok;
919    default:
920       break;
921    }
922
923    /*
924     * Get ACL info: don't bother allocating space if there is only a trivial ACL.
925     */
926    if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
927       Jmsg2(jcr, M_ERROR, 0, _("acl_get error on file \"%s\": ERR=%s\n"),
928          jcr->last_fname, acl_strerror(errno));
929       Dmsg2(100, "acl_get error file=%s ERR=%s\n",  
930          jcr->last_fname, acl_strerror(errno));
931       return bsub_exit_nok;
932    }
933
934    if (!aclp) {
935       /*
936        * The ACLs simply reflect the (already known) standard permissions
937        * So we don't send an ACL stream to the SD.
938        */
939       pm_strcpy(jcr->acl_data, "");
940       jcr->acl_data_len = 0;
941       return bsub_exit_ok;
942    }
943
944 #if defined(ACL_SID_FMT)
945    /*
946     * New format flag added in newer Solaris versions.
947     */
948    flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
949 #else
950    flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
951 #endif /* ACL_SID_FMT */
952
953    if ((acl_text = acl_totext(aclp, flags)) != NULL) {
954       jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
955       actuallyfree(acl_text);
956
957       switch (acl_type(aclp)) {
958       case ACLENT_T:
959          stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
960          break;
961       case ACE_T:
962          stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACE);
963          break;
964       default:
965          break;
966       }
967
968       acl_free(aclp);
969    }
970    return stream_status;
971 }
972
973 static bsub_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
974 {
975    acl_t *aclp;
976    int acl_enabled, error;
977
978    switch (stream) {
979    case STREAM_UNIX_ACCESS_ACL:
980    case STREAM_ACL_SOLARIS_ACLENT:
981    case STREAM_ACL_SOLARIS_ACE:
982       /*
983        * First make sure the filesystem supports acls.
984        */
985       acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
986       switch (acl_enabled) {
987       case 0:
988          Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
989             jcr->last_fname);
990          return bsub_exit_nok;
991       case -1:
992          berrno be;
993          Jmsg2(jcr, M_ERROR, 0, _("pathconf error on file \"%s\": ERR=%s\n"),
994             jcr->last_fname, be.bstrerror());
995          Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",  
996             jcr->acl_data, jcr->last_fname, be.bstrerror());
997          return bsub_exit_nok;
998       default:
999          /*
1000           * On a filesystem with ACL support make sure this particilar ACL type can be restored.
1001           */
1002          switch (stream) {
1003          case STREAM_ACL_SOLARIS_ACLENT:
1004             /*
1005              * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1006              */
1007             if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1008                Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without aclent acl support\n"),
1009                   jcr->last_fname);
1010                return bsub_exit_nok;
1011             }
1012             break;
1013          case STREAM_ACL_SOLARIS_ACE:
1014             /*
1015              * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1016              */
1017             if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1018                Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without ace acl support\n"),
1019                   jcr->last_fname);
1020                return bsub_exit_nok;
1021             }
1022             break;
1023          default:
1024             /*
1025              * Stream id which doesn't describe the type of acl which is encoded.
1026              */
1027             break;
1028          }
1029          break;
1030       }
1031
1032       if ((error = acl_fromtext(jcr->acl_data, &aclp)) != 0) {
1033          Jmsg2(jcr, M_ERROR, 0, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1034             jcr->last_fname, acl_strerror(error));
1035          Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",  
1036             jcr->acl_data, jcr->last_fname, acl_strerror(error));
1037          return bsub_exit_nok;
1038       }
1039
1040       /*
1041        * Validate that the conversion gave us the correct acl type.
1042        */
1043       switch (stream) {
1044       case STREAM_ACL_SOLARIS_ACLENT:
1045          if (acl_type(aclp) != ACLENT_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       case STREAM_ACL_SOLARIS_ACE:
1052          if (acl_type(aclp) != ACE_T) {
1053             Jmsg1(jcr, M_ERROR, 0, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1054                jcr->last_fname);
1055             return bsub_exit_nok;
1056          }
1057          break;
1058       default:
1059          /*
1060           * Stream id which doesn't describe the type of acl which is encoded.
1061           */
1062          break;
1063       }
1064
1065       /*
1066        * Restore the ACLs, but don't complain about links which really should
1067        * not have attributes, and the file it is linked to may not yet be restored.
1068        * This is only true for the old acl streams as in the new implementation we
1069        * don't save acls of symlinks (which cannot have acls anyhow)
1070        */
1071       if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
1072          Jmsg2(jcr, M_ERROR, 0, _("acl_set error on file \"%s\": ERR=%s\n"),
1073             jcr->last_fname, acl_strerror(error));
1074          Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",  
1075             jcr->acl_data, jcr->last_fname, acl_strerror(error));
1076
1077          acl_free(aclp);
1078          return bsub_exit_nok;
1079       }
1080
1081       acl_free(aclp);
1082       return bsub_exit_ok;
1083    default:
1084       return bsub_exit_nok;
1085    } /* end switch (stream) */
1086 }
1087
1088 #else /* HAVE_EXTENDED_ACL */
1089
1090 /*
1091  * Define the supported ACL streams for this OS
1092  */
1093 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
1094 static int os_default_acl_streams[1] = { -1 };
1095
1096 /*
1097  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1098  * There is no need to store those acls as we already store the stat bits too.
1099  */
1100 static bool acl_is_trivial(int count, aclent_t *entries)
1101 {
1102    int n;
1103    aclent_t *ace;
1104
1105    for (n = 0; n < count; n++) {
1106       ace = &entries[n];
1107
1108       if (!(ace->a_type == USER_OBJ ||
1109             ace->a_type == GROUP_OBJ ||
1110             ace->a_type == OTHER_OBJ ||
1111             ace->a_type == CLASS_OBJ))
1112         return false;
1113    }
1114    return true;
1115 }
1116
1117 /*
1118  * OS specific functions for handling different types of acl streams.
1119  */
1120 static bsub_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1121 {
1122    int n;
1123    aclent_t *acls;
1124    char *acl_text;
1125
1126    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
1127    if (n < MIN_ACL_ENTRIES)
1128       return bsub_exit_nok;
1129
1130    acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1131    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
1132       if (acl_is_trivial(n, acls)) {
1133          /*
1134           * The ACLs simply reflect the (already known) standard permissions
1135           * So we don't send an ACL stream to the SD.
1136           */
1137          free(acls);
1138          pm_strcpy(jcr->acl_data, "");
1139          jcr->acl_data_len = 0;
1140          return bsub_exit_ok;
1141       }
1142
1143       if ((acl_text = acltotext(acls, n)) != NULL) {
1144          jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
1145          actuallyfree(acl_text);
1146          free(acls);
1147          return send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1148       }
1149
1150       berrno be;
1151       Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
1152          jcr->last_fname, be.bstrerror());
1153       Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",  
1154          jcr->acl_data, jcr->last_fname, be.bstrerror());
1155    }
1156
1157    free(acls);
1158    return bsub_exit_nok;
1159 }
1160
1161 static bsub_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1162 {
1163    int n;
1164    aclent_t *acls;
1165
1166    acls = aclfromtext(jcr->acl_data, &n);
1167    if (!acls) {
1168       berrno be;
1169       Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
1170          jcr->last_fname, be.bstrerror());
1171       Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",  
1172          jcr->acl_data, jcr->last_fname, be.bstrerror());
1173       return bsub_exit_nok;
1174    }
1175
1176    /*
1177     * Restore the ACLs, but don't complain about links which really should
1178     * not have attributes, and the file it is linked to may not yet be restored.
1179     */
1180    if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
1181       switch (errno) {
1182       case ENOENT:
1183          actuallyfree(acls);
1184          return bsub_exit_nok;
1185       default:
1186          berrno be;
1187          Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
1188             jcr->last_fname, be.bstrerror());
1189          Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
1190             jcr->acl_data, jcr->last_fname, be.bstrerror());
1191          actuallyfree(acls);
1192          return bsub_exit_nok;
1193       }
1194    }
1195    actuallyfree(acls);
1196    return bsub_exit_ok;
1197 }
1198 #endif /* HAVE_EXTENDED_ACL */
1199
1200 /*
1201  * For this OS setup the build and parse function pointer to the OS specific functions.
1202  */
1203 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
1204 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
1205
1206 #endif /* HAVE_SUN_OS */
1207
1208 /*
1209  * Entry points when compiled with support for ACLs on a supported platform.
1210  */
1211
1212 /*
1213  * Read and send an ACL for the last encountered file.
1214  */
1215 bsub_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1216 {
1217    /*
1218     * Call the appropriate function.
1219     */
1220    if (os_build_acl_streams) {
1221       return (*os_build_acl_streams)(jcr, ff_pkt);
1222    }
1223    return bsub_exit_nok;
1224 }
1225
1226 bsub_exit_code parse_acl_streams(JCR *jcr, int stream)
1227 {
1228    switch (stream) {
1229    case STREAM_UNIX_ACCESS_ACL:
1230    case STREAM_UNIX_DEFAULT_ACL:
1231       /*
1232        * Handle legacy ACL streams.
1233        */
1234       if (os_parse_acl_streams) {
1235          return (*os_parse_acl_streams)(jcr, stream);
1236       }
1237       break;
1238    default:
1239       if (os_parse_acl_streams) {
1240          /*
1241           * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
1242           */
1243          for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1244             if (os_access_acl_streams[cnt] == stream) {
1245                return (*os_parse_acl_streams)(jcr, stream);
1246             }
1247          }
1248          /*
1249           * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
1250           */
1251          for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1252             if (os_default_acl_streams[cnt] == stream) {
1253                return (*os_parse_acl_streams)(jcr, stream);
1254             }
1255          }
1256       }
1257       break;
1258    }
1259    Qmsg2(jcr, M_WARNING, 0,
1260       _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
1261       jcr->last_fname, stream);
1262    return bsub_exit_nok;
1263 }
1264 #endif