]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
As it seems Linux doesn't comply to its own manpages.
[bacula/bacula] / bacula / src / filed / acl.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2010 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 three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    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  * Currently we support the following OSes:
32  *   - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface)
33  *   - Darwin
34  *   - FreeBSD (POSIX and NFSv4/ZFS acls)
35  *   - HPUX
36  *   - IRIX
37  *   - Linux
38  *   - Solaris (POSIX and NFSv4/ZFS acls)
39  *   - Tru64
40  *
41  * We handle two different types of ACLs: access and default ACLS.
42  * On most systems that support default ACLs they only apply to directories.
43  *
44  * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs
45  * independently, while others (eg. Solaris) provide both in one call.
46  *
47  * The Filed saves ACLs in their native format and uses different streams
48  * for all different platforms. Currently we only allow ACLs to be restored
49  * which were saved in the native format of the platform they are extracted
50  * on. Later on we might add conversion functions for mapping from one
51  * platform to an other or allow restores of systems that use the same
52  * native format.
53  *
54  * Its also interesting to see what the exact format of acl text is on
55  * certain platforms and if they use they same encoding we might allow
56  * different platform streams to be decoded on an other similar platform.
57  *
58  *   Original written by Preben 'Peppe' Guldberg, December MMIV
59  *   Major rewrite by Marco van Wieringen, November MMVIII
60  */
61   
62 #include "bacula.h"
63 #include "filed.h"
64   
65 #if !defined(HAVE_ACL)
66 /**
67  * Entry points when compiled without support for ACLs or on an unsupported platform.
68  */
69 bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
70 {
71    return bacl_exit_fatal;
72 }
73
74 bacl_exit_code parse_acl_streams(JCR *jcr, int stream)
75 {
76    return bacl_exit_fatal;
77 }
78 #else
79 /**
80  * Send an ACL stream to the SD.
81  */
82 static bacl_exit_code send_acl_stream(JCR *jcr, int stream)
83 {
84    BSOCK *sd = jcr->store_bsock;
85    POOLMEM *msgsave;
86 #ifdef FD_NO_SEND_TEST
87    return bacl_exit_ok;
88 #endif
89
90    /**
91     * Sanity check
92     */
93    if (jcr->acl_data->content_length <= 0) {
94       return bacl_exit_ok;
95    }
96
97    /**
98     * Send header
99     */
100    if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
101       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
102             sd->bstrerror());
103       return bacl_exit_fatal;
104    }
105
106    /**
107     * Send the buffer to the storage deamon
108     */
109    Dmsg1(400, "Backing up ACL <%s>\n", jcr->acl_data->content);
110    msgsave = sd->msg;
111    sd->msg = jcr->acl_data->content;
112    sd->msglen = jcr->acl_data->content_length + 1;
113    if (!sd->send()) {
114       sd->msg = msgsave;
115       sd->msglen = 0;
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    jcr->JobBytes += sd->msglen;
122    sd->msg = msgsave;
123    if (!sd->signal(BNET_EOD)) {
124       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
125             sd->bstrerror());
126       return bacl_exit_fatal;
127    }
128
129    Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
130    return bacl_exit_ok;
131 }
132
133 /**
134  * First the native ACLs.
135  */
136 #if defined(HAVE_ACL)
137 #if defined(HAVE_AIX_OS)
138
139 #if defined(HAVE_EXTENDED_ACL)
140
141 #include <sys/access.h>
142 #include <sys/acl.h>
143
144 static bool acl_is_trivial(struct acl *acl)
145 {
146    return (acl_last(acl) != acl->acl_ext ? false : true);
147 }
148
149 static bool acl_nfs4_is_trivial(nfs4_acl_int_t *acl)
150 {
151    return (acl->aclEntryN > 0 ? false : true);
152 }
153
154 /**
155  * Define the supported ACL streams for this OS
156  */
157 static int os_access_acl_streams[3] = { STREAM_ACL_AIX_TEXT, STREAM_ACL_AIX_AIXC, STREAM_ACL_AIX_NFS4 };
158 static int os_default_acl_streams[1] = { -1 };
159
160 static bacl_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
161 {
162    berrno be;
163    mode_t mode;
164    acl_type_t type;
165    size_t aclsize, acltxtsize;
166    bacl_exit_code retval = bacl_exit_error;
167    POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE);
168
169    /**
170     * First see how big the buffers should be.
171     */
172    type.u64 = ACL_ANY;
173    if (aclx_get(jcr->last_fname, GET_ACLINFO_ONLY, &type, NULL, &aclsize, NULL) < 0) {
174       switch (errno) {
175       case ENOENT:
176          retval = bacl_exit_ok;
177          goto bail_out;
178       default:
179          Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"),
180                jcr->last_fname, be.bstrerror());
181          Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
182                jcr->last_fname, be.bstrerror());
183          goto bail_out;
184       }
185    }
186
187    /**
188     * Make sure the buffers are big enough.
189     */
190    aclbuf = check_pool_memory_size(aclbuf, aclsize + 1);
191
192    /**
193     * Retrieve the ACL info.
194     */
195    if (aclx_get(jcr->last_fname, 0, &type, aclbuf, &aclsize, &mode) < 0) {
196       switch (errno) {
197       case ENOENT:
198          retval = bacl_exit_ok;
199          goto bail_out;
200       default:
201          Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"),
202                jcr->last_fname, be.bstrerror());
203          Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
204                jcr->last_fname, be.bstrerror());
205          goto bail_out;
206       }
207    }
208
209    /**
210     * See if the acl is non trivial.
211     */
212    switch (type.u64) {
213    case ACL_AIXC:
214       if (acl_is_trivial((struct acl *)aclbuf)) {
215          retval = bacl_exit_ok;
216          goto bail_out;
217       }
218       break;
219    case ACL_NFS4:
220       if (acl_nfs4_is_trivial((nfs4_acl_int_t *)aclbuf)) {
221          retval = bacl_exit_ok;
222          goto bail_out;
223       }
224       break;
225    default:
226       Mmsg2(jcr->errmsg, _("Unknown acl type encountered on file \"%s\": %ld\n"),
227             jcr->last_fname, type.u64);
228       Dmsg2(100, "Unknown acl type encountered on file \"%s\": %ld\n",
229             jcr->last_fname, type.u64);
230       goto bail_out;
231    }
232
233    /**
234     * We have a non-trivial acl lets convert it into some ASCII form.
235     */
236    acltxtsize = sizeof_pool_memory(jcr->acl_data->content);
237    if (aclx_printStr(jcr->acl_data->content, &acltxtsize, aclbuf,
238                      aclsize, type, jcr->last_fname, 0) < 0) {
239       switch (errno) {
240       case ENOSPC:
241          /**
242           * Our buffer is not big enough, acltxtsize should be updated with the value
243           * the aclx_printStr really need. So we increase the buffer and try again.
244           */
245          jcr->acl_data->content = check_pool_memory_size(jcr->acl_data->content, acltxtsize + 1);
246          if (aclx_printStr(jcr->acl_data->content, &acltxtsize, aclbuf,
247                            aclsize, type, jcr->last_fname, 0) < 0) {
248             Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"),
249                   jcr->last_fname);
250             Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
251                   jcr->last_fname, type.u64);
252             goto bail_out;
253          }
254          break;
255       default:
256          Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"),
257                jcr->last_fname);
258          Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
259                jcr->last_fname, type.u64);
260          goto bail_out;
261       }
262    }
263
264    jcr->acl_data->content_length = strlen(jcr->acl_data->content) + 1;
265    switch (type.u64) {
266    case ACL_AIXC:
267       retval = send_acl_stream(jcr, STREAM_ACL_AIX_AIXC);
268    case ACL_NFS4:
269       retval = send_acl_stream(jcr, STREAM_ACL_AIX_NFS4);
270    }
271
272 bail_out:
273    free_pool_memory(aclbuf);
274
275    return retval;
276 }
277
278 static bacl_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
279 {
280    int cnt;
281    berrno be;
282    acl_type_t type;
283    size_t aclsize;
284    bacl_exit_code retval = bacl_exit_error;
285    POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE);
286
287    switch (stream) {
288    case STREAM_ACL_AIX_TEXT:
289       /**
290        * Handle the old stream using the old system call for now.
291        */
292       if (acl_put(jcr->last_fname, jcr->acl_data->content, 0) != 0) {
293          retval = bacl_exit_error;
294          goto bail_out;
295       }
296       retval = bacl_exit_ok;
297       goto bail_out;
298    case STREAM_ACL_AIX_AIXC:
299       type.u64 = ACL_AIXC;
300       break;
301    case STREAM_ACL_AIX_NFS4:
302       type.u64 = ACL_NFS4;
303       break;
304    default:
305       goto bail_out;
306    } /* end switch (stream) */
307
308    /**
309     * Set the acl buffer to an initial size. For now we set it
310     * to the same size as the ASCII representation.
311     */
312    aclbuf = check_pool_memory_size(aclbuf, jcr->acl_data->content_length);
313    aclsize = jcr->acl_data->content_length;
314    if (aclx_scanStr(jcr->acl_data->content, aclbuf, &aclsize, type) < 0) {
315       switch (errno) {
316       case ENOSPC:
317          /**
318           * The buffer isn't big enough. The man page doesn't say that aclsize
319           * is updated to the needed size as what is done with aclx_printStr.
320           * So for now we try to increase the buffer a maximum of 3 times
321           * and retry the conversion.
322           */
323          for (cnt = 0; cnt < 3; cnt++) {
324             aclsize = 2 * aclsize;
325             aclbuf = check_pool_memory_size(aclbuf, aclsize);
326
327             if (aclx_scanStr(jcr->acl_data->content, aclbuf, &aclsize, type) == 0) {
328                break;
329             }
330
331             /**
332              * See why we failed this time, ENOSPC retry if max retries not met,
333              * otherwise abort.
334              */
335             switch (errno) {
336             case ENOSPC:
337                if (cnt < 3) {
338                   continue;
339                }
340                /* FALL THROUGH */
341             default:
342                Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
343                      jcr->last_fname, be.bstrerror());
344                Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
345                      jcr->last_fname, be.bstrerror());
346                goto bail_out;
347             }
348          }
349          break;
350       default:
351          Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
352                jcr->last_fname, be.bstrerror());
353          Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
354                jcr->last_fname, be.bstrerror());
355       }
356    }
357
358    if (aclx_put(jcr->last_fname, SET_ACL, type, aclbuf, aclsize, 0) < 0) {
359       switch (errno) {
360       case ENOENT:
361          retval = bacl_exit_ok;
362          goto bail_out;
363       default:
364          Mmsg2(jcr->errmsg, _("aclx_put error on file \"%s\": ERR=%s\n"),
365                jcr->last_fname, be.bstrerror());
366          Dmsg2(100, "aclx_put error file=%s ERR=%s\n",
367                jcr->last_fname, be.bstrerror());
368          goto bail_out;
369       }
370    }
371
372    retval = bacl_exit_ok;
373
374 bail_out:
375    free_pool_memory(aclbuf);
376
377    return retval;
378 }
379
380 #else /* HAVE_EXTENDED_ACL */
381
382 #include <sys/access.h>
383
384 /**
385  * Define the supported ACL streams for this OS
386  */
387 static int os_access_acl_streams[1] = { STREAM_ACL_AIX_TEXT };
388 static int os_default_acl_streams[1] = { -1 };
389
390 static bacl_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
391 {
392    char *acl_text;
393
394    if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
395       jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
396       actuallyfree(acl_text);
397       return send_acl_stream(jcr, STREAM_ACL_AIX_TEXT);
398    }
399    return bacl_exit_error;
400 }
401
402 static bacl_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
403 {
404    if (acl_put(jcr->last_fname, jcr->acl_data->content, 0) != 0) {
405       return bacl_exit_error;
406    }
407    return bacl_exit_ok;
408 }
409 #endif /* HAVE_EXTENDED_ACL */
410
411 /**
412  * For this OS setup the build and parse function pointer to the OS specific functions.
413  */
414 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = aix_build_acl_streams;
415 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = aix_parse_acl_streams;
416
417 #elif defined(HAVE_DARWIN_OS) || \
418       defined(HAVE_FREEBSD_OS) || \
419       defined(HAVE_IRIX_OS) || \
420       defined(HAVE_OSF1_OS) || \
421       defined(HAVE_LINUX_OS)
422
423 #include <sys/types.h>
424
425 #ifdef HAVE_SYS_ACL_H
426 #include <sys/acl.h>
427 #else
428 #error "configure failed to detect availability of sys/acl.h"
429 #endif
430
431 /**
432  * On IRIX we can get shortened ACLs
433  */
434 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
435 #define acl_to_text(acl,len)     acl_to_short_text((acl), (len))
436 #endif
437
438 /**
439  * On Linux we can get numeric and/or shorted ACLs
440  */
441 #if defined(HAVE_LINUX_OS)
442 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
443 #define BACL_ALTERNATE_TEXT            (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
444 #elif defined(BACL_WANT_SHORT_ACLS)
445 #define BACL_ALTERNATE_TEXT            TEXT_ABBREVIATE
446 #elif defined(BACL_WANT_NUMERIC_IDS)
447 #define BACL_ALTERNATE_TEXT            TEXT_NUMERIC_IDS
448 #endif
449 #ifdef BACL_ALTERNATE_TEXT
450 #include <acl/libacl.h>
451 #define acl_to_text(acl,len)     (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
452 #endif
453 #endif
454
455 /**
456  * On FreeBSD we can get numeric ACLs
457  */
458 #if defined(HAVE_FREEBSD_OS)
459 #if defined(BACL_WANT_NUMERIC_IDS)
460 #define BACL_ALTERNATE_TEXT            ACL_TEXT_NUMERIC_IDS
461 #endif
462 #ifdef BACL_ALTERNATE_TEXT
463 #define acl_to_text(acl,len)     (acl_to_text_np((acl), (len), BACL_ALTERNATE_TEXT))
464 #endif
465 #endif
466
467 /**
468  * Some generic functions used by multiple OSes.
469  */
470 static acl_type_t bac_to_os_acltype(bacl_type acltype)
471 {
472    acl_type_t ostype;
473
474    switch (acltype) {
475    case BACL_TYPE_ACCESS:
476       ostype = ACL_TYPE_ACCESS;
477       break;
478    case BACL_TYPE_DEFAULT:
479       ostype = ACL_TYPE_DEFAULT;
480       break;
481 #ifdef HAVE_ACL_TYPE_NFS4
482       /**
483        * FreeBSD has an additional acl type named ACL_TYPE_NFS4.
484        */
485    case BACL_TYPE_NFS4:
486       ostype = ACL_TYPE_NFS4;
487       break;
488 #endif
489 #ifdef HAVE_ACL_TYPE_DEFAULT_DIR
490    case BACL_TYPE_DEFAULT_DIR:
491       /**
492        * TRU64 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
493        */
494       ostype = ACL_TYPE_DEFAULT_DIR;
495       break;
496 #endif
497 #ifdef HAVE_ACL_TYPE_EXTENDED
498    case BACL_TYPE_EXTENDED:
499       /**
500        * MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
501        */
502       ostype = ACL_TYPE_EXTENDED;
503       break;
504 #endif
505    default:
506       /**
507        * This should never happen, as the per OS version function only tries acl
508        * types supported on a certain platform.
509        */
510       ostype = (acl_type_t)ACL_TYPE_NONE;
511       break;
512    }
513    return ostype;
514 }
515
516 static int acl_count_entries(acl_t acl)
517 {
518    int count = 0;
519 #if defined(HAVE_FREEBSD_OS) || \
520     defined(HAVE_LINUX_OS)
521    acl_entry_t ace;
522    int entry_available;
523
524    entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
525    while (entry_available == 1) {
526       count++;
527       entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
528    }
529 #elif defined(HAVE_IRIX_OS)
530    count = acl->acl_cnt;
531 #elif defined(HAVE_OSF1_OS)
532    count = acl->acl_num;
533 #elif defined(HAVE_DARWIN_OS)
534    acl_entry_t ace;
535    int entry_available;
536
537    entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
538    while (entry_available == 0) {
539       count++;
540       entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
541    }
542 #endif
543    return count;
544 }
545
546 #if !defined(HAVE_DARWIN_OS)
547 /**
548  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
549  * There is no need to store those acls as we already store the stat bits too.
550  */
551 static bool acl_is_trivial(acl_t acl)
552 {
553   /**
554    * acl is trivial if it has only the following entries:
555    * "user::",
556    * "group::",
557    * "other::"
558    */
559    acl_entry_t ace;
560    acl_tag_t tag;
561 #if defined(HAVE_FREEBSD_OS) || \
562     defined(HAVE_LINUX_OS)
563    int entry_available;
564
565    entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
566    while (entry_available == 1) {
567       /**
568        * Get the tag type of this acl entry.
569        * If we fail to get the tagtype we call the acl non-trivial.
570        */
571       if (acl_get_tag_type(ace, &tag) < 0)
572          return true;
573       /**
574        * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
575        */
576       if (tag != ACL_USER_OBJ &&
577           tag != ACL_GROUP_OBJ &&
578           tag != ACL_OTHER)
579          return false;
580       entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
581    }
582    return true;
583 #elif defined(HAVE_IRIX_OS)
584    int n;
585
586    for (n = 0; n < acl->acl_cnt; n++) {
587       ace = &acl->acl_entry[n];
588       tag = ace->ae_tag;
589
590       /**
591        * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
592        */
593       if (tag != ACL_USER_OBJ &&
594           tag != ACL_GROUP_OBJ &&
595           tag != ACL_OTHER_OBJ)
596          return false;
597    }
598    return true;
599 #elif defined(HAVE_OSF1_OS)
600    int count;
601
602    ace = acl->acl_first;
603    count = acl->acl_num;
604
605    while (count > 0) {
606       tag = ace->entry->acl_type;
607       /**
608        * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
609        */
610       if (tag != ACL_USER_OBJ &&
611           tag != ACL_GROUP_OBJ &&
612           tag != ACL_OTHER)
613          return false;
614       /**
615        * On Tru64, perm can also contain non-standard bits such as
616        * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
617        */
618       if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)))
619          return false;
620       ace = ace->next;
621       count--;
622    }
623    return true;
624 #endif
625 }
626 #endif
627
628 /**
629  * Generic wrapper around acl_get_file call.
630  */
631 static bacl_exit_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype)
632 {
633    acl_t acl;
634    acl_type_t ostype;
635    char *acl_text;
636    berrno be;
637    bacl_exit_code retval = bacl_exit_ok;
638
639    ostype = bac_to_os_acltype(acltype);
640    acl = acl_get_file(jcr->last_fname, ostype);
641    if (acl) {
642       /**
643        * From observation, IRIX's acl_get_file() seems to return a
644        * non-NULL acl with a count field of -1 when a file has no ACL
645        * defined, while IRIX's acl_to_text() returns NULL when presented
646        * with such an ACL. 
647        *
648        * For all other implmentations we check if there are more then
649        * zero entries in the acl returned.
650        */
651       if (acl_count_entries(acl) <= 0) {
652          goto bail_out;
653       }
654
655       /**
656        * Make sure this is not just a trivial ACL.
657        */
658 #if !defined(HAVE_DARWIN_OS)
659       if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
660          /**
661           * The ACLs simply reflect the (already known) standard permissions
662           * So we don't send an ACL stream to the SD.
663           */
664          goto bail_out;
665       }
666 #endif
667 #if defined(HAVE_FREEBSD_OS) && defined(_PC_ACL_NFS4)
668       if (acltype == BACL_TYPE_NFS4) {
669          int trivial;
670          if (acl_is_trivial_np(acl, &trivial) == 0) {
671             if (trivial == 1) {
672                /**
673                 * The ACLs simply reflect the (already known) standard permissions
674                 * So we don't send an ACL stream to the SD.
675                 */
676                goto bail_out;
677             }
678          }
679       }
680 #endif
681
682       /**
683        * Convert the internal acl representation into a text representation.
684        */
685       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
686          jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
687          acl_free(acl);
688          acl_free(acl_text);
689          return bacl_exit_ok;
690       }
691
692       Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"),
693             jcr->last_fname, be.bstrerror());
694       Dmsg2(100, "acl_to_text error file=%s ERR=%s\n",  
695             jcr->last_fname, be.bstrerror());
696
697       retval = bacl_exit_error;
698       goto bail_out;
699    } else {
700       /**
701        * Handle errors gracefully.
702        */
703       switch (errno) {
704 #if defined(BACL_ENOTSUP)
705       case BACL_ENOTSUP:
706          /**
707           * If the filesystem reports it doesn't support ACLs we clear the
708           * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
709           * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
710           * when we change from one filesystem to an other.
711           */
712          jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
713          goto bail_out;
714 #endif
715       case ENOENT:
716          goto bail_out;
717       default:
718          /* Some real error */
719          Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"),
720                jcr->last_fname, be.bstrerror());
721          Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",  
722                jcr->last_fname, be.bstrerror());
723
724          retval = bacl_exit_error;
725          goto bail_out;
726       }
727    }
728
729 bail_out:
730    if (acl) {
731       acl_free(acl);
732    }
733    pm_strcpy(jcr->acl_data->content, "");
734    jcr->acl_data->content_length = 0;
735    return retval;
736 }
737
738 /**
739  * Generic wrapper around acl_set_file call.
740  */
741 static bacl_exit_code generic_set_acl_on_os(JCR *jcr, bacl_type acltype)
742 {
743    acl_t acl;
744    acl_type_t ostype;
745    berrno be;
746
747    /**
748     * If we get empty default ACLs, clear ACLs now
749     */
750    ostype = bac_to_os_acltype(acltype);
751    if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_data->content) == 0) {
752       if (acl_delete_def_file(jcr->last_fname) == 0) {
753          return bacl_exit_ok;
754       }
755       switch (errno) {
756       case ENOENT:
757          return bacl_exit_ok;
758 #if defined(BACL_ENOTSUP)
759       case BACL_ENOTSUP:
760          Mmsg1(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"),
761                jcr->last_fname);
762          return bacl_exit_ok;
763 #endif
764       default:
765          Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
766                jcr->last_fname, be.bstrerror());
767          return bacl_exit_error;
768       }
769    }
770
771    acl = acl_from_text(jcr->acl_data->content);
772    if (acl == NULL) {
773       Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"),
774             jcr->last_fname, be.bstrerror());
775       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",  
776          jcr->acl_data->content, jcr->last_fname, be.bstrerror());
777       return bacl_exit_error;
778    }
779
780 #ifndef HAVE_FREEBSD_OS
781    /**
782     * FreeBSD always fails acl_valid() - at least on valid input...
783     * As it does the right thing, given valid input, just ignore acl_valid().
784     */
785    if (acl_valid(acl) != 0) {
786       Mmsg2(jcr->errmsg, _("acl_valid error on file \"%s\": ERR=%s\n"),
787             jcr->last_fname, be.bstrerror());
788       Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",  
789          jcr->acl_data->content, jcr->last_fname, be.bstrerror());
790       acl_free(acl);
791       return bacl_exit_error;
792    }
793 #endif
794
795    /**
796     * Restore the ACLs, but don't complain about links which really should
797     * not have attributes, and the file it is linked to may not yet be restored.
798     * This is only true for the old acl streams as in the new implementation we
799     * don't save acls of symlinks (which cannot have acls anyhow)
800     */
801    if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
802       switch (errno) {
803       case ENOENT:
804          acl_free(acl);
805          return bacl_exit_ok;
806 #if defined(BACL_ENOTSUP)
807       case BACL_ENOTSUP:
808          Mmsg1(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"),
809                jcr->last_fname);
810          Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n",
811                jcr->acl_data->content, jcr->last_fname);
812          acl_free(acl);
813          return bacl_exit_ok;
814 #endif
815       default:
816          Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"),
817                jcr->last_fname, be.bstrerror());
818          Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
819                jcr->acl_data->content, jcr->last_fname, be.bstrerror());
820          acl_free(acl);
821          return bacl_exit_error;
822       }
823    }
824    acl_free(acl);
825    return bacl_exit_ok;
826 }
827
828 /**
829  * OS specific functions for handling different types of acl streams.
830  */
831 #if defined(HAVE_DARWIN_OS)
832 /**
833  * Define the supported ACL streams for this OS
834  */
835 static int os_access_acl_streams[1] = { STREAM_ACL_DARWIN_ACCESS_ACL };
836 static int os_default_acl_streams[1] = { -1 };
837
838 static bacl_exit_code darwin_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
839 {
840 #if defined(HAVE_ACL_TYPE_EXTENDED)
841    /**
842     * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
843     * and acl_get_file (name, ACL_TYPE_DEFAULT)
844     * always return NULL / EINVAL.  There is no point in making
845     * these two useless calls.  The real ACL is retrieved through
846     * acl_get_file (name, ACL_TYPE_EXTENDED).
847     *
848     * Read access ACLs for files, dirs and links
849     */
850    if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bacl_exit_fatal)
851       return bacl_exit_fatal;
852 #else
853    /**
854     * Read access ACLs for files, dirs and links
855     */
856    if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
857       return bacl_exit_fatal;
858 #endif
859
860    if (jcr->acl_data->content_length > 0) {
861       return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS_ACL);
862    }
863    return bacl_exit_ok;
864 }
865
866 static bacl_exit_code darwin_parse_acl_streams(JCR *jcr, int stream)
867 {
868 #if defined(HAVE_ACL_TYPE_EXTENDED)
869       return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED);
870 #else
871       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
872 #endif
873 }
874
875 /**
876  * For this OS setup the build and parse function pointer to the OS specific functions.
877  */
878 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
879 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = darwin_parse_acl_streams;
880
881 #elif defined(HAVE_FREEBSD_OS)
882 /**
883  * Define the supported ACL streams for these OSes
884  */
885 static int os_access_acl_streams[2] = { STREAM_ACL_FREEBSD_ACCESS_ACL, STREAM_ACL_FREEBSD_NFS4_ACL };
886 static int os_default_acl_streams[1] = { STREAM_ACL_FREEBSD_DEFAULT_ACL };
887
888 static bacl_exit_code freebsd_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
889 {
890    int acl_enabled = 0;
891    bacl_type acltype = BACL_TYPE_NONE;
892    berrno be;
893
894 #if defined(_PC_ACL_NFS4)
895    /**
896     * See if filesystem supports NFS4 acls.
897     */
898    acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4);
899    switch (acl_enabled) {
900    case -1:
901       switch (errno) {
902       case ENOENT:
903          return bacl_exit_ok;
904       default:
905          Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
906                jcr->last_fname, be.bstrerror());
907          Dmsg2(100, "pathconf error file=%s ERR=%s\n",
908                jcr->last_fname, be.bstrerror());
909          return bacl_exit_error;
910       }
911    case 0:
912       break;
913    default:
914       acltype = BACL_TYPE_NFS4;
915       break;
916    }
917 #endif
918
919    if (acl_enabled == 0) {
920       /**
921        * See if filesystem supports POSIX acls.
922        */
923       acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
924       switch (acl_enabled) {
925       case -1:
926          switch (errno) {
927          case ENOENT:
928             return bacl_exit_ok;
929          default:
930             Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
931                   jcr->last_fname, be.bstrerror());
932             Dmsg2(100, "pathconf error file=%s ERR=%s\n",
933                   jcr->last_fname, be.bstrerror());
934             return bacl_exit_error;
935          }
936       case 0:
937          break;
938       default:
939          acltype = BACL_TYPE_ACCESS;
940          break;
941       }
942    }
943
944    /**
945     * If the filesystem reports it doesn't support ACLs we clear the
946     * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
947     * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
948     * when we change from one filesystem to an other.
949     */
950    if (acl_enabled == 0) {
951       jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
952       pm_strcpy(jcr->acl_data->content, "");
953       jcr->acl_data->content_length = 0;
954       return bacl_exit_ok;
955    }
956
957    /**
958     * Based on the supported ACLs retrieve and store them.
959     */
960    switch (acltype) {
961    case BACL_TYPE_NFS4:
962       /**
963        * Read NFS4 ACLs for files, dirs and links
964        */
965       if (generic_get_acl_from_os(jcr, BACL_TYPE_NFS4) == bacl_exit_fatal)
966          return bacl_exit_fatal;
967
968       if (jcr->acl_data->content_length > 0) {
969          if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_NFS4_ACL) == bacl_exit_fatal)
970             return bacl_exit_fatal;
971       }
972       break;
973    case BACL_TYPE_ACCESS:
974       /**
975        * Read access ACLs for files, dirs and links
976        */
977       if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
978          return bacl_exit_fatal;
979
980       if (jcr->acl_data->content_length > 0) {
981          if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_ACCESS_ACL) == bacl_exit_fatal)
982             return bacl_exit_fatal;
983       }
984
985       /**
986        * Directories can have default ACLs too
987        */
988       if (ff_pkt->type == FT_DIREND) {
989          if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
990             return bacl_exit_fatal;
991          if (jcr->acl_data->content_length > 0) {
992             if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_DEFAULT_ACL) == bacl_exit_fatal)
993                return bacl_exit_fatal;
994          }
995       }
996       break;
997    default:
998       break;
999    }
1000
1001    return bacl_exit_ok;
1002 }
1003
1004 static bacl_exit_code freebsd_parse_acl_streams(JCR *jcr, int stream)
1005 {
1006    int acl_enabled = 0;
1007    const char *acl_type_name;
1008    berrno be;
1009
1010    /**
1011     * First make sure the filesystem supports acls.
1012     */
1013    switch (stream) {
1014    case STREAM_UNIX_ACCESS_ACL:
1015    case STREAM_ACL_FREEBSD_ACCESS_ACL:
1016    case STREAM_UNIX_DEFAULT_ACL:
1017    case STREAM_ACL_FREEBSD_DEFAULT_ACL:
1018       acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
1019       acl_type_name = "POSIX";
1020       break;
1021    case STREAM_ACL_FREEBSD_NFS4_ACL:
1022 #if defined(_PC_ACL_NFS4)
1023       acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4);
1024 #endif
1025       acl_type_name = "NFS4";
1026       break;
1027    default:
1028       acl_type_name = "unknown";
1029       break;
1030    }
1031
1032    switch (acl_enabled) {
1033    case -1:
1034       switch (errno) {
1035       case ENOENT:
1036          return bacl_exit_ok;
1037       default:
1038          Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
1039                jcr->last_fname, be.bstrerror());
1040          Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
1041                jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1042          return bacl_exit_error;
1043       }
1044    case 0:
1045       Mmsg2(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"),
1046             jcr->last_fname, acl_type_name);
1047       return bacl_exit_error;
1048    default:
1049       break;
1050    }
1051
1052    /**
1053     * Restore the ACLs.
1054     */
1055    switch (stream) {
1056    case STREAM_UNIX_ACCESS_ACL:
1057    case STREAM_ACL_FREEBSD_ACCESS_ACL:
1058       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
1059    case STREAM_UNIX_DEFAULT_ACL:
1060    case STREAM_ACL_FREEBSD_DEFAULT_ACL:
1061       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
1062    case STREAM_ACL_FREEBSD_NFS4_ACL:
1063       return generic_set_acl_on_os(jcr, BACL_TYPE_NFS4);
1064    default:
1065       break;
1066    }
1067    return bacl_exit_error;
1068 }
1069
1070 /**
1071  * For this OSes setup the build and parse function pointer to the OS specific functions.
1072  */
1073 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = freebsd_build_acl_streams;
1074 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = freebsd_parse_acl_streams;
1075
1076 #elif defined(HAVE_IRIX_OS) || \
1077       defined(HAVE_LINUX_OS)
1078 /**
1079  * Define the supported ACL streams for these OSes
1080  */
1081 #if defined(HAVE_IRIX_OS)
1082 static int os_access_acl_streams[1] = { STREAM_ACL_IRIX_ACCESS_ACL };
1083 static int os_default_acl_streams[1] = { STREAM_ACL_IRIX_DEFAULT_ACL };
1084 #elif defined(HAVE_LINUX_OS)
1085 static int os_access_acl_streams[1] = { STREAM_ACL_LINUX_ACCESS_ACL };
1086 static int os_default_acl_streams[1] = { STREAM_ACL_LINUX_DEFAULT_ACL };
1087 #endif
1088
1089 static bacl_exit_code generic_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1090 {
1091    /**
1092     * Read access ACLs for files, dirs and links
1093     */
1094    if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
1095       return bacl_exit_fatal;
1096
1097    if (jcr->acl_data->content_length > 0) {
1098       if (send_acl_stream(jcr, os_access_acl_streams[0]) == bacl_exit_fatal)
1099          return bacl_exit_fatal;
1100    }
1101
1102    /**
1103     * Directories can have default ACLs too
1104     */
1105    if (ff_pkt->type == FT_DIREND) {
1106       if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
1107          return bacl_exit_fatal;
1108       if (jcr->acl_data->content_length > 0) {
1109          if (send_acl_stream(jcr, os_default_acl_streams[0]) == bacl_exit_fatal)
1110             return bacl_exit_fatal;
1111       }
1112    }
1113    return bacl_exit_ok;
1114 }
1115
1116 static bacl_exit_code generic_parse_acl_streams(JCR *jcr, int stream)
1117 {
1118    unsigned int cnt;
1119
1120    switch (stream) {
1121    case STREAM_UNIX_ACCESS_ACL:
1122       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
1123    case STREAM_UNIX_DEFAULT_ACL:
1124       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
1125    default:
1126       /**
1127        * See what type of acl it is.
1128        */
1129       for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1130          if (os_access_acl_streams[cnt] == stream) {
1131             return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
1132          }
1133       }
1134       for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1135          if (os_default_acl_streams[cnt] == stream) {
1136             return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
1137          }
1138       }
1139       break;
1140    }
1141    return bacl_exit_error;
1142 }
1143
1144 /**
1145  * For this OSes setup the build and parse function pointer to the OS specific functions.
1146  */
1147 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
1148 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = generic_parse_acl_streams;
1149
1150 #elif defined(HAVE_OSF1_OS)
1151
1152 /**
1153  * Define the supported ACL streams for this OS
1154  */
1155 static int os_access_acl_streams[1] = { STREAM_ACL_TRU64_ACCESS_ACL };
1156 static int os_default_acl_streams[2] = { STREAM_ACL_TRU64_DEFAULT_ACL, STREAM_ACL_TRU64_DEFAULT_DIR_ACL };
1157
1158 static bacl_exit_code tru64_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1159 {
1160    /**
1161     * Read access ACLs for files, dirs and links
1162     */
1163    if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS)) < 0)
1164       return bacl_exit_error;
1165    if (jcr->acl_data->content_length > 0) {
1166       if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
1167          return bacl_exit_error;
1168    }
1169    /**
1170     * Directories can have default ACLs too
1171     */
1172    if (ff_pkt->type == FT_DIREND) {
1173       if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT)) < 0)
1174          return bacl_exit_error;
1175       if (jcr->acl_data->content_length > 0) {
1176          if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
1177             return bacl_exit_error;
1178       }
1179       /**
1180        * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
1181        * This is an inherited acl for all subdirs.
1182        * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
1183        * Section 21.5 Default ACLs 
1184        */
1185       if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR)) < 0)
1186          return bacl_exit_error;
1187       if (jcr->acl_data->content_length > 0) {
1188          if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
1189             return bacl_exit_error;
1190       }
1191    }
1192    return bacl_exit_ok;
1193 }
1194
1195 static bacl_exit_code tru64_parse_acl_streams(JCR *jcr, int stream)
1196 {
1197    switch (stream) {
1198    case STREAM_UNIX_ACCESS_ACL:
1199    case STREAM_ACL_TRU64_ACCESS_ACL:
1200       return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
1201    case STREAM_UNIX_DEFAULT_ACL:
1202    case STREAM_ACL_TRU64_DEFAULT_ACL:
1203       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
1204    case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
1205       return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR);
1206 }
1207
1208 /**
1209  * For this OS setup the build and parse function pointer to the OS specific functions.
1210  */
1211 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
1212 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = tru64_parse_acl_streams;
1213
1214 #endif
1215
1216 #elif defined(HAVE_HPUX_OS)
1217 #ifdef HAVE_SYS_ACL_H
1218 #include <sys/acl.h>
1219 #else
1220 #error "configure failed to detect availability of sys/acl.h"
1221 #endif
1222
1223 #include <acllib.h>
1224
1225 /**
1226  * Define the supported ACL streams for this OS
1227  */
1228 static int os_access_acl_streams[1] = { STREAM_ACL_HPUX_ACL_ENTRY };
1229 static int os_default_acl_streams[1] = { -1 };
1230
1231 /**
1232  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1233  * There is no need to store those acls as we already store the stat bits too.
1234  */
1235 static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb)
1236 {
1237    int n;
1238    struct acl_entry ace
1239
1240    for (n = 0; n < count; n++) {
1241       ace = entries[n];
1242       /**
1243        * See if this acl just is the stat mode in acl form.
1244        */
1245       if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
1246             (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
1247             (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
1248          return false;
1249    }
1250    return true;
1251 }
1252
1253 /**
1254  * OS specific functions for handling different types of acl streams.
1255  */
1256 static bacl_exit_code hpux_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1257 {
1258    int n;
1259    struct acl_entry acls[NACLENTRIES];
1260    char *acl_text;
1261    berrno be;
1262
1263    if ((n = getacl(jcr->last_fname, 0, acls)) < 0) {
1264       switch (errno) {
1265 #if defined(BACL_ENOTSUP)
1266       case BACL_ENOTSUP:
1267          /**
1268           * Not supported, just pretend there is nothing to see
1269           *
1270           * If the filesystem reports it doesn't support ACLs we clear the
1271           * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1272           * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1273           * when we change from one filesystem to an other.
1274           */
1275          jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1276          pm_strcpy(jcr->acl_data->content, "");
1277          jcr->acl_data->content_length = 0;
1278          return bacl_exit_ok;
1279 #endif
1280       case ENOENT:
1281          pm_strcpy(jcr->acl_data->content, "");
1282          jcr->acl_data->content_length = 0;
1283          return bacl_exit_ok;
1284       default:
1285          Mmsg2(jcr->errmsg, _("getacl error on file \"%s\": ERR=%s\n"),
1286                jcr->last_fname, be.bstrerror());
1287          Dmsg2(100, "getacl error file=%s ERR=%s\n",  
1288                jcr->last_fname, be.bstrerror());
1289
1290          pm_strcpy(jcr->acl_data->content, "");
1291          jcr->acl_data->content_length = 0;
1292          return bacl_exit_error;
1293       }
1294    }
1295    if (n == 0) {
1296       pm_strcpy(jcr->acl_data->content, "");
1297       jcr->acl_data->content_length = 0;
1298       return bacl_exit_ok;
1299    }
1300    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
1301       if (acl_is_trivial(n, acls, ff_pkt->statp)) {
1302          /**
1303           * The ACLs simply reflect the (already known) standard permissions
1304           * So we don't send an ACL stream to the SD.
1305           */
1306          pm_strcpy(jcr->acl_data->content, "");
1307          jcr->acl_data->content_length = 0;
1308          return bacl_exit_ok;
1309       }
1310       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
1311          jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1312          actuallyfree(acl_text);
1313
1314          return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY);
1315       }
1316       Mmsg2(jcr->errmsg, _("acltostr error on file \"%s\": ERR=%s\n"),
1317             jcr->last_fname, be.bstrerror());
1318       Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",  
1319             jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1320       return bacl_exit_error;
1321    }
1322    return bacl_exit_error;
1323 }
1324
1325 static bacl_exit_code hpux_parse_acl_streams(JCR *jcr, int stream)
1326 {
1327    int n, stat;
1328    struct acl_entry acls[NACLENTRIES];
1329    berrno be;
1330
1331    n = strtoacl(jcr->acl_data->content, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
1332    if (n <= 0) {
1333       Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"),
1334             jcr->last_fname, be.bstrerror());
1335       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
1336             jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1337       return bacl_exit_error;
1338    }
1339    if (strtoacl(jcr->acl_data->content, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
1340       Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"),
1341             jcr->last_fname, be.bstrerror());
1342       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
1343             jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1344
1345       return bacl_exit_error;
1346    }
1347    /**
1348     * Restore the ACLs, but don't complain about links which really should
1349     * not have attributes, and the file it is linked to may not yet be restored.
1350     * This is only true for the old acl streams as in the new implementation we
1351     * don't save acls of symlinks (which cannot have acls anyhow)
1352     */
1353    if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
1354       switch (errno) {
1355       case ENOENT:
1356          return bacl_exit_ok;
1357       default:
1358          Mmsg2(jcr->errmsg, _("setacl error on file \"%s\": ERR=%s\n"),
1359                jcr->last_fname, be.bstrerror());
1360          Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
1361                jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1362          return bacl_exit_error;
1363       }
1364    }
1365    return bacl_exit_ok;
1366 }
1367
1368 /**
1369  * For this OS setup the build and parse function pointer to the OS specific functions.
1370  */
1371 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
1372 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = hpux_parse_acl_streams;
1373
1374 #elif defined(HAVE_SUN_OS)
1375 #ifdef HAVE_SYS_ACL_H
1376 #include <sys/acl.h>
1377 #else
1378 #error "configure failed to detect availability of sys/acl.h"
1379 #endif
1380
1381 #if defined(HAVE_EXTENDED_ACL)
1382 /**
1383  * We define some internals of the Solaris acl libs here as those
1384  * are not exposed yet. Probably because they want us to see the
1385  * acls as opague data. But as we need to support different platforms
1386  * and versions of Solaris we need to expose some data to be able
1387  * to determine the type of acl used to stuff it into the correct
1388  * data stream. I know this is far from portable, but maybe the
1389  * proper interface is exposed later on and we can get ride of
1390  * this kludge. Newer versions of Solaris include sys/acl_impl.h
1391  * which has implementation details of acls, if thats included we
1392  * don't have to define it ourself.
1393  */
1394 #if !defined(_SYS_ACL_IMPL_H)
1395 typedef enum acl_type {
1396    ACLENT_T = 0,
1397    ACE_T = 1
1398 } acl_type_t;
1399 #endif
1400
1401 /**
1402  * Two external references to functions in the libsec library function not in current include files.
1403  */
1404 extern "C" {
1405 int acl_type(acl_t *);
1406 char *acl_strerror(int);
1407 }
1408
1409 /**
1410  * Define the supported ACL streams for this OS
1411  */
1412 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT, STREAM_ACL_SOLARIS_ACE };
1413 static int os_default_acl_streams[1] = { -1 };
1414
1415 /**
1416  * As the new libsec interface with acl_totext and acl_fromtext also handles
1417  * the old format from acltotext we can use the new functions even
1418  * for acls retrieved and stored in the database with older fd versions. If the
1419  * new interface is not defined (Solaris 9 and older we fall back to the old code)
1420  */
1421 static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1422 {
1423    int acl_enabled, flags;
1424    acl_t *aclp;
1425    char *acl_text;
1426    bacl_exit_code stream_status = bacl_exit_error;
1427    berrno be;
1428
1429    /**
1430     * See if filesystem supports acls.
1431     */
1432    acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
1433    switch (acl_enabled) {
1434    case 0:
1435       /**
1436        * If the filesystem reports it doesn't support ACLs we clear the
1437        * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1438        * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1439        * when we change from one filesystem to an other.
1440        */
1441       jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1442       pm_strcpy(jcr->acl_data->content, "");
1443       jcr->acl_data->content_length = 0;
1444       return bacl_exit_ok;
1445    case -1:
1446       switch (errno) {
1447       case ENOENT:
1448          return bacl_exit_ok;
1449       default:
1450          Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
1451                jcr->last_fname, be.bstrerror());
1452          Dmsg2(100, "pathconf error file=%s ERR=%s\n",  
1453                jcr->last_fname, be.bstrerror());
1454          return bacl_exit_error;
1455       }
1456    default:
1457       break;
1458    }
1459
1460    /**
1461     * Get ACL info: don't bother allocating space if there is only a trivial ACL.
1462     */
1463    if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
1464       switch (errno) {
1465       case ENOENT:
1466          return bacl_exit_ok;
1467       default:
1468          Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"),
1469                jcr->last_fname, acl_strerror(errno));
1470          Dmsg2(100, "acl_get error file=%s ERR=%s\n",  
1471                jcr->last_fname, acl_strerror(errno));
1472          return bacl_exit_error;
1473       }
1474    }
1475
1476    if (!aclp) {
1477       /**
1478        * The ACLs simply reflect the (already known) standard permissions
1479        * So we don't send an ACL stream to the SD.
1480        */
1481       pm_strcpy(jcr->acl_data->content, "");
1482       jcr->acl_data->content_length = 0;
1483       return bacl_exit_ok;
1484    }
1485
1486 #if defined(ACL_SID_FMT)
1487    /**
1488     * New format flag added in newer Solaris versions.
1489     */
1490    flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1491 #else
1492    flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1493 #endif /* ACL_SID_FMT */
1494
1495    if ((acl_text = acl_totext(aclp, flags)) != NULL) {
1496       jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1497       actuallyfree(acl_text);
1498
1499       switch (acl_type(aclp)) {
1500       case ACLENT_T:
1501          stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1502          break;
1503       case ACE_T:
1504          stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACE);
1505          break;
1506       default:
1507          break;
1508       }
1509
1510       acl_free(aclp);
1511    }
1512    return stream_status;
1513 }
1514
1515 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1516 {
1517    acl_t *aclp;
1518    int acl_enabled, error;
1519    berrno be;
1520
1521    switch (stream) {
1522    case STREAM_UNIX_ACCESS_ACL:
1523    case STREAM_ACL_SOLARIS_ACLENT:
1524    case STREAM_ACL_SOLARIS_ACE:
1525       /**
1526        * First make sure the filesystem supports acls.
1527        */
1528       acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
1529       switch (acl_enabled) {
1530       case 0:
1531          Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
1532                jcr->last_fname);
1533          return bacl_exit_error;
1534       case -1:
1535          switch (errno) {
1536          case ENOENT:
1537             return bacl_exit_ok;
1538          default:
1539             Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
1540                   jcr->last_fname, be.bstrerror());
1541             Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",  
1542                   jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1543             return bacl_exit_error;
1544          }
1545       default:
1546          /**
1547           * On a filesystem with ACL support make sure this particular ACL type can be restored.
1548           */
1549          switch (stream) {
1550          case STREAM_ACL_SOLARIS_ACLENT:
1551             /**
1552              * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1553              */
1554             if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1555                Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without aclent acl support\n"),
1556                      jcr->last_fname);
1557                return bacl_exit_error;
1558             }
1559             break;
1560          case STREAM_ACL_SOLARIS_ACE:
1561             /**
1562              * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1563              */
1564             if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1565                Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without ace acl support\n"),
1566                      jcr->last_fname);
1567                return bacl_exit_error;
1568             }
1569             break;
1570          default:
1571             /**
1572              * Stream id which doesn't describe the type of acl which is encoded.
1573              */
1574             break;
1575          }
1576          break;
1577       }
1578
1579       if ((error = acl_fromtext(jcr->acl_data->content, &aclp)) != 0) {
1580          Mmsg2(jcr->errmsg, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1581                jcr->last_fname, acl_strerror(error));
1582          Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",  
1583                jcr->acl_data->content, jcr->last_fname, acl_strerror(error));
1584          return bacl_exit_error;
1585       }
1586
1587       /**
1588        * Validate that the conversion gave us the correct acl type.
1589        */
1590       switch (stream) {
1591       case STREAM_ACL_SOLARIS_ACLENT:
1592          if (acl_type(aclp) != ACLENT_T) {
1593             Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1594                   jcr->last_fname);
1595             return bacl_exit_error;
1596          }
1597          break;
1598       case STREAM_ACL_SOLARIS_ACE:
1599          if (acl_type(aclp) != ACE_T) {
1600             Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1601                   jcr->last_fname);
1602             return bacl_exit_error;
1603          }
1604          break;
1605       default:
1606          /**
1607           * Stream id which doesn't describe the type of acl which is encoded.
1608           */
1609          break;
1610       }
1611
1612       /**
1613        * Restore the ACLs, but don't complain about links which really should
1614        * not have attributes, and the file it is linked to may not yet be restored.
1615        * This is only true for the old acl streams as in the new implementation we
1616        * don't save acls of symlinks (which cannot have acls anyhow)
1617        */
1618       if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
1619          switch (errno) {
1620          case ENOENT:
1621             acl_free(aclp);
1622             return bacl_exit_ok;
1623          default:
1624             Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"),
1625                   jcr->last_fname, acl_strerror(error));
1626             Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",  
1627                   jcr->acl_data->content, jcr->last_fname, acl_strerror(error));
1628             acl_free(aclp);
1629             return bacl_exit_error;
1630          }
1631       }
1632
1633       acl_free(aclp);
1634       return bacl_exit_ok;
1635    default:
1636       return bacl_exit_error;
1637    } /* end switch (stream) */
1638 }
1639
1640 #else /* HAVE_EXTENDED_ACL */
1641
1642 /**
1643  * Define the supported ACL streams for this OS
1644  */
1645 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
1646 static int os_default_acl_streams[1] = { -1 };
1647
1648 /**
1649  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1650  * There is no need to store those acls as we already store the stat bits too.
1651  */
1652 static bool acl_is_trivial(int count, aclent_t *entries)
1653 {
1654    int n;
1655    aclent_t *ace;
1656
1657    for (n = 0; n < count; n++) {
1658       ace = &entries[n];
1659
1660       if (!(ace->a_type == USER_OBJ ||
1661             ace->a_type == GROUP_OBJ ||
1662             ace->a_type == OTHER_OBJ ||
1663             ace->a_type == CLASS_OBJ))
1664         return false;
1665    }
1666    return true;
1667 }
1668
1669 /**
1670  * OS specific functions for handling different types of acl streams.
1671  */
1672 static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1673 {
1674    int n;
1675    aclent_t *acls;
1676    char *acl_text;
1677    berrno be;
1678
1679    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
1680    if (n < MIN_ACL_ENTRIES)
1681       return bacl_exit_error;
1682
1683    acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1684    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
1685       if (acl_is_trivial(n, acls)) {
1686          /**
1687           * The ACLs simply reflect the (already known) standard permissions
1688           * So we don't send an ACL stream to the SD.
1689           */
1690          free(acls);
1691          pm_strcpy(jcr->acl_data->content, "");
1692          jcr->acl_data->content_length = 0;
1693          return bacl_exit_ok;
1694       }
1695
1696       if ((acl_text = acltotext(acls, n)) != NULL) {
1697          jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1698          actuallyfree(acl_text);
1699          free(acls);
1700          return send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1701       }
1702
1703       Mmsg2(jcr->errmsg, _("acltotext error on file \"%s\": ERR=%s\n"),
1704             jcr->last_fname, be.bstrerror());
1705       Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",  
1706             jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1707    }
1708
1709    free(acls);
1710    return bacl_exit_error;
1711 }
1712
1713 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1714 {
1715    int n;
1716    aclent_t *acls;
1717    berrno be;
1718
1719    acls = aclfromtext(jcr->acl_data->content, &n);
1720    if (!acls) {
1721       Mmsg2(jcr->errmsg, _("aclfromtext error on file \"%s\": ERR=%s\n"),
1722             jcr->last_fname, be.bstrerror());
1723       Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",  
1724             jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1725       return bacl_exit_error;
1726    }
1727
1728    /**
1729     * Restore the ACLs, but don't complain about links which really should
1730     * not have attributes, and the file it is linked to may not yet be restored.
1731     */
1732    if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
1733       switch (errno) {
1734       case ENOENT:
1735          actuallyfree(acls);
1736          return bacl_exit_ok;
1737       default:
1738          Mmsg2(jcr->errmsg, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
1739                jcr->last_fname, be.bstrerror());
1740          Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
1741                jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1742          actuallyfree(acls);
1743          return bacl_exit_error;
1744       }
1745    }
1746    actuallyfree(acls);
1747    return bacl_exit_ok;
1748 }
1749 #endif /* HAVE_EXTENDED_ACL */
1750
1751 /**
1752  * For this OS setup the build and parse function pointer to the OS specific functions.
1753  */
1754 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
1755 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
1756
1757 #endif /* HAVE_SUN_OS */
1758 #endif /* HAVE_ACL */
1759
1760 /*
1761  * Entry points when compiled with support for ACLs on a supported platform.
1762  */
1763
1764 /**
1765  * Read and send an ACL for the last encountered file.
1766  */
1767 bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1768 {
1769    /**
1770     * See if we are changing from one device to an other.
1771     * We save the current device we are scanning and compare
1772     * it with the current st_dev in the last stat performed on
1773     * the file we are currently storing.
1774     */
1775    if (jcr->acl_data->current_dev != ff_pkt->statp.st_dev) {
1776       /**
1777        * Reset the acl save flags.
1778        */
1779       jcr->acl_data->flags = 0;
1780
1781       jcr->acl_data->flags |= BACL_FLAG_SAVE_NATIVE;
1782
1783       /**
1784        * Save that we started scanning a new filesystem.
1785        */
1786       jcr->acl_data->current_dev = ff_pkt->statp.st_dev;
1787    }
1788
1789 #if defined(HAVE_ACL)
1790    /**
1791     * See if the BACL_FLAG_SAVE_NATIVE flag is set which lets us know if we should
1792     * save native ACLs.
1793     */
1794    if (jcr->acl_data->flags & BACL_FLAG_SAVE_NATIVE) {
1795       /**
1796        * Call the appropriate function.
1797        */
1798       if (os_build_acl_streams) {
1799          return (*os_build_acl_streams)(jcr, ff_pkt);
1800       }
1801    } else {
1802       return bacl_exit_ok;
1803    }
1804 #endif
1805    return bacl_exit_error;
1806 }
1807
1808 bacl_exit_code parse_acl_streams(JCR *jcr, int stream)
1809 {
1810    unsigned int cnt;
1811
1812    switch (stream) {
1813 #if defined(HAVE_ACL)
1814    case STREAM_UNIX_ACCESS_ACL:
1815    case STREAM_UNIX_DEFAULT_ACL:
1816       /**
1817        * Handle legacy ACL streams.
1818        */
1819       if (os_parse_acl_streams) {
1820          return (*os_parse_acl_streams)(jcr, stream);
1821       }
1822       break;
1823    default:
1824       if (os_parse_acl_streams) {
1825          /**
1826           * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
1827           */
1828          for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1829             if (os_access_acl_streams[cnt] == stream) {
1830                return (*os_parse_acl_streams)(jcr, stream);
1831             }
1832          }
1833          /**
1834           * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
1835           */
1836          for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1837             if (os_default_acl_streams[cnt] == stream) {
1838                return (*os_parse_acl_streams)(jcr, stream);
1839             }
1840          }
1841       }
1842       break;
1843 #else
1844    default:
1845       break;
1846 #endif
1847    }
1848    Qmsg2(jcr, M_WARNING, 0,
1849       _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
1850       jcr->last_fname, stream);
1851    return bacl_exit_error;
1852 }
1853 #endif