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 * 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
55 #if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL)
57 * Entry points when compiled without support for ACLs or on an unsupported platform.
59 bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
61 return bacl_exit_fatal;
64 bacl_exit_code parse_acl_streams(JCR *jcr, int stream)
66 return bacl_exit_fatal;
70 * Send an ACL stream to the SD.
72 static bacl_exit_code send_acl_stream(JCR *jcr, int stream)
74 BSOCK *sd = jcr->store_bsock;
76 #ifdef FD_NO_SEND_TEST
83 if (jcr->acl_data->content_length <= 0) {
90 if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
91 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
93 return bacl_exit_fatal;
97 * Send the buffer to the storage deamon
99 Dmsg1(400, "Backing up ACL <%s>\n", jcr->acl_data->content);
101 sd->msg = jcr->acl_data->content;
102 sd->msglen = jcr->acl_data->content_length + 1;
106 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
108 return bacl_exit_fatal;
111 jcr->JobBytes += sd->msglen;
113 if (!sd->signal(BNET_EOD)) {
114 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
116 return bacl_exit_fatal;
119 Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
124 * First the native ACLs.
126 #if defined(HAVE_ACL)
127 #if defined(HAVE_AIX_OS)
129 #if defined(HAVE_EXTENDED_ACL)
134 * Define the supported ACL streams for this OS
136 static int os_access_acl_streams[1] = { STREAM_ACL_AIX_TEXT, STREAM_ACL_AIX_AIXC, STREAM_ACL_AIX_NFS4 };
137 static int os_default_acl_streams[1] = { -1 };
139 static bacl_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
141 return bacl_exit_error;
144 static bacl_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
147 case STREAM_ACL_AIX_TEXT:
148 case STREAM_ACL_AIX_AIXC:
150 case STREAM_ACL_AIX_NFS4:
154 return bacl_exit_error;
157 #else /* HAVE_EXTENDED_ACL */
159 #include <sys/access.h>
162 * Define the supported ACL streams for this OS
164 static int os_access_acl_streams[1] = { STREAM_ACL_AIX_TEXT };
165 static int os_default_acl_streams[1] = { -1 };
167 static bacl_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
171 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
172 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
173 actuallyfree(acl_text);
174 return send_acl_stream(jcr, STREAM_ACL_AIX_TEXT);
176 return bacl_exit_error;
179 static bacl_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
181 if (acl_put(jcr->last_fname, jcr->acl_data->content, 0) != 0) {
182 return bacl_exit_error;
186 #endif /* HAVE_EXTENDED_ACL */
189 * For this OS setup the build and parse function pointer to the OS specific functions.
191 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = aix_build_acl_streams;
192 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = aix_parse_acl_streams;
194 #elif defined(HAVE_DARWIN_OS) || \
195 defined(HAVE_FREEBSD_OS) || \
196 defined(HAVE_IRIX_OS) || \
197 defined(HAVE_OSF1_OS) || \
198 defined(HAVE_LINUX_OS)
200 #include <sys/types.h>
202 #ifdef HAVE_SYS_ACL_H
205 #error "configure failed to detect availability of sys/acl.h"
209 * On IRIX we can get shortened ACLs
211 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
212 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
216 * In Linux we can get numeric and/or shorted ACLs
218 #if defined(HAVE_LINUX_OS)
219 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
220 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
221 #elif defined(BACL_WANT_SHORT_ACLS)
222 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
223 #elif defined(BACL_WANT_NUMERIC_IDS)
224 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
226 #ifdef BACL_ALTERNATE_TEXT
227 #include <acl/libacl.h>
228 #define acl_to_text(acl,len) (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
233 * Some generic functions used by multiple OSes.
235 static acl_type_t bac_to_os_acltype(bacl_type acltype)
240 case BACL_TYPE_ACCESS:
241 ostype = ACL_TYPE_ACCESS;
243 case BACL_TYPE_DEFAULT:
244 ostype = ACL_TYPE_DEFAULT;
247 #ifdef ACL_TYPE_DEFAULT_DIR
248 case BACL_TYPE_DEFAULT_DIR:
250 * OSF1 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
252 ostype = ACL_TYPE_DEFAULT_DIR;
255 #ifdef ACL_TYPE_EXTENDED
256 case BACL_TYPE_EXTENDED:
258 * MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
260 ostype = ACL_TYPE_EXTENDED;
265 * This should never happen, as the per OS version function only tries acl
266 * types supported on a certain platform.
268 ostype = (acl_type_t)ACL_TYPE_NONE;
274 #if !defined(HAVE_DARWIN_OS)
276 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
277 * There is no need to store those acls as we already store the stat bits too.
279 static bool acl_is_trivial(acl_t acl)
282 * acl is trivial if it has only the following entries:
289 #if defined(HAVE_FREEBSD_OS) || \
290 defined(HAVE_LINUX_OS)
293 entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
294 while (entry_available == 1) {
296 * Get the tag type of this acl entry.
297 * If we fail to get the tagtype we call the acl non-trivial.
299 if (acl_get_tag_type(ace, &tag) < 0)
302 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
304 if (tag != ACL_USER_OBJ &&
305 tag != ACL_GROUP_OBJ &&
308 entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
311 #elif defined(HAVE_IRIX_OS)
314 for (n = 0; n < acl->acl_cnt; n++) {
315 ace = &acl->acl_entry[n];
319 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
321 if (tag != ACL_USER_OBJ &&
322 tag != ACL_GROUP_OBJ &&
323 tag != ACL_OTHER_OBJ)
327 #elif defined(HAVE_OSF1_OS)
330 ace = acl->acl_first;
331 count = acl->acl_num;
334 tag = ace->entry->acl_type;
336 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
338 if (tag != ACL_USER_OBJ &&
339 tag != ACL_GROUP_OBJ &&
343 * On Tru64, perm can also contain non-standard bits such as
344 * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
346 if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)))
357 * Generic wrapper around acl_get_file call.
359 static bacl_exit_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype)
366 ostype = bac_to_os_acltype(acltype);
367 acl = acl_get_file(jcr->last_fname, ostype);
369 #if defined(HAVE_IRIX_OS)
371 * From observation, IRIX's acl_get_file() seems to return a
372 * non-NULL acl with a count field of -1 when a file has no ACL
373 * defined, while IRIX's acl_to_text() returns NULL when presented
376 * Checking the count in the acl structure before calling
377 * acl_to_text() lets us avoid error messages about files
378 * with no ACLs, without modifying the flow of the code used for
379 * other operating systems, and it saves making some calls
380 * to acl_to_text() besides.
382 if (acl->acl_cnt <= 0) {
383 pm_strcpy(jcr->acl_data->content, "");
384 jcr->acl_data->content_length = 0;
390 #if !defined(HAVE_DARWIN_OS)
392 * Make sure this is not just a trivial ACL.
394 if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
396 * The ACLs simply reflect the (already known) standard permissions
397 * So we don't send an ACL stream to the SD.
399 pm_strcpy(jcr->acl_data->content, "");
400 jcr->acl_data->content_length = 0;
406 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
407 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
413 Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"),
414 jcr->last_fname, be.bstrerror());
415 Dmsg2(100, "acl_to_text error file=%s ERR=%s\n",
416 jcr->last_fname, be.bstrerror());
418 pm_strcpy(jcr->acl_data->content, "");
419 jcr->acl_data->content_length = 0;
421 return bacl_exit_error;
425 * Handle errors gracefully.
427 if (acl == (acl_t)NULL) {
429 #if defined(BACL_ENOTSUP)
432 * If the filesystem reports it doesn't support ACLs we clear the
433 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
434 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
435 * when we change from one filesystem to an other.
437 jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
438 break; /* not supported */
441 pm_strcpy(jcr->acl_data->content, "");
442 jcr->acl_data->content_length = 0;
445 /* Some real error */
446 Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"),
447 jcr->last_fname, be.bstrerror());
448 Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",
449 jcr->last_fname, be.bstrerror());
451 pm_strcpy(jcr->acl_data->content, "");
452 jcr->acl_data->content_length = 0;
453 return bacl_exit_error;
458 * Not supported, just pretend there is nothing to see
460 pm_strcpy(jcr->acl_data->content, "");
461 jcr->acl_data->content_length = 0;
466 * Generic wrapper around acl_set_file call.
468 static bacl_exit_code generic_set_acl_on_os(JCR *jcr, bacl_type acltype)
475 * If we get empty default ACLs, clear ACLs now
477 ostype = bac_to_os_acltype(acltype);
478 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_data->content) == 0) {
479 if (acl_delete_def_file(jcr->last_fname) == 0) {
486 Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
487 jcr->last_fname, be.bstrerror());
488 return bacl_exit_error;
492 acl = acl_from_text(jcr->acl_data->content);
494 Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"),
495 jcr->last_fname, be.bstrerror());
496 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
497 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
498 return bacl_exit_error;
501 #ifndef HAVE_FREEBSD_OS
503 * FreeBSD always fails acl_valid() - at least on valid input...
504 * As it does the right thing, given valid input, just ignore acl_valid().
506 if (acl_valid(acl) != 0) {
507 Mmsg2(jcr->errmsg, _("acl_valid error on file \"%s\": ERR=%s\n"),
508 jcr->last_fname, be.bstrerror());
509 Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",
510 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
512 return bacl_exit_error;
517 * Restore the ACLs, but don't complain about links which really should
518 * not have attributes, and the file it is linked to may not yet be restored.
519 * This is only true for the old acl streams as in the new implementation we
520 * don't save acls of symlinks (which cannot have acls anyhow)
522 if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
528 Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"),
529 jcr->last_fname, be.bstrerror());
530 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
531 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
533 return bacl_exit_error;
541 * OS specific functions for handling different types of acl streams.
543 #if defined(HAVE_DARWIN_OS)
545 * Define the supported ACL streams for this OS
547 static int os_access_acl_streams[1] = { STREAM_ACL_DARWIN_ACCESS_ACL };
548 static int os_default_acl_streams[1] = { -1 };
550 static bacl_exit_code darwin_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
552 #if defined(ACL_TYPE_EXTENDED)
554 * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
555 * and acl_get_file (name, ACL_TYPE_DEFAULT)
556 * always return NULL / EINVAL. There is no point in making
557 * these two useless calls. The real ACL is retrieved through
558 * acl_get_file (name, ACL_TYPE_EXTENDED).
560 * Read access ACLs for files, dirs and links
562 if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bacl_exit_fatal)
563 return bacl_exit_fatal;
566 * Read access ACLs for files, dirs and links
568 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
569 return bacl_exit_fatal;
572 if (jcr->acl_data->content_length > 0) {
573 return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS_ACL);
578 static bacl_exit_code darwin_parse_acl_streams(JCR *jcr, int stream)
580 #if defined(ACL_TYPE_EXTENDED)
581 return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED);
583 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
588 * For this OS setup the build and parse function pointer to the OS specific functions.
590 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
591 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = darwin_parse_acl_streams;
593 #elif defined(HAVE_FREEBSD_OS) || \
594 defined(HAVE_IRIX_OS) || \
595 defined(HAVE_LINUX_OS)
598 * Define the supported ACL streams for these OSes
600 #if defined(HAVE_FREEBSD_OS)
601 static int os_access_acl_streams[1] = { STREAM_ACL_FREEBSD_ACCESS_ACL };
602 static int os_default_acl_streams[1] = { STREAM_ACL_FREEBSD_DEFAULT_ACL };
603 #elif defined(HAVE_IRIX_OS)
604 static int os_access_acl_streams[1] = { STREAM_ACL_IRIX_ACCESS_ACL };
605 static int os_default_acl_streams[1] = { STREAM_ACL_IRIX_DEFAULT_ACL };
606 #elif defined(HAVE_LINUX_OS)
607 static int os_access_acl_streams[1] = { STREAM_ACL_LINUX_ACCESS_ACL };
608 static int os_default_acl_streams[1] = { STREAM_ACL_LINUX_DEFAULT_ACL };
611 static bacl_exit_code generic_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
614 * Read access ACLs for files, dirs and links
616 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
617 return bacl_exit_fatal;
619 if (jcr->acl_data->content_length > 0) {
620 if (send_acl_stream(jcr, os_access_acl_streams[0]) == bacl_exit_fatal)
621 return bacl_exit_fatal;
625 * Directories can have default ACLs too
627 if (ff_pkt->type == FT_DIREND) {
628 if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
629 return bacl_exit_fatal;
630 if (jcr->acl_data->content_length > 0) {
631 if (send_acl_stream(jcr, os_default_acl_streams[0]) == bacl_exit_fatal)
632 return bacl_exit_fatal;
638 static bacl_exit_code generic_parse_acl_streams(JCR *jcr, int stream)
643 case STREAM_UNIX_ACCESS_ACL:
644 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
645 case STREAM_UNIX_DEFAULT_ACL:
646 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
649 * See what type of acl it is.
651 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
652 if (os_access_acl_streams[cnt] == stream) {
653 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
656 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
657 if (os_default_acl_streams[cnt] == stream) {
658 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
663 return bacl_exit_error;
667 * For this OSes setup the build and parse function pointer to the OS specific functions.
669 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
670 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = generic_parse_acl_streams;
672 #elif defined(HAVE_OSF1_OS)
675 * Define the supported ACL streams for this OS
677 static int os_access_acl_streams[1] = { STREAM_ACL_TRU64_ACCESS_ACL };
678 static int os_default_acl_streams[2] = { STREAM_ACL_TRU64_DEFAULT_ACL, STREAM_ACL_TRU64_DEFAULT_DIR_ACL };
680 static bacl_exit_code tru64_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
683 * Read access ACLs for files, dirs and links
685 if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS)) < 0)
686 return bacl_exit_error;
687 if (jcr->acl_data->content_length > 0) {
688 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
689 return bacl_exit_error;
692 * Directories can have default ACLs too
694 if (ff_pkt->type == FT_DIREND) {
695 if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT)) < 0)
696 return bacl_exit_error;
697 if (jcr->acl_data->content_length > 0) {
698 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
699 return bacl_exit_error;
702 * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
703 * This is an inherited acl for all subdirs.
704 * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
705 * Section 21.5 Default ACLs
707 if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR)) < 0)
708 return bacl_exit_error;
709 if (jcr->acl_data->content_length > 0) {
710 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
711 return bacl_exit_error;
717 static bacl_exit_code tru64_parse_acl_streams(JCR *jcr, int stream)
720 case STREAM_UNIX_ACCESS_ACL:
721 case STREAM_ACL_TRU64_ACCESS_ACL:
722 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
723 case STREAM_UNIX_DEFAULT_ACL:
724 case STREAM_ACL_TRU64_DEFAULT_ACL:
725 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
726 case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
727 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR);
731 * For this OS setup the build and parse function pointer to the OS specific functions.
733 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
734 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = tru64_parse_acl_streams;
738 #elif defined(HAVE_HPUX_OS)
739 #ifdef HAVE_SYS_ACL_H
742 #error "configure failed to detect availability of sys/acl.h"
748 * Define the supported ACL streams for this OS
750 static int os_access_acl_streams[1] = { STREAM_ACL_HPUX_ACL_ENTRY };
751 static int os_default_acl_streams[1] = { -1 };
754 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
755 * There is no need to store those acls as we already store the stat bits too.
757 static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb)
762 for (n = 0; n < count; n++) {
765 * See if this acl just is the stat mode in acl form.
767 if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
768 (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
769 (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
776 * OS specific functions for handling different types of acl streams.
778 static bacl_exit_code hpux_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
781 struct acl_entry acls[NACLENTRIES];
785 if ((n = getacl(jcr->last_fname, 0, acls)) < 0) {
787 #if defined(BACL_ENOTSUP)
790 * Not supported, just pretend there is nothing to see
792 * If the filesystem reports it doesn't support ACLs we clear the
793 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
794 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
795 * when we change from one filesystem to an other.
797 jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
798 pm_strcpy(jcr->acl_data->content, "");
799 jcr->acl_data->content_length = 0;
803 pm_strcpy(jcr->acl_data->content, "");
804 jcr->acl_data->content_length = 0;
807 Mmsg2(jcr->errmsg, _("getacl error on file \"%s\": ERR=%s\n"),
808 jcr->last_fname, be.bstrerror());
809 Dmsg2(100, "getacl error file=%s ERR=%s\n",
810 jcr->last_fname, be.bstrerror());
812 pm_strcpy(jcr->acl_data->content, "");
813 jcr->acl_data->content_length = 0;
814 return bacl_exit_error;
818 pm_strcpy(jcr->acl_data->content, "");
819 jcr->acl_data->content_length = 0;
822 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
823 if (acl_is_trivial(n, acls, ff_pkt->statp)) {
825 * The ACLs simply reflect the (already known) standard permissions
826 * So we don't send an ACL stream to the SD.
828 pm_strcpy(jcr->acl_data->content, "");
829 jcr->acl_data->content_length = 0;
832 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
833 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
834 actuallyfree(acl_text);
836 return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY);
838 Mmsg2(jcr->errmsg, _("acltostr error on file \"%s\": ERR=%s\n"),
839 jcr->last_fname, be.bstrerror());
840 Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",
841 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
842 return bacl_exit_error;
844 return bacl_exit_error;
847 static bacl_exit_code hpux_parse_acl_streams(JCR *jcr, int stream)
850 struct acl_entry acls[NACLENTRIES];
853 n = strtoacl(jcr->acl_data->content, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
855 Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"),
856 jcr->last_fname, be.bstrerror());
857 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
858 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
859 return bacl_exit_error;
861 if (strtoacl(jcr->acl_data->content, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
862 Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"),
863 jcr->last_fname, be.bstrerror());
864 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
865 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
867 return bacl_exit_error;
870 * Restore the ACLs, but don't complain about links which really should
871 * not have attributes, and the file it is linked to may not yet be restored.
872 * This is only true for the old acl streams as in the new implementation we
873 * don't save acls of symlinks (which cannot have acls anyhow)
875 if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
880 Mmsg2(jcr->errmsg, _("setacl error on file \"%s\": ERR=%s\n"),
881 jcr->last_fname, be.bstrerror());
882 Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
883 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
884 return bacl_exit_error;
891 * For this OS setup the build and parse function pointer to the OS specific functions.
893 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
894 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = hpux_parse_acl_streams;
896 #elif defined(HAVE_SUN_OS)
897 #ifdef HAVE_SYS_ACL_H
900 #error "configure failed to detect availability of sys/acl.h"
903 #if defined(HAVE_EXTENDED_ACL)
905 * We define some internals of the Solaris acl libs here as those
906 * are not exposed yet. Probably because they want us to see the
907 * acls as opague data. But as we need to support different platforms
908 * and versions of Solaris we need to expose some data to be able
909 * to determine the type of acl used to stuff it into the correct
910 * data stream. I know this is far from portable, but maybe the
911 * proper interface is exposed later on and we can get ride of
912 * this kludge. Newer versions of Solaris include sys/acl_impl.h
913 * which has implementation details of acls, if thats included we
914 * don't have to define it ourself.
916 #if !defined(_SYS_ACL_IMPL_H)
917 typedef enum acl_type {
924 * Two external references to functions in the libsec library function not in current include files.
927 int acl_type(acl_t *);
928 char *acl_strerror(int);
932 * Define the supported ACL streams for this OS
934 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT, STREAM_ACL_SOLARIS_ACE };
935 static int os_default_acl_streams[1] = { -1 };
938 * As the new libsec interface with acl_totext and acl_fromtext also handles
939 * the old format from acltotext we can use the new functions even
940 * for acls retrieved and stored in the database with older fd versions. If the
941 * new interface is not defined (Solaris 9 and older we fall back to the old code)
943 static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
945 int acl_enabled, flags;
948 bacl_exit_code stream_status = bacl_exit_error;
952 * See if filesystem supports acls.
954 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
955 switch (acl_enabled) {
958 * If the filesystem reports it doesn't support ACLs we clear the
959 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
960 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
961 * when we change from one filesystem to an other.
963 jcr->acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
964 pm_strcpy(jcr->acl_data->content, "");
965 jcr->acl_data->content_length = 0;
972 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
973 jcr->last_fname, be.bstrerror());
974 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
975 jcr->last_fname, be.bstrerror());
976 return bacl_exit_error;
983 * Get ACL info: don't bother allocating space if there is only a trivial ACL.
985 if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
990 Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"),
991 jcr->last_fname, acl_strerror(errno));
992 Dmsg2(100, "acl_get error file=%s ERR=%s\n",
993 jcr->last_fname, acl_strerror(errno));
994 return bacl_exit_error;
1000 * The ACLs simply reflect the (already known) standard permissions
1001 * So we don't send an ACL stream to the SD.
1003 pm_strcpy(jcr->acl_data->content, "");
1004 jcr->acl_data->content_length = 0;
1005 return bacl_exit_ok;
1008 #if defined(ACL_SID_FMT)
1010 * New format flag added in newer Solaris versions.
1012 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1014 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1015 #endif /* ACL_SID_FMT */
1017 if ((acl_text = acl_totext(aclp, flags)) != NULL) {
1018 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1019 actuallyfree(acl_text);
1021 switch (acl_type(aclp)) {
1023 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1026 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACE);
1034 return stream_status;
1037 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1040 int acl_enabled, error;
1044 case STREAM_UNIX_ACCESS_ACL:
1045 case STREAM_ACL_SOLARIS_ACLENT:
1046 case STREAM_ACL_SOLARIS_ACE:
1048 * First make sure the filesystem supports acls.
1050 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
1051 switch (acl_enabled) {
1053 Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
1055 return bacl_exit_error;
1059 return bacl_exit_ok;
1061 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
1062 jcr->last_fname, be.bstrerror());
1063 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
1064 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1065 return bacl_exit_error;
1069 * On a filesystem with ACL support make sure this particilar ACL type can be restored.
1072 case STREAM_ACL_SOLARIS_ACLENT:
1074 * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1076 if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1077 Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without aclent acl support\n"),
1079 return bacl_exit_error;
1082 case STREAM_ACL_SOLARIS_ACE:
1084 * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1086 if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1087 Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without ace acl support\n"),
1089 return bacl_exit_error;
1094 * Stream id which doesn't describe the type of acl which is encoded.
1101 if ((error = acl_fromtext(jcr->acl_data->content, &aclp)) != 0) {
1102 Mmsg2(jcr->errmsg, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1103 jcr->last_fname, acl_strerror(error));
1104 Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",
1105 jcr->acl_data->content, jcr->last_fname, acl_strerror(error));
1106 return bacl_exit_error;
1110 * Validate that the conversion gave us the correct acl type.
1113 case STREAM_ACL_SOLARIS_ACLENT:
1114 if (acl_type(aclp) != ACLENT_T) {
1115 Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1117 return bacl_exit_error;
1120 case STREAM_ACL_SOLARIS_ACE:
1121 if (acl_type(aclp) != ACE_T) {
1122 Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1124 return bacl_exit_error;
1129 * Stream id which doesn't describe the type of acl which is encoded.
1135 * Restore the ACLs, but don't complain about links which really should
1136 * not have attributes, and the file it is linked to may not yet be restored.
1137 * This is only true for the old acl streams as in the new implementation we
1138 * don't save acls of symlinks (which cannot have acls anyhow)
1140 if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
1144 return bacl_exit_ok;
1146 Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"),
1147 jcr->last_fname, acl_strerror(error));
1148 Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",
1149 jcr->acl_data->content, jcr->last_fname, acl_strerror(error));
1151 return bacl_exit_error;
1156 return bacl_exit_ok;
1158 return bacl_exit_error;
1159 } /* end switch (stream) */
1162 #else /* HAVE_EXTENDED_ACL */
1165 * Define the supported ACL streams for this OS
1167 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
1168 static int os_default_acl_streams[1] = { -1 };
1171 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1172 * There is no need to store those acls as we already store the stat bits too.
1174 static bool acl_is_trivial(int count, aclent_t *entries)
1179 for (n = 0; n < count; n++) {
1182 if (!(ace->a_type == USER_OBJ ||
1183 ace->a_type == GROUP_OBJ ||
1184 ace->a_type == OTHER_OBJ ||
1185 ace->a_type == CLASS_OBJ))
1192 * OS specific functions for handling different types of acl streams.
1194 static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1201 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
1202 if (n < MIN_ACL_ENTRIES)
1203 return bacl_exit_error;
1205 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1206 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
1207 if (acl_is_trivial(n, acls)) {
1209 * The ACLs simply reflect the (already known) standard permissions
1210 * So we don't send an ACL stream to the SD.
1213 pm_strcpy(jcr->acl_data->content, "");
1214 jcr->acl_data->content_length = 0;
1215 return bacl_exit_ok;
1218 if ((acl_text = acltotext(acls, n)) != NULL) {
1219 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1220 actuallyfree(acl_text);
1222 return send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1225 Mmsg2(jcr->errmsg, _("acltotext error on file \"%s\": ERR=%s\n"),
1226 jcr->last_fname, be.bstrerror());
1227 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
1228 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1232 return bacl_exit_error;
1235 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1241 acls = aclfromtext(jcr->acl_data->content, &n);
1243 Mmsg2(jcr->errmsg, _("aclfromtext error on file \"%s\": ERR=%s\n"),
1244 jcr->last_fname, be.bstrerror());
1245 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
1246 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1247 return bacl_exit_error;
1251 * Restore the ACLs, but don't complain about links which really should
1252 * not have attributes, and the file it is linked to may not yet be restored.
1254 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
1258 return bacl_exit_ok;
1260 Mmsg2(jcr->errmsg, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
1261 jcr->last_fname, be.bstrerror());
1262 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
1263 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1265 return bacl_exit_error;
1269 return bacl_exit_ok;
1271 #endif /* HAVE_EXTENDED_ACL */
1274 * For this OS setup the build and parse function pointer to the OS specific functions.
1276 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
1277 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
1279 #endif /* HAVE_SUN_OS */
1280 #endif /* HAVE_ACL */
1283 * Entry points when compiled with support for ACLs on a supported platform.
1287 * Read and send an ACL for the last encountered file.
1289 bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1292 * See if we are changing from one device to an other.
1293 * We save the current device we are scanning and compare
1294 * it with the current st_dev in the last stat performed on
1295 * the file we are currently storing.
1297 if (jcr->acl_data->current_dev != ff_pkt->statp.st_dev) {
1299 * Reset the acl save flags.
1301 jcr->acl_data->flags = 0;
1303 jcr->acl_data->flags |= BACL_FLAG_SAVE_NATIVE;
1306 * Save that we started scanning a new filesystem.
1308 jcr->acl_data->current_dev = ff_pkt->statp.st_dev;
1311 #if defined(HAVE_ACL)
1313 * See if the BACL_FLAG_SAVE_NATIVE flag is set which lets us know if we should
1316 if (jcr->acl_data->flags & BACL_FLAG_SAVE_NATIVE) {
1318 * Call the appropriate function.
1320 if (os_build_acl_streams) {
1321 return (*os_build_acl_streams)(jcr, ff_pkt);
1324 return bacl_exit_ok;
1327 return bacl_exit_error;
1330 bacl_exit_code parse_acl_streams(JCR *jcr, int stream)
1335 #if defined(HAVE_ACL)
1336 case STREAM_UNIX_ACCESS_ACL:
1337 case STREAM_UNIX_DEFAULT_ACL:
1339 * Handle legacy ACL streams.
1341 if (os_parse_acl_streams) {
1342 return (*os_parse_acl_streams)(jcr, stream);
1346 if (os_parse_acl_streams) {
1348 * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
1350 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1351 if (os_access_acl_streams[cnt] == stream) {
1352 return (*os_parse_acl_streams)(jcr, stream);
1356 * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
1358 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1359 if (os_default_acl_streams[cnt] == stream) {
1360 return (*os_parse_acl_streams)(jcr, stream);
1370 Qmsg2(jcr, M_WARNING, 0,
1371 _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
1372 jcr->last_fname, stream);
1373 return bacl_exit_error;