2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2007 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 plus additions
11 that are listed in the file LICENSE.
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 John Walker.
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) ((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 ((acl_text = acl_to_text(acl, NULL)) != NULL) {
180 len = pm_strcpy(jcr->acl_text, acl_text);
186 Jmsg2(jcr, M_ERROR, 0, _("acl_to_text error on file \"%s\": ERR=%s\n"),
187 jcr->last_fname, be.bstrerror());
188 Dmsg3(100, "acl_to_text error acl=%s file=%s ERR=%s\n",
189 jcr->acl_text, jcr->last_fname, be.bstrerror());
191 #ifndef HAVE_OSF1_OS /* BACL_ENOTSUP not defined for OSF1 */
192 } else if (errno == BACL_ENOTSUP) {
193 /* Not supported, just pretend there is nothing to see */
194 return pm_strcpy(jcr->acl_text, "");
197 /***** Do we really want to silently ignore errors from acl_get_file
198 and acl_to_text? *****/
202 int bacl_set(JCR *jcr, int acltype)
207 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
209 /* If we get empty default ACLs, clear ACLs now */
210 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
211 if (acl_delete_def_file(jcr->last_fname) == 0) {
215 Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
216 jcr->last_fname, be.bstrerror());
220 acl = acl_from_text(jcr->acl_text);
223 Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
224 jcr->last_fname, be.bstrerror());
225 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
226 jcr->acl_text, jcr->last_fname, be.bstrerror());
231 * FreeBSD always fails acl_valid() - at least on valid input...
232 * As it does the right thing, given valid input, just ignore acl_valid().
234 #ifndef HAVE_FREEBSD_OS
235 if (acl_valid(acl) != 0) {
237 Jmsg2(jcr, M_ERROR, 0, _("ac_valid error on file \"%s\": ERR=%s\n"),
238 jcr->last_fname, be.bstrerror());
239 Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",
240 jcr->acl_text, jcr->last_fname, be.bstrerror());
247 * Restore the ACLs, but don't complain about links which really should
248 * not have attributes, and the file it is linked to may not yet be restored.
250 if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
252 Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
253 jcr->last_fname, be.bstrerror());
254 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
255 jcr->acl_text, jcr->last_fname, be.bstrerror());
263 #elif defined(HAVE_HPUX_OS)
267 int bacl_get(JCR *jcr, int acltype)
270 struct acl_entry acls[NACLENTRIES];
273 if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
274 if (errno == BACL_ENOTSUP) {
275 return pm_strcpy(jcr->acl_text, "");
279 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
280 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
281 len = pm_strcpy(jcr->acl_text, acl_text);
282 actuallyfree(acl_text);
286 Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
287 jcr->last_fname, be.bstrerror());
288 Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",
289 jcr->acl_text, jcr->last_fname, be.bstrerror());
295 int bacl_set(JCR *jcr, int acltype)
298 struct acl_entry acls[NACLENTRIES];
300 n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
303 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
304 jcr->last_fname, be.bstrerror());
305 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
306 jcr->acl_text, jcr->last_fname, be.bstrerror());
309 if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
311 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
312 jcr->last_fname, be.bstrerror());
313 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
314 jcr->acl_text, jcr->last_fname, be.bstrerror());
318 * Restore the ACLs, but don't complain about links which really should
319 * not have attributes, and the file it is linked to may not yet be restored.
321 if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
323 Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
324 jcr->last_fname, be.bstrerror());
325 Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
326 jcr->acl_text, jcr->last_fname, be.bstrerror());
332 #elif defined(HAVE_SUN_OS)
335 int bacl_get(JCR *jcr, int acltype)
341 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
342 if (n < MIN_ACL_ENTRIES) {
344 } else if (n == MIN_ACL_ENTRIES) {
345 /* The ACLs simply reflect the (already known) standard permissions */
346 return pm_strcpy(jcr->acl_text, "");
348 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
351 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
352 if ((acl_text = acltotext(acls, n)) != NULL) {
353 len = pm_strcpy(jcr->acl_text, acl_text);
354 actuallyfree(acl_text);
359 Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
360 jcr->last_fname, be.bstrerror());
361 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
362 jcr->acl_text, jcr->last_fname, be.bstrerror());
368 int bacl_set(JCR *jcr, int acltype)
373 acls = aclfromtext(jcr->acl_text, &n);
376 Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
377 jcr->last_fname, be.bstrerror());
378 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
379 jcr->acl_text, jcr->last_fname, be.bstrerror());
383 * Restore the ACLs, but don't complain about links which really should
384 * not have attributes, and the file it is linked to may not yet be restored.
386 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
388 Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
389 jcr->last_fname, be.bstrerror());
390 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
391 jcr->acl_text, jcr->last_fname, be.bstrerror());
407 * Compile and set up with eg. with eg.
409 * $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
412 * You can then list ACLs with acl and copy them with aclcp.
414 * For a list of compiler flags, see the list preceding the big #if below.
420 #include <sys/stat.h>
423 #define BACLLEN 65535
424 #define pm_strcpy(d,s) (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
425 #define Dmsg0(n,s) fprintf(stderr, s)
426 #define Dmsg1(n,s,a1) fprintf(stderr, s, a1)
427 #define Dmsg2(n,s,a1,a2) fprintf(stderr, s, a1, a2)
429 int aclls(char *fname);
430 int aclcp(char *src, char *dst);
434 char acl_text[BACLLEN];
436 typedef struct JCRstruct JCR;
439 int main(int argc, char **argv)
445 Dmsg0(200, "Cannot determine my own name\n");
449 prgname = last_path_separator(argv[0]);
450 if (prgname == NULL || *++prgname == '\0') {
456 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
457 if (strcmp(prgname, "aclcp") == 0) {
459 if (strcmp(*argv, "-v") == 0) {
465 Dmsg2(200, "%s: wrong number of arguments\n"
466 "usage:\t%s [-v] source destination\n"
467 "\tCopies ACLs from source to destination.\n"
468 "\tSpecify -v to show ACLs after copy for verification.\n",
472 if (strcmp(argv[0], argv[1]) == 0) {
473 Dmsg2(200, "%s: identical source and destination.\n"
474 "usage:\t%s [-v] source destination\n"
475 "\tCopies ACLs from source to destination.\n"
476 "\tSpecify -v to show ACLs after copy for verification.\n",
483 status = aclcp(argv[0], argv[1]);
484 if (verbose && status == 0) {
490 /* Default: just list ACLs */
492 Dmsg2(200, "%s: missing arguments\n"
493 "usage:\t%s file ...\n"
494 "\tLists ACLs of specified files or directories.\n",
499 if (!aclls(*argv++)) {
500 status = EXIT_FAILURE;
507 /**** Test program *****/
508 int aclcp(char *src, char *dst)
512 if (lstat(dst, &st) != 0) {
513 Dmsg0(200, "aclcp: destination does not exist\n");
516 if (S_ISLNK(st.st_mode)) {
517 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
520 if (lstat(src, &st) != 0) {
521 Dmsg0(200, "aclcp: source does not exist\n");
524 if (S_ISLNK(st.st_mode)) {
525 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
529 jcr.last_fname = src;
530 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
531 Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
534 jcr.last_fname = dst;
535 if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
536 Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
541 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
542 jcr.last_fname = src;
543 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
544 Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
547 jcr.last_fname = dst;
548 if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
549 Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
558 /**** Test program *****/
559 int aclls(char *fname)
564 if (lstat(fname, &st) != 0) {
565 Dmsg0(200, "acl: source does not exist\n");
568 if (S_ISLNK(st.st_mode)) {
569 Dmsg0(200, "acl: will not read ACL from symlinks\n");
573 jcr.last_fname = fname;
575 len = bacl_get(&jcr, BACL_TYPE_ACCESS);
577 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
579 } else if (len == 0) {
580 printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
582 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
585 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
586 len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
588 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
590 } else if (len == 0) {
591 printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
593 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);