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)
327 ostype = bac_to_os_acltype(acltype);
328 acl = acl_get_file(jcr->last_fname, ostype);
330 #if defined(HAVE_IRIX_OS)
332 * From observation, IRIX's acl_get_file() seems to return a
333 * non-NULL acl with a count field of -1 when a file has no ACL
334 * defined, while IRIX's acl_to_text() returns NULL when presented
337 * Checking the count in the acl structure before calling
338 * acl_to_text() lets us avoid error messages about files
339 * with no ACLs, without modifying the flow of the code used for
340 * other operating systems, and it saves making some calls
341 * to acl_to_text() besides.
343 if (acl->acl_cnt <= 0) {
344 pm_strcpy(jcr->acl_data, "");
345 jcr->acl_data_len = 0;
351 #if !defined(HAVE_DARWIN_OS)
353 * Make sure this is not just a trivial ACL.
355 if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
357 * The ACLs simply reflect the (already known) standard permissions
358 * So we don't send an ACL stream to the SD.
360 pm_strcpy(jcr->acl_data, "");
361 jcr->acl_data_len = 0;
367 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
368 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;
401 /* Some real error */
402 Jmsg2(jcr, M_ERROR, 0, _("acl_get_file error on file \"%s\": ERR=%s\n"),
403 jcr->last_fname, be.bstrerror());
404 Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",
405 jcr->last_fname, be.bstrerror());
407 pm_strcpy(jcr->acl_data, "");
408 jcr->acl_data_len = 0;
409 return bsub_exit_nok;
413 * Not supported, just pretend there is nothing to see
415 pm_strcpy(jcr->acl_data, "");
416 jcr->acl_data_len = 0;
421 * Generic wrapper around acl_set_file call.
423 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) {
441 Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
442 jcr->last_fname, be.bstrerror());
443 return bsub_exit_nok;
447 acl = acl_from_text(jcr->acl_data);
450 Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
451 jcr->last_fname, be.bstrerror());
452 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
453 jcr->acl_data, jcr->last_fname, be.bstrerror());
454 return bsub_exit_nok;
457 #ifndef HAVE_FREEBSD_OS
459 * FreeBSD always fails acl_valid() - at least on valid input...
460 * As it does the right thing, given valid input, just ignore acl_valid().
462 if (acl_valid(acl) != 0) {
464 Jmsg2(jcr, M_ERROR, 0, _("acl_valid error on file \"%s\": ERR=%s\n"),
465 jcr->last_fname, be.bstrerror());
466 Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",
467 jcr->acl_data, jcr->last_fname, be.bstrerror());
469 return bsub_exit_nok;
474 * Restore the ACLs, but don't complain about links which really should
475 * not have attributes, and the file it is linked to may not yet be restored.
476 * This is only true for the old acl streams as in the new implementation we
477 * don't save acls of symlinks (which cannot have acls anyhow)
479 if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
483 return bsub_exit_nok;
486 Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
487 jcr->last_fname, be.bstrerror());
488 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
489 jcr->acl_data, jcr->last_fname, be.bstrerror());
491 return bsub_exit_nok;
499 * OS specific functions for handling different types of acl streams.
501 #if defined(HAVE_DARWIN_OS)
503 * Define the supported ACL streams for this OS
505 static int os_access_acl_streams[1] = { STREAM_ACL_DARWIN_ACCESS_ACL };
506 static int os_default_acl_streams[1] = { -1 };
508 static bsub_exit_code darwin_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
510 #if defined(ACL_TYPE_EXTENDED)
512 * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
513 * and acl_get_file (name, ACL_TYPE_DEFAULT)
514 * always return NULL / EINVAL. There is no point in making
515 * these two useless calls. The real ACL is retrieved through
516 * acl_get_file (name, ACL_TYPE_EXTENDED).
518 * Read access ACLs for files, dirs and links
520 if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bsub_exit_fatal)
521 return bsub_exit_fatal;
524 * Read access ACLs for files, dirs and links
526 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bsub_exit_fatal)
527 return bsub_exit_fatal;
530 if (jcr->acl_data_len > 0) {
531 return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS_ACL);
536 static bsub_exit_code darwin_parse_acl_streams(JCR *jcr, int stream)
538 #if defined(ACL_TYPE_EXTENDED)
539 return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED);
541 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
546 * For this OS setup the build and parse function pointer to the OS specific functions.
548 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
549 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = darwin_parse_acl_streams;
551 #elif defined(HAVE_FREEBSD_OS) || defined(HAVE_IRIX_OS) || defined(HAVE_LINUX_OS)
554 * Define the supported ACL streams for these OSes
556 #if defined(HAVE_FREEBSD_OS)
557 static int os_access_acl_streams[1] = { STREAM_ACL_FREEBSD_ACCESS_ACL };
558 static int os_default_acl_streams[1] = { STREAM_ACL_FREEBSD_DEFAULT_ACL };
559 #elif defined(HAVE_IRIX_OS)
560 static int os_access_acl_streams[1] = { STREAM_ACL_IRIX_ACCESS_ACL };
561 static int os_default_acl_streams[1] = { STREAM_ACL_IRIX_DEFAULT_ACL };
562 #elif defined(HAVE_LINUX_OS)
563 static int os_access_acl_streams[1] = { STREAM_ACL_LINUX_ACCESS_ACL };
564 static int os_default_acl_streams[1] = { STREAM_ACL_LINUX_DEFAULT_ACL };
567 static bsub_exit_code generic_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
570 * Read access ACLs for files, dirs and links
572 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bsub_exit_fatal)
573 return bsub_exit_fatal;
575 if (jcr->acl_data_len > 0) {
576 if (send_acl_stream(jcr, os_access_acl_streams[0]) == bsub_exit_fatal)
577 return bsub_exit_fatal;
581 * Directories can have default ACLs too
583 if (ff_pkt->type == FT_DIREND) {
584 if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bsub_exit_fatal)
585 return bsub_exit_fatal;
586 if (jcr->acl_data_len > 0) {
587 if (send_acl_stream(jcr, os_default_acl_streams[0]) == bsub_exit_fatal)
588 return bsub_exit_fatal;
594 static bsub_exit_code generic_parse_acl_streams(JCR *jcr, int stream)
599 case STREAM_UNIX_ACCESS_ACL:
600 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
601 case STREAM_UNIX_DEFAULT_ACL:
602 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
605 * See what type of acl it is.
607 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
608 if (os_access_acl_streams[cnt] == stream) {
609 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
612 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
613 if (os_default_acl_streams[cnt] == stream) {
614 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
619 return bsub_exit_nok;
623 * For this OSes setup the build and parse function pointer to the OS specific functions.
625 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
626 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = generic_parse_acl_streams;
628 #elif defined(HAVE_OSF1_OS)
631 * Define the supported ACL streams for this OS
633 static int os_access_acl_streams[1] = { STREAM_ACL_TRU64_ACCESS_ACL };
634 static int os_default_acl_streams[2] = { STREAM_ACL_TRU64_DEFAULT_ACL, STREAM_ACL_TRU64_DEFAULT_DIR_ACL };
636 static bsub_exit_code tru64_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
639 * Read access ACLs for files, dirs and links
641 if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS)) < 0)
642 return bsub_exit_nok;
643 if (jcr->acl_data_len > 0) {
644 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
645 return bsub_exit_nok;
648 * Directories can have default ACLs too
650 if (ff_pkt->type == FT_DIREND) {
651 if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT)) < 0)
652 return bsub_exit_nok;
653 if (jcr->acl_data_len > 0) {
654 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
655 return bsub_exit_nok;
658 * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
659 * This is an inherited acl for all subdirs.
660 * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
661 * Section 21.5 Default ACLs
663 if ((jcr->acl_data_len = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR)) < 0)
664 return bsub_exit_nok;
665 if (jcr->acl_data_len > 0) {
666 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
667 return bsub_exit_nok;
673 static bsub_exit_code tru64_parse_acl_streams(JCR *jcr, int stream)
676 case STREAM_UNIX_ACCESS_ACL:
677 case STREAM_ACL_TRU64_ACCESS_ACL:
678 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
679 case STREAM_UNIX_DEFAULT_ACL:
680 case STREAM_ACL_TRU64_DEFAULT_ACL:
681 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
682 case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
683 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR);
687 * For this OS setup the build and parse function pointer to the OS specific functions.
689 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
690 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = tru64_parse_acl_streams;
694 #elif defined(HAVE_HPUX_OS)
695 #ifdef HAVE_SYS_ACL_H
698 #error "configure failed to detect availability of sys/acl.h"
704 * Define the supported ACL streams for this OS
706 static int os_access_acl_streams[1] = { STREAM_ACL_HPUX_ACL_ENTRY };
707 static int os_default_acl_streams[1] = { -1 };
710 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
711 * There is no need to store those acls as we already store the stat bits too.
713 static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb)
718 for (n = 0; n < count; n++) {
721 * See if this acl just is the stat mode in acl form.
723 if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
724 (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
725 (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
732 * OS specific functions for handling different types of acl streams.
734 static bsub_exit_code hpux_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
737 struct acl_entry acls[NACLENTRIES];
740 if ((n = getacl(jcr->last_fname, 0, acls)) < 0) {
742 #if defined(BACL_ENOTSUP)
745 * Not supported, just pretend there is nothing to see
747 pm_strcpy(jcr->acl_data, "");
748 jcr->acl_data_len = 0;
752 pm_strcpy(jcr->acl_data, "");
753 jcr->acl_data_len = 0;
757 Jmsg2(jcr, M_ERROR, 0, _("getacl error on file \"%s\": ERR=%s\n"),
758 jcr->last_fname, be.bstrerror());
759 Dmsg2(100, "getacl error file=%s ERR=%s\n",
760 jcr->last_fname, be.bstrerror());
762 pm_strcpy(jcr->acl_data, "");
763 jcr->acl_data_len = 0;
764 return bsub_exit_nok;
768 pm_strcpy(jcr->acl_data, "");
769 jcr->acl_data_len = 0;
772 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
773 if (acl_is_trivial(n, acls, ff_pkt->statp)) {
775 * The ACLs simply reflect the (already known) standard permissions
776 * So we don't send an ACL stream to the SD.
778 pm_strcpy(jcr->acl_data, "");
779 jcr->acl_data_len = 0;
782 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
783 jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
784 actuallyfree(acl_text);
786 return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY);
789 Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
790 jcr->last_fname, be.bstrerror());
791 Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",
792 jcr->acl_data, jcr->last_fname, be.bstrerror());
793 return bsub_exit_nok;
795 return bsub_exit_nok;
798 static bsub_exit_code hpux_parse_acl_streams(JCR *jcr, int stream)
801 struct acl_entry acls[NACLENTRIES];
803 n = strtoacl(jcr->acl_data, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
806 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
807 jcr->last_fname, be.bstrerror());
808 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
809 jcr->acl_data, jcr->last_fname, be.bstrerror());
810 return bsub_exit_nok;
812 if (strtoacl(jcr->acl_data, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
814 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
815 jcr->last_fname, be.bstrerror());
816 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
817 jcr->acl_data, jcr->last_fname, be.bstrerror());
819 return bsub_exit_nok;
822 * Restore the ACLs, but don't complain about links which really should
823 * not have attributes, and the file it is linked to may not yet be restored.
824 * This is only true for the old acl streams as in the new implementation we
825 * don't save acls of symlinks (which cannot have acls anyhow)
827 if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
830 return bsub_exit_nok;
833 Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
834 jcr->last_fname, be.bstrerror());
835 Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
836 jcr->acl_data, jcr->last_fname, be.bstrerror());
837 return bsub_exit_nok;
844 * For this OS setup the build and parse function pointer to the OS specific functions.
846 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
847 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = hpux_parse_acl_streams;
849 #elif defined(HAVE_SUN_OS)
850 #ifdef HAVE_SYS_ACL_H
853 #error "configure failed to detect availability of sys/acl.h"
856 #if defined(HAVE_EXTENDED_ACL)
858 * We define some internals of the Solaris acl libs here as those
859 * are not exposed yet. Probably because they want us to see the
860 * acls as opague data. But as we need to support different platforms
861 * and versions of Solaris we need to expose some data to be able
862 * to determine the type of acl used to stuff it into the correct
863 * data stream. I know this is far from portable, but maybe the
864 * proper interface is exposed later on and we can get ride of
865 * this kludge. Newer versions of Solaris include sys/acl_impl.h
866 * which has implementation details of acls, if thats included we
867 * don't have to define it ourself.
869 #if !defined(_SYS_ACL_IMPL_H)
870 typedef enum acl_type {
877 * Two external references to functions in the libsec library function not in current include files.
880 int acl_type(acl_t *);
881 char *acl_strerror(int);
885 * Define the supported ACL streams for this OS
887 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT, STREAM_ACL_SOLARIS_ACE };
888 static int os_default_acl_streams[1] = { -1 };
891 * As the new libsec interface with acl_totext and acl_fromtext also handles
892 * the old format from acltotext we can use the new functions even
893 * for acls retrieved and stored in the database with older fd versions. If the
894 * new interface is not defined (Solaris 9 and older we fall back to the old code)
896 static bsub_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
898 int acl_enabled, flags;
901 bsub_exit_code stream_status = bsub_exit_nok;
904 * See if filesystem supports acls.
906 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
907 switch (acl_enabled) {
909 pm_strcpy(jcr->acl_data, "");
910 jcr->acl_data_len = 0;
914 Jmsg2(jcr, M_ERROR, 0, _("pathconf error on file \"%s\": ERR=%s\n"),
915 jcr->last_fname, be.bstrerror());
916 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
917 jcr->last_fname, be.bstrerror());
918 return bsub_exit_nok;
924 * Get ACL info: don't bother allocating space if there is only a trivial ACL.
926 if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
927 Jmsg2(jcr, M_ERROR, 0, _("acl_get error on file \"%s\": ERR=%s\n"),
928 jcr->last_fname, acl_strerror(errno));
929 Dmsg2(100, "acl_get error file=%s ERR=%s\n",
930 jcr->last_fname, acl_strerror(errno));
931 return bsub_exit_nok;
936 * The ACLs simply reflect the (already known) standard permissions
937 * So we don't send an ACL stream to the SD.
939 pm_strcpy(jcr->acl_data, "");
940 jcr->acl_data_len = 0;
944 #if defined(ACL_SID_FMT)
946 * New format flag added in newer Solaris versions.
948 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
950 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
951 #endif /* ACL_SID_FMT */
953 if ((acl_text = acl_totext(aclp, flags)) != NULL) {
954 jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
955 actuallyfree(acl_text);
957 switch (acl_type(aclp)) {
959 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
962 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACE);
970 return stream_status;
973 static bsub_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
976 int acl_enabled, error;
979 case STREAM_UNIX_ACCESS_ACL:
980 case STREAM_ACL_SOLARIS_ACLENT:
981 case STREAM_ACL_SOLARIS_ACE:
983 * First make sure the filesystem supports acls.
985 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
986 switch (acl_enabled) {
988 Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
990 return bsub_exit_nok;
993 Jmsg2(jcr, M_ERROR, 0, _("pathconf error on file \"%s\": ERR=%s\n"),
994 jcr->last_fname, be.bstrerror());
995 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
996 jcr->acl_data, jcr->last_fname, be.bstrerror());
997 return bsub_exit_nok;
1000 * On a filesystem with ACL support make sure this particilar ACL type can be restored.
1003 case STREAM_ACL_SOLARIS_ACLENT:
1005 * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1007 if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1008 Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without aclent acl support\n"),
1010 return bsub_exit_nok;
1013 case STREAM_ACL_SOLARIS_ACE:
1015 * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1017 if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1018 Jmsg1(jcr, M_ERROR, 0, _("Trying to restore acl on file \"%s\" on filesystem without ace acl support\n"),
1020 return bsub_exit_nok;
1025 * Stream id which doesn't describe the type of acl which is encoded.
1032 if ((error = acl_fromtext(jcr->acl_data, &aclp)) != 0) {
1033 Jmsg2(jcr, M_ERROR, 0, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1034 jcr->last_fname, acl_strerror(error));
1035 Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",
1036 jcr->acl_data, jcr->last_fname, acl_strerror(error));
1037 return bsub_exit_nok;
1041 * Validate that the conversion gave us the correct acl type.
1044 case STREAM_ACL_SOLARIS_ACLENT:
1045 if (acl_type(aclp) != ACLENT_T) {
1046 Jmsg1(jcr, M_ERROR, 0, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1048 return bsub_exit_nok;
1051 case STREAM_ACL_SOLARIS_ACE:
1052 if (acl_type(aclp) != ACE_T) {
1053 Jmsg1(jcr, M_ERROR, 0, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1055 return bsub_exit_nok;
1060 * Stream id which doesn't describe the type of acl which is encoded.
1066 * Restore the ACLs, but don't complain about links which really should
1067 * not have attributes, and the file it is linked to may not yet be restored.
1068 * This is only true for the old acl streams as in the new implementation we
1069 * don't save acls of symlinks (which cannot have acls anyhow)
1071 if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
1072 Jmsg2(jcr, M_ERROR, 0, _("acl_set error on file \"%s\": ERR=%s\n"),
1073 jcr->last_fname, acl_strerror(error));
1074 Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",
1075 jcr->acl_data, jcr->last_fname, acl_strerror(error));
1078 return bsub_exit_nok;
1082 return bsub_exit_ok;
1084 return bsub_exit_nok;
1085 } /* end switch (stream) */
1088 #else /* HAVE_EXTENDED_ACL */
1091 * Define the supported ACL streams for this OS
1093 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
1094 static int os_default_acl_streams[1] = { -1 };
1097 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1098 * There is no need to store those acls as we already store the stat bits too.
1100 static bool acl_is_trivial(int count, aclent_t *entries)
1105 for (n = 0; n < count; n++) {
1108 if (!(ace->a_type == USER_OBJ ||
1109 ace->a_type == GROUP_OBJ ||
1110 ace->a_type == OTHER_OBJ ||
1111 ace->a_type == CLASS_OBJ))
1118 * OS specific functions for handling different types of acl streams.
1120 static bsub_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1126 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
1127 if (n < MIN_ACL_ENTRIES)
1128 return bsub_exit_nok;
1130 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1131 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
1132 if (acl_is_trivial(n, acls)) {
1134 * The ACLs simply reflect the (already known) standard permissions
1135 * So we don't send an ACL stream to the SD.
1138 pm_strcpy(jcr->acl_data, "");
1139 jcr->acl_data_len = 0;
1140 return bsub_exit_ok;
1143 if ((acl_text = acltotext(acls, n)) != NULL) {
1144 jcr->acl_data_len = pm_strcpy(jcr->acl_data, acl_text);
1145 actuallyfree(acl_text);
1147 return send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1151 Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
1152 jcr->last_fname, be.bstrerror());
1153 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
1154 jcr->acl_data, jcr->last_fname, be.bstrerror());
1158 return bsub_exit_nok;
1161 static bsub_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1166 acls = aclfromtext(jcr->acl_data, &n);
1169 Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
1170 jcr->last_fname, be.bstrerror());
1171 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
1172 jcr->acl_data, jcr->last_fname, be.bstrerror());
1173 return bsub_exit_nok;
1177 * Restore the ACLs, but don't complain about links which really should
1178 * not have attributes, and the file it is linked to may not yet be restored.
1180 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
1184 return bsub_exit_nok;
1187 Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
1188 jcr->last_fname, be.bstrerror());
1189 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
1190 jcr->acl_data, jcr->last_fname, be.bstrerror());
1192 return bsub_exit_nok;
1196 return bsub_exit_ok;
1198 #endif /* HAVE_EXTENDED_ACL */
1201 * For this OS setup the build and parse function pointer to the OS specific functions.
1203 static bsub_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
1204 static bsub_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
1206 #endif /* HAVE_SUN_OS */
1209 * Entry points when compiled with support for ACLs on a supported platform.
1213 * Read and send an ACL for the last encountered file.
1215 bsub_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1218 * Call the appropriate function.
1220 if (os_build_acl_streams) {
1221 return (*os_build_acl_streams)(jcr, ff_pkt);
1223 return bsub_exit_nok;
1226 bsub_exit_code parse_acl_streams(JCR *jcr, int stream)
1229 case STREAM_UNIX_ACCESS_ACL:
1230 case STREAM_UNIX_DEFAULT_ACL:
1232 * Handle legacy ACL streams.
1234 if (os_parse_acl_streams) {
1235 return (*os_parse_acl_streams)(jcr, stream);
1239 if (os_parse_acl_streams) {
1241 * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
1243 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1244 if (os_access_acl_streams[cnt] == stream) {
1245 return (*os_parse_acl_streams)(jcr, stream);
1249 * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
1251 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1252 if (os_default_acl_streams[cnt] == stream) {
1253 return (*os_parse_acl_streams)(jcr, stream);
1259 Qmsg2(jcr, M_WARNING, 0,
1260 _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
1261 jcr->last_fname, stream);
1262 return bsub_exit_nok;