2 Bacula® - The Network Backup Solution
4 Copyright (C) 2008-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 Extended Attributes for bacula.
31 * Extended Attributes are so OS specific we only restore Extended Attributes if
32 * they were saved using a filed on the same platform.
34 * Currently we support the following OSes:
35 * - Darwin (Extended Attributes)
36 * - Linux (Extended Attributes)
37 * - NetBSD (Extended Attributes)
38 * - FreeBSD (Extended Attributes)
39 * - OpenBSD (Extended Attributes)
40 * (As it seems either they never implemented xattr or they are removed
41 * the support as it stated it was in version 3.1 but the current syscall
42 * tabled shows the extattr_ functions are not implemented. So as such we
43 * might eventually support xattr on OpenBSD when they implemented them using
44 * the same interface as FreeBSD and NetBSD.
45 * - Solaris (Extended Attributes and Extensible Attributes)
47 * Written by Marco van Wieringen, November MMVIII
54 #if !defined(HAVE_XATTR)
56 * Entry points when compiled without support for XATTRs or on an unsupported platform.
58 bxattr_exit_code build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
60 return bxattr_exit_fatal;
63 bxattr_exit_code parse_xattr_streams(JCR *jcr, int stream)
65 return bxattr_exit_fatal;
69 * Send a XATTR stream to the SD.
71 static bxattr_exit_code send_xattr_stream(JCR *jcr, int stream)
73 BSOCK *sd = jcr->store_bsock;
75 #ifdef FD_NO_SEND_TEST
76 return bxattr_exit_ok;
82 if (jcr->xattr_data->content_length <= 0) {
83 return bxattr_exit_ok;
89 if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
90 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
92 return bxattr_exit_fatal;
96 * Send the buffer to the storage deamon
98 Dmsg1(400, "Backing up XATTR <%s>\n", jcr->xattr_data->content);
100 sd->msg = jcr->xattr_data->content;
101 sd->msglen = jcr->xattr_data->content_length;
105 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
107 return bxattr_exit_fatal;
110 jcr->JobBytes += sd->msglen;
112 if (!sd->signal(BNET_EOD)) {
113 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
115 return bxattr_exit_fatal;
117 Dmsg1(200, "XATTR of file: %s successfully backed up!\n", jcr->last_fname);
118 return bxattr_exit_ok;
122 * First some generic functions for OSes that use the same xattr encoding scheme.
124 #if defined(HAVE_DARWIN_OS) || \
125 defined(HAVE_LINUX_OS) || \
126 defined(HAVE_NETBSD_OS) || \
127 defined(HAVE_FREEBSD_OS) || \
128 defined(HAVE_OPENBSD_OS)
130 static void xattr_drop_internal_table(alist *xattr_value_list)
132 xattr_t *current_xattr;
135 * Walk the list of xattrs and free allocated memory on traversing.
137 foreach_alist(current_xattr, xattr_value_list) {
139 * See if we can shortcut.
141 if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC)
144 free(current_xattr->name);
146 if (current_xattr->value_length > 0)
147 free(current_xattr->value);
152 delete xattr_value_list;
156 * The xattr stream for OSX, FreeBSD, Linux and NetBSD is a serialized stream of bytes
157 * which encodes one or more xattr_t structures.
159 * The Serialized stream consists of the following elements:
160 * magic - A magic string which makes it easy to detect any binary incompatabilites
161 * name_length - The length of the following xattr name
162 * name - The name of the extended attribute
163 * value_length - The length of the following xattr data
164 * value - The actual content of the extended attribute
166 * This is repeated 1 or more times.
169 static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len, alist *xattr_value_list)
171 xattr_t *current_xattr;
175 * Make sure the serialized stream fits in the poolmem buffer.
176 * We allocate some more to be sure the stream is gonna fit.
178 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, expected_serialize_len + 10);
179 ser_begin(jcr->xattr_data->content, expected_serialize_len + 10);
182 * Walk the list of xattrs and serialize the data.
184 foreach_alist(current_xattr, xattr_value_list) {
186 * See if we can shortcut.
188 if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC)
191 ser_uint32(current_xattr->magic);
192 ser_uint32(current_xattr->name_length);
193 ser_bytes(current_xattr->name, current_xattr->name_length);
195 ser_uint32(current_xattr->value_length);
196 ser_bytes(current_xattr->value, current_xattr->value_length);
199 ser_end(jcr->xattr_data->content, expected_serialize_len + 10);
200 jcr->xattr_data->content_length = ser_length(jcr->xattr_data->content);
202 return jcr->xattr_data->content_length;
205 static bxattr_exit_code unserialize_xattr_stream(JCR *jcr, alist *xattr_value_list)
208 xattr_t *current_xattr;
209 bxattr_exit_code retval = bxattr_exit_ok;
212 * Parse the stream and call restore_xattr_on_file for each extended attribute.
214 * Start unserializing the data. We keep on looping while we have not
215 * unserialized all bytes in the stream.
217 unser_begin(jcr->xattr_data->content, jcr->xattr_data->content_length);
218 while (unser_length(jcr->xattr_data->content) < jcr->xattr_data->content_length) {
220 * First make sure the magic is present. This way we can easily catch corruption.
221 * Any missing MAGIC is fatal we do NOT try to continue.
224 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
225 unser_uint32(current_xattr->magic);
226 if (current_xattr->magic != XATTR_MAGIC) {
227 Mmsg1(jcr->errmsg, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"),
229 Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n",
232 return bxattr_exit_error;
236 * Decode the valuepair. First decode the length of the name.
238 unser_uint32(current_xattr->name_length);
241 * Allocate room for the name and decode its content.
243 current_xattr->name = (char *)malloc(current_xattr->name_length + 1);
244 unser_bytes(current_xattr->name, current_xattr->name_length);
247 * The xattr_name needs to be null terminated for lsetxattr.
249 current_xattr->name[current_xattr->name_length] = '\0';
252 * Decode the value length.
254 unser_uint32(current_xattr->value_length);
257 * Allocate room for the value and decode its content.
259 current_xattr->value = (char *)malloc(current_xattr->value_length);
260 unser_bytes(current_xattr->value, current_xattr->value_length);
262 xattr_value_list->append(current_xattr);
265 unser_end(jcr->xattr_data->content, jcr->xattr_data->content_length);
271 * This is a supported OS, See what kind of interface we should use.
273 #if defined(HAVE_DARWIN_OS) || \
274 defined(HAVE_LINUX_OS)
276 #if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)) || \
277 (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) || \
278 (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR))
279 #error "Missing either full support for the LXATTR or XATTR functions."
282 #ifdef HAVE_SYS_XATTR_H
283 #include <sys/xattr.h>
285 #error "Missing sys/xattr.h header file"
289 * Define the supported XATTR streams for this OS
291 #if defined(HAVE_DARWIN_OS)
292 static int os_default_xattr_streams[1] = { STREAM_XATTR_DARWIN };
293 static const char *xattr_acl_skiplist[2] = { "com.apple.system.Security", NULL };
294 static const char *xattr_skiplist[3] = { "com.apple.system.extendedsecurity", "com.apple.ResourceFork", NULL };
295 #elif defined(HAVE_LINUX_OS)
296 static int os_default_xattr_streams[1] = { STREAM_XATTR_LINUX };
297 static const char *xattr_acl_skiplist[2] = { "system.posix_acl_access", NULL };
298 static const char *xattr_skiplist[1] = { NULL };
302 * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
303 * listxattr, getxattr and setxattr with an extra options argument
304 * which mimics the l variants of the functions when we specify
305 * XATTR_NOFOLLOW as the options value.
307 #if defined(HAVE_DARWIN_OS)
308 #define llistxattr(path, list, size) listxattr((path), (list), (size), XATTR_NOFOLLOW)
309 #define lgetxattr(path, name, value, size) getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
310 #define lsetxattr(path, name, value, size, flags) setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
313 * Fallback to the non l-functions when those are not available.
315 #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
316 #define lgetxattr getxattr
318 #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
319 #define lsetxattr setxattr
321 #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
322 #define llistxattr listxattr
326 static bxattr_exit_code linux_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt)
329 char *xattr_list, *bp;
330 int cnt, xattr_count = 0;
331 int32_t xattr_list_len,
333 uint32_t expected_serialize_len = 0;
334 xattr_t *current_xattr;
335 alist *xattr_value_list = NULL;
336 bxattr_exit_code retval = bxattr_exit_error;
340 * First get the length of the available list with extended attributes.
342 xattr_list_len = llistxattr(jcr->last_fname, NULL, 0);
343 if (xattr_list_len < 0) {
346 return bxattr_exit_ok;
348 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
349 jcr->last_fname, be.bstrerror());
350 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
351 jcr->last_fname, be.bstrerror());
352 return bxattr_exit_error;
354 } else if (xattr_list_len == 0) {
355 return bxattr_exit_ok;
359 * Allocate room for the extented attribute list.
361 xattr_list = (char *)malloc(xattr_list_len + 1);
362 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
365 * Get the actual list of extended attributes names for a file.
367 xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len);
368 if (xattr_list_len < 0) {
371 retval = bxattr_exit_ok;
374 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
375 jcr->last_fname, be.bstrerror());
376 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
377 jcr->last_fname, be.bstrerror());
381 xattr_list[xattr_list_len] = '\0';
383 xattr_value_list = New(alist(10, not_owned_by_alist));
386 * Walk the list of extended attributes names and retrieve the data.
387 * We already count the bytes needed for serializing the stream later on.
390 while ((bp - xattr_list) + 1 < xattr_list_len) {
394 * On some OSes you also get the acls in the extented attribute list.
395 * So we check if we are already backing up acls and if we do we
396 * don't store the extended attribute with the same info.
398 if (ff_pkt->flags & FO_ACL) {
399 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
400 if (bstrcmp(bp, xattr_acl_skiplist[cnt])) {
408 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
411 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
412 if (bstrcmp(bp, xattr_skiplist[cnt])) {
420 bp = strchr(bp, '\0') + 1;
425 * Each xattr valuepair starts with a magic so we can parse it easier.
427 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
428 current_xattr->magic = XATTR_MAGIC;
429 expected_serialize_len += sizeof(current_xattr->magic);
432 * Allocate space for storing the name.
434 current_xattr->name_length = strlen(bp);
435 current_xattr->name = (char *)malloc(current_xattr->name_length);
436 memcpy((caddr_t)current_xattr->name, (caddr_t)bp, current_xattr->name_length);
438 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
441 * First see how long the value is for the extended attribute.
443 xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0);
444 if (xattr_value_len < 0) {
447 retval = bxattr_exit_ok;
448 free(current_xattr->name);
452 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
453 jcr->last_fname, be.bstrerror());
454 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
455 jcr->last_fname, be.bstrerror());
456 free(current_xattr->name);
463 * Allocate space for storing the value.
465 current_xattr->value = (char *)malloc(xattr_value_len);
466 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
468 xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
469 if (xattr_value_len < 0) {
472 retval = bxattr_exit_ok;
473 free(current_xattr->value);
474 free(current_xattr->name);
478 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
479 jcr->last_fname, be.bstrerror());
480 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
481 jcr->last_fname, be.bstrerror());
482 free(current_xattr->value);
483 free(current_xattr->name);
490 * Store the actual length of the value.
492 current_xattr->value_length = xattr_value_len;
493 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
496 * Protect ourself against things getting out of hand.
498 if (expected_serialize_len >= MAX_XATTR_STREAM) {
499 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
500 jcr->last_fname, MAX_XATTR_STREAM);
501 free(current_xattr->value);
502 free(current_xattr->name);
507 xattr_value_list->append(current_xattr);
509 bp = strchr(bp, '\0') + 1;
513 xattr_list = (char *)NULL;
516 * If we found any xattr send them to the SD.
518 if (xattr_count > 0) {
520 * Serialize the datastream.
522 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
523 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
525 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
530 xattr_drop_internal_table(xattr_value_list);
533 * Send the datastream to the SD.
535 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
537 xattr_drop_internal_table(xattr_value_list);
539 return bxattr_exit_ok;
543 if (xattr_list != NULL) {
546 if (xattr_value_list != NULL) {
547 xattr_drop_internal_table(xattr_value_list);
552 static bxattr_exit_code linux_xattr_parse_streams(JCR *jcr, int stream)
554 xattr_t *current_xattr;
555 alist *xattr_value_list;
558 xattr_value_list = New(alist(10, not_owned_by_alist));
560 if (unserialize_xattr_stream(jcr, xattr_value_list) != bxattr_exit_ok) {
561 xattr_drop_internal_table(xattr_value_list);
562 return bxattr_exit_error;
565 foreach_alist(current_xattr, xattr_value_list) {
566 if (lsetxattr(jcr->last_fname, current_xattr->name, current_xattr->value, current_xattr->value_length, 0) != 0) {
571 Mmsg2(jcr->errmsg, _("lsetxattr error on file \"%s\": ERR=%s\n"),
572 jcr->last_fname, be.bstrerror());
573 Dmsg2(100, "lsetxattr error file=%s ERR=%s\n",
574 jcr->last_fname, be.bstrerror());
580 xattr_drop_internal_table(xattr_value_list);
581 return bxattr_exit_ok;
584 xattr_drop_internal_table(xattr_value_list);
585 return bxattr_exit_error;
589 * Function pointers to the build and parse function to use for these xattrs.
591 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = linux_xattr_build_streams;
592 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = linux_xattr_parse_streams;
594 #elif defined(HAVE_FREEBSD_OS) || \
595 defined(HAVE_NETBSD_OS) || \
596 defined(HAVE_OPENBSD_OS)
598 #if (!defined(HAVE_EXTATTR_GET_LINK) && !defined(HAVE_EXTATTR_GET_FILE)) || \
599 (!defined(HAVE_EXTATTR_SET_LINK) && !defined(HAVE_EXTATTR_SET_FILE)) || \
600 (!defined(HAVE_EXTATTR_LIST_LINK) && !defined(HAVE_EXTATTR_LIST_FILE)) || \
601 !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \
602 !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE)
603 #error "Missing full support for the extattr functions."
606 #ifdef HAVE_SYS_EXTATTR_H
607 #include <sys/extattr.h>
609 #error "Missing sys/extattr.h header file"
612 #ifdef HAVE_LIBUTIL_H
616 #if !defined(HAVE_EXTATTR_GET_LINK) && defined(HAVE_EXTATTR_GET_FILE)
617 #define extattr_get_link extattr_get_file
619 #if !defined(HAVE_EXTATTR_SET_LINK) && defined(HAVE_EXTATTR_SET_FILE)
620 #define extattr_set_link extattr_set_file
622 #if !defined(HAVE_EXTATTR_LIST_LINK) && defined(HAVE_EXTATTR_LIST_FILE)
623 #define extattr_list_link extattr_list_file
626 #if defined(HAVE_FREEBSD_OS)
627 static int os_default_xattr_streams[1] = { STREAM_XATTR_FREEBSD };
628 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
629 static const char *xattr_acl_skiplist[2] = { "system.posix1e.acl_access", NULL };
630 static const char *xattr_skiplist[1] = { NULL };
631 #elif defined(HAVE_NETBSD_OS)
632 static int os_default_xattr_streams[1] = { STREAM_XATTR_NETBSD };
633 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
634 static const char *xattr_acl_skiplist[1] = { NULL };
635 static const char *xattr_skiplist[1] = { NULL };
636 #elif defined(HAVE_OPENBSD_OS)
637 static int os_default_xattr_streams[1] = { STREAM_XATTR_OPENBSD };
638 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
639 static const char *xattr_acl_skiplist[1] = { NULL };
640 static const char *xattr_skiplist[1] = { NULL };
643 static bxattr_exit_code bsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
647 int cnt, index, xattr_count = 0;
648 int32_t xattr_list_len,
650 uint32_t expected_serialize_len = 0;
651 unsigned int namespace_index;
653 char *current_attrnamespace = NULL;
654 char current_attrname[XATTR_BUFSIZ], current_attrtuple[XATTR_BUFSIZ];
655 xattr_t *current_xattr;
656 alist *xattr_value_list = NULL;
657 bxattr_exit_code retval = bxattr_exit_error;
660 xattr_value_list = New(alist(10, not_owned_by_alist));
663 * Loop over all available xattr namespaces.
665 for (namespace_index = 0; namespace_index < sizeof(os_default_xattr_namespaces) / sizeof(int); namespace_index++) {
666 attrnamespace = os_default_xattr_namespaces[namespace_index];
669 * Convert the numeric attrnamespace into a string representation and make a private copy of that string.
670 * The extattr_namespace_to_string functions returns a strdupped string which we need to free.
672 if (extattr_namespace_to_string(attrnamespace, ¤t_attrnamespace) != 0) {
673 Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"),
674 attrnamespace, jcr->last_fname);
675 Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n",
676 attrnamespace, jcr->last_fname);
681 * First get the length of the available list with extended attributes.
682 * If we get EPERM on system namespace, don't return error.
683 * This is expected for normal users trying to archive the system
684 * namespace on FreeBSD 6.2 and later. On NetBSD 3.1 and later,
685 * they've decided to return EOPNOTSUPP instead.
687 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, NULL, 0);
688 if (xattr_list_len < 0) {
691 retval = bxattr_exit_ok;
693 #if defined(EOPNOTSUPP)
697 if (attrnamespace == EXTATTR_NAMESPACE_SYSTEM) {
698 actuallyfree(current_attrnamespace);
699 current_attrnamespace = NULL;
706 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
707 jcr->last_fname, be.bstrerror());
708 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
709 jcr->last_fname, be.bstrerror());
712 } else if (xattr_list_len == 0) {
717 * Allocate room for the extented attribute list.
719 xattr_list = (char *)malloc(xattr_list_len + 1);
720 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
723 * Get the actual list of extended attributes names for a file.
725 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, xattr_list, xattr_list_len);
726 if (xattr_list_len < 0) {
729 retval = bxattr_exit_ok;
732 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
733 jcr->last_fname, be.bstrerror());
734 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
735 jcr->last_fname, be.bstrerror());
739 xattr_list[xattr_list_len] = '\0';
742 * Walk the list of extended attributes names and retrieve the data.
743 * We already count the bytes needed for serializing the stream later on.
745 for (index = 0; index < xattr_list_len; index += xattr_list[index] + 1) {
749 * Print the current name into the buffer as its not null terminated we need to
750 * use the length encoded in the string for copying only the needed bytes.
752 cnt = xattr_list[index];
753 if (cnt > ((int)sizeof(current_attrname) - 1)) {
754 cnt = ((int)sizeof(current_attrname) - 1);
756 strncpy(current_attrname, xattr_list + (index + 1), cnt);
757 current_attrname[cnt] = '\0';
760 * First make a xattr tuple of the current namespace and the name of the xattr.
761 * e.g. something like user.<attrname> or system.<attrname>
763 bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s", current_attrnamespace, current_attrname);
766 * On some OSes you also get the acls in the extented attribute list.
767 * So we check if we are already backing up acls and if we do we
768 * don't store the extended attribute with the same info.
770 if (ff_pkt->flags & FO_ACL) {
771 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
772 if (bstrcmp(current_attrtuple, xattr_acl_skiplist[cnt])) {
780 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
783 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
784 if (bstrcmp(current_attrtuple, xattr_skiplist[cnt])) {
796 * Each xattr valuepair starts with a magic so we can parse it easier.
798 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
799 current_xattr->magic = XATTR_MAGIC;
800 expected_serialize_len += sizeof(current_xattr->magic);
803 * Allocate space for storing the name.
805 current_xattr->name_length = strlen(current_attrtuple);
806 current_xattr->name = (char *)malloc(current_xattr->name_length);
807 memcpy((caddr_t)current_xattr->name, (caddr_t)current_attrtuple, current_xattr->name_length);
809 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
812 * First see how long the value is for the extended attribute.
814 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, NULL, 0);
815 if (xattr_value_len < 0) {
818 retval = bxattr_exit_ok;
819 free(current_xattr->name);
823 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
824 jcr->last_fname, be.bstrerror());
825 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
826 jcr->last_fname, be.bstrerror());
827 free(current_xattr->name);
834 * Allocate space for storing the value.
836 current_xattr->value = (char *)malloc(xattr_value_len);
837 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
839 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, current_xattr->value, xattr_value_len);
840 if (xattr_value_len < 0) {
843 retval = bxattr_exit_ok;
844 free(current_xattr->value);
845 free(current_xattr->name);
849 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
850 jcr->last_fname, be.bstrerror());
851 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
852 jcr->last_fname, be.bstrerror());
853 free(current_xattr->value);
854 free(current_xattr->name);
861 * Store the actual length of the value.
863 current_xattr->value_length = xattr_value_len;
864 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
867 * Protect ourself against things getting out of hand.
869 if (expected_serialize_len >= MAX_XATTR_STREAM) {
870 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
871 jcr->last_fname, MAX_XATTR_STREAM);
872 free(current_xattr->value);
873 free(current_xattr->name);
878 xattr_value_list->append(current_xattr);
884 * Drop the local copy of the current_attrnamespace.
886 actuallyfree(current_attrnamespace);
887 current_attrnamespace = NULL;
890 * We are done with this xattr list.
893 xattr_list = (char *)NULL;
897 * If we found any xattr send them to the SD.
899 if (xattr_count > 0) {
901 * Serialize the datastream.
903 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
904 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
906 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
911 xattr_drop_internal_table(xattr_value_list);
912 xattr_value_list = NULL;
915 * Send the datastream to the SD.
917 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
919 xattr_drop_internal_table(xattr_value_list);
920 xattr_value_list = NULL;
922 return bxattr_exit_ok;
926 if (current_attrnamespace != NULL) {
927 actuallyfree(current_attrnamespace);
928 current_attrnamespace = NULL;
930 if (xattr_list != NULL) {
933 if (xattr_value_list != NULL) {
934 xattr_drop_internal_table(xattr_value_list);
935 xattr_value_list = NULL;
940 static bxattr_exit_code bsd_parse_xattr_streams(JCR *jcr, int stream)
942 xattr_t *current_xattr;
943 alist *xattr_value_list;
944 int current_attrnamespace, cnt;
945 char *attrnamespace, *attrname;
948 xattr_value_list = New(alist(10, not_owned_by_alist));
950 if (unserialize_xattr_stream(jcr, xattr_value_list) != bxattr_exit_ok) {
951 xattr_drop_internal_table(xattr_value_list);
952 return bxattr_exit_error;
955 foreach_alist(current_xattr, xattr_value_list) {
957 * Try splitting the xattr_name into a namespace and name part.
958 * The splitting character is a .
960 attrnamespace = current_xattr->name;
961 if ((attrname = strchr(attrnamespace, '.')) == (char *)NULL) {
962 Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"),
963 current_xattr->name, jcr->last_fname);
964 Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n",
965 current_xattr->name, jcr->last_fname);
971 * Make sure the attrnamespace makes sense.
973 if (extattr_string_to_namespace(attrnamespace, ¤t_attrnamespace) != 0) {
974 Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"),
975 attrnamespace, jcr->last_fname);
976 Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n",
977 attrnamespace, jcr->last_fname);
982 * Try restoring the extended attribute.
984 cnt = extattr_set_link(jcr->last_fname, current_attrnamespace,
985 attrname, current_xattr->value, current_xattr->value_length);
986 if (cnt < 0 || cnt != (int)current_xattr->value_length) {
992 Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"),
993 jcr->last_fname, be.bstrerror());
994 Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n",
995 jcr->last_fname, be.bstrerror());
1002 xattr_drop_internal_table(xattr_value_list);
1003 return bxattr_exit_ok;
1006 xattr_drop_internal_table(xattr_value_list);
1007 return bxattr_exit_error;
1011 * Function pointers to the build and parse function to use for these xattrs.
1013 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = bsd_build_xattr_streams;
1014 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = bsd_parse_xattr_streams;
1016 #elif defined(HAVE_SUN_OS)
1018 * Solaris extended attributes were introduced in Solaris 9
1021 * Solaris extensible attributes were introduced in OpenSolaris
1022 * by PSARC 2007/315 Solaris extensible attributes are also
1023 * sometimes called extended system attributes.
1025 * man fsattr(5) on Solaris gives a wealth of info. The most
1026 * important bits are:
1028 * Attributes are logically supported as files within the file
1029 * system. The file system is therefore augmented with an
1030 * orthogonal name space of file attributes. Any file (includ-
1031 * ing attribute files) can have an arbitrarily deep attribute
1032 * tree associated with it. Attribute values are accessed by
1033 * file descriptors obtained through a special attribute inter-
1034 * face. This logical view of "attributes as files" allows the
1035 * leveraging of existing file system interface functionality
1036 * to support the construction, deletion, and manipulation of
1039 * The special files "." and ".." retain their accustomed
1040 * semantics within the attribute hierarchy. The "." attribute
1041 * file refers to the current directory and the ".." attribute
1042 * file refers to the parent directory. The unnamed directory
1043 * at the head of each attribute tree is considered the "child"
1044 * of the file it is associated with and the ".." file refers
1045 * to the associated file. For any non-directory file with
1046 * attributes, the ".." entry in the unnamed directory refers
1047 * to a file that is not a directory.
1049 * Conceptually, the attribute model is fully general. Extended
1050 * attributes can be any type of file (doors, links, direc-
1051 * tories, and so forth) and can even have their own attributes
1052 * (fully recursive). As a result, the attributes associated
1053 * with a file could be an arbitrarily deep directory hierarchy
1054 * where each attribute could have an equally complex attribute
1055 * tree associated with it. Not all implementations are able
1056 * to, or want to, support the full model. Implementation are
1057 * therefore permitted to reject operations that are not sup-
1058 * ported. For example, the implementation for the UFS file
1059 * system allows only regular files as attributes (for example,
1060 * no sub-directories) and rejects attempts to place attributes
1063 * The following list details the operations that are rejected
1064 * in the current implementation:
1066 * link Any attempt to create links between
1067 * attribute and non-attribute space
1068 * is rejected to prevent security-
1069 * related or otherwise sensitive
1070 * attributes from being exposed, and
1071 * therefore manipulable, as regular
1074 * rename Any attempt to rename between
1075 * attribute and non-attribute space
1076 * is rejected to prevent an already
1077 * linked file from being renamed and
1078 * thereby circumventing the link res-
1081 * mkdir, symlink, mknod Any attempt to create a "non-
1082 * regular" file in attribute space is
1083 * rejected to reduce the functional-
1084 * ity, and therefore exposure and
1085 * risk, of the initial implementa-
1088 * The entire available name space has been allocated to "gen-
1089 * eral use" to bring the implementation in line with the NFSv4
1090 * draft standard [NFSv4]. That standard defines "named attri-
1091 * butes" (equivalent to Solaris Extended Attributes) with no
1092 * naming restrictions. All Sun applications making use of
1093 * opaque extended attributes will use the prefix "SUNW".
1096 #ifdef HAVE_SYS_ATTR_H
1097 #include <sys/attr.h>
1104 #ifdef HAVE_SYS_NVPAIR_H
1105 #include <sys/nvpair.h>
1108 #ifdef HAVE_SYS_ACL_H
1109 #include <sys/acl.h>
1112 #if !defined(HAVE_OPENAT) || \
1113 !defined(HAVE_UNLINKAT) || \
1114 !defined(HAVE_FCHOWNAT) || \
1115 !defined(HAVE_FUTIMESAT)
1116 #error "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function"
1120 * Define the supported XATTR streams for this OS
1122 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1123 static int os_default_xattr_streams[2] = { STREAM_XATTR_SOLARIS, STREAM_XATTR_SOLARIS_SYS};
1125 static int os_default_xattr_streams[1] = { STREAM_XATTR_SOLARIS };
1126 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
1129 * This code creates a temporary cache with entries for each xattr which has
1130 * a link count > 1 (which indicates it has one or more hard linked counterpart(s))
1132 static xattr_link_cache_entry_t *find_xattr_link_cache_entry(JCR *jcr, ino_t inum)
1134 xattr_link_cache_entry_t *ptr;
1136 foreach_alist(ptr, jcr->xattr_data->link_cache) {
1137 if (ptr && ptr->inum == inum) {
1144 static void add_xattr_link_cache_entry(JCR *jcr, ino_t inum, char *target)
1146 xattr_link_cache_entry_t *ptr;
1148 ptr = (xattr_link_cache_entry_t *)malloc(sizeof(xattr_link_cache_entry_t));
1149 memset((caddr_t)ptr, 0, sizeof(xattr_link_cache_entry_t));
1151 bstrncpy(ptr->target, target, sizeof(ptr->target));
1152 jcr->xattr_data->link_cache->append(ptr);
1155 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1157 * This function returns true if a non default extended system attribute
1158 * list is associated with fd and returns false when an error has occured
1159 * or when only extended system attributes other than archive,
1160 * av_modified or crtime are set.
1162 * The function returns true for the following cases:
1164 * - any extended system attribute other than the default attributes
1165 * ('archive', 'av_modified' and 'crtime') is set
1166 * - nvlist has NULL name string
1167 * - nvpair has data type of 'nvlist'
1168 * - default data type.
1170 static bool solaris_has_non_transient_extensible_attributes(int fd)
1178 bool retval = false;
1180 if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
1185 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
1186 name = nvpair_name(pair);
1189 fattr = name_to_attr(name);
1195 type = nvpair_type(pair);
1197 case DATA_TYPE_BOOLEAN_VALUE:
1198 if (nvpair_value_boolean_value(pair, &value) != 0) {
1201 if (value && fattr != F_ARCHIVE &&
1202 fattr != F_AV_MODIFIED) {
1207 case DATA_TYPE_UINT64_ARRAY:
1208 if (fattr != F_CRTIME) {
1213 case DATA_TYPE_NVLIST:
1221 if (response != NULL) {
1222 nvlist_free(response);
1226 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1228 #if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL)
1230 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1231 * There is no need to store those acls as we already store the stat bits too.
1233 static bool acl_is_trivial(int count, aclent_t *entries)
1238 for (n = 0; n < count; n++) {
1240 if (!(ace->a_type == USER_OBJ ||
1241 ace->a_type == GROUP_OBJ ||
1242 ace->a_type == OTHER_OBJ ||
1243 ace->a_type == CLASS_OBJ))
1248 #endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */
1250 static bxattr_exit_code solaris_save_xattr_acl(JCR *jcr, int fd, const char *attrname, char **acl_text)
1253 #ifdef HAVE_EXTENDED_ACL
1259 * See if this attribute has an ACL
1261 if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0) ||
1262 pathconf(attrname, _PC_ACL_ENABLED) > 0) {
1264 * See if there is a non trivial acl on the file.
1266 if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0) ||
1267 acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) {
1270 return bxattr_exit_ok;
1272 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1273 attrname, jcr->last_fname, be.bstrerror());
1274 Dmsg3(100, "facl_get/acl_get of xattr %s on \"%s\" failed: ERR=%s\n",
1275 attrname, jcr->last_fname, be.bstrerror());
1276 return bxattr_exit_error;
1281 #if defined(ACL_SID_FMT)
1283 * New format flag added in newer Solaris versions.
1285 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1287 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1288 #endif /* ACL_SID_FMT */
1290 *acl_text = acl_totext(aclp, flags);
1298 return bxattr_exit_ok;
1299 #else /* HAVE_EXTENDED_ACL */
1301 aclent_t *acls = NULL;
1305 * See if this attribute has an ACL
1308 n = facl(fd, GETACLCNT, 0, NULL);
1310 n = acl(attrname, GETACLCNT, 0, NULL);
1313 if (n >= MIN_ACL_ENTRIES) {
1314 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1315 if ((fd != -1 && facl(fd, GETACL, n, acls) != n) ||
1316 acl(attrname, GETACL, n, acls) != n) {
1320 return bxattr_exit_ok;
1322 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1323 attrname, jcr->last_fname, be.bstrerror());
1324 Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n",
1325 attrname, jcr->last_fname, be.bstrerror());
1327 return bxattr_exit_error;
1332 * See if there is a non trivial acl on the file.
1334 if (!acl_is_trivial(n, acls)) {
1335 if ((*acl_text = acltotext(acls, n)) == NULL) {
1336 Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"),
1337 attrname, jcr->last_fname, be.bstrerror());
1338 Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n",
1339 attrname, jcr->last_fname, be.bstrerror());
1341 return bxattr_exit_error;
1351 return bxattr_exit_ok;
1352 #endif /* HAVE_EXTENDED_ACL */
1354 #else /* HAVE_ACL */
1355 return bxattr_exit_ok;
1356 #endif /* HAVE_ACL */
1360 * Forward declaration for recursive function call.
1362 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent);
1365 * Save an extended or extensible attribute.
1366 * This is stored as an opaque stream of bytes with the following encoding:
1368 * <xattr_name>\0<stat_buffer>\0<acl_string>\0<actual_xattr_data>
1370 * or for a hardlinked or symlinked attribute
1372 * <xattr_name>\0<stat_buffer>\0<xattr_link_source>\0
1374 * xattr_name can be a subpath relative to the file the xattr is on.
1375 * stat_buffer is the string representation of the stat struct.
1376 * acl_string is an acl text when a non trivial acl is set on the xattr.
1377 * actual_xattr_data is the content of the xattr file.
1379 static bxattr_exit_code solaris_save_xattr(JCR *jcr, int fd, const char *xattr_namespace,
1380 const char *attrname, bool toplevel_hidden_dir, int stream)
1385 xattr_link_cache_entry_t *xlce;
1386 char target_attrname[PATH_MAX];
1387 char link_source[PATH_MAX];
1388 char *acl_text = NULL;
1389 char attribs[MAXSTRING];
1390 char buffer[XATTR_BUFSIZ];
1391 bxattr_exit_code retval = bxattr_exit_error;
1394 bsnprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace, attrname);
1397 * Get the stats of the extended or extensible attribute.
1399 if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) {
1402 retval = bxattr_exit_ok;
1405 Mmsg3(jcr->errmsg, _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"),
1406 target_attrname, jcr->last_fname, be.bstrerror());
1407 Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n",
1408 target_attrname, jcr->last_fname, be.bstrerror());
1414 * Based on the filetype perform the correct action. We support most filetypes here, more
1415 * then the actual implementation on Solaris supports so some code may never get executed
1416 * due to limitations in the implementation.
1418 switch (st.st_mode & S_IFMT) {
1423 * Get any acl on the xattr.
1425 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1429 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1430 * Encode the stat struct into an ASCII representation.
1432 encode_stat(attribs, &st, 0, stream);
1433 cnt = bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c",
1434 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1438 * Get any acl on the xattr.
1440 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1444 * See if this is the toplevel_hidden_dir being saved.
1446 if (toplevel_hidden_dir) {
1448 * Save the data for later storage when we encounter a real xattr. We store the data
1449 * in the jcr->xattr_data->content buffer and flush that just before sending out the
1450 * first real xattr. Encode the stat struct into an ASCII representation and jump
1451 * out of the function.
1453 encode_stat(attribs, &st, 0, stream);
1454 cnt = bsnprintf(buffer, sizeof(buffer),
1456 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1457 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1458 jcr->xattr_data->content_length = cnt;
1462 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1463 * Encode the stat struct into an ASCII representation.
1465 encode_stat(attribs, &st, 0, stream);
1466 cnt = bsnprintf(buffer, sizeof(buffer),
1468 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1473 * If this is a hardlinked file check the inode cache for a hit.
1475 if (st.st_nlink > 1) {
1477 * See if the cache already knows this inode number.
1479 if ((xlce = find_xattr_link_cache_entry(jcr, st.st_ino)) != NULL) {
1481 * Generate a xattr encoding with the reference to the target in there.
1483 encode_stat(attribs, &st, st.st_ino, stream);
1484 cnt = bsnprintf(buffer, sizeof(buffer),
1486 target_attrname, 0, attribs, 0, xlce->target, 0);
1487 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1488 jcr->xattr_data->content_length = cnt;
1489 retval = send_xattr_stream(jcr, stream);
1492 * For a hard linked file we are ready now, no need to recursively save the attributes.
1498 * Store this hard linked file in the cache.
1499 * Store the name relative to the top level xattr space.
1501 add_xattr_link_cache_entry(jcr, st.st_ino, target_attrname + 1);
1505 * Get any acl on the xattr.
1507 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok) {
1512 * Encode the stat struct into an ASCII representation.
1514 encode_stat(attribs, &st, 0, stream);
1515 cnt = bsnprintf(buffer, sizeof(buffer),
1517 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1520 * Open the extended or extensible attribute file.
1522 if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) {
1525 retval = bxattr_exit_ok;
1528 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on \"%s\": ERR=%s\n"),
1529 target_attrname, jcr->last_fname, be.bstrerror());
1530 Dmsg3(100, "openat of xattr %s on \"%s\" failed: ERR=%s\n",
1531 target_attrname, jcr->last_fname, be.bstrerror());
1538 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1539 * Encode the stat struct into an ASCII representation.
1541 if (readlink(attrname, link_source, sizeof(link_source)) < 0) {
1544 retval = bxattr_exit_ok;
1547 Mmsg3(jcr->errmsg, _("Unable to read symlin %s on \"%s\": ERR=%s\n"),
1548 target_attrname, jcr->last_fname, be.bstrerror());
1549 Dmsg3(100, "readlink of xattr %s on \"%s\" failed: ERR=%s\n",
1550 target_attrname, jcr->last_fname, be.bstrerror());
1556 * Generate a xattr encoding with the reference to the target in there.
1558 encode_stat(attribs, &st, st.st_ino, stream);
1559 cnt = bsnprintf(buffer, sizeof(buffer),
1561 target_attrname, 0, attribs, 0, link_source, 0);
1562 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1563 jcr->xattr_data->content_length = cnt;
1564 retval = send_xattr_stream(jcr, stream);
1566 if (retval == bxattr_exit_ok) {
1567 jcr->xattr_data->nr_saved++;
1571 * For a soft linked file we are ready now, no need to recursively save the attributes.
1579 * See if this is the first real xattr being saved.
1580 * If it is save the toplevel_hidden_dir attributes first.
1581 * This is easy as its stored already in the jcr->xattr_data->content buffer.
1583 if (jcr->xattr_data->nr_saved == 0) {
1584 retval = send_xattr_stream(jcr, STREAM_XATTR_SOLARIS);
1585 if (retval != bxattr_exit_ok) {
1588 jcr->xattr_data->nr_saved++;
1591 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1592 jcr->xattr_data->content_length = cnt;
1595 * Only dump the content of regular files.
1597 switch (st.st_mode & S_IFMT) {
1599 if (st.st_size > 0) {
1601 * Protect ourself against things getting out of hand.
1603 if (st.st_size >= MAX_XATTR_STREAM) {
1604 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
1605 jcr->last_fname, MAX_XATTR_STREAM);
1609 while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) {
1610 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, jcr->xattr_data->content_length + cnt);
1611 memcpy(jcr->xattr_data->content + jcr->xattr_data->content_length, buffer, cnt);
1612 jcr->xattr_data->content_length += cnt;
1616 Mmsg2(jcr->errmsg, _("Unable to read content of xattr %s on file \"%s\"\n"),
1617 target_attrname, jcr->last_fname);
1618 Dmsg2(100, "read of data from xattr %s on \"%s\" failed\n",
1619 target_attrname, jcr->last_fname);
1630 retval = send_xattr_stream(jcr, stream);
1631 if (retval == bxattr_exit_ok) {
1632 jcr->xattr_data->nr_saved++;
1637 * Recursivly call solaris_save_extended_attributes for archiving the attributes
1638 * available on this extended attribute.
1641 retval = solaris_save_xattrs(jcr, xattr_namespace, attrname);
1644 * The recursive call could change our working dir so change back to the wanted workdir.
1646 if (fchdir(fd) < 0) {
1649 retval = bxattr_exit_ok;
1652 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"),
1653 jcr->last_fname, be.bstrerror());
1654 Dmsg3(100, "Unable to fchdir to xattr space of file \"%s\" using fd %d: ERR=%s\n",
1655 jcr->last_fname, fd, be.bstrerror());
1662 if (acl_text != NULL) {
1671 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent)
1674 int fd, filefd = -1, attrdirfd = -1;
1677 char current_xattr_namespace[PATH_MAX];
1678 bxattr_exit_code retval = bxattr_exit_error;
1682 * Determine what argument to use. Use attr_parent when set
1683 * (recursive call) or jcr->last_fname for first call. Also save
1684 * the current depth of the xattr_space we are in.
1688 if (xattr_namespace) {
1689 bsnprintf(current_xattr_namespace, sizeof(current_xattr_namespace), "%s%s/",
1690 xattr_namespace, attr_parent);
1692 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1695 name = jcr->last_fname;
1696 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1700 * Open the file on which to save the xattrs read-only.
1702 if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
1705 retval = bxattr_exit_ok;
1708 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1709 jcr->last_fname, be.bstrerror());
1710 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1711 jcr->last_fname, be.bstrerror());
1717 * Open the xattr naming space.
1719 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1723 * Gentile way of the system saying this type of xattr layering is not supported.
1724 * Which is not problem we just forget about this this xattr.
1725 * But as this is not an error we return a positive return value.
1727 retval = bxattr_exit_ok;
1730 retval = bxattr_exit_ok;
1733 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1734 name, jcr->last_fname, be.bstrerror());
1735 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1736 name, jcr->last_fname, be.bstrerror());
1742 * We need to change into the attribute directory to determine if each of the
1743 * attributes should be saved.
1745 if (fchdir(attrdirfd) < 0) {
1746 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1747 jcr->last_fname, be.bstrerror());
1748 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1749 jcr->last_fname, attrdirfd, be.bstrerror());
1754 * Save the data of the toplevel xattr hidden_dir. We save this one before anything
1755 * else because the readdir returns "." entry after the extensible attr entry.
1756 * And as we want this entry before anything else we better just save its data.
1759 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, ".",
1760 true, STREAM_XATTR_SOLARIS);
1762 if ((fd = dup(attrdirfd)) == -1 ||
1763 (dirp = fdopendir(fd)) == (DIR *)NULL) {
1764 Mmsg2(jcr->errmsg, _("Unable to list the xattr space on file \"%s\": ERR=%s\n"),
1765 jcr->last_fname, be.bstrerror());
1766 Dmsg3(100, "Unable to fdopendir xattr space on file \"%s\" using fd %d: ERR=%s\n",
1767 jcr->last_fname, fd, be.bstrerror());
1773 * Walk the namespace.
1775 while (dp = readdir(dirp)) {
1777 * Skip only the toplevel . dir.
1779 if (!attr_parent && bstrcmp(dp->d_name, "."))
1783 * Skip all .. directories
1785 if (bstrcmp(dp->d_name, ".."))
1788 Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n",
1789 current_xattr_namespace, dp->d_name, jcr->last_fname);
1791 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1793 * We are not interested in read-only extensible attributes.
1795 if (bstrcmp(dp->d_name, VIEW_READONLY)) {
1796 Dmsg3(400, "Skipping readonly extensible attributes %s%s on file \"%s\"\n",
1797 current_xattr_namespace, dp->d_name, jcr->last_fname);
1803 * We are only interested in read-write extensible attributes
1804 * when they contain non-transient values.
1806 if (bstrcmp(dp->d_name, VIEW_READWRITE)) {
1808 * Determine if there are non-transient system attributes at the toplevel.
1809 * We need to provide a fd to the open file.
1811 if (!solaris_has_non_transient_extensible_attributes(filefd)) {
1812 Dmsg3(400, "Skipping transient extensible attributes %s%s on file \"%s\"\n",
1813 current_xattr_namespace, dp->d_name, jcr->last_fname);
1820 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1821 false, STREAM_XATTR_SOLARIS_SYS);
1824 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1829 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1830 false, STREAM_XATTR_SOLARIS);
1834 retval = bxattr_exit_ok;
1837 if (attrdirfd != -1)
1845 static bxattr_exit_code solaris_restore_xattr_acl(JCR *jcr, int fd, const char *attrname, char *acl_text)
1847 #ifdef HAVE_EXTENDED_ACL
1852 if ((error = acl_fromtext(acl_text, &aclp)) != 0) {
1853 Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"),
1855 return bxattr_exit_error;
1858 if ((fd != -1 && facl_set(fd, aclp) != 0) ||
1859 acl_set(attrname, aclp) != 0) {
1860 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1861 attrname, jcr->last_fname, be.bstrerror());
1862 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1863 attrname, jcr->last_fname, be.bstrerror());
1864 return bxattr_exit_error;
1870 return bxattr_exit_ok;
1872 #else /* HAVE_EXTENDED_ACL */
1874 aclent_t *acls = NULL;
1877 acls = aclfromtext(acl_text, &n);
1879 if ((fd != -1 && facl(fd, SETACL, n, acls) != 0) ||
1880 acl(attrname, SETACL, n, acls) != 0) {
1881 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1882 attrname, jcr->last_fname, be.bstrerror());
1883 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1884 attrname, jcr->last_fname, be.bstrerror());
1885 return bxattr_exit_error;
1892 return bxattr_exit_ok;
1894 #endif /* HAVE_EXTENDED_ACL */
1897 #endif /* HAVE_ACL */
1899 static bxattr_exit_code solaris_restore_xattrs(JCR *jcr, bool is_extensible)
1901 int fd, filefd = -1, attrdirfd = -1, attrfd = -1;
1902 int used_bytes, total_bytes, cnt;
1903 char *bp, *target_attrname, *attribs;
1904 char *linked_target = NULL;
1905 char *acl_text = NULL;
1909 struct timeval times[2];
1910 bxattr_exit_code retval = bxattr_exit_error;
1914 * Parse the xattr stream. First the part that is the same for all xattrs.
1917 total_bytes = jcr->xattr_data->content_length;
1920 * The name of the target xattr has a leading / we are not interested
1921 * in that so skip it when decoding the string. We always start a the /
1922 * of the xattr space anyway.
1924 target_attrname = jcr->xattr_data->content + 1;
1925 if ((bp = strchr(target_attrname, '\0')) == (char *)NULL ||
1926 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1932 * Open the file on which to restore the xattrs read-only.
1934 if ((filefd = open(jcr->last_fname, O_RDONLY | O_NONBLOCK)) < 0) {
1935 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1936 jcr->last_fname, be.bstrerror());
1937 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1938 jcr->last_fname, be.bstrerror());
1943 * Open the xattr naming space and make it the current working dir.
1945 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1946 Mmsg2(jcr->errmsg, _("Unable to open xattr space on file \"%s\": ERR=%s\n"),
1947 jcr->last_fname, be.bstrerror());
1948 Dmsg2(100, "Unable to open xattr space on file \"%s\": ERR=%s\n",
1949 jcr->last_fname, be.bstrerror());
1953 if (fchdir(attrdirfd) < 0) {
1954 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1955 jcr->last_fname, be.bstrerror());
1956 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1957 jcr->last_fname, attrdirfd, be.bstrerror());
1962 * Try to open the correct xattr subdir based on the target_attrname given.
1963 * e.g. check if its a subdir attrname. Each / in the string makes us go
1966 while ((bp = strchr(target_attrname, '/')) != (char *)NULL) {
1969 if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) {
1970 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
1971 target_attrname, jcr->last_fname, be.bstrerror());
1972 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
1973 target_attrname, jcr->last_fname, be.bstrerror());
1981 * Open the xattr naming space.
1983 if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1984 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1985 target_attrname, jcr->last_fname, be.bstrerror());
1986 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1987 target_attrname, jcr->last_fname, be.bstrerror());
1995 * Make the xattr space our current workingdir.
1997 if (fchdir(attrdirfd) < 0) {
1998 Mmsg3(jcr->errmsg, _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"),
1999 target_attrname, jcr->last_fname, be.bstrerror());
2000 Dmsg4(100, "Unable to fchdir to xattr space %s on file \"%s\" using fd %d: ERR=%s\n",
2001 target_attrname, jcr->last_fname, attrdirfd, be.bstrerror());
2005 target_attrname = ++bp;
2009 * Decode the attributes from the stream.
2011 decode_stat(attribs, &st, &inum);
2014 * Decode the next field (acl_text).
2016 if ((bp = strchr(attribs, '\0')) == (char *)NULL ||
2017 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
2023 * Based on the filetype perform the correct action. We support most filetypes here, more
2024 * then the actual implementation on Solaris supports so some code may never get executed
2025 * due to limitations in the implementation.
2027 switch (st.st_mode & S_IFMT) {
2030 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2032 unlinkat(attrdirfd, target_attrname, 0);
2033 if (mkfifo(target_attrname, st.st_mode) < 0) {
2034 Mmsg3(jcr->errmsg, _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"),
2035 target_attrname, jcr->last_fname, be.bstrerror());
2036 Dmsg3(100, "Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n",
2037 target_attrname, jcr->last_fname, be.bstrerror());
2044 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2046 unlinkat(attrdirfd, target_attrname, 0);
2047 if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) {
2048 Mmsg3(jcr->errmsg, _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"),
2049 target_attrname, jcr->last_fname, be.bstrerror());
2050 Dmsg3(100, "Unable to mknod xattr %s on file \"%s\": ERR=%s\n",
2051 target_attrname, jcr->last_fname, be.bstrerror());
2057 * If its not the hidden_dir create the entry.
2058 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2060 if (!bstrcmp(target_attrname, ".")) {
2061 unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR);
2062 if (mkdir(target_attrname, st.st_mode) < 0) {
2063 Jmsg3(jcr, M_WARNING, 0, _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"),
2064 target_attrname, jcr->last_fname, be.bstrerror());
2065 Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n",
2066 target_attrname, jcr->last_fname, be.bstrerror());
2073 * See if this is a hard linked file. e.g. inum != 0
2078 unlinkat(attrdirfd, target_attrname, 0);
2079 if (link(linked_target, target_attrname) < 0) {
2080 Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"),
2081 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2082 Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n",
2083 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2088 * Successfully restored xattr.
2090 retval = bxattr_exit_ok;
2093 if ((bp = strchr(acl_text, '\0')) == (char *)NULL ||
2094 (used_bytes = (bp - jcr->xattr_data->content)) >= total_bytes) {
2098 if (used_bytes < (total_bytes - 1))
2102 * Restore the actual xattr.
2104 if (!is_extensible) {
2105 unlinkat(attrdirfd, target_attrname, 0);
2108 if ((attrfd = openat(attrdirfd, target_attrname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) {
2109 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
2110 target_attrname, jcr->last_fname, be.bstrerror());
2111 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
2112 target_attrname, jcr->last_fname, be.bstrerror());
2118 * Restore the actual data.
2120 if (st.st_size > 0) {
2121 used_bytes = (data - jcr->xattr_data->content);
2122 cnt = total_bytes - used_bytes;
2125 * Do a sanity check, the st.st_size should be the same as the number of bytes
2126 * we have available as data of the stream.
2128 if (cnt != st.st_size) {
2129 Mmsg2(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n"),
2130 target_attrname, jcr->last_fname);
2131 Dmsg2(100, "Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n",
2132 target_attrname, jcr->last_fname);
2137 cnt = write(attrfd, data, cnt);
2139 Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"),
2140 target_attrname, jcr->last_fname, be.bstrerror());
2141 Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n",
2142 target_attrname, jcr->last_fname, be.bstrerror());
2148 cnt = total_bytes - used_bytes;
2154 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2158 if (symlink(linked_target, target_attrname) < 0) {
2159 Mmsg4(jcr->errmsg, _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"),
2160 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2161 Dmsg4(100, "Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n",
2162 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2167 * Successfully restored xattr.
2169 retval = bxattr_exit_ok;
2176 * Restore owner and acl for non extensible attributes.
2178 if (!is_extensible) {
2179 if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0) {
2183 * Gentile way of the system saying this type of xattr layering is not supported.
2184 * But as this is not an error we return a positive return value.
2186 retval = bxattr_exit_ok;
2189 retval = bxattr_exit_ok;
2192 Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"),
2193 target_attrname, jcr->last_fname, be.bstrerror());
2194 Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n",
2195 target_attrname, jcr->last_fname, be.bstrerror());
2202 if (acl_text && *acl_text)
2203 if (solaris_restore_xattr_acl(jcr, attrfd, target_attrname, acl_text) != bxattr_exit_ok)
2205 #endif /* HAVE_ACL */
2208 * For a non extensible attribute restore access and modification time on the xattr.
2210 if (!is_extensible) {
2211 times[0].tv_sec = st.st_atime;
2212 times[0].tv_usec = 0;
2213 times[1].tv_sec = st.st_mtime;
2214 times[1].tv_usec = 0;
2216 if (futimesat(attrdirfd, target_attrname, times) < 0) {
2217 Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"),
2218 target_attrname, jcr->last_fname, be.bstrerror());
2219 Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n",
2220 target_attrname, jcr->last_fname, be.bstrerror());
2226 * Successfully restored xattr.
2228 retval = bxattr_exit_ok;
2232 Mmsg1(jcr->errmsg, _("Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n"),
2234 Dmsg1(100, "Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n",
2241 if (attrdirfd != -1) {
2250 static bxattr_exit_code solaris_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2253 bxattr_exit_code retval = bxattr_exit_ok;
2256 * First see if extended attributes or extensible attributes are present.
2257 * If not just pretend things went ok.
2259 if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0) {
2260 jcr->xattr_data->nr_saved = 0;
2261 jcr->xattr_data->link_cache = New(alist(10, not_owned_by_alist));
2264 * As we change the cwd in the save function save the current cwd
2265 * for restore after return from the solaris_save_xattrs function.
2267 getcwd(cwd, sizeof(cwd));
2268 retval = solaris_save_xattrs(jcr, NULL, NULL);
2270 delete jcr->xattr_data->link_cache;
2271 jcr->xattr_data->link_cache = NULL;
2276 static bxattr_exit_code solaris_parse_xattr_streams(JCR *jcr, int stream)
2279 bool is_extensible = false;
2280 bxattr_exit_code retval;
2283 * First make sure we can restore xattr on the filesystem.
2286 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
2287 case STREAM_XATTR_SOLARIS_SYS:
2288 if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0) {
2289 Mmsg1(jcr->errmsg, _("Failed to restore extensible attributes on file \"%s\"\n"), jcr->last_fname);
2290 Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n",
2292 return bxattr_exit_error;
2295 is_extensible = true;
2298 case STREAM_XATTR_SOLARIS:
2299 if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0) {
2300 Mmsg1(jcr->errmsg, _("Failed to restore extended attributes on file \"%s\"\n"), jcr->last_fname);
2301 Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n",
2303 return bxattr_exit_error;
2307 return bxattr_exit_error;
2311 * As we change the cwd in the restore function save the current cwd
2312 * for restore after return from the solaris_restore_xattrs function.
2314 getcwd(cwd, sizeof(cwd));
2315 retval = solaris_restore_xattrs(jcr, is_extensible);
2322 * Function pointers to the build and parse function to use for these xattrs.
2324 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_xattr_streams;
2325 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = solaris_parse_xattr_streams;
2327 #endif /* defined(HAVE_SUN_OS) */
2330 * Entry points when compiled with support for XATTRs on a supported platform.
2332 bxattr_exit_code build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2334 if (os_build_xattr_streams) {
2335 return (*os_build_xattr_streams)(jcr, ff_pkt);
2337 return bxattr_exit_error;
2340 bxattr_exit_code parse_xattr_streams(JCR *jcr, int stream)
2344 if (os_parse_xattr_streams) {
2346 * See if we can parse this stream, and ifso give it a try.
2348 for (cnt = 0; cnt < sizeof(os_default_xattr_streams) / sizeof(int); cnt++) {
2349 if (os_default_xattr_streams[cnt] == stream) {
2350 return (*os_parse_xattr_streams)(jcr, stream);
2355 * Issue a warning and discard the message. But pretend the restore was ok.
2357 Jmsg2(jcr, M_WARNING, 0,
2358 _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"),
2359 jcr->last_fname, stream);
2360 return bxattr_exit_error;