2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2008 Free Software Foundation Europe e.V.
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
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.
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
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.
29 * Functions to handle ACL for bacula.
31 * We handle two different typers of ACLs: access and default ACLS.
32 * Default ACLs only apply to directories.
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.
37 * As for the streams to use, we have two choices:
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.
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.
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.
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.
54 * Written by Preben 'Peppe' Guldberg, December MMIV
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.
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 */ \
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.
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) \
101 /* bacl_get() returns the lenght of the string, or -1 on error. */
102 int bacl_get(JCR *jcr, int acltype)
104 Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
108 int bacl_set(JCR *jcr, int acltype)
110 Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
114 #elif defined(HAVE_AIX_OS)
116 #include <sys/access.h>
118 int bacl_get(JCR *jcr, int acltype)
123 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
124 len = pm_strcpy(jcr->acl_text, acl_text);
125 actuallyfree(acl_text);
131 int bacl_set(JCR *jcr, int acltype)
133 if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
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)
145 #include <sys/types.h>
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))
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
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))
168 int bacl_get(JCR *jcr, int acltype)
175 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
177 acl = acl_get_file(jcr->last_fname, ostype);
179 #if defined(HAVE_IRIX_OS)
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
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.
192 if (acl->acl_cnt <= 0) {
197 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
198 len = pm_strcpy(jcr->acl_text, acl_text);
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());
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, "");
215 /***** Do we really want to silently ignore errors from acl_get_file
216 and acl_to_text? *****/
220 int bacl_set(JCR *jcr, int acltype)
225 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
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) {
233 Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
234 jcr->last_fname, be.bstrerror());
238 acl = acl_from_text(jcr->acl_text);
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());
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().
252 #ifndef HAVE_FREEBSD_OS
253 if (acl_valid(acl) != 0) {
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());
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.
268 if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
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());
281 #elif defined(HAVE_HPUX_OS)
285 int bacl_get(JCR *jcr, int acltype)
288 struct acl_entry acls[NACLENTRIES];
291 if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
292 if (errno == BACL_ENOTSUP) {
293 return pm_strcpy(jcr->acl_text, "");
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);
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());
313 int bacl_set(JCR *jcr, int acltype)
316 struct acl_entry acls[NACLENTRIES];
318 n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
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());
327 if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
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());
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.
339 if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
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());
350 #elif defined(HAVE_SUN_OS)
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)
359 #if defined(HAVE_EXTENDED_ACL)
360 int bacl_get(JCR *jcr, int acltype)
367 * Get ACL info: don't bother allocating space if there is only a trivial ACL.
369 if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0)
373 /* The ACLs simply reflect the (already known) standard permissions */
374 return pm_strcpy(jcr->acl_text, "");
377 #if defined(ACL_SID_FMT)
379 * New format flag added in newer Solaris versions.
381 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
383 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
384 #endif /* ACL_SID_FMT */
386 if ((acl_text = acl_totext(aclp, flags)) != NULL) {
387 len = pm_strcpy(jcr->acl_text, acl_text);
388 actuallyfree(acl_text);
401 * As the header acl.h doesn't seem to define this one we need to.
404 char *acl_strerror(int);
407 int bacl_set(JCR *jcr, int acltype)
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));
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.
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));
438 #else /* HAVE_EXTENDED_ACL */
440 int bacl_get(JCR *jcr, int acltype)
446 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
447 if (n < MIN_ACL_ENTRIES) {
449 } else if (n == MIN_ACL_ENTRIES) {
450 /* The ACLs simply reflect the (already known) standard permissions */
451 return pm_strcpy(jcr->acl_text, "");
453 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
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);
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());
473 int bacl_set(JCR *jcr, int acltype)
478 acls = aclfromtext(jcr->acl_text, &n);
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());
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.
491 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
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());
504 #endif /* HAVE_EXTENDED_ACL */
505 #endif /* HAVE_SUN_OS */
513 * Compile and set up with eg. with eg.
515 * $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
518 * You can then list ACLs with acl and copy them with aclcp.
520 * For a list of compiler flags, see the list preceding the big #if below.
526 #include <sys/stat.h>
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)
535 int aclls(char *fname);
536 int aclcp(char *src, char *dst);
540 char acl_text[BACLLEN];
542 typedef struct JCRstruct JCR;
545 int main(int argc, char **argv)
551 Dmsg0(200, "Cannot determine my own name\n");
555 prgname = last_path_separator(argv[0]);
556 if (prgname == NULL || *++prgname == '\0') {
562 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
563 if (strcmp(prgname, "aclcp") == 0) {
565 if (strcmp(*argv, "-v") == 0) {
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",
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",
589 status = aclcp(argv[0], argv[1]);
590 if (verbose && status == 0) {
596 /* Default: just list ACLs */
598 Dmsg2(200, "%s: missing arguments\n"
599 "usage:\t%s file ...\n"
600 "\tLists ACLs of specified files or directories.\n",
605 if (!aclls(*argv++)) {
606 status = EXIT_FAILURE;
613 /**** Test program *****/
614 int aclcp(char *src, char *dst)
618 if (lstat(dst, &st) != 0) {
619 Dmsg0(200, "aclcp: destination does not exist\n");
622 if (S_ISLNK(st.st_mode)) {
623 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
626 if (lstat(src, &st) != 0) {
627 Dmsg0(200, "aclcp: source does not exist\n");
630 if (S_ISLNK(st.st_mode)) {
631 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
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);
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);
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);
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);
664 /**** Test program *****/
665 int aclls(char *fname)
670 if (lstat(fname, &st) != 0) {
671 Dmsg0(200, "acl: source does not exist\n");
674 if (S_ISLNK(st.st_mode)) {
675 Dmsg0(200, "acl: will not read ACL from symlinks\n");
679 jcr.last_fname = fname;
681 len = bacl_get(&jcr, BACL_TYPE_ACCESS);
683 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
685 } else if (len == 0) {
686 printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
688 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
691 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
692 len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
694 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
696 } else if (len == 0) {
697 printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
699 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);