2 * Functions to handle ACL for bacula.
4 * We handle two different typers of ACLs: access and default ACLS.
5 * Default ACLs only apply to directories.
7 * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs
8 * independently, while others (eg. Solaris) provide both in one call.
10 * As for the streams to use, we have two choices:
12 * 1. Build a generic framework.
13 * With two different types of ACLs, supported differently, we
14 * probably end up encoding and decoding everything ourselves.
16 * 2. Take the easy way out.
17 * Just handle each platform individually, assuming that backups
18 * and restores are done on the same (kind of) client.
20 * Currently we take the easy way out. We use two kinds of streams, one
21 * for access ACLs and one for default ACLs. If an OS mixes the two, we
22 * send the mix in the access ACL stream.
24 * Looking at more man pages, supporting a framework seems really hard
25 * if we want to support HP-UX. Deity knows what AIX is up to.
27 * Written by Preben 'Peppe' Guldberg, December MMIV
36 /* So we can free system allocated memory */
39 #define malloc &* dont use malloc in this routine
45 * Compile and set up with eg. with eg.
47 * $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
50 * You can then list ACLs with acl and copy them with aclcp.
52 * For a list of compiler flags, see the list preceding the big #if below.
62 #define pm_strcpy(d,s) (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
63 #define Dmsg0(n,s) fprintf(stderr, s)
64 #define Dmsg1(n,s,a1) fprintf(stderr, s, a1)
65 #define Dmsg2(n,s,a1,a2) fprintf(stderr, s, a1, a2)
67 int aclls(char *fname);
68 int aclcp(char *src, char *dst);
72 char acl_text[BACLLEN];
74 typedef struct JCRstruct JCR;
79 * List of supported OSs.
80 * Not sure if all the HAVE_XYZ_OS are correct for autoconf.
81 * The ones that says man page, are coded according to man pages only.
83 #if !defined(HAVE_ACL) /* ACL support is required, of course */ \
84 || !( defined(HAVE_AIX_OS) /* man page -- may need flags */ \
85 || defined(HAVE_FREEBSD_OS) /* tested -- compile wihtout flags */ \
86 || defined(HAVE_IRIX_OS) /* man page -- compile without flags */ \
87 || defined(HAVE_OSF1_OS) /* man page -- may need -lpacl */ \
88 || defined(HAVE_LINUX_OS) /* tested -- compile with -lacl */ \
89 || defined(HAVE_HPUX_OS) /* man page -- may need flags */ \
90 || defined(HAVE_SUN_OS) /* tested -- compile with -lsec */ \
94 * For now we abandon this test and only test for Linux:
95 * 1. This is backwards compatible.
96 * 2. If we allow any of the other now, we may have to provide conversion
97 * routines if we ever want to distinguish them. Or just do our best
98 * with what we have and give all ACL streams a new number/type.
101 #if !defined(HAVE_ACL) || !defined(HAVE_LINUX_OS)
103 /* bacl_get() returns the lenght of the string, or -1 on error. */
104 int bacl_get(JCR *jcr, int acltype)
109 int bacl_set(JCR *jcr, int acltype)
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);
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_IRIX_OS) \
141 || defined(HAVE_OSF1_OS) \
142 || defined(HAVE_LINUX_OS)
144 #include <sys/types.h>
147 /* On IRIX we can get shortened ACLs */
148 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
149 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
152 /* In Linux we can get numeric and/or shorted ACLs */
153 #if defined(HAVE_LINUX_OS)
154 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
155 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
156 #elif defined(BACL_WANT_SHORT_ACLS)
157 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
158 #elif defined(BACL_WANT_NUMERIC_IDS)
159 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
161 #ifdef BACL_ALTERNATE_TEXT
162 #include <acl/libacl.h>
163 #define acl_to_text(acl,len) ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
167 int bacl_get(JCR *jcr, int acltype)
173 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
175 acl = acl_get_file(jcr->last_fname, ostype);
177 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
178 len = pm_strcpy(jcr->acl_text, acl_text);
184 #ifndef HAVE_OSF1_OS /* BACL_ENOTSUP not defined for OSF1 */
185 } else if (errno == BACL_ENOTSUP) {
186 /* Not supported, just pretend there is nothing to see */
187 return pm_strcpy(jcr->acl_text, "");
190 /***** Do we really want to silently ignore errors from acl_get_file
191 and acl_to_text? *****/
195 int bacl_set(JCR *jcr, int acltype)
200 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
202 /* If we get empty default ACLs, clear ACLs now */
203 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
204 if (acl_delete_def_file(jcr->last_fname) == 0) {
210 acl = acl_from_text(jcr->acl_text);
216 * FreeBSD always fails acl_valid() - at least on valid input...
217 * As it does the right thing, given valid input, just ignore acl_valid().
219 #ifndef HAVE_FREEBSD_OS
220 if (acl_valid(acl) != 0) {
226 if (acl_set_file(jcr->last_fname, ostype, acl) != 0) {
234 #elif defined(HAVE_HPUX_OS)
238 int bacl_get(JCR *jcr, int acltype)
241 struct acl_entry acls[NACLENTRIES];
244 if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
245 if (errno == BACL_ENOTSUP) {
246 return pm_strcpy(jcr->acl_text, "");
250 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
251 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
252 len = pm_strcpy(jcr->acl_text, acl_text);
260 int bacl_set(JCR *jcr, int acltype)
263 struct acl_entry acls[NACLENTRIES];
265 n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
269 if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
272 if (setacl(jcr->last_fname, n, acls) != 0) {
278 #elif defined(HAVE_SUN_OS)
281 int bacl_get(JCR *jcr, int acltype)
287 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
288 if (n < MIN_ACL_ENTRIES) {
290 } else if (n == MIN_ACL_ENTRIES) {
291 /* The ACLs simply reflect the (already known) standard permissions */
292 return pm_strcpy(jcr->acl_text, "");
294 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
297 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
298 if ((acl_text = acltotext(acls, n)) != NULL) {
299 len = pm_strcpy(jcr->acl_text, acl_text);
309 int bacl_set(JCR *jcr, int acltype)
314 acls = aclfromtext(jcr->acl_text, &n);
318 if (acl(jcr->last_fname, SETACL, n, acls) != 0) {
330 int main(int argc, char **argv)
336 Dmsg0(200, "Cannot determine my own name\n");
340 prgname = strrchr(argv[0], '/');
341 if (prgname == NULL || *++prgname == '\0') {
347 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
348 if (strcmp(prgname, "aclcp") == 0) {
350 if (strcmp(*argv, "-v") == 0) {
356 Dmsg2(200, "%s: wrong number of arguments\n"
357 "usage:\t%s [-v] source destination\n"
358 "\tCopies ACLs from source to destination.\n"
359 "\tSpecify -v to show ACLs after copy for verification.\n",
363 if (strcmp(argv[0], argv[1]) == 0) {
364 Dmsg2(200, "%s: identical source and destination.\n"
365 "usage:\t%s [-v] source destination\n"
366 "\tCopies ACLs from source to destination.\n"
367 "\tSpecify -v to show ACLs after copy for verification.\n",
374 status = aclcp(argv[0], argv[1]);
375 if (verbose && status == 0) {
381 /* Default: just list ACLs */
383 Dmsg2(200, "%s: missing arguments\n"
384 "usage:\t%s file ...\n"
385 "\tLists ACLs of specified files or directories.\n",
390 if (!aclls(*argv++)) {
391 status = EXIT_FAILURE;
398 /**** Test program *****/
399 int aclcp(char *src, char *dst)
403 if (lstat(dst, &st) != 0) {
404 Dmsg0(200, "aclcp: destination does not exist\n");
407 if (S_ISLNK(st.st_mode)) {
408 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
411 if (lstat(src, &st) != 0) {
412 Dmsg0(200, "aclcp: source does not exist\n");
415 if (S_ISLNK(st.st_mode)) {
416 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
420 jcr.last_fname = src;
421 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
422 Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
425 jcr.last_fname = dst;
426 if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
427 Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
432 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
433 jcr.last_fname = src;
434 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
435 Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
438 jcr.last_fname = dst;
439 if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
440 Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
449 /**** Test program *****/
450 int aclls(char *fname)
455 if (lstat(fname, &st) != 0) {
456 Dmsg0(200, "acl: source does not exist\n");
459 if (S_ISLNK(st.st_mode)) {
460 Dmsg0(200, "acl: will not read ACL from symlinks\n");
464 jcr.last_fname = fname;
466 len = bacl_get(&jcr, BACL_TYPE_ACCESS);
468 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
470 } else if (len == 0) {
471 printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
473 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
476 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
477 len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
479 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
481 } else if (len == 0) {
482 printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
484 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);