]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
kes Apply patch from Marco van Wieringen that implements the new
[bacula/bacula] / bacula / src / filed / acl.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2008 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 ACL for bacula.
30  *
31  * We handle two different typers of ACLs: access and default ACLS.
32  * Default ACLs 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  * As for the streams to use, we have two choices:
38  *
39  * 1. Build a generic framework.
40  *    With two different types of ACLs, supported differently, we
41  *    probably end up encoding and decoding everything ourselves.
42  *
43  * 2. Take the easy way out.
44  *    Just handle each platform individually, assuming that backups
45  *    and restores are done on the same (kind of) client.
46  *
47  * Currently we take the easy way out. We use two kinds of streams, one
48  * for access ACLs and one for default ACLs. If an OS mixes the two, we
49  * send the mix in the access ACL stream.
50  *
51  * Looking at more man pages, supporting a framework seems really hard
52  * if we want to support HP-UX. Deity knows what AIX is up to.
53  *
54  *   Written by Preben 'Peppe' Guldberg, December MMIV
55  *
56  *   Version $Id$
57  */
58
59
60 #ifndef TEST_PROGRAM
61
62 #include "bacula.h"
63 #include "filed.h"
64
65 #endif
66
67 /*
68  * List of supported OSs.
69  * Not sure if all the HAVE_XYZ_OS are correct for autoconf.
70  * The ones that says man page, are coded according to man pages only.
71  */
72 #if !defined(HAVE_ACL)              /* ACL support is required, of course */ \
73    || !( defined(HAVE_AIX_OS)       /* man page -- may need flags         */ \
74       || defined(HAVE_FREEBSD_OS)   /* tested   -- compile without flags  */ \
75       || defined(HAVE_DARWIN_OS)    /* tested   -- compile without flags  */ \
76       || defined(HAVE_IRIX_OS)      /* man page -- compile without flags  */ \
77       || defined(HAVE_OSF1_OS)      /* man page -- may need -lpacl        */ \
78       || defined(HAVE_LINUX_OS)     /* tested   -- compile with -lacl     */ \
79       || defined(HAVE_HPUX_OS)      /* man page -- may need flags         */ \
80       || defined(HAVE_SUN_OS)       /* tested   -- compile with -lsec     */ \
81        )
82 /*
83  * ***FIXME***
84  * For now we abandon this test and only test for Linux:
85  * 1. This is backwards compatible.
86  * 2. If we allow any of the other now, we may have to provide conversion
87  *    routines if we ever want to distinguish them. Or just do our best
88  *    with what we have and give all ACL streams a new number/type.
89  */
90 #endif
91
92 #if !defined(HAVE_ACL) \
93    || !( defined(HAVE_LINUX_OS) \
94       || defined(HAVE_FREEBSD_OS) \
95       || defined(HAVE_DARWIN_OS) \
96       || defined(HAVE_IRIX_OS) \
97       || defined(HAVE_OSF1_OS) \
98       || defined(HAVE_SUN_OS) \
99        )
100
101 /* bacl_get() returns the lenght of the string, or -1 on error. */
102 int bacl_get(JCR *jcr, int acltype)
103 {
104    Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
105    return -1;
106 }
107
108 int bacl_set(JCR *jcr, int acltype)
109 {
110    Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
111    return -1;
112 }
113
114 #elif defined(HAVE_AIX_OS)
115
116 #include <sys/access.h>
117
118 int bacl_get(JCR *jcr, int acltype)
119 {
120    char *acl_text;
121    int len;
122
123    if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
124       len = pm_strcpy(jcr->acl_text, acl_text);
125       actuallyfree(acl_text);
126       return len;
127    }
128    return -1;
129 }
130
131 int bacl_set(JCR *jcr, int acltype)
132 {
133    if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
134       return -1;
135    }
136    return 0;
137 }
138
139 #elif defined(HAVE_FREEBSD_OS) \
140    || defined(HAVE_DARWIN_OS) \
141    || defined(HAVE_IRIX_OS) \
142    || defined(HAVE_OSF1_OS) \
143    || defined(HAVE_LINUX_OS)
144
145 #include <sys/types.h>
146 #include <sys/acl.h>
147
148 /* On IRIX we can get shortened ACLs */
149 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
150 #define acl_to_text(acl,len)     acl_to_short_text((acl), (len))
151 #endif
152
153 /* In Linux we can get numeric and/or shorted ACLs */
154 #if defined(HAVE_LINUX_OS)
155 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
156 #define BACL_ALTERNATE_TEXT            (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
157 #elif defined(BACL_WANT_SHORT_ACLS)
158 #define BACL_ALTERNATE_TEXT            TEXT_ABBREVIATE
159 #elif defined(BACL_WANT_NUMERIC_IDS)
160 #define BACL_ALTERNATE_TEXT            TEXT_NUMERIC_IDS
161 #endif
162 #ifdef BACL_ALTERNATE_TEXT
163 #include <acl/libacl.h>
164 #define acl_to_text(acl,len)     (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
165 #endif
166 #endif
167
168 int bacl_get(JCR *jcr, int acltype)
169 {
170    acl_t acl;
171    int len;
172    acl_type_t ostype;
173    char *acl_text;
174
175    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
176
177    acl = acl_get_file(jcr->last_fname, ostype);
178    if (acl) {
179 #if defined(HAVE_IRIX_OS)
180       /* 
181        * From observation, IRIX's acl_get_file() seems to return a
182        * non-NULL acl with a count field of -1 when a file has no ACL
183        * defined, while IRIX's acl_to_text() returns NULL when presented
184        * with such an ACL. 
185        *
186        * Checking the count in the acl structure before calling
187        * acl_to_text() lets us avoid error messages about files
188        * with no ACLs, without modifying the flow of the code used for 
189        * other operating systems, and it saves making some calls
190        * to acl_to_text() besides.
191        */
192       if (acl->acl_cnt <= 0) {
193         acl_free(acl);
194          return 0;
195       }
196 #endif
197       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
198          len = pm_strcpy(jcr->acl_text, acl_text);
199          acl_free(acl);
200          acl_free(acl_text);
201          return len;
202       }
203       berrno be;
204       Jmsg2(jcr, M_ERROR, 0, _("acl_to_text error on file \"%s\": ERR=%s\n"),
205          jcr->last_fname, be.bstrerror());
206       Dmsg3(100, "acl_to_text error acl=%s file=%s ERR=%s\n",  
207          jcr->acl_text, jcr->last_fname, be.bstrerror());
208       acl_free(acl);
209 #ifndef HAVE_OSF1_OS          /* BACL_ENOTSUP not defined for OSF1 */
210    } else if (errno == BACL_ENOTSUP) {
211       /* Not supported, just pretend there is nothing to see */
212       return pm_strcpy(jcr->acl_text, "");
213 #endif
214    }
215    /***** Do we really want to silently ignore errors from acl_get_file
216      and acl_to_text?  *****/
217    return 0;
218 }
219
220 int bacl_set(JCR *jcr, int acltype)
221 {
222    acl_t acl;
223    acl_type_t ostype;
224
225    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
226
227    /* If we get empty default ACLs, clear ACLs now */
228    if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
229       if (acl_delete_def_file(jcr->last_fname) == 0) {
230          return 0;
231       }
232       berrno be;
233       Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
234          jcr->last_fname, be.bstrerror());
235       return -1;
236    }
237
238    acl = acl_from_text(jcr->acl_text);
239    if (acl == NULL) {
240       berrno be;
241       Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
242          jcr->last_fname, be.bstrerror());
243       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",  
244          jcr->acl_text, jcr->last_fname, be.bstrerror());
245       return -1;
246    }
247
248    /*
249     * FreeBSD always fails acl_valid() - at least on valid input...
250     * As it does the right thing, given valid input, just ignore acl_valid().
251     */
252 #ifndef HAVE_FREEBSD_OS
253    if (acl_valid(acl) != 0) {
254       berrno be;
255       Jmsg2(jcr, M_ERROR, 0, _("ac_valid error on file \"%s\": ERR=%s\n"),
256          jcr->last_fname, be.bstrerror());
257       Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",  
258          jcr->acl_text, jcr->last_fname, be.bstrerror());
259       acl_free(acl);
260       return -1;
261    }
262 #endif
263
264    /*
265     * Restore the ACLs, but don't complain about links which really should
266     * not have attributes, and the file it is linked to may not yet be restored.
267     */
268    if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
269       berrno be;
270       Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
271          jcr->last_fname, be.bstrerror());
272       Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",  
273          jcr->acl_text, jcr->last_fname, be.bstrerror());
274       acl_free(acl);
275       return -1;
276    }
277    acl_free(acl);
278    return 0;
279 }
280
281 #elif defined(HAVE_HPUX_OS)
282 #include <sys/acl.h>
283 #include <acllib.h>
284
285 int bacl_get(JCR *jcr, int acltype)
286 {
287    int n, len;
288    struct acl_entry acls[NACLENTRIES];
289    char *acl_text;
290
291    if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
292       if (errno == BACL_ENOTSUP) {
293          return pm_strcpy(jcr->acl_text, "");
294       }
295       return -1;
296    }
297    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
298       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
299          len = pm_strcpy(jcr->acl_text, acl_text);
300          actuallyfree(acl_text);
301          return len;
302       }
303       berrno be;
304       Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
305          jcr->last_fname, be.bstrerror());
306       Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",  
307          jcr->acl_text, jcr->last_fname, be.bstrerror());
308       return -1;
309    }
310    return -1;
311 }
312
313 int bacl_set(JCR *jcr, int acltype)
314 {
315    int n, stat;
316    struct acl_entry acls[NACLENTRIES];
317
318    n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
319    if (n <= 0) {
320       berrno be;
321       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
322          jcr->last_fname, be.bstrerror());
323       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
324          jcr->acl_text, jcr->last_fname, be.bstrerror());
325       return -1;
326    }
327    if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
328       berrno be;
329       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
330          jcr->last_fname, be.bstrerror());
331       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
332          jcr->acl_text, jcr->last_fname, be.bstrerror());
333       return -1;
334    }
335    /*
336     * Restore the ACLs, but don't complain about links which really should
337     * not have attributes, and the file it is linked to may not yet be restored.
338     */
339    if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
340       berrno be;
341       Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
342          jcr->last_fname, be.bstrerror());
343       Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",  
344          jcr->acl_text, jcr->last_fname, be.bstrerror());
345       return -1;
346    }
347    return 0;
348 }
349
350 #elif defined(HAVE_SUN_OS)
351 #include <sys/acl.h>
352
353 /*
354  * As the new libsec interface with acl_totext and acl_fromtext also handles
355  * the old format from acltotext we can use the new functions even
356  * for acls retrieved and stored in the database with older fd versions. If the
357  * new interface is not defined (Solaris 9 and older we fall back to the old code)
358  */
359 #if defined(HAVE_EXTENDED_ACL)
360 int bacl_get(JCR *jcr, int acltype)
361 {
362    int len, flags;
363    acl_t *aclp;
364    char *acl_text;
365
366    /*
367     * Get ACL info: don't bother allocating space if there is only a trivial ACL.
368     */
369    if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0)
370       return -1;
371
372    if (aclp == NULL) {
373       /* The ACLs simply reflect the (already known) standard permissions */
374       return pm_strcpy(jcr->acl_text, "");
375    }
376
377 #if defined(ACL_SID_FMT)
378    /*
379     * New format flag added in newer Solaris versions.
380     */
381    flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
382 #else
383    flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
384 #endif /* ACL_SID_FMT */
385
386    if ((acl_text = acl_totext(aclp, flags)) != NULL) {
387       len = pm_strcpy(jcr->acl_text, acl_text);
388       actuallyfree(acl_text);
389
390       acl_free(aclp);
391
392       return len;
393    }
394
395    acl_free(aclp);
396
397    return -1;
398 }
399
400 /*
401  * As the header acl.h doesn't seem to define this one we need to.
402  */
403 extern "C" {
404 char *acl_strerror(int);
405 }
406
407 int bacl_set(JCR *jcr, int acltype)
408 {
409    acl_t *aclp;
410    int error;
411
412    if ((error = acl_fromtext(jcr->acl_text, &aclp)) != 0) {
413       Jmsg2(jcr, M_ERROR, 0, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
414          jcr->last_fname, acl_strerror(error));
415       Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",  
416          jcr->acl_text, jcr->last_fname, acl_strerror(error));
417       return -1;
418    }
419
420    /*
421     * Restore the ACLs, but don't complain about links which really should
422     * not have attributes, and the file it is linked to may not yet be restored.
423     */
424    if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
425       Jmsg2(jcr, M_ERROR, 0, _("acl_set error on file \"%s\": ERR=%s\n"),
426          jcr->last_fname, acl_strerror(error));
427       Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",  
428          jcr->acl_text, jcr->last_fname, acl_strerror(error));
429
430       acl_free(aclp);
431       return -1;
432    }
433
434    acl_free(aclp);
435    return 0;
436 }
437
438 #else /* HAVE_EXTENDED_ACL */
439
440 int bacl_get(JCR *jcr, int acltype)
441 {
442    int n, len;
443    aclent_t *acls;
444    char *acl_text;
445
446    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
447    if (n < MIN_ACL_ENTRIES) {
448       return -1;
449    } else if (n == MIN_ACL_ENTRIES) {
450       /* The ACLs simply reflect the (already known) standard permissions */
451       return pm_strcpy(jcr->acl_text, "");
452    }
453    if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
454       return -1;
455    }
456    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
457       if ((acl_text = acltotext(acls, n)) != NULL) {
458          len = pm_strcpy(jcr->acl_text, acl_text);
459          actuallyfree(acl_text);
460          free(acls);
461          return len;
462       }
463       berrno be;
464       Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
465          jcr->last_fname, be.bstrerror());
466       Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",  
467          jcr->acl_text, jcr->last_fname, be.bstrerror());
468    }
469    free(acls);
470    return -1;
471 }
472
473 int bacl_set(JCR *jcr, int acltype)
474 {
475    int n;
476    aclent_t *acls;
477
478    acls = aclfromtext(jcr->acl_text, &n);
479    if (!acls) {
480       berrno be;
481       Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
482          jcr->last_fname, be.bstrerror());
483       Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",  
484          jcr->acl_text, jcr->last_fname, be.bstrerror());
485       return -1;
486    }
487    /*
488     * Restore the ACLs, but don't complain about links which really should
489     * not have attributes, and the file it is linked to may not yet be restored.
490     */
491    if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
492       berrno be;
493       Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
494          jcr->last_fname, be.bstrerror());
495       Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",  
496          jcr->acl_text, jcr->last_fname, be.bstrerror());
497       actuallyfree(acls);
498       return -1;
499    }
500    actuallyfree(acls);
501    return 0;
502 }
503
504 #endif /* HAVE_EXTENDED_ACL */
505 #endif /* HAVE_SUN_OS */
506
507
508 #ifdef TEST_PROGRAM
509
510 /*
511  * Test program setup 
512  *
513  * Compile and set up with eg. with eg.
514  *
515  *    $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
516  *    $ ln -s acl aclcp
517  *
518  * You can then list ACLs with acl and copy them with aclcp.
519  *
520  * For a list of compiler flags, see the list preceding the big #if below.
521  */
522 #include <errno.h>
523 #include <stdio.h>
524 #include <stdlib.h>
525 #include <string.h>
526 #include <sys/stat.h>
527 #include "acl.h"
528
529 #define BACLLEN 65535
530 #define pm_strcpy(d,s)     (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
531 #define Dmsg0(n,s)         fprintf(stderr, s)
532 #define Dmsg1(n,s,a1)      fprintf(stderr, s, a1)
533 #define Dmsg2(n,s,a1,a2)   fprintf(stderr, s, a1, a2)
534
535 int aclls(char *fname);
536 int aclcp(char *src, char *dst);
537
538 struct JCRstruct {
539    char *last_fname;
540    char acl_text[BACLLEN];
541 };
542 typedef struct JCRstruct JCR;
543 JCR jcr;
544
545 int main(int argc, char **argv)
546 {
547    char *prgname;
548    int status = 0;
549
550    if (argc < 1) {
551       Dmsg0(200, "Cannot determine my own name\n");
552       return EXIT_FAILURE;
553    }
554
555    prgname = last_path_separator(argv[0]);
556    if (prgname == NULL || *++prgname == '\0') {
557       prgname = argv[0];
558    }
559    --argc;
560    ++argv;
561
562    /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
563    if (strcmp(prgname, "aclcp") == 0) {
564       int verbose = 0;
565       if (strcmp(*argv, "-v") == 0) {
566          ++verbose;
567          --argc;
568          ++argv;
569       }
570       if (argc != 2) {
571          Dmsg2(200, "%s: wrong number of arguments\n"
572                "usage:\t%s [-v] source destination\n"
573                "\tCopies ACLs from source to destination.\n"
574                "\tSpecify -v to show ACLs after copy for verification.\n",
575                prgname, prgname);
576          return EXIT_FAILURE;
577       }
578       if (strcmp(argv[0], argv[1]) == 0) {
579          Dmsg2(200, "%s: identical source and destination.\n"
580                "usage:\t%s [-v] source destination\n"
581                "\tCopies ACLs from source to destination.\n"
582                "\tSpecify -v to show ACLs after copy for verification.\n",
583                prgname, prgname);
584          return EXIT_FAILURE;
585       }
586       if (verbose) {
587          aclls(argv[0]);
588       }
589       status = aclcp(argv[0], argv[1]);
590       if (verbose && status == 0) {
591          aclls(argv[1]);
592       }
593       return status;
594    }
595
596    /* Default: just list ACLs */
597    if (argc < 1) {
598       Dmsg2(200, "%s: missing arguments\n"
599             "usage:\t%s file ...\n"
600             "\tLists ACLs of specified files or directories.\n",
601             prgname, prgname);
602       return EXIT_FAILURE;
603    }
604    while (argc--) {
605       if (!aclls(*argv++)) {
606          status = EXIT_FAILURE;
607       }
608    }
609
610    return status;
611 }
612
613 /**** Test program *****/
614 int aclcp(char *src, char *dst)
615 {
616    struct stat st;
617
618    if (lstat(dst, &st) != 0) {
619       Dmsg0(200, "aclcp: destination does not exist\n");
620       return EXIT_FAILURE;
621    }
622    if (S_ISLNK(st.st_mode)) {
623       Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
624       return EXIT_FAILURE;
625    }
626    if (lstat(src, &st) != 0) {
627       Dmsg0(200, "aclcp: source does not exist\n");
628       return EXIT_FAILURE;
629    }
630    if (S_ISLNK(st.st_mode)) {
631       Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
632       return EXIT_FAILURE;
633    }
634
635    jcr.last_fname = src;
636    if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
637       Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
638       return EXIT_FAILURE;
639    } else {
640       jcr.last_fname = dst;
641       if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
642          Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
643          return EXIT_FAILURE;
644       }
645    }
646
647    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
648       jcr.last_fname = src;
649       if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
650          Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
651          return EXIT_FAILURE;
652       } else {
653          jcr.last_fname = dst;
654          if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
655             Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
656             return EXIT_FAILURE;
657          }
658       }
659    }
660
661    return 0;
662 }
663
664 /**** Test program *****/
665 int aclls(char *fname)
666 {
667    struct stat st;
668    int len;
669
670    if (lstat(fname, &st) != 0) {
671       Dmsg0(200, "acl: source does not exist\n");
672       return EXIT_FAILURE;
673    }
674    if (S_ISLNK(st.st_mode)) {
675       Dmsg0(200, "acl: will not read ACL from symlinks\n");
676       return EXIT_FAILURE;
677    }
678
679    jcr.last_fname = fname;
680
681    len = bacl_get(&jcr, BACL_TYPE_ACCESS);
682    if (len < 0) {
683       Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
684       return EXIT_FAILURE;
685    } else if (len == 0) {
686       printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
687    } else {
688       printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
689    }
690
691    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
692       len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
693       if (len < 0) {
694          Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
695          return EXIT_FAILURE;
696       } else if (len == 0) {
697          printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
698       } else {
699          printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
700       }
701    }
702
703    return 0;
704 }
705 #endif