2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2010 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 three of the GNU Affero 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 Affero 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 * Currently we support the following OSes:
32 * - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface)
34 * - FreeBSD (POSIX and NFSv4/ZFS acls)
38 * - Solaris (POSIX and NFSv4/ZFS acls)
41 * We handle two different types of ACLs: access and default ACLS.
42 * On most systems that support default ACLs they only apply to directories.
44 * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs
45 * independently, while others (eg. Solaris) provide both in one call.
47 * The Filed saves ACLs in their native format and uses different streams
48 * for all different platforms. Currently we only allow ACLs to be restored
49 * which were saved in the native format of the platform they are extracted
50 * on. Later on we might add conversion functions for mapping from one
51 * platform to an other or allow restores of systems that use the same
54 * Its also interesting to see what the exact format of acl text is on
55 * certain platforms and if they use they same encoding we might allow
56 * different platform streams to be decoded on an other similar platform.
58 * Original written by Preben 'Peppe' Guldberg, December MMIV
59 * Major rewrite by Marco van Wieringen, November MMVIII
65 #if !defined(HAVE_ACL)
67 * Entry points when compiled without support for ACLs or on an unsupported platform.
69 bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
71 return bacl_exit_fatal;
74 bacl_exit_code parse_acl_streams(JCR *jcr, int stream)
76 return bacl_exit_fatal;
80 * Send an ACL stream to the SD.
82 static bacl_exit_code send_acl_stream(JCR *jcr, int stream)
84 BSOCK *sd = jcr->store_bsock;
86 #ifdef FD_NO_SEND_TEST
93 if (jcr->acl_data->content_length <= 0) {
100 if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
101 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
103 return bacl_exit_fatal;
107 * Send the buffer to the storage deamon
109 Dmsg1(400, "Backing up ACL <%s>\n", jcr->acl_data->content);
111 sd->msg = jcr->acl_data->content;
112 sd->msglen = jcr->acl_data->content_length + 1;
116 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
118 return bacl_exit_fatal;
121 jcr->JobBytes += sd->msglen;
123 if (!sd->signal(BNET_EOD)) {
124 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
126 return bacl_exit_fatal;
129 Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
134 * First the native ACLs.
136 #if defined(HAVE_ACL)
137 #if defined(HAVE_AIX_OS)
139 #if defined(HAVE_EXTENDED_ACL)
141 #include <sys/access.h>
144 static bool acl_is_trivial(struct acl *acl)
146 return (acl_last(acl) != acl->acl_ext ? false : true);
149 static bool acl_nfs4_is_trivial(nfs4_acl_int_t *acl)
151 return (acl->aclEntryN > 0 ? false : true);
155 * Define the supported ACL streams for this OS
157 static int os_access_acl_streams[3] = { STREAM_ACL_AIX_TEXT, STREAM_ACL_AIX_AIXC, STREAM_ACL_AIX_NFS4 };
158 static int os_default_acl_streams[1] = { -1 };
160 static bacl_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
165 size_t aclsize, acltxtsize;
166 bacl_exit_code retval = bacl_exit_error;
167 POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE);
170 * First see how big the buffers should be.
173 if (aclx_get(jcr->last_fname, GET_ACLINFO_ONLY, &type, NULL, &aclsize, NULL) < 0) {
176 retval = bacl_exit_ok;
179 Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"),
180 jcr->last_fname, be.bstrerror());
181 Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
182 jcr->last_fname, be.bstrerror());
188 * Make sure the buffers are big enough.
190 aclbuf = check_pool_memory_size(aclbuf, aclsize + 1);
193 * Retrieve the ACL info.
195 if (aclx_get(jcr->last_fname, 0, &type, aclbuf, &aclsize, &mode) < 0) {
198 retval = bacl_exit_ok;
201 Mmsg2(jcr->errmsg, _("aclx_get error on file \"%s\": ERR=%s\n"),
202 jcr->last_fname, be.bstrerror());
203 Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
204 jcr->last_fname, be.bstrerror());
210 * See if the acl is non trivial.
214 if (acl_is_trivial((struct acl *)aclbuf)) {
215 retval = bacl_exit_ok;
220 if (acl_nfs4_is_trivial((nfs4_acl_int_t *)aclbuf)) {
221 retval = bacl_exit_ok;
226 Mmsg2(jcr->errmsg, _("Unknown acl type encountered on file \"%s\": %ld\n"),
227 jcr->last_fname, type.u64);
228 Dmsg2(100, "Unknown acl type encountered on file \"%s\": %ld\n",
229 jcr->last_fname, type.u64);
234 * We have a non-trivial acl lets convert it into some ASCII form.
236 acltxtsize = sizeof_pool_memory(jcr->acl_data->content);
237 if (aclx_printStr(jcr->acl_data->content, &acltxtsize, aclbuf,
238 aclsize, type, jcr->last_fname, 0) < 0) {
242 * Our buffer is not big enough, acltxtsize should be updated with the value
243 * the aclx_printStr really need. So we increase the buffer and try again.
245 jcr->acl_data->content = check_pool_memory_size(jcr->acl_data->content, acltxtsize + 1);
246 if (aclx_printStr(jcr->acl_data->content, &acltxtsize, aclbuf,
247 aclsize, type, jcr->last_fname, 0) < 0) {
248 Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"),
250 Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
251 jcr->last_fname, type.u64);
256 Mmsg1(jcr->errmsg, _("Failed to convert acl into text on file \"%s\"\n"),
258 Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
259 jcr->last_fname, type.u64);
264 jcr->acl_data->content_length = strlen(jcr->acl_data->content) + 1;
267 retval = send_acl_stream(jcr, STREAM_ACL_AIX_AIXC);
269 retval = send_acl_stream(jcr, STREAM_ACL_AIX_NFS4);
273 free_pool_memory(aclbuf);
278 static bacl_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
284 bacl_exit_code retval = bacl_exit_error;
285 POOLMEM *aclbuf = get_pool_memory(PM_MESSAGE);
288 case STREAM_ACL_AIX_TEXT:
290 * Handle the old stream using the old system call for now.
292 if (acl_put(jcr->last_fname, jcr->acl_data->content, 0) != 0) {
293 retval = bacl_exit_error;
296 retval = bacl_exit_ok;
298 case STREAM_ACL_AIX_AIXC:
301 case STREAM_ACL_AIX_NFS4:
306 } /* end switch (stream) */
309 * Set the acl buffer to an initial size. For now we set it
310 * to the same size as the ASCII representation.
312 aclbuf = check_pool_memory_size(aclbuf, jcr->acl_data->content_length);
313 aclsize = jcr->acl_data->content_length;
314 if (aclx_scanStr(jcr->acl_data->content, aclbuf, &aclsize, type) < 0) {
318 * The buffer isn't big enough. The man page doesn't say that aclsize
319 * is updated to the needed size as what is done with aclx_printStr.
320 * So for now we try to increase the buffer a maximum of 3 times
321 * and retry the conversion.
323 for (cnt = 0; cnt < 3; cnt++) {
324 aclsize = 2 * aclsize;
325 aclbuf = check_pool_memory_size(aclbuf, aclsize);
327 if (aclx_scanStr(jcr->acl_data->content, aclbuf, &aclsize, type) == 0) {
332 * See why we failed this time, ENOSPC retry if max retries not met,
342 Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
343 jcr->last_fname, be.bstrerror());
344 Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
345 jcr->last_fname, be.bstrerror());
351 Mmsg2(jcr->errmsg, _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
352 jcr->last_fname, be.bstrerror());
353 Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
354 jcr->last_fname, be.bstrerror());
358 if (aclx_put(jcr->last_fname, SET_ACL, type, aclbuf, aclsize, 0) < 0) {
361 retval = bacl_exit_ok;
364 Mmsg2(jcr->errmsg, _("aclx_put error on file \"%s\": ERR=%s\n"),
365 jcr->last_fname, be.bstrerror());
366 Dmsg2(100, "aclx_put error file=%s ERR=%s\n",
367 jcr->last_fname, be.bstrerror());
372 retval = bacl_exit_ok;
375 free_pool_memory(aclbuf);
380 #else /* HAVE_EXTENDED_ACL */
382 #include <sys/access.h>
385 * Define the supported ACL streams for this OS
387 static int os_access_acl_streams[1] = { STREAM_ACL_AIX_TEXT };
388 static int os_default_acl_streams[1] = { -1 };
390 static bacl_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
394 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
395 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
396 actuallyfree(acl_text);
397 return send_acl_stream(jcr, STREAM_ACL_AIX_TEXT);
399 return bacl_exit_error;
402 static bacl_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
404 if (acl_put(jcr->last_fname, jcr->acl_data->content, 0) != 0) {
405 return bacl_exit_error;
409 #endif /* HAVE_EXTENDED_ACL */
412 * For this OS setup the build and parse function pointer to the OS specific functions.
414 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = aix_build_acl_streams;
415 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = aix_parse_acl_streams;
417 #elif defined(HAVE_DARWIN_OS) || \
418 defined(HAVE_FREEBSD_OS) || \
419 defined(HAVE_IRIX_OS) || \
420 defined(HAVE_OSF1_OS) || \
421 defined(HAVE_LINUX_OS)
423 #include <sys/types.h>
425 #ifdef HAVE_SYS_ACL_H
428 #error "configure failed to detect availability of sys/acl.h"
432 * On IRIX we can get shortened ACLs
434 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
435 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
439 * On Linux we can get numeric and/or shorted ACLs
441 #if defined(HAVE_LINUX_OS)
442 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
443 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
444 #elif defined(BACL_WANT_SHORT_ACLS)
445 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
446 #elif defined(BACL_WANT_NUMERIC_IDS)
447 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
449 #ifdef BACL_ALTERNATE_TEXT
450 #include <acl/libacl.h>
451 #define acl_to_text(acl,len) (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
456 * On FreeBSD we can get numeric ACLs
458 #if defined(HAVE_FREEBSD_OS)
459 #if defined(BACL_WANT_NUMERIC_IDS)
460 #define BACL_ALTERNATE_TEXT ACL_TEXT_NUMERIC_IDS
462 #ifdef BACL_ALTERNATE_TEXT
463 #define acl_to_text(acl,len) (acl_to_text_np((acl), (len), BACL_ALTERNATE_TEXT))
468 * Some generic functions used by multiple OSes.
470 static acl_type_t bac_to_os_acltype(bacl_type acltype)
475 case BACL_TYPE_ACCESS:
476 ostype = ACL_TYPE_ACCESS;
478 case BACL_TYPE_DEFAULT:
479 ostype = ACL_TYPE_DEFAULT;
481 #ifdef HAVE_ACL_TYPE_NFS4
483 * FreeBSD has an additional acl type named ACL_TYPE_NFS4.
486 ostype = ACL_TYPE_NFS4;
489 #ifdef HAVE_ACL_TYPE_DEFAULT_DIR
490 case BACL_TYPE_DEFAULT_DIR:
492 * TRU64 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
494 ostype = ACL_TYPE_DEFAULT_DIR;
497 #ifdef HAVE_ACL_TYPE_EXTENDED
498 case BACL_TYPE_EXTENDED:
500 * MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
502 ostype = ACL_TYPE_EXTENDED;
507 * This should never happen, as the per OS version function only tries acl
508 * types supported on a certain platform.
510 ostype = (acl_type_t)ACL_TYPE_NONE;
516 static int acl_count_entries(acl_t acl)
519 #if defined(HAVE_FREEBSD_OS) || \
520 defined(HAVE_LINUX_OS)
524 entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
525 while (entry_available == 1) {
527 entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
529 #elif defined(HAVE_IRIX_OS)
530 count = acl->acl_cnt;
531 #elif defined(HAVE_OSF1_OS)
532 count = acl->acl_num;
533 #elif defined(HAVE_DARWIN_OS)
537 entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
538 while (entry_available == 0) {
540 entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
546 #if !defined(HAVE_DARWIN_OS)
548 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
549 * There is no need to store those acls as we already store the stat bits too.
551 static bool acl_is_trivial(acl_t acl)
554 * acl is trivial if it has only the following entries:
561 #if defined(HAVE_FREEBSD_OS) || \
562 defined(HAVE_LINUX_OS)
565 entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
566 while (entry_available == 1) {
568 * Get the tag type of this acl entry.
569 * If we fail to get the tagtype we call the acl non-trivial.
571 if (acl_get_tag_type(ace, &tag) < 0)
574 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
576 if (tag != ACL_USER_OBJ &&
577 tag != ACL_GROUP_OBJ &&
580 entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
583 #elif defined(HAVE_IRIX_OS)
586 for (n = 0; n < acl->acl_cnt; n++) {
587 ace = &acl->acl_entry[n];
591 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
593 if (tag != ACL_USER_OBJ &&
594 tag != ACL_GROUP_OBJ &&
595 tag != ACL_OTHER_OBJ)
599 #elif defined(HAVE_OSF1_OS)
602 ace = acl->acl_first;
603 count = acl->acl_num;
606 tag = ace->entry->acl_type;
608 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
610 if (tag != ACL_USER_OBJ &&
611 tag != ACL_GROUP_OBJ &&
615 * On Tru64, perm can also contain non-standard bits such as
616 * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
618 if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)))
629 * Generic wrapper around acl_get_file call.
631 static bacl_exit_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype)
637 bacl_exit_code retval = bacl_exit_ok;
639 ostype = bac_to_os_acltype(acltype);
640 acl = acl_get_file(jcr->last_fname, ostype);
643 * From observation, IRIX's acl_get_file() seems to return a
644 * non-NULL acl with a count field of -1 when a file has no ACL
645 * defined, while IRIX's acl_to_text() returns NULL when presented
648 * For all other implmentations we check if there are more then
649 * zero entries in the acl returned.
651 if (acl_count_entries(acl) <= 0) {
656 * Make sure this is not just a trivial ACL.
658 #if !defined(HAVE_DARWIN_OS)
659 if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
661 * The ACLs simply reflect the (already known) standard permissions
662 * So we don't send an ACL stream to the SD.
667 #if defined(HAVE_FREEBSD_OS) && defined(_PC_ACL_NFS4)
668 if (acltype == BACL_TYPE_NFS4) {
670 if (acl_is_trivial_np(acl, &trivial) == 0) {
673 * The ACLs simply reflect the (already known) standard permissions
674 * So we don't send an ACL stream to the SD.
683 * Convert the internal acl representation into a text representation.
685 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
686 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
692 Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"),
693 jcr->last_fname, be.bstrerror());
694 Dmsg2(100, "acl_to_text error file=%s ERR=%s\n",
695 jcr->last_fname, be.bstrerror());
697 retval = bacl_exit_error;
701 * Handle errors gracefully.
704 #if defined(BACL_ENOTSUP)
707 * If the filesystem reports it doesn't support ACLs we clear the
708 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
709 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
710 * when we change from one filesystem to an other.
712 jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
718 /* Some real error */
719 Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"),
720 jcr->last_fname, be.bstrerror());
721 Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",
722 jcr->last_fname, be.bstrerror());
724 retval = bacl_exit_error;
733 pm_strcpy(jcr->acl_data->content, "");
734 jcr->acl_data->content_length = 0;
739 * Generic wrapper around acl_set_file call.
741 static bacl_exit_code generic_set_acl_on_os(JCR *jcr, bacl_type acltype)
748 * If we get empty default ACLs, clear ACLs now
750 ostype = bac_to_os_acltype(acltype);
751 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_data->content) == 0) {
752 if (acl_delete_def_file(jcr->last_fname) == 0) {
758 #if defined(BACL_ENOTSUP)
761 * If the filesystem reports it doesn't support ACLs we clear the
762 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
763 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
764 * when we change from one filesystem to an other.
766 jcr->acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
767 Mmsg1(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"),
769 return bacl_exit_error;
772 Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
773 jcr->last_fname, be.bstrerror());
774 return bacl_exit_error;
778 acl = acl_from_text(jcr->acl_data->content);
780 Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"),
781 jcr->last_fname, be.bstrerror());
782 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
783 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
784 return bacl_exit_error;
787 #ifndef HAVE_FREEBSD_OS
789 * FreeBSD always fails acl_valid() - at least on valid input...
790 * As it does the right thing, given valid input, just ignore acl_valid().
792 if (acl_valid(acl) != 0) {
793 Mmsg2(jcr->errmsg, _("acl_valid error on file \"%s\": ERR=%s\n"),
794 jcr->last_fname, be.bstrerror());
795 Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",
796 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
798 return bacl_exit_error;
803 * Restore the ACLs, but don't complain about links which really should
804 * not have attributes, and the file it is linked to may not yet be restored.
805 * This is only true for the old acl streams as in the new implementation we
806 * don't save acls of symlinks (which cannot have acls anyhow)
808 if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
813 #if defined(BACL_ENOTSUP)
816 * If the filesystem reports it doesn't support ACLs we clear the
817 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
818 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
819 * when we change from one filesystem to an other.
821 jcr->acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
822 Mmsg1(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"),
824 Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n",
825 jcr->acl_data->content, jcr->last_fname);
827 return bacl_exit_error;
830 Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"),
831 jcr->last_fname, be.bstrerror());
832 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
833 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
835 return bacl_exit_error;
843 * OS specific functions for handling different types of acl streams.
845 #if defined(HAVE_DARWIN_OS)
847 * Define the supported ACL streams for this OS
849 static int os_access_acl_streams[1] = { STREAM_ACL_DARWIN_ACCESS_ACL };
850 static int os_default_acl_streams[1] = { -1 };
852 static bacl_exit_code darwin_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
854 #if defined(HAVE_ACL_TYPE_EXTENDED)
856 * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
857 * and acl_get_file (name, ACL_TYPE_DEFAULT)
858 * always return NULL / EINVAL. There is no point in making
859 * these two useless calls. The real ACL is retrieved through
860 * acl_get_file (name, ACL_TYPE_EXTENDED).
862 * Read access ACLs for files, dirs and links
864 if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bacl_exit_fatal)
865 return bacl_exit_fatal;
868 * Read access ACLs for files, dirs and links
870 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
871 return bacl_exit_fatal;
874 if (jcr->acl_data->content_length > 0) {
875 return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS_ACL);
880 static bacl_exit_code darwin_parse_acl_streams(JCR *jcr, int stream)
882 #if defined(HAVE_ACL_TYPE_EXTENDED)
883 return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED);
885 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
890 * For this OS setup the build and parse function pointer to the OS specific functions.
892 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
893 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = darwin_parse_acl_streams;
895 #elif defined(HAVE_FREEBSD_OS)
897 * Define the supported ACL streams for these OSes
899 static int os_access_acl_streams[2] = { STREAM_ACL_FREEBSD_ACCESS_ACL, STREAM_ACL_FREEBSD_NFS4_ACL };
900 static int os_default_acl_streams[1] = { STREAM_ACL_FREEBSD_DEFAULT_ACL };
902 static bacl_exit_code freebsd_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
905 bacl_type acltype = BACL_TYPE_NONE;
908 #if defined(_PC_ACL_NFS4)
910 * See if filesystem supports NFS4 acls.
912 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4);
913 switch (acl_enabled) {
919 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
920 jcr->last_fname, be.bstrerror());
921 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
922 jcr->last_fname, be.bstrerror());
923 return bacl_exit_error;
928 acltype = BACL_TYPE_NFS4;
933 if (acl_enabled == 0) {
935 * See if filesystem supports POSIX acls.
937 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
938 switch (acl_enabled) {
944 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
945 jcr->last_fname, be.bstrerror());
946 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
947 jcr->last_fname, be.bstrerror());
948 return bacl_exit_error;
953 acltype = BACL_TYPE_ACCESS;
959 * If the filesystem reports it doesn't support ACLs we clear the
960 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
961 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
962 * when we change from one filesystem to an other.
964 if (acl_enabled == 0) {
965 jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
966 pm_strcpy(jcr->acl_data->content, "");
967 jcr->acl_data->content_length = 0;
972 * Based on the supported ACLs retrieve and store them.
977 * Read NFS4 ACLs for files, dirs and links
979 if (generic_get_acl_from_os(jcr, BACL_TYPE_NFS4) == bacl_exit_fatal)
980 return bacl_exit_fatal;
982 if (jcr->acl_data->content_length > 0) {
983 if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_NFS4_ACL) == bacl_exit_fatal)
984 return bacl_exit_fatal;
987 case BACL_TYPE_ACCESS:
989 * Read access ACLs for files, dirs and links
991 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
992 return bacl_exit_fatal;
994 if (jcr->acl_data->content_length > 0) {
995 if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_ACCESS_ACL) == bacl_exit_fatal)
996 return bacl_exit_fatal;
1000 * Directories can have default ACLs too
1002 if (ff_pkt->type == FT_DIREND) {
1003 if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
1004 return bacl_exit_fatal;
1005 if (jcr->acl_data->content_length > 0) {
1006 if (send_acl_stream(jcr, STREAM_ACL_FREEBSD_DEFAULT_ACL) == bacl_exit_fatal)
1007 return bacl_exit_fatal;
1015 return bacl_exit_ok;
1018 static bacl_exit_code freebsd_parse_acl_streams(JCR *jcr, int stream)
1020 int acl_enabled = 0;
1021 const char *acl_type_name;
1025 * First make sure the filesystem supports acls.
1028 case STREAM_UNIX_ACCESS_ACL:
1029 case STREAM_ACL_FREEBSD_ACCESS_ACL:
1030 case STREAM_UNIX_DEFAULT_ACL:
1031 case STREAM_ACL_FREEBSD_DEFAULT_ACL:
1032 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
1033 acl_type_name = "POSIX";
1035 case STREAM_ACL_FREEBSD_NFS4_ACL:
1036 #if defined(_PC_ACL_NFS4)
1037 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_NFS4);
1039 acl_type_name = "NFS4";
1042 acl_type_name = "unknown";
1046 switch (acl_enabled) {
1050 return bacl_exit_ok;
1052 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
1053 jcr->last_fname, be.bstrerror());
1054 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
1055 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1056 return bacl_exit_error;
1060 * If the filesystem reports it doesn't support ACLs we clear the
1061 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
1062 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
1063 * when we change from one filesystem to an other.
1065 jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1066 Mmsg2(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"),
1067 jcr->last_fname, acl_type_name);
1068 return bacl_exit_error;
1077 case STREAM_UNIX_ACCESS_ACL:
1078 case STREAM_ACL_FREEBSD_ACCESS_ACL:
1079 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
1080 case STREAM_UNIX_DEFAULT_ACL:
1081 case STREAM_ACL_FREEBSD_DEFAULT_ACL:
1082 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
1083 case STREAM_ACL_FREEBSD_NFS4_ACL:
1084 return generic_set_acl_on_os(jcr, BACL_TYPE_NFS4);
1088 return bacl_exit_error;
1092 * For this OSes setup the build and parse function pointer to the OS specific functions.
1094 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = freebsd_build_acl_streams;
1095 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = freebsd_parse_acl_streams;
1097 #elif defined(HAVE_IRIX_OS) || \
1098 defined(HAVE_LINUX_OS)
1100 * Define the supported ACL streams for these OSes
1102 #if defined(HAVE_IRIX_OS)
1103 static int os_access_acl_streams[1] = { STREAM_ACL_IRIX_ACCESS_ACL };
1104 static int os_default_acl_streams[1] = { STREAM_ACL_IRIX_DEFAULT_ACL };
1105 #elif defined(HAVE_LINUX_OS)
1106 static int os_access_acl_streams[1] = { STREAM_ACL_LINUX_ACCESS_ACL };
1107 static int os_default_acl_streams[1] = { STREAM_ACL_LINUX_DEFAULT_ACL };
1110 static bacl_exit_code generic_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1113 * Read access ACLs for files, dirs and links
1115 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
1116 return bacl_exit_fatal;
1118 if (jcr->acl_data->content_length > 0) {
1119 if (send_acl_stream(jcr, os_access_acl_streams[0]) == bacl_exit_fatal)
1120 return bacl_exit_fatal;
1124 * Directories can have default ACLs too
1126 if (ff_pkt->type == FT_DIREND) {
1127 if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
1128 return bacl_exit_fatal;
1129 if (jcr->acl_data->content_length > 0) {
1130 if (send_acl_stream(jcr, os_default_acl_streams[0]) == bacl_exit_fatal)
1131 return bacl_exit_fatal;
1134 return bacl_exit_ok;
1137 static bacl_exit_code generic_parse_acl_streams(JCR *jcr, int stream)
1142 case STREAM_UNIX_ACCESS_ACL:
1143 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
1144 case STREAM_UNIX_DEFAULT_ACL:
1145 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
1148 * See what type of acl it is.
1150 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1151 if (os_access_acl_streams[cnt] == stream) {
1152 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
1155 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1156 if (os_default_acl_streams[cnt] == stream) {
1157 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
1162 return bacl_exit_error;
1166 * For this OSes setup the build and parse function pointer to the OS specific functions.
1168 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
1169 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = generic_parse_acl_streams;
1171 #elif defined(HAVE_OSF1_OS)
1174 * Define the supported ACL streams for this OS
1176 static int os_access_acl_streams[1] = { STREAM_ACL_TRU64_ACCESS_ACL };
1177 static int os_default_acl_streams[2] = { STREAM_ACL_TRU64_DEFAULT_ACL, STREAM_ACL_TRU64_DEFAULT_DIR_ACL };
1179 static bacl_exit_code tru64_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1182 * Read access ACLs for files, dirs and links
1184 if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS)) < 0)
1185 return bacl_exit_error;
1186 if (jcr->acl_data->content_length > 0) {
1187 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
1188 return bacl_exit_error;
1191 * Directories can have default ACLs too
1193 if (ff_pkt->type == FT_DIREND) {
1194 if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT)) < 0)
1195 return bacl_exit_error;
1196 if (jcr->acl_data->content_length > 0) {
1197 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
1198 return bacl_exit_error;
1201 * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
1202 * This is an inherited acl for all subdirs.
1203 * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
1204 * Section 21.5 Default ACLs
1206 if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR)) < 0)
1207 return bacl_exit_error;
1208 if (jcr->acl_data->content_length > 0) {
1209 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
1210 return bacl_exit_error;
1213 return bacl_exit_ok;
1216 static bacl_exit_code tru64_parse_acl_streams(JCR *jcr, int stream)
1219 case STREAM_UNIX_ACCESS_ACL:
1220 case STREAM_ACL_TRU64_ACCESS_ACL:
1221 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
1222 case STREAM_UNIX_DEFAULT_ACL:
1223 case STREAM_ACL_TRU64_DEFAULT_ACL:
1224 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
1225 case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
1226 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR);
1230 * For this OS setup the build and parse function pointer to the OS specific functions.
1232 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
1233 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = tru64_parse_acl_streams;
1237 #elif defined(HAVE_HPUX_OS)
1238 #ifdef HAVE_SYS_ACL_H
1239 #include <sys/acl.h>
1241 #error "configure failed to detect availability of sys/acl.h"
1247 * Define the supported ACL streams for this OS
1249 static int os_access_acl_streams[1] = { STREAM_ACL_HPUX_ACL_ENTRY };
1250 static int os_default_acl_streams[1] = { -1 };
1253 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1254 * There is no need to store those acls as we already store the stat bits too.
1256 static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb)
1259 struct acl_entry ace
1261 for (n = 0; n < count; n++) {
1264 * See if this acl just is the stat mode in acl form.
1266 if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
1267 (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
1268 (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
1275 * OS specific functions for handling different types of acl streams.
1277 static bacl_exit_code hpux_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1280 struct acl_entry acls[NACLENTRIES];
1284 if ((n = getacl(jcr->last_fname, 0, acls)) < 0) {
1286 #if defined(BACL_ENOTSUP)
1289 * Not supported, just pretend there is nothing to see
1291 * If the filesystem reports it doesn't support ACLs we clear the
1292 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1293 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1294 * when we change from one filesystem to an other.
1296 jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1297 pm_strcpy(jcr->acl_data->content, "");
1298 jcr->acl_data->content_length = 0;
1299 return bacl_exit_ok;
1302 pm_strcpy(jcr->acl_data->content, "");
1303 jcr->acl_data->content_length = 0;
1304 return bacl_exit_ok;
1306 Mmsg2(jcr->errmsg, _("getacl error on file \"%s\": ERR=%s\n"),
1307 jcr->last_fname, be.bstrerror());
1308 Dmsg2(100, "getacl error file=%s ERR=%s\n",
1309 jcr->last_fname, be.bstrerror());
1311 pm_strcpy(jcr->acl_data->content, "");
1312 jcr->acl_data->content_length = 0;
1313 return bacl_exit_error;
1317 pm_strcpy(jcr->acl_data->content, "");
1318 jcr->acl_data->content_length = 0;
1319 return bacl_exit_ok;
1321 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
1322 if (acl_is_trivial(n, acls, ff_pkt->statp)) {
1324 * The ACLs simply reflect the (already known) standard permissions
1325 * So we don't send an ACL stream to the SD.
1327 pm_strcpy(jcr->acl_data->content, "");
1328 jcr->acl_data->content_length = 0;
1329 return bacl_exit_ok;
1331 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
1332 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1333 actuallyfree(acl_text);
1335 return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY);
1337 Mmsg2(jcr->errmsg, _("acltostr error on file \"%s\": ERR=%s\n"),
1338 jcr->last_fname, be.bstrerror());
1339 Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",
1340 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1341 return bacl_exit_error;
1343 return bacl_exit_error;
1346 static bacl_exit_code hpux_parse_acl_streams(JCR *jcr, int stream)
1349 struct acl_entry acls[NACLENTRIES];
1352 n = strtoacl(jcr->acl_data->content, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
1354 Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"),
1355 jcr->last_fname, be.bstrerror());
1356 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
1357 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1358 return bacl_exit_error;
1360 if (strtoacl(jcr->acl_data->content, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
1361 Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"),
1362 jcr->last_fname, be.bstrerror());
1363 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
1364 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1366 return bacl_exit_error;
1369 * Restore the ACLs, but don't complain about links which really should
1370 * not have attributes, and the file it is linked to may not yet be restored.
1371 * This is only true for the old acl streams as in the new implementation we
1372 * don't save acls of symlinks (which cannot have acls anyhow)
1374 if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
1377 return bacl_exit_ok;
1378 #if defined(BACL_ENOTSUP)
1381 * If the filesystem reports it doesn't support ACLs we clear the
1382 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
1383 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
1384 * when we change from one filesystem to an other.
1386 jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1387 Mmsg1(jcr->errmsg, _("setacl error on file \"%s\": filesystem doesn't support ACLs\n"),
1389 Dmsg2(100, "setacl error acl=%s file=%s filesystem doesn't support ACLs\n",
1390 jcr->acl_data->content, jcr->last_fname);
1391 return bacl_exit_error;
1394 Mmsg2(jcr->errmsg, _("setacl error on file \"%s\": ERR=%s\n"),
1395 jcr->last_fname, be.bstrerror());
1396 Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
1397 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1398 return bacl_exit_error;
1401 return bacl_exit_ok;
1405 * For this OS setup the build and parse function pointer to the OS specific functions.
1407 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
1408 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = hpux_parse_acl_streams;
1410 #elif defined(HAVE_SUN_OS)
1411 #ifdef HAVE_SYS_ACL_H
1412 #include <sys/acl.h>
1414 #error "configure failed to detect availability of sys/acl.h"
1417 #if defined(HAVE_EXTENDED_ACL)
1419 * We define some internals of the Solaris acl libs here as those
1420 * are not exposed yet. Probably because they want us to see the
1421 * acls as opague data. But as we need to support different platforms
1422 * and versions of Solaris we need to expose some data to be able
1423 * to determine the type of acl used to stuff it into the correct
1424 * data stream. I know this is far from portable, but maybe the
1425 * proper interface is exposed later on and we can get ride of
1426 * this kludge. Newer versions of Solaris include sys/acl_impl.h
1427 * which has implementation details of acls, if thats included we
1428 * don't have to define it ourself.
1430 #if !defined(_SYS_ACL_IMPL_H)
1431 typedef enum acl_type {
1438 * Two external references to functions in the libsec library function not in current include files.
1441 int acl_type(acl_t *);
1442 char *acl_strerror(int);
1446 * Define the supported ACL streams for this OS
1448 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT, STREAM_ACL_SOLARIS_ACE };
1449 static int os_default_acl_streams[1] = { -1 };
1452 * As the new libsec interface with acl_totext and acl_fromtext also handles
1453 * the old format from acltotext we can use the new functions even
1454 * for acls retrieved and stored in the database with older fd versions. If the
1455 * new interface is not defined (Solaris 9 and older we fall back to the old code)
1457 static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1459 int acl_enabled, flags;
1462 bacl_exit_code stream_status = bacl_exit_error;
1466 * See if filesystem supports acls.
1468 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
1469 switch (acl_enabled) {
1472 * If the filesystem reports it doesn't support ACLs we clear the
1473 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1474 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1475 * when we change from one filesystem to an other.
1477 jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1478 pm_strcpy(jcr->acl_data->content, "");
1479 jcr->acl_data->content_length = 0;
1480 return bacl_exit_ok;
1484 return bacl_exit_ok;
1486 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
1487 jcr->last_fname, be.bstrerror());
1488 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
1489 jcr->last_fname, be.bstrerror());
1490 return bacl_exit_error;
1497 * Get ACL info: don't bother allocating space if there is only a trivial ACL.
1499 if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
1502 return bacl_exit_ok;
1504 Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"),
1505 jcr->last_fname, acl_strerror(errno));
1506 Dmsg2(100, "acl_get error file=%s ERR=%s\n",
1507 jcr->last_fname, acl_strerror(errno));
1508 return bacl_exit_error;
1514 * The ACLs simply reflect the (already known) standard permissions
1515 * So we don't send an ACL stream to the SD.
1517 pm_strcpy(jcr->acl_data->content, "");
1518 jcr->acl_data->content_length = 0;
1519 return bacl_exit_ok;
1522 #if defined(ACL_SID_FMT)
1524 * New format flag added in newer Solaris versions.
1526 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1528 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1529 #endif /* ACL_SID_FMT */
1531 if ((acl_text = acl_totext(aclp, flags)) != NULL) {
1532 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1533 actuallyfree(acl_text);
1535 switch (acl_type(aclp)) {
1537 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1540 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACE);
1548 return stream_status;
1551 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1554 int acl_enabled, error;
1558 case STREAM_UNIX_ACCESS_ACL:
1559 case STREAM_ACL_SOLARIS_ACLENT:
1560 case STREAM_ACL_SOLARIS_ACE:
1562 * First make sure the filesystem supports acls.
1564 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
1565 switch (acl_enabled) {
1568 * If the filesystem reports it doesn't support ACLs we clear the
1569 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
1570 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
1571 * when we change from one filesystem to an other.
1573 jcr->acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
1574 Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
1576 return bacl_exit_error;
1580 return bacl_exit_ok;
1582 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
1583 jcr->last_fname, be.bstrerror());
1584 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
1585 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1586 return bacl_exit_error;
1590 * On a filesystem with ACL support make sure this particular ACL type can be restored.
1593 case STREAM_ACL_SOLARIS_ACLENT:
1595 * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1597 if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1598 Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without aclent acl support\n"),
1600 return bacl_exit_error;
1603 case STREAM_ACL_SOLARIS_ACE:
1605 * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1607 if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1608 Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without ace acl support\n"),
1610 return bacl_exit_error;
1615 * Stream id which doesn't describe the type of acl which is encoded.
1622 if ((error = acl_fromtext(jcr->acl_data->content, &aclp)) != 0) {
1623 Mmsg2(jcr->errmsg, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1624 jcr->last_fname, acl_strerror(error));
1625 Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",
1626 jcr->acl_data->content, jcr->last_fname, acl_strerror(error));
1627 return bacl_exit_error;
1631 * Validate that the conversion gave us the correct acl type.
1634 case STREAM_ACL_SOLARIS_ACLENT:
1635 if (acl_type(aclp) != ACLENT_T) {
1636 Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1638 return bacl_exit_error;
1641 case STREAM_ACL_SOLARIS_ACE:
1642 if (acl_type(aclp) != ACE_T) {
1643 Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1645 return bacl_exit_error;
1650 * Stream id which doesn't describe the type of acl which is encoded.
1656 * Restore the ACLs, but don't complain about links which really should
1657 * not have attributes, and the file it is linked to may not yet be restored.
1658 * This is only true for the old acl streams as in the new implementation we
1659 * don't save acls of symlinks (which cannot have acls anyhow)
1661 if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
1665 return bacl_exit_ok;
1667 Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"),
1668 jcr->last_fname, acl_strerror(error));
1669 Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",
1670 jcr->acl_data->content, jcr->last_fname, acl_strerror(error));
1672 return bacl_exit_error;
1677 return bacl_exit_ok;
1679 return bacl_exit_error;
1680 } /* end switch (stream) */
1683 #else /* HAVE_EXTENDED_ACL */
1686 * Define the supported ACL streams for this OS
1688 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
1689 static int os_default_acl_streams[1] = { -1 };
1692 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1693 * There is no need to store those acls as we already store the stat bits too.
1695 static bool acl_is_trivial(int count, aclent_t *entries)
1700 for (n = 0; n < count; n++) {
1703 if (!(ace->a_type == USER_OBJ ||
1704 ace->a_type == GROUP_OBJ ||
1705 ace->a_type == OTHER_OBJ ||
1706 ace->a_type == CLASS_OBJ))
1713 * OS specific functions for handling different types of acl streams.
1715 static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1722 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
1723 if (n < MIN_ACL_ENTRIES)
1724 return bacl_exit_error;
1726 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1727 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
1728 if (acl_is_trivial(n, acls)) {
1730 * The ACLs simply reflect the (already known) standard permissions
1731 * So we don't send an ACL stream to the SD.
1734 pm_strcpy(jcr->acl_data->content, "");
1735 jcr->acl_data->content_length = 0;
1736 return bacl_exit_ok;
1739 if ((acl_text = acltotext(acls, n)) != NULL) {
1740 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1741 actuallyfree(acl_text);
1743 return send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1746 Mmsg2(jcr->errmsg, _("acltotext error on file \"%s\": ERR=%s\n"),
1747 jcr->last_fname, be.bstrerror());
1748 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
1749 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1753 return bacl_exit_error;
1756 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1762 acls = aclfromtext(jcr->acl_data->content, &n);
1764 Mmsg2(jcr->errmsg, _("aclfromtext error on file \"%s\": ERR=%s\n"),
1765 jcr->last_fname, be.bstrerror());
1766 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
1767 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1768 return bacl_exit_error;
1772 * Restore the ACLs, but don't complain about links which really should
1773 * not have attributes, and the file it is linked to may not yet be restored.
1775 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
1779 return bacl_exit_ok;
1781 Mmsg2(jcr->errmsg, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
1782 jcr->last_fname, be.bstrerror());
1783 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
1784 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1786 return bacl_exit_error;
1790 return bacl_exit_ok;
1792 #endif /* HAVE_EXTENDED_ACL */
1795 * For this OS setup the build and parse function pointer to the OS specific functions.
1797 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
1798 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
1800 #endif /* HAVE_SUN_OS */
1801 #endif /* HAVE_ACL */
1804 * Entry points when compiled with support for ACLs on a supported platform.
1808 * Read and send an ACL for the last encountered file.
1810 bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1813 * See if we are changing from one device to an other.
1814 * We save the current device we are scanning and compare
1815 * it with the current st_dev in the last stat performed on
1816 * the file we are currently storing.
1818 if (jcr->acl_data->current_dev != ff_pkt->statp.st_dev) {
1820 * Reset the acl save flags.
1822 jcr->acl_data->flags = 0;
1824 jcr->acl_data->flags |= BACL_FLAG_SAVE_NATIVE;
1827 * Save that we started scanning a new filesystem.
1829 jcr->acl_data->current_dev = ff_pkt->statp.st_dev;
1832 #if defined(HAVE_ACL)
1834 * See if the BACL_FLAG_SAVE_NATIVE flag is set which lets us know if we should
1837 if (jcr->acl_data->flags & BACL_FLAG_SAVE_NATIVE) {
1839 * Call the appropriate function.
1841 if (os_build_acl_streams) {
1842 return os_build_acl_streams(jcr, ff_pkt);
1845 return bacl_exit_ok;
1848 return bacl_exit_error;
1851 bacl_exit_code parse_acl_streams(JCR *jcr, int stream)
1856 * See if we are changing from one device to an other.
1857 * We save the current device we are scanning and compare
1858 * it with the current st_dev in the last stat performed on
1859 * the file we are currently storing.
1861 if (jcr->acl_data->current_dev != ff_pkt->statp.st_dev) {
1863 * Reset the acl save flags.
1865 jcr->acl_data->flags = 0;
1866 jcr->acl_data->flags |= BACL_FLAG_RESTORE_NATIVE;
1869 * Save that we started scanning a new filesystem.
1871 jcr->acl_data->current_dev = ff_pkt->statp.st_dev;
1875 #if defined(HAVE_ACL)
1876 case STREAM_UNIX_ACCESS_ACL:
1877 case STREAM_UNIX_DEFAULT_ACL:
1879 * Handle legacy ACL streams.
1881 if ((jcr->acl_data->flags & BACL_FLAG_RESTORE_NATIVE) && os_parse_acl_streams) {
1882 return os_parse_acl_streams(jcr, stream);
1885 * Increment error count but don't log an error again for the same filesystem.
1887 jcr->acl_data->nr_errors++;
1888 return bacl_exit_ok;
1892 if ((jcr->acl_data->flags & BACL_FLAG_RESTORE_NATIVE) && os_parse_acl_streams) {
1894 * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
1896 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1897 if (os_access_acl_streams[cnt] == stream) {
1898 return os_parse_acl_streams(jcr, stream);
1902 * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
1904 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1905 if (os_default_acl_streams[cnt] == stream) {
1906 return os_parse_acl_streams(jcr, stream);
1911 * Increment error count but don't log an error again for the same filesystem.
1913 jcr->acl_data->nr_errors++;
1914 return bacl_exit_ok;
1922 Qmsg2(jcr, M_WARNING, 0,
1923 _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
1924 jcr->last_fname, stream);
1925 return bacl_exit_error;