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 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
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 #include <sys/access.h>
132 * Define the supported ACL streams for this OS
134 static int os_access_acl_streams[1] = { STREAM_ACL_AIX_TEXT };
135 static int os_default_acl_streams[1] = { -1 };
137 static bacl_exit_code aix_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
141 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
142 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
143 actuallyfree(acl_text);
144 return send_acl_stream(jcr, STREAM_ACL_AIX_TEXT);
146 return bacl_exit_error;
149 static bacl_exit_code aix_parse_acl_streams(JCR *jcr, int stream)
151 if (acl_put(jcr->last_fname, jcr->acl_data->content, 0) != 0) {
152 return bacl_exit_error;
158 * For this OS setup the build and parse function pointer to the OS specific functions.
160 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = aix_build_acl_streams;
161 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = aix_parse_acl_streams;
163 #elif defined(HAVE_DARWIN_OS) || \
164 defined(HAVE_FREEBSD_OS) || \
165 defined(HAVE_IRIX_OS) || \
166 defined(HAVE_OSF1_OS) || \
167 defined(HAVE_LINUX_OS)
169 #include <sys/types.h>
171 #ifdef HAVE_SYS_ACL_H
174 #error "configure failed to detect availability of sys/acl.h"
177 /* On IRIX we can get shortened ACLs */
178 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
179 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
182 /* In Linux we can get numeric and/or shorted ACLs */
183 #if defined(HAVE_LINUX_OS)
184 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
185 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
186 #elif defined(BACL_WANT_SHORT_ACLS)
187 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
188 #elif defined(BACL_WANT_NUMERIC_IDS)
189 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
191 #ifdef BACL_ALTERNATE_TEXT
192 #include <acl/libacl.h>
193 #define acl_to_text(acl,len) (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
198 * Some generic functions used by multiple OSes.
200 static acl_type_t bac_to_os_acltype(bacl_type acltype)
205 case BACL_TYPE_ACCESS:
206 ostype = ACL_TYPE_ACCESS;
208 case BACL_TYPE_DEFAULT:
209 ostype = ACL_TYPE_DEFAULT;
212 #ifdef ACL_TYPE_DEFAULT_DIR
213 case BACL_TYPE_DEFAULT_DIR:
215 * OSF1 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
217 ostype = ACL_TYPE_DEFAULT_DIR;
220 #ifdef ACL_TYPE_EXTENDED
221 case BACL_TYPE_EXTENDED:
223 * MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
225 ostype = ACL_TYPE_EXTENDED;
230 * This should never happen, as the per OS version function only tries acl
231 * types supported on a certain platform.
233 ostype = (acl_type_t)ACL_TYPE_NONE;
239 #if !defined(HAVE_DARWIN_OS)
241 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
242 * There is no need to store those acls as we already store the stat bits too.
244 static bool acl_is_trivial(acl_t acl)
247 * acl is trivial if it has only the following entries:
254 #if defined(HAVE_FREEBSD_OS) || \
255 defined(HAVE_LINUX_OS)
258 entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
259 while (entry_available == 1) {
261 * Get the tag type of this acl entry.
262 * If we fail to get the tagtype we call the acl non-trivial.
264 if (acl_get_tag_type(ace, &tag) < 0)
267 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
269 if (tag != ACL_USER_OBJ &&
270 tag != ACL_GROUP_OBJ &&
273 entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
276 #elif defined(HAVE_IRIX_OS)
279 for (n = 0; n < acl->acl_cnt; n++) {
280 ace = &acl->acl_entry[n];
284 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
286 if (tag != ACL_USER_OBJ &&
287 tag != ACL_GROUP_OBJ &&
288 tag != ACL_OTHER_OBJ)
292 #elif defined(HAVE_OSF1_OS)
295 ace = acl->acl_first;
296 count = acl->acl_num;
299 tag = ace->entry->acl_type;
301 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
303 if (tag != ACL_USER_OBJ &&
304 tag != ACL_GROUP_OBJ &&
308 * On Tru64, perm can also contain non-standard bits such as
309 * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
311 if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)))
322 * Generic wrapper around acl_get_file call.
324 static bacl_exit_code generic_get_acl_from_os(JCR *jcr, bacl_type acltype)
331 ostype = bac_to_os_acltype(acltype);
332 acl = acl_get_file(jcr->last_fname, ostype);
334 #if defined(HAVE_IRIX_OS)
336 * From observation, IRIX's acl_get_file() seems to return a
337 * non-NULL acl with a count field of -1 when a file has no ACL
338 * defined, while IRIX's acl_to_text() returns NULL when presented
341 * Checking the count in the acl structure before calling
342 * acl_to_text() lets us avoid error messages about files
343 * with no ACLs, without modifying the flow of the code used for
344 * other operating systems, and it saves making some calls
345 * to acl_to_text() besides.
347 if (acl->acl_cnt <= 0) {
348 pm_strcpy(jcr->acl_data->content, "");
349 jcr->acl_data->content_length = 0;
355 #if !defined(HAVE_DARWIN_OS)
357 * Make sure this is not just a trivial ACL.
359 if (acltype == BACL_TYPE_ACCESS && acl_is_trivial(acl)) {
361 * The ACLs simply reflect the (already known) standard permissions
362 * So we don't send an ACL stream to the SD.
364 pm_strcpy(jcr->acl_data->content, "");
365 jcr->acl_data->content_length = 0;
371 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
372 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
378 Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"),
379 jcr->last_fname, be.bstrerror());
380 Dmsg2(100, "acl_to_text error file=%s ERR=%s\n",
381 jcr->last_fname, be.bstrerror());
383 pm_strcpy(jcr->acl_data->content, "");
384 jcr->acl_data->content_length = 0;
386 return bacl_exit_error;
390 * Handle errors gracefully.
392 if (acl == (acl_t)NULL) {
394 #if defined(BACL_ENOTSUP)
396 break; /* not supported */
399 pm_strcpy(jcr->acl_data->content, "");
400 jcr->acl_data->content_length = 0;
403 /* Some real error */
404 Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"),
405 jcr->last_fname, be.bstrerror());
406 Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",
407 jcr->last_fname, be.bstrerror());
409 pm_strcpy(jcr->acl_data->content, "");
410 jcr->acl_data->content_length = 0;
411 return bacl_exit_error;
415 * Not supported, just pretend there is nothing to see
417 pm_strcpy(jcr->acl_data->content, "");
418 jcr->acl_data->content_length = 0;
423 * Generic wrapper around acl_set_file call.
425 static bacl_exit_code generic_set_acl_on_os(JCR *jcr, bacl_type acltype)
432 * If we get empty default ACLs, clear ACLs now
434 ostype = bac_to_os_acltype(acltype);
435 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_data->content) == 0) {
436 if (acl_delete_def_file(jcr->last_fname) == 0) {
443 Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
444 jcr->last_fname, be.bstrerror());
445 return bacl_exit_error;
449 acl = acl_from_text(jcr->acl_data->content);
451 Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"),
452 jcr->last_fname, be.bstrerror());
453 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
454 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
455 return bacl_exit_error;
458 #ifndef HAVE_FREEBSD_OS
460 * FreeBSD always fails acl_valid() - at least on valid input...
461 * As it does the right thing, given valid input, just ignore acl_valid().
463 if (acl_valid(acl) != 0) {
464 Mmsg2(jcr->errmsg, _("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->content, jcr->last_fname, be.bstrerror());
469 return bacl_exit_error;
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) {
485 Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"),
486 jcr->last_fname, be.bstrerror());
487 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
488 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
490 return bacl_exit_error;
498 * OS specific functions for handling different types of acl streams.
500 #if defined(HAVE_DARWIN_OS)
502 * Define the supported ACL streams for this OS
504 static int os_access_acl_streams[1] = { STREAM_ACL_DARWIN_ACCESS_ACL };
505 static int os_default_acl_streams[1] = { -1 };
507 static bacl_exit_code darwin_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
509 #if defined(ACL_TYPE_EXTENDED)
511 * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
512 * and acl_get_file (name, ACL_TYPE_DEFAULT)
513 * always return NULL / EINVAL. There is no point in making
514 * these two useless calls. The real ACL is retrieved through
515 * acl_get_file (name, ACL_TYPE_EXTENDED).
517 * Read access ACLs for files, dirs and links
519 if (generic_get_acl_from_os(jcr, BACL_TYPE_EXTENDED) == bacl_exit_fatal)
520 return bacl_exit_fatal;
523 * Read access ACLs for files, dirs and links
525 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
526 return bacl_exit_fatal;
529 if (jcr->acl_data->content_length > 0) {
530 return send_acl_stream(jcr, STREAM_ACL_DARWIN_ACCESS_ACL);
535 static bacl_exit_code darwin_parse_acl_streams(JCR *jcr, int stream)
537 #if defined(ACL_TYPE_EXTENDED)
538 return generic_set_acl_on_os(jcr, BACL_TYPE_EXTENDED);
540 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
545 * For this OS setup the build and parse function pointer to the OS specific functions.
547 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = darwin_build_acl_streams;
548 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = darwin_parse_acl_streams;
550 #elif defined(HAVE_FREEBSD_OS) || \
551 defined(HAVE_IRIX_OS) || \
552 defined(HAVE_LINUX_OS)
555 * Define the supported ACL streams for these OSes
557 #if defined(HAVE_FREEBSD_OS)
558 static int os_access_acl_streams[1] = { STREAM_ACL_FREEBSD_ACCESS_ACL };
559 static int os_default_acl_streams[1] = { STREAM_ACL_FREEBSD_DEFAULT_ACL };
560 #elif defined(HAVE_IRIX_OS)
561 static int os_access_acl_streams[1] = { STREAM_ACL_IRIX_ACCESS_ACL };
562 static int os_default_acl_streams[1] = { STREAM_ACL_IRIX_DEFAULT_ACL };
563 #elif defined(HAVE_LINUX_OS)
564 static int os_access_acl_streams[1] = { STREAM_ACL_LINUX_ACCESS_ACL };
565 static int os_default_acl_streams[1] = { STREAM_ACL_LINUX_DEFAULT_ACL };
568 static bacl_exit_code generic_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
571 * Read access ACLs for files, dirs and links
573 if (generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS) == bacl_exit_fatal)
574 return bacl_exit_fatal;
576 if (jcr->acl_data->content_length > 0) {
577 if (send_acl_stream(jcr, os_access_acl_streams[0]) == bacl_exit_fatal)
578 return bacl_exit_fatal;
582 * Directories can have default ACLs too
584 if (ff_pkt->type == FT_DIREND) {
585 if (generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
586 return bacl_exit_fatal;
587 if (jcr->acl_data->content_length > 0) {
588 if (send_acl_stream(jcr, os_default_acl_streams[0]) == bacl_exit_fatal)
589 return bacl_exit_fatal;
595 static bacl_exit_code generic_parse_acl_streams(JCR *jcr, int stream)
600 case STREAM_UNIX_ACCESS_ACL:
601 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
602 case STREAM_UNIX_DEFAULT_ACL:
603 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
606 * See what type of acl it is.
608 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
609 if (os_access_acl_streams[cnt] == stream) {
610 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
613 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
614 if (os_default_acl_streams[cnt] == stream) {
615 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
620 return bacl_exit_error;
624 * For this OSes setup the build and parse function pointer to the OS specific functions.
626 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_build_acl_streams;
627 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = generic_parse_acl_streams;
629 #elif defined(HAVE_OSF1_OS)
632 * Define the supported ACL streams for this OS
634 static int os_access_acl_streams[1] = { STREAM_ACL_TRU64_ACCESS_ACL };
635 static int os_default_acl_streams[2] = { STREAM_ACL_TRU64_DEFAULT_ACL, STREAM_ACL_TRU64_DEFAULT_DIR_ACL };
637 static bacl_exit_code tru64_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
640 * Read access ACLs for files, dirs and links
642 if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_ACCESS)) < 0)
643 return bacl_exit_error;
644 if (jcr->acl_data->content_length > 0) {
645 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_ACCESS_ACL))
646 return bacl_exit_error;
649 * Directories can have default ACLs too
651 if (ff_pkt->type == FT_DIREND) {
652 if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT)) < 0)
653 return bacl_exit_error;
654 if (jcr->acl_data->content_length > 0) {
655 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_ACL))
656 return bacl_exit_error;
659 * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
660 * This is an inherited acl for all subdirs.
661 * See http://www.helsinki.fi/atk/unix/dec_manuals/DOC_40D/AQ0R2DTE/DOCU_018.HTM
662 * Section 21.5 Default ACLs
664 if ((jcr->acl_data->content_length = generic_get_acl_from_os(jcr, BACL_TYPE_DEFAULT_DIR)) < 0)
665 return bacl_exit_error;
666 if (jcr->acl_data->content_length > 0) {
667 if (!send_acl_stream(jcr, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
668 return bacl_exit_error;
674 static bacl_exit_code tru64_parse_acl_streams(JCR *jcr, int stream)
677 case STREAM_UNIX_ACCESS_ACL:
678 case STREAM_ACL_TRU64_ACCESS_ACL:
679 return generic_set_acl_on_os(jcr, BACL_TYPE_ACCESS);
680 case STREAM_UNIX_DEFAULT_ACL:
681 case STREAM_ACL_TRU64_DEFAULT_ACL:
682 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT);
683 case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
684 return generic_set_acl_on_os(jcr, BACL_TYPE_DEFAULT_DIR);
688 * For this OS setup the build and parse function pointer to the OS specific functions.
690 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = tru64_build_acl_streams;
691 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = tru64_parse_acl_streams;
695 #elif defined(HAVE_HPUX_OS)
696 #ifdef HAVE_SYS_ACL_H
699 #error "configure failed to detect availability of sys/acl.h"
705 * Define the supported ACL streams for this OS
707 static int os_access_acl_streams[1] = { STREAM_ACL_HPUX_ACL_ENTRY };
708 static int os_default_acl_streams[1] = { -1 };
711 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
712 * There is no need to store those acls as we already store the stat bits too.
714 static bool acl_is_trivial(int count, struct acl_entry *entries, struct stat sb)
719 for (n = 0; n < count; n++) {
722 * See if this acl just is the stat mode in acl form.
724 if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
725 (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
726 (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
733 * OS specific functions for handling different types of acl streams.
735 static bacl_exit_code hpux_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
738 struct acl_entry acls[NACLENTRIES];
742 if ((n = getacl(jcr->last_fname, 0, acls)) < 0) {
744 #if defined(BACL_ENOTSUP)
747 * Not supported, just pretend there is nothing to see
749 pm_strcpy(jcr->acl_data->content, "");
750 jcr->acl_data->content_length = 0;
754 pm_strcpy(jcr->acl_data->content, "");
755 jcr->acl_data->content_length = 0;
758 Mmsg2(jcr->errmsg, _("getacl error on file \"%s\": ERR=%s\n"),
759 jcr->last_fname, be.bstrerror());
760 Dmsg2(100, "getacl error file=%s ERR=%s\n",
761 jcr->last_fname, be.bstrerror());
763 pm_strcpy(jcr->acl_data->content, "");
764 jcr->acl_data->content_length = 0;
765 return bacl_exit_error;
769 pm_strcpy(jcr->acl_data->content, "");
770 jcr->acl_data->content_length = 0;
773 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
774 if (acl_is_trivial(n, acls, ff_pkt->statp)) {
776 * The ACLs simply reflect the (already known) standard permissions
777 * So we don't send an ACL stream to the SD.
779 pm_strcpy(jcr->acl_data->content, "");
780 jcr->acl_data->content_length = 0;
783 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
784 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
785 actuallyfree(acl_text);
787 return send_acl_stream(jcr, STREAM_ACL_HPUX_ACL_ENTRY);
789 Mmsg2(jcr->errmsg, _("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->content, jcr->last_fname, be.bstrerror());
793 return bacl_exit_error;
795 return bacl_exit_error;
798 static bacl_exit_code hpux_parse_acl_streams(JCR *jcr, int stream)
801 struct acl_entry acls[NACLENTRIES];
804 n = strtoacl(jcr->acl_data->content, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
806 Mmsg2(jcr->errmsg, _("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->content, jcr->last_fname, be.bstrerror());
810 return bacl_exit_error;
812 if (strtoacl(jcr->acl_data->content, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
813 Mmsg2(jcr->errmsg, _("strtoacl error on file \"%s\": ERR=%s\n"),
814 jcr->last_fname, be.bstrerror());
815 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
816 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
818 return bacl_exit_error;
821 * Restore the ACLs, but don't complain about links which really should
822 * not have attributes, and the file it is linked to may not yet be restored.
823 * This is only true for the old acl streams as in the new implementation we
824 * don't save acls of symlinks (which cannot have acls anyhow)
826 if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
831 Mmsg2(jcr->errmsg, _("setacl error on file \"%s\": ERR=%s\n"),
832 jcr->last_fname, be.bstrerror());
833 Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
834 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
835 return bacl_exit_error;
842 * For this OS setup the build and parse function pointer to the OS specific functions.
844 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = hpux_build_acl_streams;
845 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = hpux_parse_acl_streams;
847 #elif defined(HAVE_SUN_OS)
848 #ifdef HAVE_SYS_ACL_H
851 #error "configure failed to detect availability of sys/acl.h"
854 #if defined(HAVE_EXTENDED_ACL)
856 * We define some internals of the Solaris acl libs here as those
857 * are not exposed yet. Probably because they want us to see the
858 * acls as opague data. But as we need to support different platforms
859 * and versions of Solaris we need to expose some data to be able
860 * to determine the type of acl used to stuff it into the correct
861 * data stream. I know this is far from portable, but maybe the
862 * proper interface is exposed later on and we can get ride of
863 * this kludge. Newer versions of Solaris include sys/acl_impl.h
864 * which has implementation details of acls, if thats included we
865 * don't have to define it ourself.
867 #if !defined(_SYS_ACL_IMPL_H)
868 typedef enum acl_type {
875 * Two external references to functions in the libsec library function not in current include files.
878 int acl_type(acl_t *);
879 char *acl_strerror(int);
883 * Define the supported ACL streams for this OS
885 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT, STREAM_ACL_SOLARIS_ACE };
886 static int os_default_acl_streams[1] = { -1 };
889 * As the new libsec interface with acl_totext and acl_fromtext also handles
890 * the old format from acltotext we can use the new functions even
891 * for acls retrieved and stored in the database with older fd versions. If the
892 * new interface is not defined (Solaris 9 and older we fall back to the old code)
894 static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
896 int acl_enabled, flags;
899 bacl_exit_code stream_status = bacl_exit_error;
903 * See if filesystem supports acls.
905 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
906 switch (acl_enabled) {
908 pm_strcpy(jcr->acl_data->content, "");
909 jcr->acl_data->content_length = 0;
916 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
917 jcr->last_fname, be.bstrerror());
918 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
919 jcr->last_fname, be.bstrerror());
920 return bacl_exit_error;
927 * Get ACL info: don't bother allocating space if there is only a trivial ACL.
929 if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
934 Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"),
935 jcr->last_fname, acl_strerror(errno));
936 Dmsg2(100, "acl_get error file=%s ERR=%s\n",
937 jcr->last_fname, acl_strerror(errno));
938 return bacl_exit_error;
944 * The ACLs simply reflect the (already known) standard permissions
945 * So we don't send an ACL stream to the SD.
947 pm_strcpy(jcr->acl_data->content, "");
948 jcr->acl_data->content_length = 0;
952 #if defined(ACL_SID_FMT)
954 * New format flag added in newer Solaris versions.
956 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
958 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
959 #endif /* ACL_SID_FMT */
961 if ((acl_text = acl_totext(aclp, flags)) != NULL) {
962 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
963 actuallyfree(acl_text);
965 switch (acl_type(aclp)) {
967 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
970 stream_status = send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACE);
978 return stream_status;
981 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
984 int acl_enabled, error;
988 case STREAM_UNIX_ACCESS_ACL:
989 case STREAM_ACL_SOLARIS_ACLENT:
990 case STREAM_ACL_SOLARIS_ACE:
992 * First make sure the filesystem supports acls.
994 acl_enabled = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
995 switch (acl_enabled) {
997 Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
999 return bacl_exit_error;
1003 return bacl_exit_ok;
1005 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"),
1006 jcr->last_fname, be.bstrerror());
1007 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
1008 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1009 return bacl_exit_error;
1013 * On a filesystem with ACL support make sure this particilar ACL type can be restored.
1016 case STREAM_ACL_SOLARIS_ACLENT:
1018 * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1020 if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1021 Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without aclent acl support\n"),
1023 return bacl_exit_error;
1026 case STREAM_ACL_SOLARIS_ACE:
1028 * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1030 if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1031 Mmsg1(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without ace acl support\n"),
1033 return bacl_exit_error;
1038 * Stream id which doesn't describe the type of acl which is encoded.
1045 if ((error = acl_fromtext(jcr->acl_data->content, &aclp)) != 0) {
1046 Mmsg2(jcr->errmsg, _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1047 jcr->last_fname, acl_strerror(error));
1048 Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",
1049 jcr->acl_data->content, jcr->last_fname, acl_strerror(error));
1050 return bacl_exit_error;
1054 * Validate that the conversion gave us the correct acl type.
1057 case STREAM_ACL_SOLARIS_ACLENT:
1058 if (acl_type(aclp) != ACLENT_T) {
1059 Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1061 return bacl_exit_error;
1064 case STREAM_ACL_SOLARIS_ACE:
1065 if (acl_type(aclp) != ACE_T) {
1066 Mmsg1(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1068 return bacl_exit_error;
1073 * Stream id which doesn't describe the type of acl which is encoded.
1079 * Restore the ACLs, but don't complain about links which really should
1080 * not have attributes, and the file it is linked to may not yet be restored.
1081 * This is only true for the old acl streams as in the new implementation we
1082 * don't save acls of symlinks (which cannot have acls anyhow)
1084 if ((error = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK) {
1088 return bacl_exit_ok;
1090 Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"),
1091 jcr->last_fname, acl_strerror(error));
1092 Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",
1093 jcr->acl_data->content, jcr->last_fname, acl_strerror(error));
1095 return bacl_exit_error;
1100 return bacl_exit_ok;
1102 return bacl_exit_error;
1103 } /* end switch (stream) */
1106 #else /* HAVE_EXTENDED_ACL */
1109 * Define the supported ACL streams for this OS
1111 static int os_access_acl_streams[2] = { STREAM_ACL_SOLARIS_ACLENT };
1112 static int os_default_acl_streams[1] = { -1 };
1115 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1116 * There is no need to store those acls as we already store the stat bits too.
1118 static bool acl_is_trivial(int count, aclent_t *entries)
1123 for (n = 0; n < count; n++) {
1126 if (!(ace->a_type == USER_OBJ ||
1127 ace->a_type == GROUP_OBJ ||
1128 ace->a_type == OTHER_OBJ ||
1129 ace->a_type == CLASS_OBJ))
1136 * OS specific functions for handling different types of acl streams.
1138 static bacl_exit_code solaris_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1145 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
1146 if (n < MIN_ACL_ENTRIES)
1147 return bacl_exit_error;
1149 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1150 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
1151 if (acl_is_trivial(n, acls)) {
1153 * The ACLs simply reflect the (already known) standard permissions
1154 * So we don't send an ACL stream to the SD.
1157 pm_strcpy(jcr->acl_data->content, "");
1158 jcr->acl_data->content_length = 0;
1159 return bacl_exit_ok;
1162 if ((acl_text = acltotext(acls, n)) != NULL) {
1163 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1164 actuallyfree(acl_text);
1166 return send_acl_stream(jcr, STREAM_ACL_SOLARIS_ACLENT);
1169 Mmsg2(jcr->errmsg, _("acltotext error on file \"%s\": ERR=%s\n"),
1170 jcr->last_fname, be.bstrerror());
1171 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
1172 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1176 return bacl_exit_error;
1179 static bacl_exit_code solaris_parse_acl_streams(JCR *jcr, int stream)
1185 acls = aclfromtext(jcr->acl_data->content, &n);
1187 Mmsg2(jcr->errmsg, _("aclfromtext error on file \"%s\": ERR=%s\n"),
1188 jcr->last_fname, be.bstrerror());
1189 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
1190 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1191 return bacl_exit_error;
1195 * Restore the ACLs, but don't complain about links which really should
1196 * not have attributes, and the file it is linked to may not yet be restored.
1198 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
1202 return bacl_exit_ok;
1204 Mmsg2(jcr->errmsg, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
1205 jcr->last_fname, be.bstrerror());
1206 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
1207 jcr->acl_data->content, jcr->last_fname, be.bstrerror());
1209 return bacl_exit_error;
1213 return bacl_exit_ok;
1215 #endif /* HAVE_EXTENDED_ACL */
1218 * For this OS setup the build and parse function pointer to the OS specific functions.
1220 static bacl_exit_code (*os_build_acl_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_acl_streams;
1221 static bacl_exit_code (*os_parse_acl_streams)(JCR *jcr, int stream) = solaris_parse_acl_streams;
1223 #endif /* HAVE_SUN_OS */
1224 #endif /* HAVE_ACL */
1226 #if defined(HAVE_AFS_ACL)
1228 #include <afs/stds.h>
1229 #include <afs/afs.h>
1230 #include <afs/auth.h>
1231 #include <afs/venus.h>
1232 #include <afs/prs_fs.h>
1235 * External references to functions in the libsys library function not in current include files.
1238 long pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow);
1241 static bacl_exit_code afs_build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1244 struct ViceIoctl vip;
1245 char acl_text[BUFSIZ];
1249 * AFS ACLs can only be set on a directory, so no need to try to
1250 * request them for anything other then that.
1252 if (ff_pkt->type != FT_DIREND) {
1253 return bacl_exit_ok;
1259 vip.out_size = sizeof(acl_text);
1260 memset((caddr_t)acl_text, 0, sizeof(acl_text));
1262 if ((error = pioctl(jcr->last_fname, VIOCGETAL, &vip, 0)) < 0) {
1263 Mmsg2(jcr->errmsg, _("pioctl VIOCGETAL error on file \"%s\": ERR=%s\n"),
1264 jcr->last_fname, be.bstrerror());
1265 Dmsg2(100, "pioctl VIOCGETAL error file=%s ERR=%s\n",
1266 jcr->last_fname, be.bstrerror());
1267 return bacl_exit_error;
1269 jcr->acl_data->content_length = pm_strcpy(jcr->acl_data->content, acl_text);
1270 return send_acl_stream(jcr, STREAM_ACL_AFS_TEXT);
1273 static bacl_exit_code afs_parse_acl_stream(JCR *jcr, int stream)
1276 struct ViceIoctl vip;
1279 vip.in = jcr->acl_data->content;
1280 vip.in_size = jcr->acl_data->content_length;
1284 if ((error = pioctl(jcr->last_fname, VIOCSETAL, &vip, 0)) < 0) {
1285 Mmsg2(jcr->errmsg, _("pioctl VIOCSETAL error on file \"%s\": ERR=%s\n"),
1286 jcr->last_fname, be.bstrerror());
1287 Dmsg2(100, "pioctl VIOCSETAL error file=%s ERR=%s\n",
1288 jcr->last_fname, be.bstrerror());
1290 return bacl_exit_error;
1292 return bacl_exit_ok;
1294 #endif /* HAVE_AFS_ACL */
1297 * Entry points when compiled with support for ACLs on a supported platform.
1301 * Read and send an ACL for the last encountered file.
1303 bacl_exit_code build_acl_streams(JCR *jcr, FF_PKT *ff_pkt)
1305 #if defined(HAVE_AFS_ACL)
1307 * AFS is a non OS specific filesystem so see if this path is on an AFS filesystem
1308 * and retrieve its ACL if it is.
1310 if (fstype_equals(jcr->last_fname, "afs")) {
1311 return afs_build_acl_streams(jcr, ff_pkt);
1314 #if defined(HAVE_ACL)
1316 * Call the appropriate function.
1318 if (os_build_acl_streams) {
1319 return (*os_build_acl_streams)(jcr, ff_pkt);
1322 return bacl_exit_error;
1325 bacl_exit_code parse_acl_streams(JCR *jcr, int stream)
1330 #if defined(HAVE_AFS_ACL)
1331 case STREAM_ACL_AFS_TEXT:
1332 return afs_parse_acl_stream(jcr, stream);
1334 #if defined(HAVE_ACL)
1335 case STREAM_UNIX_ACCESS_ACL:
1336 case STREAM_UNIX_DEFAULT_ACL:
1338 * Handle legacy ACL streams.
1340 if (os_parse_acl_streams) {
1341 return (*os_parse_acl_streams)(jcr, stream);
1345 if (os_parse_acl_streams) {
1347 * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
1349 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1350 if (os_access_acl_streams[cnt] == stream) {
1351 return (*os_parse_acl_streams)(jcr, stream);
1355 * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
1357 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1358 if (os_default_acl_streams[cnt] == stream) {
1359 return (*os_parse_acl_streams)(jcr, stream);
1369 Qmsg2(jcr, M_WARNING, 0,
1370 _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
1371 jcr->last_fname, stream);
1372 return bacl_exit_error;