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