]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
Merge branch 'master' into basejobv3
[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 bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
63 {
64    return bacl_exit_fatal;
65 }
66
67 bacl_exit_code parse_acl_stream(JCR *jcr, int stream)
68 {
69    return bacl_exit_fatal;
70 }
71 #else
72 /*
73  * Send an ACL stream to the SD.
74  */
75 static bacl_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 bacl_exit_ok;
81 #endif
82
83    /*
84     * Sanity check
85     */
86    if (jcr->acl_data_len <= 0)
87       return bacl_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 bacl_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 bacl_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 bacl_exit_fatal;
119    }
120
121    Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
122    return bacl_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 bacl_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 bacl_exit_error;
145 }
146
147 static bacl_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 bacl_exit_error;
151    }
152    return bacl_exit_ok;
153 }
154
155 /*
156  * For this OS setup the build and parse function pointer to the OS specific functions.
157  */
158 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = aix_build_acl_streams;
159 static bacl_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 bacl_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 bacl_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 bacl_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 bacl_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 bacl_exit_error;
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 bacl_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 bacl_exit_error;
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 bacl_exit_ok;
418 }
419
420 /*
421  * Generic wrapper around acl_set_file call.
422  */
423 static bacl_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 bacl_exit_ok;
436       }
437       switch (errno) {
438       case ENOENT:
439          return bacl_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 bacl_exit_error;
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 bacl_exit_error;
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 bacl_exit_error;
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 bacl_exit_ok;
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 bacl_exit_error;
489       }
490    }
491    acl_free(acl);
492    return bacl_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 bacl_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) == bacl_exit_fatal)
518       return bacl_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) == bacl_exit_fatal)
524       return bacl_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 bacl_exit_ok;
531 }
532
533 static bacl_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 bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
546 static bacl_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 bacl_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) == bacl_exit_fatal)
572       return bacl_exit_fatal;
573
574    if (jcr->acl_data_len > 0) {
575       if (send_acl_stream(jcr, os_access_acl_streams[0]) == bacl_exit_fatal)
576          return bacl_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) == bacl_exit_fatal)
584          return bacl_exit_fatal;
585       if (jcr->acl_data_len > 0) {
586          if (send_acl_stream(jcr, os_default_acl_streams[0]) == bacl_exit_fatal)
587             return bacl_exit_fatal;
588       }
589    }
590    return bacl_exit_ok;
591 }
592
593 static bacl_exit_code generic_parse_acl_streams(JCR *jcr, int stream)
594 {
595    unsigned 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 bacl_exit_error;
619 }
620
621 /*
622  * For this OSes setup the build and parse function pointer to the OS specific functions.
623  */
624 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
625 static bacl_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 bacl_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 bacl_exit_error;
642    if (jcr->acl_data_len > 0) {
643       if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
644          return bacl_exit_error;
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 bacl_exit_error;
652       if (jcr->acl_data_len > 0) {
653          if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
654             return bacl_exit_error;
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 bacl_exit_error;
664       if (jcr->acl_data_len > 0) {
665          if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
666             return bacl_exit_error;
667       }
668    }
669    return bacl_exit_ok;
670 }
671
672 static bacl_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 bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
689 static bacl_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 bacl_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 bacl_exit_ok;
750 #endif
751       case ENOENT:
752          pm_strcpy(jcr->acl_data, "");
753          jcr->acl_data_len = 0;
754          return bacl_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 bacl_exit_error;
764       }
765    }
766    if (n == 0) {
767       pm_strcpy(jcr->acl_data, "");
768       jcr->acl_data_len = 0;
769       return bacl_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 bacl_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 bacl_exit_error;
792    }
793    return bacl_exit_error;
794 }
795
796 static bacl_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 bacl_exit_error;
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 bacl_exit_error;
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 bacl_exit_ok;
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 bacl_exit_error;
834       }
835    }
836    return bacl_exit_ok;
837 }
838
839 /*
840  * For this OS setup the build and parse function pointer to the OS specific functions.
841  */
842 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
843 static bacl_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 bacl_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    bacl_exit_code stream_status = bacl_exit_error;
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 bacl_exit_ok;
909    case -1:
910       switch (errno) {
911       case ENOENT:
912          return bacl_exit_ok;
913       default:
914          Mmsg2(jcr->errmsg, _("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 bacl_exit_error;
919       }
920    default:
921       break;
922    }
923
924    /*
925     * Get ACL info: don't bother allocating space if there is only a trivial ACL.
926     */
927    if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
928       switch (errno) {
929       case ENOENT:
930          return bacl_exit_ok;
931       default:
932          Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"),
933                jcr->last_fname, acl_strerror(errno));
934          Dmsg2(100, "acl_get error file=%s ERR=%s\n",  
935                jcr->last_fname, acl_strerror(errno));
936          return bacl_exit_error;
937       }
938    }
939
940    if (!aclp) {
941       /*
942        * The ACLs simply reflect the (already known) standard permissions
943        * So we don't send an ACL stream to the SD.
944        */
945       pm_strcpy(jcr->acl_data, "");
946       jcr->acl_data_len = 0;
947       return bacl_exit_ok;
948    }
949
950 #if defined(ACL_SID_FMT)
951    /*
952     * New format flag added in newer Solaris versions.
953     */
954    flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
955 #else
956    flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
957 #endif /* ACL_SID_FMT */
958
959    if ((acl_text = acl_totext(aclp, flags)) != NULL) {
960       jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
961       actuallyfree(acl_text);
962
963       switch (acl_type(aclp)) {
964       case ACLENT_T:
965          stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
966          break;
967       case ACE_T:
968          stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACE);
969          break;
970       default:
971          break;
972       }
973
974       acl_free(aclp);
975    }
976    return stream_status;
977 }
978
979 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
980 {
981    acl_t *aclp;
982    int acl_enabled, error;
983    berrno be;
984
985    switch (stream) {
986    case STREAM_UNIX_ACCESS_ACL:
987    case STREAM_ACL_SOLARIS_ACLENT:
988    case STREAM_ACL_SOLARIS_ACE:
989       /*
990        * First make sure the filesystem supports acls.
991        */
992       acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
993       switch (acl_enabled) {
994       case 0:
995          Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
996                jcr->last_fname);
997          return bacl_exit_error;
998       case -1:
999          switch (errno) {
1000          case ENOENT:
1001             return bacl_exit_ok;
1002          default:
1003             Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
1004                   jcr->last_fname, be.bstrerror());
1005             Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",  
1006                   jcr->acl_data, jcr->last_fname, be.bstrerror());
1007             return bacl_exit_error;
1008          }
1009       default:
1010          /*
1011           * On a filesystem with ACL support make sure this particilar ACL type can be restored.
1012           */
1013          switch (stream) {
1014          case STREAM_ACL_SOLARIS_ACLENT:
1015             /*
1016              * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1017              */
1018             if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1019                Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without aclent acl support\n"),
1020                      jcr->last_fname);
1021                return bacl_exit_error;
1022             }
1023             break;
1024          case STREAM_ACL_SOLARIS_ACE:
1025             /*
1026              * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1027              */
1028             if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1029                Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without ace acl support\n"),
1030                      jcr->last_fname);
1031                return bacl_exit_error;
1032             }
1033             break;
1034          default:
1035             /*
1036              * Stream id which doesn't describe the type of acl which is encoded.
1037              */
1038             break;
1039          }
1040          break;
1041       }
1042
1043       if ((error = acl_fromtext(jcr->acl_data, &aclp)) != 0) {
1044          Mmsg2(jcr->errmsg, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1045                jcr->last_fname, acl_strerror(error));
1046          Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",  
1047                jcr->acl_data, jcr->last_fname, acl_strerror(error));
1048          return bacl_exit_error;
1049       }
1050
1051       /*
1052        * Validate that the conversion gave us the correct acl type.
1053        */
1054       switch (stream) {
1055       case STREAM_ACL_SOLARIS_ACLENT:
1056          if (acl_type(aclp) != ACLENT_T) {
1057             Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1058                   jcr->last_fname);
1059             return bacl_exit_error;
1060          }
1061          break;
1062       case STREAM_ACL_SOLARIS_ACE:
1063          if (acl_type(aclp) != ACE_T) {
1064             Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1065                   jcr->last_fname);
1066             return bacl_exit_error;
1067          }
1068          break;
1069       default:
1070          /*
1071           * Stream id which doesn't describe the type of acl which is encoded.
1072           */
1073          break;
1074       }
1075
1076       /*
1077        * Restore the ACLs, but don't complain about links which really should
1078        * not have attributes, and the file it is linked to may not yet be restored.
1079        * This is only true for the old acl streams as in the new implementation we
1080        * don't save acls of symlinks (which cannot have acls anyhow)
1081        */
1082       if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
1083          switch (errno) {
1084          case ENOENT:
1085             acl_free(aclp);
1086             return bacl_exit_ok;
1087          default:
1088             Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"),
1089                   jcr->last_fname, acl_strerror(error));
1090             Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",  
1091                   jcr->acl_data, jcr->last_fname, acl_strerror(error));
1092             acl_free(aclp);
1093             return bacl_exit_error;
1094          }
1095       }
1096
1097       acl_free(aclp);
1098       return bacl_exit_ok;
1099    default:
1100       return bacl_exit_error;
1101    } /* end switch (stream) */
1102 }
1103
1104 #else /* HAVE_EXTENDED_ACL */
1105
1106 /*
1107  * Define the supported ACL streams for this OS
1108  */
1109 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
1110 static int os_default_acl_streams[1] = { -1 };
1111
1112 /*
1113  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1114  * There is no need to store those acls as we already store the stat bits too.
1115  */
1116 static bool acl_is_trivial(int count, aclent_t *entries)
1117 {
1118    int n;
1119    aclent_t *ace;
1120
1121    for (n = 0; n < count; n++) {
1122       ace = &entries[n];
1123
1124       if (!(ace->a_type == USER_OBJ ||
1125             ace->a_type == GROUP_OBJ ||
1126             ace->a_type == OTHER_OBJ ||
1127             ace->a_type == CLASS_OBJ))
1128         return false;
1129    }
1130    return true;
1131 }
1132
1133 /*
1134  * OS specific functions for handling different types of acl streams.
1135  */
1136 static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1137 {
1138    int n;
1139    aclent_t *acls;
1140    char *acl_text;
1141    berrno be;
1142
1143    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
1144    if (n < MIN_ACL_ENTRIES)
1145       return bacl_exit_error;
1146
1147    acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1148    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
1149       if (acl_is_trivial(n, acls)) {
1150          /*
1151           * The ACLs simply reflect the (already known) standard permissions
1152           * So we don't send an ACL stream to the SD.
1153           */
1154          free(acls);
1155          pm_strcpy(jcr->acl_data, "");
1156          jcr->acl_data_len = 0;
1157          return bacl_exit_ok;
1158       }
1159
1160       if ((acl_text = acltotext(acls, n)) != NULL) {
1161          jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
1162          actuallyfree(acl_text);
1163          free(acls);
1164          return send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1165       }
1166
1167       Mmsg2(jcr->errmsg, _("acltotext error on file \"%s\": ERR=%s\n"),
1168             jcr->last_fname, be.bstrerror());
1169       Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",  
1170             jcr->acl_data, jcr->last_fname, be.bstrerror());
1171    }
1172
1173    free(acls);
1174    return bacl_exit_error;
1175 }
1176
1177 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1178 {
1179    int n;
1180    aclent_t *acls;
1181    berrno be;
1182
1183    acls = aclfromtext(jcr->acl_data, &n);
1184    if (!acls) {
1185       Mmsg2(jcr->errmsg, _("aclfromtext error on file \"%s\": ERR=%s\n"),
1186             jcr->last_fname, be.bstrerror());
1187       Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",  
1188             jcr->acl_data, jcr->last_fname, be.bstrerror());
1189       return bacl_exit_error;
1190    }
1191
1192    /*
1193     * Restore the ACLs, but don't complain about links which really should
1194     * not have attributes, and the file it is linked to may not yet be restored.
1195     */
1196    if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
1197       switch (errno) {
1198       case ENOENT:
1199          actuallyfree(acls);
1200          return bacl_exit_ok;
1201       default:
1202          Mmsg2(jcr->errmsg, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
1203                jcr->last_fname, be.bstrerror());
1204          Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
1205                jcr->acl_data, jcr->last_fname, be.bstrerror());
1206          actuallyfree(acls);
1207          return bacl_exit_error;
1208       }
1209    }
1210    actuallyfree(acls);
1211    return bacl_exit_ok;
1212 }
1213 #endif /* HAVE_EXTENDED_ACL */
1214
1215 /*
1216  * For this OS setup the build and parse function pointer to the OS specific functions.
1217  */
1218 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
1219 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
1220
1221 #endif /* HAVE_SUN_OS */
1222
1223 /*
1224  * Entry points when compiled with support for ACLs on a supported platform.
1225  */
1226
1227 /*
1228  * Read and send an ACL for the last encountered file.
1229  */
1230 bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1231 {
1232    /*
1233     * Call the appropriate function.
1234     */
1235    if (os_build_acl_streams) {
1236       return (*os_build_acl_streams)(jcr, ff_pkt);
1237    }
1238    return bacl_exit_error;
1239 }
1240
1241 bacl_exit_code parse_acl_streams(JCR *jcr, int stream)
1242 {
1243    unsigned int cnt;
1244
1245    switch (stream) {
1246    case STREAM_UNIX_ACCESS_ACL:
1247    case STREAM_UNIX_DEFAULT_ACL:
1248       /*
1249        * Handle legacy ACL streams.
1250        */
1251       if (os_parse_acl_streams) {
1252          return (*os_parse_acl_streams)(jcr, stream);
1253       }
1254       break;
1255    default:
1256       if (os_parse_acl_streams) {
1257          /*
1258           * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
1259           */
1260          for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1261             if (os_access_acl_streams[cnt] == stream) {
1262                return (*os_parse_acl_streams)(jcr, stream);
1263             }
1264          }
1265          /*
1266           * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
1267           */
1268          for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1269             if (os_default_acl_streams[cnt] == stream) {
1270                return (*os_parse_acl_streams)(jcr, stream);
1271             }
1272          }
1273       }
1274       break;
1275    }
1276    Qmsg2(jcr, M_WARNING, 0,
1277       _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
1278       jcr->last_fname, stream);
1279    return bacl_exit_error;
1280 }
1281 #endif