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 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) (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)
353 int bacl_get(JCR *jcr, int acltype)
359 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
360 if (n < MIN_ACL_ENTRIES) {
362 } else if (n == MIN_ACL_ENTRIES) {
363 /* The ACLs simply reflect the (already known) standard permissions */
364 return pm_strcpy(jcr->acl_text, "");
366 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
369 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
370 if ((acl_text = acltotext(acls, n)) != NULL) {
371 len = pm_strcpy(jcr->acl_text, acl_text);
372 actuallyfree(acl_text);
377 Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
378 jcr->last_fname, be.bstrerror());
379 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
380 jcr->acl_text, jcr->last_fname, be.bstrerror());
386 int bacl_set(JCR *jcr, int acltype)
391 acls = aclfromtext(jcr->acl_text, &n);
394 Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
395 jcr->last_fname, be.bstrerror());
396 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
397 jcr->acl_text, jcr->last_fname, be.bstrerror());
401 * Restore the ACLs, but don't complain about links which really should
402 * not have attributes, and the file it is linked to may not yet be restored.
404 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
406 Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
407 jcr->last_fname, be.bstrerror());
408 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
409 jcr->acl_text, jcr->last_fname, be.bstrerror());
425 * Compile and set up with eg. with eg.
427 * $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
430 * You can then list ACLs with acl and copy them with aclcp.
432 * For a list of compiler flags, see the list preceding the big #if below.
438 #include <sys/stat.h>
441 #define BACLLEN 65535
442 #define pm_strcpy(d,s) (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
443 #define Dmsg0(n,s) fprintf(stderr, s)
444 #define Dmsg1(n,s,a1) fprintf(stderr, s, a1)
445 #define Dmsg2(n,s,a1,a2) fprintf(stderr, s, a1, a2)
447 int aclls(char *fname);
448 int aclcp(char *src, char *dst);
452 char acl_text[BACLLEN];
454 typedef struct JCRstruct JCR;
457 int main(int argc, char **argv)
463 Dmsg0(200, "Cannot determine my own name\n");
467 prgname = last_path_separator(argv[0]);
468 if (prgname == NULL || *++prgname == '\0') {
474 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
475 if (strcmp(prgname, "aclcp") == 0) {
477 if (strcmp(*argv, "-v") == 0) {
483 Dmsg2(200, "%s: wrong number of arguments\n"
484 "usage:\t%s [-v] source destination\n"
485 "\tCopies ACLs from source to destination.\n"
486 "\tSpecify -v to show ACLs after copy for verification.\n",
490 if (strcmp(argv[0], argv[1]) == 0) {
491 Dmsg2(200, "%s: identical source and destination.\n"
492 "usage:\t%s [-v] source destination\n"
493 "\tCopies ACLs from source to destination.\n"
494 "\tSpecify -v to show ACLs after copy for verification.\n",
501 status = aclcp(argv[0], argv[1]);
502 if (verbose && status == 0) {
508 /* Default: just list ACLs */
510 Dmsg2(200, "%s: missing arguments\n"
511 "usage:\t%s file ...\n"
512 "\tLists ACLs of specified files or directories.\n",
517 if (!aclls(*argv++)) {
518 status = EXIT_FAILURE;
525 /**** Test program *****/
526 int aclcp(char *src, char *dst)
530 if (lstat(dst, &st) != 0) {
531 Dmsg0(200, "aclcp: destination does not exist\n");
534 if (S_ISLNK(st.st_mode)) {
535 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
538 if (lstat(src, &st) != 0) {
539 Dmsg0(200, "aclcp: source does not exist\n");
542 if (S_ISLNK(st.st_mode)) {
543 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
547 jcr.last_fname = src;
548 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
549 Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
552 jcr.last_fname = dst;
553 if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
554 Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
559 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
560 jcr.last_fname = src;
561 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
562 Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
565 jcr.last_fname = dst;
566 if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
567 Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
576 /**** Test program *****/
577 int aclls(char *fname)
582 if (lstat(fname, &st) != 0) {
583 Dmsg0(200, "acl: source does not exist\n");
586 if (S_ISLNK(st.st_mode)) {
587 Dmsg0(200, "acl: will not read ACL from symlinks\n");
591 jcr.last_fname = fname;
593 len = bacl_get(&jcr, BACL_TYPE_ACCESS);
595 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
597 } else if (len == 0) {
598 printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
600 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
603 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
604 len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
606 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
608 } else if (len == 0) {
609 printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
611 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);