2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2009 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 ACLs for bacula.
31 * We handle two different types of ACLs: access and default ACLS.
32 * On most systems that support default ACLs they 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 * The Filed saves ACLs in their native format and uses different streams
38 * for all different platforms. Currently we only allow ACLs to be restored
39 * which were saved in the native format of the platform they are extracted
40 * on. Later on we might add conversion functions for mapping from one
41 * platform to an other or allow restores of systems that use the same
44 * Its also interesting to see what the exact format of acl text is on
45 * certain platforms and if they use they same encoding we might allow
46 * different platform streams to be decoded on an other similar platform.
48 * Original written by Preben 'Peppe' Guldberg, December MMIV
49 * Major rewrite by Marco van Wieringen, November MMVIII
58 #if !defined(HAVE_ACL)
60 * Entry points when compiled without support for ACLs or on an unsupported platform.
62 bsub_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
64 return bsub_exit_fatal;
67 bsub_exit_code parse_acl_stream(JCR *jcr, int stream)
69 return bsub_exit_fatal;
73 * Send an ACL stream to the SD.
75 static bsub_exit_code send_acl_stream(JCR *jcr, int stream)
77 BSOCK *sd = jcr->store_bsock;
79 #ifdef FD_NO_SEND_TEST
86 if (jcr->acl_data_len <= 0)
92 if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
93 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
95 return bsub_exit_fatal;
99 * Send the buffer to the storage deamon
101 Dmsg1(400, "Backing up ACL <%s>\n", jcr->acl_data);
103 sd->msg = jcr->acl_data;
104 sd->msglen = jcr->acl_data_len + 1;
108 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
110 return bsub_exit_fatal;
113 jcr->JobBytes += sd->msglen;
115 if (!sd->signal(BNET_EOD)) {
116 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
118 return bsub_exit_fatal;
121 Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
125 #if defined(HAVE_AIX_OS)
127 #include <sys/access.h>
130 * Define the supported ACL streams for this OS
132 static int os_access_acl_streams[1] = { STREAM_ACL_AIX_TEXT };
133 static int os_default_acl_streams[1] = { -1 };
135 static bsub_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
139 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
140 jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
141 actuallyfree(acl_text);
142 return send_acl_stream(jcr, STREAM_ACL_AIX_TEXT);
144 return bsub_exit_nok;
147 static bsub_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
149 if (acl_put(jcr->last_fname, jcr->acl_data, 0) != 0) {
150 return bsub_exit_nok;
156 * For this OS setup the build and parse function pointer to the OS specific functions.
158 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = aix_build_acl_streams;
159 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = aix_parse_acl_streams;
161 #elif defined(HAVE_DARWIN_OS) \
162 || defined(HAVE_FREEBSD_OS) \
163 || defined(HAVE_IRIX_OS) \
164 || defined(HAVE_OSF1_OS) \
165 || defined(HAVE_LINUX_OS)
167 #include <sys/types.h>
169 #ifdef HAVE_SYS_ACL_H
172 #error "configure failed to detect availability of sys/acl.h"
175 /* On IRIX we can get shortened ACLs */
176 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
177 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
180 /* In Linux we can get numeric and/or shorted ACLs */
181 #if defined(HAVE_LINUX_OS)
182 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
183 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
184 #elif defined(BACL_WANT_SHORT_ACLS)
185 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
186 #elif defined(BACL_WANT_NUMERIC_IDS)
187 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
189 #ifdef BACL_ALTERNATE_TEXT
190 #include <acl/libacl.h>
191 #define acl_to_text(acl,len) (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
196 * Some generic functions used by multiple OSes.
198 static acl_type_t bac_to_os_acltype(bacl_type acltype)
203 case BACL_TYPE_ACCESS:
204 ostype = ACL_TYPE_ACCESS;
206 case BACL_TYPE_DEFAULT:
207 ostype = ACL_TYPE_DEFAULT;
210 #ifdef ACL_TYPE_DEFAULT_DIR
211 case BACL_TYPE_DEFAULT_DIR:
213 * OSF1 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
215 ostype = ACL_TYPE_DEFAULT_DIR;
218 #ifdef ACL_TYPE_EXTENDED
219 case BACL_TYPE_EXTENDED:
221 * MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
223 ostype = ACL_TYPE_EXTENDED;
228 * This should never happen, as the per OS version function only tries acl
229 * types supported on a certain platform.
231 ostype = (acl_type_t)ACL_TYPE_NONE;
237 #if !defined(HAVE_DARWIN_OS)
239 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
240 * There is no need to store those acls as we already store the stat bits too.
242 static bool acl_is_trivial(acl_t acl)
245 * acl is trivial if it has only the following entries:
252 #if defined(HAVE_FREEBSD_OS) || defined(HAVE_LINUX_OS)
255 entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
256 while (entry_available == 1) {
258 * Get the tag type of this acl entry.
259 * If we fail to get the tagtype we call the acl non-trivial.
261 if (acl_get_tag_type(ace, &tag) < 0)
264 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
266 if (tag != ACL_USER_OBJ &&
267 tag != ACL_GROUP_OBJ &&
270 entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
273 #elif defined(HAVE_IRIX_OS)
276 for (n = 0; n < acl->acl_cnt; n++) {
277 ace = &acl->acl_entry[n];
281 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
283 if (tag != ACL_USER_OBJ &&
284 tag != ACL_GROUP_OBJ &&
285 tag != ACL_OTHER_OBJ)
289 #elif defined(HAVE_OSF1_OS)
292 ace = acl->acl_first;
293 count = acl->acl_num;
296 tag = ace->entry->acl_type;
298 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
300 if (tag != ACL_USER_OBJ &&
301 tag != ACL_GROUP_OBJ &&
305 * On Tru64, perm can also contain non-standard bits such as
306 * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
308 if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)))
319 * Generic wrapper around acl_get_file call.
321 static bsub_exit_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype)
328 ostype = bac_to_os_acltype(acltype);
329 acl = acl_get_file(jcr->last_fname, ostype);
331 #if defined(HAVE_IRIX_OS)
333 * From observation, IRIX's acl_get_file() seems to return a
334 * non-NULL acl with a count field of -1 when a file has no ACL
335 * defined, while IRIX's acl_to_text() returns NULL when presented
338 * Checking the count in the acl structure before calling
339 * acl_to_text() lets us avoid error messages about files
340 * with no ACLs, without modifying the flow of the code used for
341 * other operating systems, and it saves making some calls
342 * to acl_to_text() besides.
344 if (acl->acl_cnt <= 0) {
345 pm_strcpy(jcr->acl_data, "");
346 jcr->acl_data_len = 0;
352 #if !defined(HAVE_DARWIN_OS)
354 * Make sure this is not just a trivial ACL.
356 if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
358 * The ACLs simply reflect the (already known) standard permissions
359 * So we don't send an ACL stream to the SD.
361 pm_strcpy(jcr->acl_data, "");
362 jcr->acl_data_len = 0;
368 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
369 jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
375 Jmsg2(jcr, M_ERROR, 0, _("acl_to_text error on file \"%s\": ERR=%s\n"),
376 jcr->last_fname, be.bstrerror());
377 Dmsg2(100, "acl_to_text error file=%s ERR=%s\n",
378 jcr->last_fname, be.bstrerror());
380 pm_strcpy(jcr->acl_data, "");
381 jcr->acl_data_len = 0;
383 return bsub_exit_nok;
387 * Handle errors gracefully.
389 if (acl == (acl_t)NULL) {
391 #if defined(BACL_ENOTSUP)
393 break; /* not supported */
396 pm_strcpy(jcr->acl_data, "");
397 jcr->acl_data_len = 0;
400 /* Some real error */
401 Jmsg2(jcr, M_ERROR, 0, _("acl_get_file error on file \"%s\": ERR=%s\n"),
402 jcr->last_fname, be.bstrerror());
403 Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",
404 jcr->last_fname, be.bstrerror());
406 pm_strcpy(jcr->acl_data, "");
407 jcr->acl_data_len = 0;
408 return bsub_exit_nok;
412 * Not supported, just pretend there is nothing to see
414 pm_strcpy(jcr->acl_data, "");
415 jcr->acl_data_len = 0;
420 * Generic wrapper around acl_set_file call.
422 static bsub_exit_code generic_set_acl_on_os(JCR *jcr, bacl_type acltype)
429 * If we get empty default ACLs, clear ACLs now
431 ostype = bac_to_os_acltype(acltype);
432 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_data) == 0) {
433 if (acl_delete_def_file(jcr->last_fname) == 0) {
440 Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
441 jcr->last_fname, be.bstrerror());
442 return bsub_exit_nok;
446 acl = acl_from_text(jcr->acl_data);
448 Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
449 jcr->last_fname, be.bstrerror());
450 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
451 jcr->acl_data, jcr->last_fname, be.bstrerror());
452 return bsub_exit_nok;
455 #ifndef HAVE_FREEBSD_OS
457 * FreeBSD always fails acl_valid() - at least on valid input...
458 * As it does the right thing, given valid input, just ignore acl_valid().
460 if (acl_valid(acl) != 0) {
461 Jmsg2(jcr, M_ERROR, 0, _("acl_valid error on file \"%s\": ERR=%s\n"),
462 jcr->last_fname, be.bstrerror());
463 Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",
464 jcr->acl_data, jcr->last_fname, be.bstrerror());
466 return bsub_exit_nok;
471 * Restore the ACLs, but don't complain about links which really should
472 * not have attributes, and the file it is linked to may not yet be restored.
473 * This is only true for the old acl streams as in the new implementation we
474 * don't save acls of symlinks (which cannot have acls anyhow)
476 if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
480 return bsub_exit_nok;
482 Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
483 jcr->last_fname, be.bstrerror());
484 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
485 jcr->acl_data, jcr->last_fname, be.bstrerror());
487 return bsub_exit_nok;
495 * OS specific functions for handling different types of acl streams.
497 #if defined(HAVE_DARWIN_OS)
499 * Define the supported ACL streams for this OS
501 static int os_access_acl_streams[1] = { STREAM_ACL_DARWIN_ACCESS_ACL };
502 static int os_default_acl_streams[1] = { -1 };
504 static bsub_exit_code darwin_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
506 #if defined(ACL_TYPE_EXTENDED)
508 * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
509 * and acl_get_file (name, ACL_TYPE_DEFAULT)
510 * always return NULL / EINVAL. There is no point in making
511 * these two useless calls. The real ACL is retrieved through
512 * acl_get_file (name, ACL_TYPE_EXTENDED).
514 * Read access ACLs for files, dirs and links
516 if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bsub_exit_fatal)
517 return bsub_exit_fatal;
520 * Read access ACLs for files, dirs and links
522 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bsub_exit_fatal)
523 return bsub_exit_fatal;
526 if (jcr->acl_data_len > 0) {
527 return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS_ACL);
532 static bsub_exit_code darwin_parse_acl_streams(JCR *jcr, int stream)
534 #if defined(ACL_TYPE_EXTENDED)
535 return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED);
537 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
542 * For this OS setup the build and parse function pointer to the OS specific functions.
544 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
545 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = darwin_parse_acl_streams;
547 #elif defined(HAVE_FREEBSD_OS) || defined(HAVE_IRIX_OS) || defined(HAVE_LINUX_OS)
550 * Define the supported ACL streams for these OSes
552 #if defined(HAVE_FREEBSD_OS)
553 static int os_access_acl_streams[1] = { STREAM_ACL_FREEBSD_ACCESS_ACL };
554 static int os_default_acl_streams[1] = { STREAM_ACL_FREEBSD_DEFAULT_ACL };
555 #elif defined(HAVE_IRIX_OS)
556 static int os_access_acl_streams[1] = { STREAM_ACL_IRIX_ACCESS_ACL };
557 static int os_default_acl_streams[1] = { STREAM_ACL_IRIX_DEFAULT_ACL };
558 #elif defined(HAVE_LINUX_OS)
559 static int os_access_acl_streams[1] = { STREAM_ACL_LINUX_ACCESS_ACL };
560 static int os_default_acl_streams[1] = { STREAM_ACL_LINUX_DEFAULT_ACL };
563 static bsub_exit_code generic_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
566 * Read access ACLs for files, dirs and links
568 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bsub_exit_fatal)
569 return bsub_exit_fatal;
571 if (jcr->acl_data_len > 0) {
572 if (send_acl_stream(jcr, os_access_acl_streams[0]) == bsub_exit_fatal)
573 return bsub_exit_fatal;
577 * Directories can have default ACLs too
579 if (ff_pkt->type == FT_DIREND) {
580 if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bsub_exit_fatal)
581 return bsub_exit_fatal;
582 if (jcr->acl_data_len > 0) {
583 if (send_acl_stream(jcr, os_default_acl_streams[0]) == bsub_exit_fatal)
584 return bsub_exit_fatal;
590 static bsub_exit_code generic_parse_acl_streams(JCR *jcr, int stream)
595 case STREAM_UNIX_ACCESS_ACL:
596 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
597 case STREAM_UNIX_DEFAULT_ACL:
598 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
601 * See what type of acl it is.
603 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
604 if (os_access_acl_streams[cnt] == stream) {
605 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
608 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
609 if (os_default_acl_streams[cnt] == stream) {
610 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
615 return bsub_exit_nok;
619 * For this OSes setup the build and parse function pointer to the OS specific functions.
621 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
622 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = generic_parse_acl_streams;
624 #elif defined(HAVE_OSF1_OS)
627 * Define the supported ACL streams for this OS
629 static int os_access_acl_streams[1] = { STREAM_ACL_TRU64_ACCESS_ACL };
630 static int os_default_acl_streams[2] = { STREAM_ACL_TRU64_DEFAULT_ACL, STREAM_ACL_TRU64_DEFAULT_DIR_ACL };
632 static bsub_exit_code tru64_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
635 * Read access ACLs for files, dirs and links
637 if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS)) < 0)
638 return bsub_exit_nok;
639 if (jcr->acl_data_len > 0) {
640 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
641 return bsub_exit_nok;
644 * Directories can have default ACLs too
646 if (ff_pkt->type == FT_DIREND) {
647 if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT)) < 0)
648 return bsub_exit_nok;
649 if (jcr->acl_data_len > 0) {
650 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
651 return bsub_exit_nok;
654 * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
655 * This is an inherited acl for all subdirs.
656 * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
657 * Section 21.5 Default ACLs
659 if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR)) < 0)
660 return bsub_exit_nok;
661 if (jcr->acl_data_len > 0) {
662 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
663 return bsub_exit_nok;
669 static bsub_exit_code tru64_parse_acl_streams(JCR *jcr, int stream)
672 case STREAM_UNIX_ACCESS_ACL:
673 case STREAM_ACL_TRU64_ACCESS_ACL:
674 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
675 case STREAM_UNIX_DEFAULT_ACL:
676 case STREAM_ACL_TRU64_DEFAULT_ACL:
677 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
678 case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
679 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR);
683 * For this OS setup the build and parse function pointer to the OS specific functions.
685 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
686 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = tru64_parse_acl_streams;
690 #elif defined(HAVE_HPUX_OS)
691 #ifdef HAVE_SYS_ACL_H
694 #error "configure failed to detect availability of sys/acl.h"
700 * Define the supported ACL streams for this OS
702 static int os_access_acl_streams[1] = { STREAM_ACL_HPUX_ACL_ENTRY };
703 static int os_default_acl_streams[1] = { -1 };
706 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
707 * There is no need to store those acls as we already store the stat bits too.
709 static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb)
714 for (n = 0; n < count; n++) {
717 * See if this acl just is the stat mode in acl form.
719 if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
720 (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
721 (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
728 * OS specific functions for handling different types of acl streams.
730 static bsub_exit_code hpux_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
733 struct acl_entry acls[NACLENTRIES];
737 if ((n = getacl(jcr->last_fname, 0, acls)) < 0) {
739 #if defined(BACL_ENOTSUP)
742 * Not supported, just pretend there is nothing to see
744 pm_strcpy(jcr->acl_data, "");
745 jcr->acl_data_len = 0;
749 pm_strcpy(jcr->acl_data, "");
750 jcr->acl_data_len = 0;
753 Jmsg2(jcr, M_ERROR, 0, _("getacl error on file \"%s\": ERR=%s\n"),
754 jcr->last_fname, be.bstrerror());
755 Dmsg2(100, "getacl error file=%s ERR=%s\n",
756 jcr->last_fname, be.bstrerror());
758 pm_strcpy(jcr->acl_data, "");
759 jcr->acl_data_len = 0;
760 return bsub_exit_nok;
764 pm_strcpy(jcr->acl_data, "");
765 jcr->acl_data_len = 0;
768 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
769 if (acl_is_trivial(n, acls, ff_pkt->statp)) {
771 * The ACLs simply reflect the (already known) standard permissions
772 * So we don't send an ACL stream to the SD.
774 pm_strcpy(jcr->acl_data, "");
775 jcr->acl_data_len = 0;
778 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
779 jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
780 actuallyfree(acl_text);
782 return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY);
784 Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
785 jcr->last_fname, be.bstrerror());
786 Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",
787 jcr->acl_data, jcr->last_fname, be.bstrerror());
788 return bsub_exit_nok;
790 return bsub_exit_nok;
793 static bsub_exit_code hpux_parse_acl_streams(JCR *jcr, int stream)
796 struct acl_entry acls[NACLENTRIES];
799 n = strtoacl(jcr->acl_data, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
801 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
802 jcr->last_fname, be.bstrerror());
803 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
804 jcr->acl_data, jcr->last_fname, be.bstrerror());
805 return bsub_exit_nok;
807 if (strtoacl(jcr->acl_data, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
808 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
809 jcr->last_fname, be.bstrerror());
810 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
811 jcr->acl_data, jcr->last_fname, be.bstrerror());
813 return bsub_exit_nok;
816 * Restore the ACLs, but don't complain about links which really should
817 * not have attributes, and the file it is linked to may not yet be restored.
818 * This is only true for the old acl streams as in the new implementation we
819 * don't save acls of symlinks (which cannot have acls anyhow)
821 if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
824 return bsub_exit_nok;
826 Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
827 jcr->last_fname, be.bstrerror());
828 Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
829 jcr->acl_data, jcr->last_fname, be.bstrerror());
830 return bsub_exit_nok;
837 * For this OS setup the build and parse function pointer to the OS specific functions.
839 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
840 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = hpux_parse_acl_streams;
842 #elif defined(HAVE_SUN_OS)
843 #ifdef HAVE_SYS_ACL_H
846 #error "configure failed to detect availability of sys/acl.h"
849 #if defined(HAVE_EXTENDED_ACL)
851 * We define some internals of the Solaris acl libs here as those
852 * are not exposed yet. Probably because they want us to see the
853 * acls as opague data. But as we need to support different platforms
854 * and versions of Solaris we need to expose some data to be able
855 * to determine the type of acl used to stuff it into the correct
856 * data stream. I know this is far from portable, but maybe the
857 * proper interface is exposed later on and we can get ride of
858 * this kludge. Newer versions of Solaris include sys/acl_impl.h
859 * which has implementation details of acls, if thats included we
860 * don't have to define it ourself.
862 #if !defined(_SYS_ACL_IMPL_H)
863 typedef enum acl_type {
870 * Two external references to functions in the libsec library function not in current include files.
873 int acl_type(acl_t *);
874 char *acl_strerror(int);
878 * Define the supported ACL streams for this OS
880 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT, STREAM_ACL_SOLARIS_ACE };
881 static int os_default_acl_streams[1] = { -1 };
884 * As the new libsec interface with acl_totext and acl_fromtext also handles
885 * the old format from acltotext we can use the new functions even
886 * for acls retrieved and stored in the database with older fd versions. If the
887 * new interface is not defined (Solaris 9 and older we fall back to the old code)
889 static bsub_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
891 int acl_enabled, flags;
894 bsub_exit_code stream_status = bsub_exit_nok;
898 * See if filesystem supports acls.
900 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
901 switch (acl_enabled) {
903 pm_strcpy(jcr->acl_data, "");
904 jcr->acl_data_len = 0;
907 Jmsg2(jcr, M_ERROR, 0, _("pathconf error on file \"%s\": ERR=%s\n"),
908 jcr->last_fname, be.bstrerror());
909 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
910 jcr->last_fname, be.bstrerror());
911 return bsub_exit_nok;
917 * Get ACL info: don't bother allocating space if there is only a trivial ACL.
919 if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
920 Jmsg2(jcr, M_ERROR, 0, _("acl_get error on file \"%s\": ERR=%s\n"),
921 jcr->last_fname, acl_strerror(errno));
922 Dmsg2(100, "acl_get error file=%s ERR=%s\n",
923 jcr->last_fname, acl_strerror(errno));
924 return bsub_exit_nok;
929 * The ACLs simply reflect the (already known) standard permissions
930 * So we don't send an ACL stream to the SD.
932 pm_strcpy(jcr->acl_data, "");
933 jcr->acl_data_len = 0;
937 #if defined(ACL_SID_FMT)
939 * New format flag added in newer Solaris versions.
941 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
943 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
944 #endif /* ACL_SID_FMT */
946 if ((acl_text = acl_totext(aclp, flags)) != NULL) {
947 jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
948 actuallyfree(acl_text);
950 switch (acl_type(aclp)) {
952 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
955 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACE);
963 return stream_status;
966 static bsub_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
969 int acl_enabled, error;
973 case STREAM_UNIX_ACCESS_ACL:
974 case STREAM_ACL_SOLARIS_ACLENT:
975 case STREAM_ACL_SOLARIS_ACE:
977 * First make sure the filesystem supports acls.
979 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
980 switch (acl_enabled) {
982 Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
984 return bsub_exit_nok;
986 Jmsg2(jcr, M_ERROR, 0, _("pathconf error on file \"%s\": ERR=%s\n"),
987 jcr->last_fname, be.bstrerror());
988 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
989 jcr->acl_data, jcr->last_fname, be.bstrerror());
990 return bsub_exit_nok;
993 * On a filesystem with ACL support make sure this particilar ACL type can be restored.
996 case STREAM_ACL_SOLARIS_ACLENT:
998 * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1000 if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1001 Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without aclent acl support\n"),
1003 return bsub_exit_nok;
1006 case STREAM_ACL_SOLARIS_ACE:
1008 * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1010 if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1011 Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without ace acl support\n"),
1013 return bsub_exit_nok;
1018 * Stream id which doesn't describe the type of acl which is encoded.
1025 if ((error = acl_fromtext(jcr->acl_data, &aclp)) != 0) {
1026 Jmsg2(jcr, M_ERROR, 0, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1027 jcr->last_fname, acl_strerror(error));
1028 Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",
1029 jcr->acl_data, jcr->last_fname, acl_strerror(error));
1030 return bsub_exit_nok;
1034 * Validate that the conversion gave us the correct acl type.
1037 case STREAM_ACL_SOLARIS_ACLENT:
1038 if (acl_type(aclp) != ACLENT_T) {
1039 Jmsg1(jcr, M_ERROR, 0, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1041 return bsub_exit_nok;
1044 case STREAM_ACL_SOLARIS_ACE:
1045 if (acl_type(aclp) != ACE_T) {
1046 Jmsg1(jcr, M_ERROR, 0, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1048 return bsub_exit_nok;
1053 * Stream id which doesn't describe the type of acl which is encoded.
1059 * Restore the ACLs, but don't complain about links which really should
1060 * not have attributes, and the file it is linked to may not yet be restored.
1061 * This is only true for the old acl streams as in the new implementation we
1062 * don't save acls of symlinks (which cannot have acls anyhow)
1064 if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
1065 Jmsg2(jcr, M_ERROR, 0, _("acl_set error on file \"%s\": ERR=%s\n"),
1066 jcr->last_fname, acl_strerror(error));
1067 Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",
1068 jcr->acl_data, jcr->last_fname, acl_strerror(error));
1071 return bsub_exit_nok;
1075 return bsub_exit_ok;
1077 return bsub_exit_nok;
1078 } /* end switch (stream) */
1081 #else /* HAVE_EXTENDED_ACL */
1084 * Define the supported ACL streams for this OS
1086 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
1087 static int os_default_acl_streams[1] = { -1 };
1090 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1091 * There is no need to store those acls as we already store the stat bits too.
1093 static bool acl_is_trivial(int count, aclent_t *entries)
1098 for (n = 0; n < count; n++) {
1101 if (!(ace->a_type == USER_OBJ ||
1102 ace->a_type == GROUP_OBJ ||
1103 ace->a_type == OTHER_OBJ ||
1104 ace->a_type == CLASS_OBJ))
1111 * OS specific functions for handling different types of acl streams.
1113 static bsub_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1120 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
1121 if (n < MIN_ACL_ENTRIES)
1122 return bsub_exit_nok;
1124 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1125 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
1126 if (acl_is_trivial(n, acls)) {
1128 * The ACLs simply reflect the (already known) standard permissions
1129 * So we don't send an ACL stream to the SD.
1132 pm_strcpy(jcr->acl_data, "");
1133 jcr->acl_data_len = 0;
1134 return bsub_exit_ok;
1137 if ((acl_text = acltotext(acls, n)) != NULL) {
1138 jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
1139 actuallyfree(acl_text);
1141 return send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1144 Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
1145 jcr->last_fname, be.bstrerror());
1146 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
1147 jcr->acl_data, jcr->last_fname, be.bstrerror());
1151 return bsub_exit_nok;
1154 static bsub_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1160 acls = aclfromtext(jcr->acl_data, &n);
1162 Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
1163 jcr->last_fname, be.bstrerror());
1164 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
1165 jcr->acl_data, jcr->last_fname, be.bstrerror());
1166 return bsub_exit_nok;
1170 * Restore the ACLs, but don't complain about links which really should
1171 * not have attributes, and the file it is linked to may not yet be restored.
1173 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
1177 return bsub_exit_nok;
1179 Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
1180 jcr->last_fname, be.bstrerror());
1181 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
1182 jcr->acl_data, jcr->last_fname, be.bstrerror());
1184 return bsub_exit_nok;
1188 return bsub_exit_ok;
1190 #endif /* HAVE_EXTENDED_ACL */
1193 * For this OS setup the build and parse function pointer to the OS specific functions.
1195 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
1196 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
1198 #endif /* HAVE_SUN_OS */
1201 * Entry points when compiled with support for ACLs on a supported platform.
1205 * Read and send an ACL for the last encountered file.
1207 bsub_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1210 * Call the appropriate function.
1212 if (os_build_acl_streams) {
1213 return (*os_build_acl_streams)(jcr, ff_pkt);
1215 return bsub_exit_nok;
1218 bsub_exit_code parse_acl_streams(JCR *jcr, int stream)
1223 case STREAM_UNIX_ACCESS_ACL:
1224 case STREAM_UNIX_DEFAULT_ACL:
1226 * Handle legacy ACL streams.
1228 if (os_parse_acl_streams) {
1229 return (*os_parse_acl_streams)(jcr, stream);
1233 if (os_parse_acl_streams) {
1235 * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
1237 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1238 if (os_access_acl_streams[cnt] == stream) {
1239 return (*os_parse_acl_streams)(jcr, stream);
1243 * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
1245 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1246 if (os_default_acl_streams[cnt] == stream) {
1247 return (*os_parse_acl_streams)(jcr, stream);
1253 Qmsg2(jcr, M_WARNING, 0,
1254 _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
1255 jcr->last_fname, stream);
1256 return bsub_exit_nok;