2 Bacula® - The Network Backup Solution
4 Copyright (C) 2008-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Functions to handle 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);
150 delete xattr_value_list;
154 * The xattr stream for OSX, FreeBSD, Linux and NetBSD is a serialized stream of bytes
155 * which encodes one or more xattr_t structures.
157 * The Serialized stream consists of the following elements:
158 * magic - A magic string which makes it easy to detect any binary incompatabilites
159 * name_length - The length of the following xattr name
160 * name - The name of the extended attribute
161 * value_length - The length of the following xattr data
162 * value - The actual content of the extended attribute
164 * This is repeated 1 or more times.
167 static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len, alist *xattr_value_list)
169 xattr_t *current_xattr;
173 * Make sure the serialized stream fits in the poolmem buffer.
174 * We allocate some more to be sure the stream is gonna fit.
176 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, expected_serialize_len + 10);
177 ser_begin(jcr->xattr_data->content, expected_serialize_len + 10);
180 * Walk the list of xattrs and serialize the data.
182 foreach_alist(current_xattr, xattr_value_list) {
184 * See if we can shortcut.
186 if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC)
189 ser_uint32(current_xattr->magic);
190 ser_uint32(current_xattr->name_length);
191 ser_bytes(current_xattr->name, current_xattr->name_length);
193 ser_uint32(current_xattr->value_length);
194 ser_bytes(current_xattr->value, current_xattr->value_length);
197 ser_end(jcr->xattr_data->content, expected_serialize_len + 10);
198 jcr->xattr_data->content_length = ser_length(jcr->xattr_data->content);
200 return jcr->xattr_data->content_length;
203 static bxattr_exit_code unserialize_xattr_stream(JCR *jcr, alist *xattr_value_list)
206 xattr_t *current_xattr;
207 bxattr_exit_code retval = bxattr_exit_ok;
210 * Parse the stream and call restore_xattr_on_file for each extended attribute.
212 * Start unserializing the data. We keep on looping while we have not
213 * unserialized all bytes in the stream.
215 unser_begin(jcr->xattr_data->content, jcr->xattr_data->content_length);
216 while (unser_length(jcr->xattr_data->content) < jcr->xattr_data->content_length) {
218 * First make sure the magic is present. This way we can easily catch corruption.
219 * Any missing MAGIC is fatal we do NOT try to continue.
222 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
223 unser_uint32(current_xattr->magic);
224 if (current_xattr->magic != XATTR_MAGIC) {
225 Mmsg1(jcr->errmsg, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"),
227 Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n",
230 return bxattr_exit_error;
234 * Decode the valuepair. First decode the length of the name.
236 unser_uint32(current_xattr->name_length);
239 * Allocate room for the name and decode its content.
241 current_xattr->name = (char *)malloc(current_xattr->name_length + 1);
242 unser_bytes(current_xattr->name, current_xattr->name_length);
245 * The xattr_name needs to be null terminated for lsetxattr.
247 current_xattr->name[current_xattr->name_length] = '\0';
250 * Decode the value length.
252 unser_uint32(current_xattr->value_length);
255 * Allocate room for the value and decode its content.
257 current_xattr->value = (char *)malloc(current_xattr->value_length);
258 unser_bytes(current_xattr->value, current_xattr->value_length);
260 xattr_value_list->append(current_xattr);
263 unser_end(jcr->xattr_data->content, jcr->xattr_data->content_length);
269 * This is a supported OS, See what kind of interface we should use.
271 #if defined(HAVE_DARWIN_OS) || \
272 defined(HAVE_LINUX_OS)
274 #if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)) || \
275 (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) || \
276 (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR))
277 #error "Missing either full support for the LXATTR or XATTR functions."
280 #ifdef HAVE_SYS_XATTR_H
281 #include <sys/xattr.h>
283 #error "Missing sys/xattr.h header file"
287 * Define the supported XATTR streams for this OS
289 #if defined(HAVE_DARWIN_OS)
290 static int os_default_xattr_streams[1] = { STREAM_XATTR_DARWIN };
291 static const char *xattr_acl_skiplist[2] = { "com.apple.system.Security", NULL };
292 static const char *xattr_skiplist[3] = { "com.apple.system.extendedsecurity", "com.apple.ResourceFork", NULL };
293 #elif defined(HAVE_LINUX_OS)
294 static int os_default_xattr_streams[1] = { STREAM_XATTR_LINUX };
295 static const char *xattr_acl_skiplist[2] = { "system.posix_acl_access", NULL };
296 static const char *xattr_skiplist[1] = { NULL };
300 * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
301 * listxattr, getxattr and setxattr with an extra options argument
302 * which mimics the l variants of the functions when we specify
303 * XATTR_NOFOLLOW as the options value.
305 #if defined(HAVE_DARWIN_OS)
306 #define llistxattr(path, list, size) listxattr((path), (list), (size), XATTR_NOFOLLOW)
307 #define lgetxattr(path, name, value, size) getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
308 #define lsetxattr(path, name, value, size, flags) setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
311 * Fallback to the non l-functions when those are not available.
313 #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
314 #define lgetxattr getxattr
316 #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
317 #define lsetxattr setxattr
319 #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
320 #define llistxattr listxattr
324 static bxattr_exit_code linux_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt)
327 char *xattr_list, *bp;
328 int cnt, xattr_count = 0;
329 int32_t xattr_list_len,
331 uint32_t expected_serialize_len = 0;
332 xattr_t *current_xattr;
333 alist *xattr_value_list = NULL;
334 bxattr_exit_code retval = bxattr_exit_error;
338 * First get the length of the available list with extended attributes.
340 xattr_list_len = llistxattr(jcr->last_fname, NULL, 0);
341 if (xattr_list_len < 0) {
344 return bxattr_exit_ok;
346 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
347 jcr->last_fname, be.bstrerror());
348 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
349 jcr->last_fname, be.bstrerror());
350 return bxattr_exit_error;
352 } else if (xattr_list_len == 0) {
353 return bxattr_exit_ok;
357 * Allocate room for the extented attribute list.
359 xattr_list = (char *)malloc(xattr_list_len + 1);
360 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
363 * Get the actual list of extended attributes names for a file.
365 xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len);
366 if (xattr_list_len < 0) {
369 retval = bxattr_exit_ok;
372 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
373 jcr->last_fname, be.bstrerror());
374 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
375 jcr->last_fname, be.bstrerror());
379 xattr_list[xattr_list_len] = '\0';
381 xattr_value_list = New(alist(10, not_owned_by_alist));
384 * Walk the list of extended attributes names and retrieve the data.
385 * We already count the bytes needed for serializing the stream later on.
388 while ((bp - xattr_list) + 1 < xattr_list_len) {
392 * On some OSes you also get the acls in the extented attribute list.
393 * So we check if we are already backing up acls and if we do we
394 * don't store the extended attribute with the same info.
396 if (ff_pkt->flags & FO_ACL) {
397 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
398 if (bstrcmp(bp, xattr_acl_skiplist[cnt])) {
406 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
409 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
410 if (bstrcmp(bp, xattr_skiplist[cnt])) {
418 bp = strchr(bp, '\0') + 1;
423 * Each xattr valuepair starts with a magic so we can parse it easier.
425 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
426 current_xattr->magic = XATTR_MAGIC;
427 expected_serialize_len += sizeof(current_xattr->magic);
430 * Allocate space for storing the name.
432 current_xattr->name_length = strlen(bp);
433 current_xattr->name = (char *)malloc(current_xattr->name_length);
434 memcpy((caddr_t)current_xattr->name, (caddr_t)bp, current_xattr->name_length);
436 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
439 * First see how long the value is for the extended attribute.
441 xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0);
442 if (xattr_value_len < 0) {
445 retval = bxattr_exit_ok;
446 free(current_xattr->name);
450 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
451 jcr->last_fname, be.bstrerror());
452 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
453 jcr->last_fname, be.bstrerror());
454 free(current_xattr->name);
461 * Allocate space for storing the value.
463 current_xattr->value = (char *)malloc(xattr_value_len);
464 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
466 xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
467 if (xattr_value_len < 0) {
470 retval = bxattr_exit_ok;
471 free(current_xattr->value);
472 free(current_xattr->name);
476 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
477 jcr->last_fname, be.bstrerror());
478 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
479 jcr->last_fname, be.bstrerror());
480 free(current_xattr->value);
481 free(current_xattr->name);
488 * Store the actual length of the value.
490 current_xattr->value_length = xattr_value_len;
491 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
494 * Protect ourself against things getting out of hand.
496 if (expected_serialize_len >= MAX_XATTR_STREAM) {
497 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
498 jcr->last_fname, MAX_XATTR_STREAM);
499 free(current_xattr->value);
500 free(current_xattr->name);
505 xattr_value_list->append(current_xattr);
507 bp = strchr(bp, '\0') + 1;
511 xattr_list = (char *)NULL;
514 * If we found any xattr send them to the SD.
516 if (xattr_count > 0) {
518 * Serialize the datastream.
520 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
521 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
523 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
528 xattr_drop_internal_table(xattr_value_list);
531 * Send the datastream to the SD.
533 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
535 xattr_drop_internal_table(xattr_value_list);
537 return bxattr_exit_ok;
541 if (xattr_list != NULL) {
544 if (xattr_value_list != NULL) {
545 xattr_drop_internal_table(xattr_value_list);
550 static bxattr_exit_code linux_xattr_parse_streams(JCR *jcr, int stream)
552 xattr_t *current_xattr;
553 alist *xattr_value_list;
556 xattr_value_list = New(alist(10, not_owned_by_alist));
558 if (unserialize_xattr_stream(jcr, xattr_value_list) != bxattr_exit_ok) {
559 xattr_drop_internal_table(xattr_value_list);
560 return bxattr_exit_error;
563 foreach_alist(current_xattr, xattr_value_list) {
564 if (lsetxattr(jcr->last_fname, current_xattr->name, current_xattr->value, current_xattr->value_length, 0) != 0) {
569 Mmsg2(jcr->errmsg, _("lsetxattr error on file \"%s\": ERR=%s\n"),
570 jcr->last_fname, be.bstrerror());
571 Dmsg2(100, "lsetxattr error file=%s ERR=%s\n",
572 jcr->last_fname, be.bstrerror());
578 xattr_drop_internal_table(xattr_value_list);
579 return bxattr_exit_ok;
582 xattr_drop_internal_table(xattr_value_list);
583 return bxattr_exit_error;
587 * Function pointers to the build and parse function to use for these xattrs.
589 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = linux_xattr_build_streams;
590 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = linux_xattr_parse_streams;
592 #elif defined(HAVE_FREEBSD_OS) || \
593 defined(HAVE_NETBSD_OS) || \
594 defined(HAVE_OPENBSD_OS)
596 #if (!defined(HAVE_EXTATTR_GET_LINK) && !defined(HAVE_EXTATTR_GET_FILE)) || \
597 (!defined(HAVE_EXTATTR_SET_LINK) && !defined(HAVE_EXTATTR_SET_FILE)) || \
598 (!defined(HAVE_EXTATTR_LIST_LINK) && !defined(HAVE_EXTATTR_LIST_FILE)) || \
599 !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \
600 !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE)
601 #error "Missing full support for the extattr functions."
604 #ifdef HAVE_SYS_EXTATTR_H
605 #include <sys/extattr.h>
607 #error "Missing sys/extattr.h header file"
610 #ifdef HAVE_LIBUTIL_H
614 #if !defined(HAVE_EXTATTR_GET_LINK) && defined(HAVE_EXTATTR_GET_FILE)
615 #define extattr_get_link extattr_get_file
617 #if !defined(HAVE_EXTATTR_SET_LINK) && defined(HAVE_EXTATTR_SET_FILE)
618 #define extattr_set_link extattr_set_file
620 #if !defined(HAVE_EXTATTR_LIST_LINK) && defined(HAVE_EXTATTR_LIST_FILE)
621 #define extattr_list_link extattr_list_file
624 #if defined(HAVE_FREEBSD_OS)
625 static int os_default_xattr_streams[1] = { STREAM_XATTR_FREEBSD };
626 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
627 static const char *xattr_acl_skiplist[2] = { "system.posix1e.acl_access", NULL };
628 static const char *xattr_skiplist[1] = { NULL };
629 #elif defined(HAVE_NETBSD_OS)
630 static int os_default_xattr_streams[1] = { STREAM_XATTR_NETBSD };
631 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
632 static const char *xattr_acl_skiplist[1] = { NULL };
633 static const char *xattr_skiplist[1] = { NULL };
634 #elif defined(HAVE_OPENBSD_OS)
635 static int os_default_xattr_streams[1] = { STREAM_XATTR_OPENBSD };
636 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
637 static const char *xattr_acl_skiplist[1] = { NULL };
638 static const char *xattr_skiplist[1] = { NULL };
641 static bxattr_exit_code bsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
645 int cnt, index, xattr_count = 0;
646 int32_t xattr_list_len,
648 uint32_t expected_serialize_len = 0;
649 unsigned int namespace_index;
651 char *current_attrnamespace = NULL;
652 char current_attrname[BUFSIZ], current_attrtuple[BUFSIZ];
653 xattr_t *current_xattr;
654 alist *xattr_value_list = NULL;
655 bxattr_exit_code retval = bxattr_exit_error;
658 xattr_value_list = New(alist(10, not_owned_by_alist));
661 * Loop over all available xattr namespaces.
663 for (namespace_index = 0; namespace_index < sizeof(os_default_xattr_namespaces) / sizeof(int); namespace_index++) {
664 attrnamespace = os_default_xattr_namespaces[namespace_index];
667 * Convert the numeric attrnamespace into a string representation and make a private copy of that string.
668 * The extattr_namespace_to_string functions returns a strdupped string which we need to free.
670 if (extattr_namespace_to_string(attrnamespace, ¤t_attrnamespace) != 0) {
671 Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"),
672 attrnamespace, jcr->last_fname);
673 Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n",
674 attrnamespace, jcr->last_fname);
679 * First get the length of the available list with extended attributes.
680 * If we get EPERM on system namespace, don't return error.
681 * This is expected for normal users trying to archive the system
682 * namespace on FreeBSD 6.2 and later. On NetBSD 3.1 and later,
683 * they've decided to return EOPNOTSUPP instead.
685 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, NULL, 0);
686 if (xattr_list_len < 0) {
689 retval = bxattr_exit_ok;
691 #if defined(EOPNOTSUPP)
695 if (attrnamespace == EXTATTR_NAMESPACE_SYSTEM) {
696 actuallyfree(current_attrnamespace);
697 current_attrnamespace = NULL;
704 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
705 jcr->last_fname, be.bstrerror());
706 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
707 jcr->last_fname, be.bstrerror());
710 } else if (xattr_list_len == 0) {
715 * Allocate room for the extented attribute list.
717 xattr_list = (char *)malloc(xattr_list_len + 1);
718 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
721 * Get the actual list of extended attributes names for a file.
723 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, xattr_list, xattr_list_len);
724 if (xattr_list_len < 0) {
727 retval = bxattr_exit_ok;
730 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
731 jcr->last_fname, be.bstrerror());
732 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
733 jcr->last_fname, be.bstrerror());
737 xattr_list[xattr_list_len] = '\0';
740 * Walk the list of extended attributes names and retrieve the data.
741 * We already count the bytes needed for serializing the stream later on.
743 for (index = 0; index < xattr_list_len; index += xattr_list[index] + 1) {
747 * Print the current name into the buffer as its not null terminated we need to
748 * use the length encoded in the string for copying only the needed bytes.
750 cnt = MIN((sizeof(current_attrname) - 1), xattr_list[index]);
751 strncpy(current_attrname, xattr_list + (index + 1), cnt);
752 current_attrname[cnt] = '\0';
755 * First make a xattr tuple of the current namespace and the name of the xattr.
756 * e.g. something like user.<attrname> or system.<attrname>
758 bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s", current_attrnamespace, current_attrname);
761 * On some OSes you also get the acls in the extented attribute list.
762 * So we check if we are already backing up acls and if we do we
763 * don't store the extended attribute with the same info.
765 if (ff_pkt->flags & FO_ACL) {
766 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
767 if (bstrcmp(current_attrtuple, xattr_acl_skiplist[cnt])) {
775 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
778 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
779 if (bstrcmp(current_attrtuple, xattr_skiplist[cnt])) {
791 * Each xattr valuepair starts with a magic so we can parse it easier.
793 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
794 current_xattr->magic = XATTR_MAGIC;
795 expected_serialize_len += sizeof(current_xattr->magic);
798 * Allocate space for storing the name.
800 current_xattr->name_length = strlen(current_attrtuple);
801 current_xattr->name = (char *)malloc(current_xattr->name_length);
802 memcpy((caddr_t)current_xattr->name, (caddr_t)current_attrtuple, current_xattr->name_length);
804 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
807 * First see how long the value is for the extended attribute.
809 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, NULL, 0);
810 if (xattr_value_len < 0) {
813 retval = bxattr_exit_ok;
814 free(current_xattr->name);
818 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
819 jcr->last_fname, be.bstrerror());
820 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
821 jcr->last_fname, be.bstrerror());
822 free(current_xattr->name);
829 * Allocate space for storing the value.
831 current_xattr->value = (char *)malloc(xattr_value_len);
832 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
834 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, current_xattr->value, xattr_value_len);
835 if (xattr_value_len < 0) {
838 retval = bxattr_exit_ok;
839 free(current_xattr->value);
840 free(current_xattr->name);
844 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
845 jcr->last_fname, be.bstrerror());
846 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
847 jcr->last_fname, be.bstrerror());
848 free(current_xattr->value);
849 free(current_xattr->name);
856 * Store the actual length of the value.
858 current_xattr->value_length = xattr_value_len;
859 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
862 * Protect ourself against things getting out of hand.
864 if (expected_serialize_len >= MAX_XATTR_STREAM) {
865 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
866 jcr->last_fname, MAX_XATTR_STREAM);
867 free(current_xattr->value);
868 free(current_xattr->name);
873 xattr_value_list->append(current_xattr);
879 * Drop the local copy of the current_attrnamespace.
881 actuallyfree(current_attrnamespace);
882 current_attrnamespace = NULL;
885 * We are done with this xattr list.
888 xattr_list = (char *)NULL;
892 * If we found any xattr send them to the SD.
894 if (xattr_count > 0) {
896 * Serialize the datastream.
898 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
899 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
901 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
906 xattr_drop_internal_table(xattr_value_list);
907 xattr_value_list = NULL;
910 * Send the datastream to the SD.
912 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
914 xattr_drop_internal_table(xattr_value_list);
915 xattr_value_list = NULL;
917 return bxattr_exit_ok;
921 if (current_attrnamespace != NULL) {
922 actuallyfree(current_attrnamespace);
923 current_attrnamespace = NULL;
925 if (xattr_list != NULL) {
928 if (xattr_value_list != NULL) {
929 xattr_drop_internal_table(xattr_value_list);
930 xattr_value_list = NULL;
935 static bxattr_exit_code bsd_parse_xattr_streams(JCR *jcr, int stream)
937 xattr_t *current_xattr;
938 alist *xattr_value_list;
939 int current_attrnamespace, cnt;
940 char *attrnamespace, *attrname;
943 xattr_value_list = New(alist(10, not_owned_by_alist));
945 if (unserialize_xattr_stream(jcr, xattr_value_list) != bxattr_exit_ok) {
946 xattr_drop_internal_table(xattr_value_list);
947 return bxattr_exit_error;
950 foreach_alist(current_xattr, xattr_value_list) {
952 * Try splitting the xattr_name into a namespace and name part.
953 * The splitting character is a .
955 attrnamespace = current_xattr->name;
956 if ((attrname = strchr(attrnamespace, '.')) == (char *)NULL) {
957 Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"),
958 current_xattr->name, jcr->last_fname);
959 Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n",
960 current_xattr->name, jcr->last_fname);
966 * Make sure the attrnamespace makes sense.
968 if (extattr_string_to_namespace(attrnamespace, ¤t_attrnamespace) != 0) {
969 Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"),
970 attrnamespace, jcr->last_fname);
971 Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n",
972 attrnamespace, jcr->last_fname);
977 * Try restoring the extended attribute.
979 cnt = extattr_set_link(jcr->last_fname, current_attrnamespace,
980 attrname, current_xattr->value, current_xattr->value_length);
981 if (cnt < 0 || cnt != current_xattr->value_length) {
987 Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"),
988 jcr->last_fname, be.bstrerror());
989 Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n",
990 jcr->last_fname, be.bstrerror());
997 xattr_drop_internal_table(xattr_value_list);
998 return bxattr_exit_ok;
1001 xattr_drop_internal_table(xattr_value_list);
1002 return bxattr_exit_error;
1006 * Function pointers to the build and parse function to use for these xattrs.
1008 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = bsd_build_xattr_streams;
1009 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = bsd_parse_xattr_streams;
1011 #elif defined(HAVE_SUN_OS)
1013 * Solaris extended attributes were introduced in Solaris 9
1016 * Solaris extensible attributes were introduced in OpenSolaris
1017 * by PSARC 2007/315 Solaris extensible attributes are also
1018 * sometimes called extended system attributes.
1020 * man fsattr(5) on Solaris gives a wealth of info. The most
1021 * important bits are:
1023 * Attributes are logically supported as files within the file
1024 * system. The file system is therefore augmented with an
1025 * orthogonal name space of file attributes. Any file (includ-
1026 * ing attribute files) can have an arbitrarily deep attribute
1027 * tree associated with it. Attribute values are accessed by
1028 * file descriptors obtained through a special attribute inter-
1029 * face. This logical view of "attributes as files" allows the
1030 * leveraging of existing file system interface functionality
1031 * to support the construction, deletion, and manipulation of
1034 * The special files "." and ".." retain their accustomed
1035 * semantics within the attribute hierarchy. The "." attribute
1036 * file refers to the current directory and the ".." attribute
1037 * file refers to the parent directory. The unnamed directory
1038 * at the head of each attribute tree is considered the "child"
1039 * of the file it is associated with and the ".." file refers
1040 * to the associated file. For any non-directory file with
1041 * attributes, the ".." entry in the unnamed directory refers
1042 * to a file that is not a directory.
1044 * Conceptually, the attribute model is fully general. Extended
1045 * attributes can be any type of file (doors, links, direc-
1046 * tories, and so forth) and can even have their own attributes
1047 * (fully recursive). As a result, the attributes associated
1048 * with a file could be an arbitrarily deep directory hierarchy
1049 * where each attribute could have an equally complex attribute
1050 * tree associated with it. Not all implementations are able
1051 * to, or want to, support the full model. Implementation are
1052 * therefore permitted to reject operations that are not sup-
1053 * ported. For example, the implementation for the UFS file
1054 * system allows only regular files as attributes (for example,
1055 * no sub-directories) and rejects attempts to place attributes
1058 * The following list details the operations that are rejected
1059 * in the current implementation:
1061 * link Any attempt to create links between
1062 * attribute and non-attribute space
1063 * is rejected to prevent security-
1064 * related or otherwise sensitive
1065 * attributes from being exposed, and
1066 * therefore manipulable, as regular
1069 * rename Any attempt to rename between
1070 * attribute and non-attribute space
1071 * is rejected to prevent an already
1072 * linked file from being renamed and
1073 * thereby circumventing the link res-
1076 * mkdir, symlink, mknod Any attempt to create a "non-
1077 * regular" file in attribute space is
1078 * rejected to reduce the functional-
1079 * ity, and therefore exposure and
1080 * risk, of the initial implementa-
1083 * The entire available name space has been allocated to "gen-
1084 * eral use" to bring the implementation in line with the NFSv4
1085 * draft standard [NFSv4]. That standard defines "named attri-
1086 * butes" (equivalent to Solaris Extended Attributes) with no
1087 * naming restrictions. All Sun applications making use of
1088 * opaque extended attributes will use the prefix "SUNW".
1091 #ifdef HAVE_SYS_ATTR_H
1092 #include <sys/attr.h>
1099 #ifdef HAVE_SYS_NVPAIR_H
1100 #include <sys/nvpair.h>
1103 #ifdef HAVE_SYS_ACL_H
1104 #include <sys/acl.h>
1107 #if !defined(HAVE_OPENAT) || \
1108 !defined(HAVE_UNLINKAT) || \
1109 !defined(HAVE_FCHOWNAT) || \
1110 !defined(HAVE_FUTIMESAT)
1111 #error "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function"
1115 * Define the supported XATTR streams for this OS
1117 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1118 static int os_default_xattr_streams[2] = { STREAM_XATTR_SOLARIS, STREAM_XATTR_SOLARIS_SYS};
1120 static int os_default_xattr_streams[1] = { STREAM_XATTR_SOLARIS };
1121 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
1124 * This code creates a temporary cache with entries for each xattr which has
1125 * a link count > 1 (which indicates it has one or more hard linked counterpart(s))
1127 static xattr_link_cache_entry_t *find_xattr_link_cache_entry(JCR *jcr, ino_t inum)
1129 xattr_link_cache_entry_t *ptr;
1131 foreach_alist(ptr, jcr->xattr_data->link_cache) {
1132 if (ptr && ptr->inum == inum) {
1139 static void add_xattr_link_cache_entry(JCR *jcr, ino_t inum, char *target)
1141 xattr_link_cache_entry_t *ptr;
1143 ptr = (xattr_link_cache_entry_t *)malloc(sizeof(xattr_link_cache_entry_t));
1144 memset((caddr_t)ptr, 0, sizeof(xattr_link_cache_entry_t));
1146 bstrncpy(ptr->target, target, sizeof(ptr->target));
1147 jcr->xattr_data->link_cache->append(ptr);
1150 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1152 * This function returns true if a non default extended system attribute
1153 * list is associated with fd and returns false when an error has occured
1154 * or when only extended system attributes other than archive,
1155 * av_modified or crtime are set.
1157 * The function returns true for the following cases:
1159 * - any extended system attribute other than the default attributes
1160 * ('archive', 'av_modified' and 'crtime') is set
1161 * - nvlist has NULL name string
1162 * - nvpair has data type of 'nvlist'
1163 * - default data type.
1165 static bool solaris_has_non_transient_extensible_attributes(int fd)
1173 bool retval = false;
1175 if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
1180 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
1181 name = nvpair_name(pair);
1184 fattr = name_to_attr(name);
1190 type = nvpair_type(pair);
1192 case DATA_TYPE_BOOLEAN_VALUE:
1193 if (nvpair_value_boolean_value(pair, &value) != 0) {
1196 if (value && fattr != F_ARCHIVE &&
1197 fattr != F_AV_MODIFIED) {
1202 case DATA_TYPE_UINT64_ARRAY:
1203 if (fattr != F_CRTIME) {
1208 case DATA_TYPE_NVLIST:
1216 if (response != NULL) {
1217 nvlist_free(response);
1221 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1223 #if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL)
1225 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1226 * There is no need to store those acls as we already store the stat bits too.
1228 static bool acl_is_trivial(int count, aclent_t *entries)
1233 for (n = 0; n < count; n++) {
1235 if (!(ace->a_type == USER_OBJ ||
1236 ace->a_type == GROUP_OBJ ||
1237 ace->a_type == OTHER_OBJ ||
1238 ace->a_type == CLASS_OBJ))
1243 #endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */
1245 static bxattr_exit_code solaris_save_xattr_acl(JCR *jcr, int fd, const char *attrname, char **acl_text)
1248 #ifdef HAVE_EXTENDED_ACL
1254 * See if this attribute has an ACL
1256 if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0) ||
1257 pathconf(attrname, _PC_ACL_ENABLED) > 0) {
1259 * See if there is a non trivial acl on the file.
1261 if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0) ||
1262 acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) {
1265 return bxattr_exit_ok;
1267 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1268 attrname, jcr->last_fname, be.bstrerror());
1269 Dmsg3(100, "facl_get/acl_get of xattr %s on \"%s\" failed: ERR=%s\n",
1270 attrname, jcr->last_fname, be.bstrerror());
1271 return bxattr_exit_error;
1276 #if defined(ACL_SID_FMT)
1278 * New format flag added in newer Solaris versions.
1280 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1282 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1283 #endif /* ACL_SID_FMT */
1285 *acl_text = acl_totext(aclp, flags);
1293 return bxattr_exit_ok;
1294 #else /* HAVE_EXTENDED_ACL */
1296 aclent_t *acls = NULL;
1300 * See if this attribute has an ACL
1303 n = facl(fd, GETACLCNT, 0, NULL);
1305 n = acl(attrname, GETACLCNT, 0, NULL);
1308 if (n >= MIN_ACL_ENTRIES) {
1309 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1310 if ((fd != -1 && facl(fd, GETACL, n, acls) != n) ||
1311 acl(attrname, GETACL, n, acls) != n) {
1315 return bxattr_exit_ok;
1317 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1318 attrname, jcr->last_fname, be.bstrerror());
1319 Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n",
1320 attrname, jcr->last_fname, be.bstrerror());
1322 return bxattr_exit_error;
1327 * See if there is a non trivial acl on the file.
1329 if (!acl_is_trivial(n, acls)) {
1330 if ((*acl_text = acltotext(acls, n)) == NULL) {
1331 Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"),
1332 attrname, jcr->last_fname, be.bstrerror());
1333 Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n",
1334 attrname, jcr->last_fname, be.bstrerror());
1336 return bxattr_exit_error;
1346 return bxattr_exit_ok;
1347 #endif /* HAVE_EXTENDED_ACL */
1349 #else /* HAVE_ACL */
1350 return bxattr_exit_ok;
1351 #endif /* HAVE_ACL */
1355 * Forward declaration for recursive function call.
1357 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent);
1360 * Save an extended or extensible attribute.
1361 * This is stored as an opaque stream of bytes with the following encoding:
1363 * <xattr_name>\0<stat_buffer>\0<acl_string>\0<actual_xattr_data>
1365 * or for a hardlinked or symlinked attribute
1367 * <xattr_name>\0<stat_buffer>\0<xattr_link_source>\0
1369 * xattr_name can be a subpath relative to the file the xattr is on.
1370 * stat_buffer is the string representation of the stat struct.
1371 * acl_string is an acl text when a non trivial acl is set on the xattr.
1372 * actual_xattr_data is the content of the xattr file.
1374 static bxattr_exit_code solaris_save_xattr(JCR *jcr, int fd, const char *xattr_namespace,
1375 const char *attrname, bool toplevel_hidden_dir, int stream)
1380 xattr_link_cache_entry_t *xlce;
1381 char target_attrname[PATH_MAX];
1382 char link_source[PATH_MAX];
1383 char *acl_text = NULL;
1384 char attribs[MAXSTRING];
1385 char buffer[BUFSIZ];
1386 bxattr_exit_code retval = bxattr_exit_error;
1389 bsnprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace, attrname);
1392 * Get the stats of the extended or extensible attribute.
1394 if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) {
1397 retval = bxattr_exit_ok;
1400 Mmsg3(jcr->errmsg, _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"),
1401 target_attrname, jcr->last_fname, be.bstrerror());
1402 Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n",
1403 target_attrname, jcr->last_fname, be.bstrerror());
1409 * Based on the filetype perform the correct action. We support most filetypes here, more
1410 * then the actual implementation on Solaris supports so some code may never get executed
1411 * due to limitations in the implementation.
1413 switch (st.st_mode & S_IFMT) {
1418 * Get any acl on the xattr.
1420 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1424 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1425 * Encode the stat struct into an ASCII representation.
1427 encode_stat(attribs, &st, 0, stream);
1428 cnt = bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c",
1429 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1433 * Get any acl on the xattr.
1435 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1439 * See if this is the toplevel_hidden_dir being saved.
1441 if (toplevel_hidden_dir) {
1443 * Save the data for later storage when we encounter a real xattr. We store the data
1444 * in the jcr->xattr_data->content buffer and flush that just before sending out the
1445 * first real xattr. Encode the stat struct into an ASCII representation and jump
1446 * out of the function.
1448 encode_stat(attribs, &st, 0, stream);
1449 cnt = bsnprintf(buffer, sizeof(buffer),
1451 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1452 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1453 jcr->xattr_data->content_length = cnt;
1457 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1458 * Encode the stat struct into an ASCII representation.
1460 encode_stat(attribs, &st, 0, stream);
1461 cnt = bsnprintf(buffer, sizeof(buffer),
1463 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1468 * If this is a hardlinked file check the inode cache for a hit.
1470 if (st.st_nlink > 1) {
1472 * See if the cache already knows this inode number.
1474 if ((xlce = find_xattr_link_cache_entry(jcr, st.st_ino)) != NULL) {
1476 * Generate a xattr encoding with the reference to the target in there.
1478 encode_stat(attribs, &st, st.st_ino, stream);
1479 cnt = bsnprintf(buffer, sizeof(buffer),
1481 target_attrname, 0, attribs, 0, xlce->target, 0);
1482 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1483 jcr->xattr_data->content_length = cnt;
1484 retval = send_xattr_stream(jcr, stream);
1487 * For a hard linked file we are ready now, no need to recursively save the attributes.
1493 * Store this hard linked file in the cache.
1494 * Store the name relative to the top level xattr space.
1496 add_xattr_link_cache_entry(jcr, st.st_ino, target_attrname + 1);
1500 * Get any acl on the xattr.
1502 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok) {
1507 * Encode the stat struct into an ASCII representation.
1509 encode_stat(attribs, &st, 0, stream);
1510 cnt = bsnprintf(buffer, sizeof(buffer),
1512 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1515 * Open the extended or extensible attribute file.
1517 if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) {
1520 retval = bxattr_exit_ok;
1523 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on \"%s\": ERR=%s\n"),
1524 target_attrname, jcr->last_fname, be.bstrerror());
1525 Dmsg3(100, "openat of xattr %s on \"%s\" failed: ERR=%s\n",
1526 target_attrname, jcr->last_fname, be.bstrerror());
1533 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1534 * Encode the stat struct into an ASCII representation.
1536 if (readlink(attrname, link_source, sizeof(link_source)) < 0) {
1539 retval = bxattr_exit_ok;
1542 Mmsg3(jcr->errmsg, _("Unable to read symlin %s on \"%s\": ERR=%s\n"),
1543 target_attrname, jcr->last_fname, be.bstrerror());
1544 Dmsg3(100, "readlink of xattr %s on \"%s\" failed: ERR=%s\n",
1545 target_attrname, jcr->last_fname, be.bstrerror());
1551 * Generate a xattr encoding with the reference to the target in there.
1553 encode_stat(attribs, &st, st.st_ino, stream);
1554 cnt = bsnprintf(buffer, sizeof(buffer),
1556 target_attrname, 0, attribs, 0, link_source, 0);
1557 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1558 jcr->xattr_data->content_length = cnt;
1559 retval = send_xattr_stream(jcr, stream);
1561 if (retval == bxattr_exit_ok) {
1562 jcr->xattr_data->nr_saved++;
1566 * For a soft linked file we are ready now, no need to recursively save the attributes.
1574 * See if this is the first real xattr being saved.
1575 * If it is save the toplevel_hidden_dir attributes first.
1576 * This is easy as its stored already in the jcr->xattr_data->content buffer.
1578 if (jcr->xattr_data->nr_saved == 0) {
1579 retval = send_xattr_stream(jcr, STREAM_XATTR_SOLARIS);
1580 if (retval != bxattr_exit_ok) {
1583 jcr->xattr_data->nr_saved++;
1586 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1587 jcr->xattr_data->content_length = cnt;
1590 * Only dump the content of regular files.
1592 switch (st.st_mode & S_IFMT) {
1594 if (st.st_size > 0) {
1596 * Protect ourself against things getting out of hand.
1598 if (st.st_size >= MAX_XATTR_STREAM) {
1599 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
1600 jcr->last_fname, MAX_XATTR_STREAM);
1604 while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) {
1605 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, jcr->xattr_data->content_length + cnt);
1606 memcpy(jcr->xattr_data->content + jcr->xattr_data->content_length, buffer, cnt);
1607 jcr->xattr_data->content_length += cnt;
1611 Mmsg2(jcr->errmsg, _("Unable to read content of xattr %s on file \"%s\"\n"),
1612 target_attrname, jcr->last_fname);
1613 Dmsg2(100, "read of data from xattr %s on \"%s\" failed\n",
1614 target_attrname, jcr->last_fname);
1625 retval = send_xattr_stream(jcr, stream);
1626 if (retval == bxattr_exit_ok) {
1627 jcr->xattr_data->nr_saved++;
1632 * Recursivly call solaris_save_extended_attributes for archiving the attributes
1633 * available on this extended attribute.
1636 retval = solaris_save_xattrs(jcr, xattr_namespace, attrname);
1639 * The recursive call could change our working dir so change back to the wanted workdir.
1641 if (fchdir(fd) < 0) {
1644 retval = bxattr_exit_ok;
1647 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"),
1648 jcr->last_fname, be.bstrerror());
1649 Dmsg3(100, "Unable to fchdir to xattr space of file \"%s\" using fd %d: ERR=%s\n",
1650 jcr->last_fname, fd, be.bstrerror());
1657 if (acl_text != NULL) {
1666 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent)
1669 int fd, filefd = -1, attrdirfd = -1;
1672 char current_xattr_namespace[PATH_MAX];
1673 bxattr_exit_code retval = bxattr_exit_error;
1677 * Determine what argument to use. Use attr_parent when set
1678 * (recursive call) or jcr->last_fname for first call. Also save
1679 * the current depth of the xattr_space we are in.
1683 if (xattr_namespace) {
1684 bsnprintf(current_xattr_namespace, sizeof(current_xattr_namespace), "%s%s/",
1685 xattr_namespace, attr_parent);
1687 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1690 name = jcr->last_fname;
1691 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1695 * Open the file on which to save the xattrs read-only.
1697 if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
1700 retval = bxattr_exit_ok;
1703 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1704 jcr->last_fname, be.bstrerror());
1705 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1706 jcr->last_fname, be.bstrerror());
1712 * Open the xattr naming space.
1714 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1718 * Gentile way of the system saying this type of xattr layering is not supported.
1719 * Which is not problem we just forget about this this xattr.
1720 * But as this is not an error we return a positive return value.
1722 retval = bxattr_exit_ok;
1725 retval = bxattr_exit_ok;
1728 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1729 name, jcr->last_fname, be.bstrerror());
1730 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1731 name, jcr->last_fname, be.bstrerror());
1737 * We need to change into the attribute directory to determine if each of the
1738 * attributes should be saved.
1740 if (fchdir(attrdirfd) < 0) {
1741 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1742 jcr->last_fname, be.bstrerror());
1743 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1744 jcr->last_fname, attrdirfd, be.bstrerror());
1749 * Save the data of the toplevel xattr hidden_dir. We save this one before anything
1750 * else because the readdir returns "." entry after the extensible attr entry.
1751 * And as we want this entry before anything else we better just save its data.
1754 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, ".",
1755 true, STREAM_XATTR_SOLARIS);
1757 if ((fd = dup(attrdirfd)) == -1 ||
1758 (dirp = fdopendir(fd)) == (DIR *)NULL) {
1759 Mmsg2(jcr->errmsg, _("Unable to list the xattr space on file \"%s\": ERR=%s\n"),
1760 jcr->last_fname, be.bstrerror());
1761 Dmsg3(100, "Unable to fdopendir xattr space on file \"%s\" using fd %d: ERR=%s\n",
1762 jcr->last_fname, fd, be.bstrerror());
1768 * Walk the namespace.
1770 while (dp = readdir(dirp)) {
1772 * Skip only the toplevel . dir.
1774 if (!attr_parent && bstrcmp(dp->d_name, "."))
1778 * Skip all .. directories
1780 if (bstrcmp(dp->d_name, ".."))
1783 Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n",
1784 current_xattr_namespace, dp->d_name, jcr->last_fname);
1786 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1788 * We are not interested in read-only extensible attributes.
1790 if (bstrcmp(dp->d_name, VIEW_READONLY)) {
1791 Dmsg3(400, "Skipping readonly extensible attributes %s%s on file \"%s\"\n",
1792 current_xattr_namespace, dp->d_name, jcr->last_fname);
1798 * We are only interested in read-write extensible attributes
1799 * when they contain non-transient values.
1801 if (bstrcmp(dp->d_name, VIEW_READWRITE)) {
1803 * Determine if there are non-transient system attributes at the toplevel.
1804 * We need to provide a fd to the open file.
1806 if (!solaris_has_non_transient_extensible_attributes(filefd)) {
1807 Dmsg3(400, "Skipping transient extensible attributes %s%s on file \"%s\"\n",
1808 current_xattr_namespace, dp->d_name, jcr->last_fname);
1815 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1816 false, STREAM_XATTR_SOLARIS_SYS);
1819 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1824 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1825 false, STREAM_XATTR_SOLARIS);
1829 retval = bxattr_exit_ok;
1832 if (attrdirfd != -1)
1840 static bxattr_exit_code solaris_restore_xattr_acl(JCR *jcr, int fd, const char *attrname, char *acl_text)
1842 #ifdef HAVE_EXTENDED_ACL
1847 if ((error = acl_fromtext(acl_text, &aclp)) != 0) {
1848 Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"),
1850 return bxattr_exit_error;
1853 if ((fd != -1 && facl_set(fd, aclp) != 0) ||
1854 acl_set(attrname, aclp) != 0) {
1855 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1856 attrname, jcr->last_fname, be.bstrerror());
1857 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1858 attrname, jcr->last_fname, be.bstrerror());
1859 return bxattr_exit_error;
1865 return bxattr_exit_ok;
1867 #else /* HAVE_EXTENDED_ACL */
1869 aclent_t *acls = NULL;
1872 acls = aclfromtext(acl_text, &n);
1874 if ((fd != -1 && facl(fd, SETACL, n, acls) != 0) ||
1875 acl(attrname, SETACL, n, acls) != 0) {
1876 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1877 attrname, jcr->last_fname, be.bstrerror());
1878 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1879 attrname, jcr->last_fname, be.bstrerror());
1880 return bxattr_exit_error;
1887 return bxattr_exit_ok;
1889 #endif /* HAVE_EXTENDED_ACL */
1892 #endif /* HAVE_ACL */
1894 static bxattr_exit_code solaris_restore_xattrs(JCR *jcr, bool is_extensible)
1896 int fd, filefd = -1, attrdirfd = -1, attrfd = -1;
1897 int used_bytes, total_bytes, cnt;
1898 char *bp, *target_attrname, *attribs;
1899 char *linked_target = NULL;
1900 char *acl_text = NULL;
1904 struct timeval times[2];
1905 bxattr_exit_code retval = bxattr_exit_error;
1909 * Parse the xattr stream. First the part that is the same for all xattrs.
1912 total_bytes = jcr->xattr_data->content_length;
1915 * The name of the target xattr has a leading / we are not interested
1916 * in that so skip it when decoding the string. We always start a the /
1917 * of the xattr space anyway.
1919 target_attrname = jcr->xattr_data->content + 1;
1920 if ((bp = strchr(target_attrname, '\0')) == (char *)NULL ||
1921 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1927 * Open the file on which to restore the xattrs read-only.
1929 if ((filefd = open(jcr->last_fname, O_RDONLY | O_NONBLOCK)) < 0) {
1930 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1931 jcr->last_fname, be.bstrerror());
1932 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1933 jcr->last_fname, be.bstrerror());
1938 * Open the xattr naming space and make it the current working dir.
1940 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1941 Mmsg2(jcr->errmsg, _("Unable to open xattr space on file \"%s\": ERR=%s\n"),
1942 jcr->last_fname, be.bstrerror());
1943 Dmsg2(100, "Unable to open xattr space on file \"%s\": ERR=%s\n",
1944 jcr->last_fname, be.bstrerror());
1948 if (fchdir(attrdirfd) < 0) {
1949 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1950 jcr->last_fname, be.bstrerror());
1951 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1952 jcr->last_fname, attrdirfd, be.bstrerror());
1957 * Try to open the correct xattr subdir based on the target_attrname given.
1958 * e.g. check if its a subdir attrname. Each / in the string makes us go
1961 while ((bp = strchr(target_attrname, '/')) != (char *)NULL) {
1964 if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) {
1965 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
1966 target_attrname, jcr->last_fname, be.bstrerror());
1967 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
1968 target_attrname, jcr->last_fname, be.bstrerror());
1976 * Open the xattr naming space.
1978 if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1979 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1980 target_attrname, jcr->last_fname, be.bstrerror());
1981 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1982 target_attrname, jcr->last_fname, be.bstrerror());
1990 * Make the xattr space our current workingdir.
1992 if (fchdir(attrdirfd) < 0) {
1993 Mmsg3(jcr->errmsg, _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"),
1994 target_attrname, jcr->last_fname, be.bstrerror());
1995 Dmsg4(100, "Unable to fchdir to xattr space %s on file \"%s\" using fd %d: ERR=%s\n",
1996 target_attrname, jcr->last_fname, attrdirfd, be.bstrerror());
2000 target_attrname = ++bp;
2004 * Decode the attributes from the stream.
2006 decode_stat(attribs, &st, &inum);
2009 * Decode the next field (acl_text).
2011 if ((bp = strchr(attribs, '\0')) == (char *)NULL ||
2012 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
2018 * Based on the filetype perform the correct action. We support most filetypes here, more
2019 * then the actual implementation on Solaris supports so some code may never get executed
2020 * due to limitations in the implementation.
2022 switch (st.st_mode & S_IFMT) {
2025 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2027 unlinkat(attrdirfd, target_attrname, 0);
2028 if (mkfifo(target_attrname, st.st_mode) < 0) {
2029 Mmsg3(jcr->errmsg, _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"),
2030 target_attrname, jcr->last_fname, be.bstrerror());
2031 Dmsg3(100, "Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n",
2032 target_attrname, jcr->last_fname, be.bstrerror());
2039 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2041 unlinkat(attrdirfd, target_attrname, 0);
2042 if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) {
2043 Mmsg3(jcr->errmsg, _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"),
2044 target_attrname, jcr->last_fname, be.bstrerror());
2045 Dmsg3(100, "Unable to mknod xattr %s on file \"%s\": ERR=%s\n",
2046 target_attrname, jcr->last_fname, be.bstrerror());
2052 * If its not the hidden_dir create the entry.
2053 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2055 if (!bstrcmp(target_attrname, ".")) {
2056 unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR);
2057 if (mkdir(target_attrname, st.st_mode) < 0) {
2058 Jmsg3(jcr, M_WARNING, 0, _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"),
2059 target_attrname, jcr->last_fname, be.bstrerror());
2060 Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n",
2061 target_attrname, jcr->last_fname, be.bstrerror());
2068 * See if this is a hard linked file. e.g. inum != 0
2073 unlinkat(attrdirfd, target_attrname, 0);
2074 if (link(linked_target, target_attrname) < 0) {
2075 Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"),
2076 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2077 Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n",
2078 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2083 * Successfully restored xattr.
2085 retval = bxattr_exit_ok;
2088 if ((bp = strchr(acl_text, '\0')) == (char *)NULL ||
2089 (used_bytes = (bp - jcr->xattr_data->content)) >= total_bytes) {
2093 if (used_bytes < (total_bytes - 1))
2097 * Restore the actual xattr.
2099 if (!is_extensible) {
2100 unlinkat(attrdirfd, target_attrname, 0);
2103 if ((attrfd = openat(attrdirfd, target_attrname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) {
2104 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
2105 target_attrname, jcr->last_fname, be.bstrerror());
2106 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
2107 target_attrname, jcr->last_fname, be.bstrerror());
2113 * Restore the actual data.
2115 if (st.st_size > 0) {
2116 used_bytes = (data - jcr->xattr_data->content);
2117 cnt = total_bytes - used_bytes;
2120 * Do a sanity check, the st.st_size should be the same as the number of bytes
2121 * we have available as data of the stream.
2123 if (cnt != st.st_size) {
2124 Mmsg2(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n"),
2125 target_attrname, jcr->last_fname);
2126 Dmsg2(100, "Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n",
2127 target_attrname, jcr->last_fname);
2132 cnt = write(attrfd, data, cnt);
2134 Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"),
2135 target_attrname, jcr->last_fname, be.bstrerror());
2136 Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n",
2137 target_attrname, jcr->last_fname, be.bstrerror());
2143 cnt = total_bytes - used_bytes;
2149 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2153 if (symlink(linked_target, target_attrname) < 0) {
2154 Mmsg4(jcr->errmsg, _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"),
2155 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2156 Dmsg4(100, "Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n",
2157 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2162 * Successfully restored xattr.
2164 retval = bxattr_exit_ok;
2171 * Restore owner and acl for non extensible attributes.
2173 if (!is_extensible) {
2174 if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0) {
2178 * Gentile way of the system saying this type of xattr layering is not supported.
2179 * But as this is not an error we return a positive return value.
2181 retval = bxattr_exit_ok;
2184 retval = bxattr_exit_ok;
2187 Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"),
2188 target_attrname, jcr->last_fname, be.bstrerror());
2189 Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n",
2190 target_attrname, jcr->last_fname, be.bstrerror());
2197 if (acl_text && *acl_text)
2198 if (solaris_restore_xattr_acl(jcr, attrfd, target_attrname, acl_text) != bxattr_exit_ok)
2200 #endif /* HAVE_ACL */
2203 * For a non extensible attribute restore access and modification time on the xattr.
2205 if (!is_extensible) {
2206 times[0].tv_sec = st.st_atime;
2207 times[0].tv_usec = 0;
2208 times[1].tv_sec = st.st_mtime;
2209 times[1].tv_usec = 0;
2211 if (futimesat(attrdirfd, target_attrname, times) < 0) {
2212 Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"),
2213 target_attrname, jcr->last_fname, be.bstrerror());
2214 Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n",
2215 target_attrname, jcr->last_fname, be.bstrerror());
2221 * Successfully restored xattr.
2223 retval = bxattr_exit_ok;
2227 Mmsg1(jcr->errmsg, _("Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n"),
2229 Dmsg1(100, "Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n",
2236 if (attrdirfd != -1) {
2245 static bxattr_exit_code solaris_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2248 bxattr_exit_code retval = bxattr_exit_ok;
2251 * First see if extended attributes or extensible attributes are present.
2252 * If not just pretend things went ok.
2254 if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0) {
2255 jcr->xattr_data->nr_saved = 0;
2256 jcr->xattr_data->link_cache = New(alist(10, not_owned_by_alist));
2259 * As we change the cwd in the save function save the current cwd
2260 * for restore after return from the solaris_save_xattrs function.
2262 getcwd(cwd, sizeof(cwd));
2263 retval = solaris_save_xattrs(jcr, NULL, NULL);
2265 delete jcr->xattr_data->link_cache;
2266 jcr->xattr_data->link_cache = NULL;
2271 static bxattr_exit_code solaris_parse_xattr_streams(JCR *jcr, int stream)
2274 bool is_extensible = false;
2275 bxattr_exit_code retval;
2278 * First make sure we can restore xattr on the filesystem.
2281 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
2282 case STREAM_XATTR_SOLARIS_SYS:
2283 if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0) {
2284 Mmsg1(jcr->errmsg, _("Failed to restore extensible attributes on file \"%s\"\n"), jcr->last_fname);
2285 Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n",
2287 return bxattr_exit_error;
2290 is_extensible = true;
2293 case STREAM_XATTR_SOLARIS:
2294 if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0) {
2295 Mmsg1(jcr->errmsg, _("Failed to restore extended attributes on file \"%s\"\n"), jcr->last_fname);
2296 Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n",
2298 return bxattr_exit_error;
2302 return bxattr_exit_error;
2306 * As we change the cwd in the restore function save the current cwd
2307 * for restore after return from the solaris_restore_xattrs function.
2309 getcwd(cwd, sizeof(cwd));
2310 retval = solaris_restore_xattrs(jcr, is_extensible);
2317 * Function pointers to the build and parse function to use for these xattrs.
2319 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_xattr_streams;
2320 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = solaris_parse_xattr_streams;
2322 #endif /* defined(HAVE_SUN_OS) */
2325 * Entry points when compiled with support for XATTRs on a supported platform.
2327 bxattr_exit_code build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2329 if (os_build_xattr_streams) {
2330 return (*os_build_xattr_streams)(jcr, ff_pkt);
2332 return bxattr_exit_error;
2335 bxattr_exit_code parse_xattr_streams(JCR *jcr, int stream)
2339 if (os_parse_xattr_streams) {
2341 * See if we can parse this stream, and ifso give it a try.
2343 for (cnt = 0; cnt < sizeof(os_default_xattr_streams) / sizeof(int); cnt++) {
2344 if (os_default_xattr_streams[cnt] == stream) {
2345 return (*os_parse_xattr_streams)(jcr, stream);
2350 * Issue a warning and discard the message. But pretend the restore was ok.
2352 Jmsg2(jcr, M_WARNING, 0,
2353 _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"),
2354 jcr->last_fname, stream);
2355 return bxattr_exit_error;