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 * - Solaris (Extended Attributes and Extensible Attributes)
42 * Written by Marco van Wieringen, November MMVIII
49 #if !defined(HAVE_XATTR)
51 * Entry points when compiled without support for XATTRs or on an unsupported platform.
53 bxattr_exit_code build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
55 return bxattr_exit_fatal;
58 bxattr_exit_code parse_xattr_streams(JCR *jcr, int stream)
60 return bxattr_exit_fatal;
64 * Send a XATTR stream to the SD.
66 static bxattr_exit_code send_xattr_stream(JCR *jcr, int stream)
68 BSOCK *sd = jcr->store_bsock;
70 #ifdef FD_NO_SEND_TEST
71 return bxattr_exit_ok;
77 if (jcr->xattr_data->content_length <= 0) {
78 return bxattr_exit_ok;
84 if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
85 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
87 return bxattr_exit_fatal;
91 * Send the buffer to the storage deamon
93 Dmsg1(400, "Backing up XATTR <%s>\n", jcr->xattr_data->content);
95 sd->msg = jcr->xattr_data->content;
96 sd->msglen = jcr->xattr_data->content_length;
100 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
102 return bxattr_exit_fatal;
105 jcr->JobBytes += sd->msglen;
107 if (!sd->signal(BNET_EOD)) {
108 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
110 return bxattr_exit_fatal;
112 Dmsg1(200, "XATTR of file: %s successfully backed up!\n", jcr->last_fname);
113 return bxattr_exit_ok;
117 * First some generic functions for OSes that use the same xattr encoding scheme.
119 #if defined(HAVE_DARWIN_OS) || \
120 defined(HAVE_LINUX_OS) || \
121 defined(HAVE_NETBSD_OS) || \
122 defined(HAVE_FREEBSD_OS) || \
123 defined(HAVE_OPENBSD_OS)
125 static void xattr_drop_internal_table(alist *xattr_value_list)
127 xattr_t *current_xattr;
130 * Walk the list of xattrs and free allocated memory on traversing.
132 foreach_alist(current_xattr, xattr_value_list) {
134 * See if we can shortcut.
136 if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC)
139 free(current_xattr->name);
141 if (current_xattr->value_length > 0)
142 free(current_xattr->value);
147 * The xattr stream for OSX, FreeBSD, Linux and NetBSD is a serialized stream of bytes
148 * which encodes one or more xattr_t structures.
150 * The Serialized stream consists of the following elements:
151 * magic - A magic string which makes it easy to detect any binary incompatabilites
152 * name_length - The length of the following xattr name
153 * name - The name of the extended attribute
154 * value_length - The length of the following xattr data
155 * value - The actual content of the extended attribute
157 * This is repeated 1 or more times.
160 static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len, alist *xattr_value_list)
162 xattr_t *current_xattr;
166 * Make sure the serialized stream fits in the poolmem buffer.
167 * We allocate some more to be sure the stream is gonna fit.
169 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, expected_serialize_len + 10);
170 ser_begin(jcr->xattr_data->content, expected_serialize_len + 10);
173 * Walk the list of xattrs and serialize the data.
175 foreach_alist(current_xattr, xattr_value_list) {
177 * See if we can shortcut.
179 if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC)
182 ser_uint32(current_xattr->magic);
183 ser_uint32(current_xattr->name_length);
184 ser_bytes(current_xattr->name, current_xattr->name_length);
186 ser_uint32(current_xattr->value_length);
187 ser_bytes(current_xattr->value, current_xattr->value_length);
190 ser_end(jcr->xattr_data->content, expected_serialize_len + 10);
191 jcr->xattr_data->content_length = ser_length(jcr->xattr_data->content);
193 return jcr->xattr_data->content_length;
197 * Forward declaration for restore function.
199 static bxattr_exit_code restore_xattr_on_file(JCR *jcr, xattr_t *xattr);
201 static bxattr_exit_code unserialize_xattr_stream(JCR *jcr)
204 xattr_t current_xattr;
205 bxattr_exit_code retval = bxattr_exit_ok;
208 * Parse the stream and call restore_xattr_on_file for each extended attribute.
210 * Start unserializing the data. We keep on looping while we have not
211 * unserialized all bytes in the stream.
213 unser_begin(jcr->xattr_data->content, jcr->xattr_data->content_length);
214 while (unser_length(jcr->xattr_data->content) < jcr->xattr_data->content_length) {
216 * First make sure the magic is present. This way we can easily catch corruption.
217 * Any missing MAGIC is fatal we do NOT try to continue.
219 unser_uint32(current_xattr.magic);
220 if (current_xattr.magic != XATTR_MAGIC) {
221 Mmsg1(jcr->errmsg, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"),
223 Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n",
225 return bxattr_exit_error;
229 * Decode the valuepair. First decode the length of the name.
231 unser_uint32(current_xattr.name_length);
234 * Allocate room for the name and decode its content.
236 current_xattr.name = (char *)malloc(current_xattr.name_length + 1);
237 unser_bytes(current_xattr.name, current_xattr.name_length);
240 * The xattr_name needs to be null terminated for lsetxattr.
242 current_xattr.name[current_xattr.name_length] = '\0';
245 * Decode the value length.
247 unser_uint32(current_xattr.value_length);
250 * Allocate room for the value and decode its content.
252 current_xattr.value = (char *)malloc(current_xattr.value_length);
253 unser_bytes(current_xattr.value, current_xattr.value_length);
256 * Try to set the extended attribute on the file.
257 * If we fail to set this attribute we flag the error but its not fatal,
258 * we try to restore the other extended attributes too.
260 if (restore_xattr_on_file(jcr, ¤t_xattr) != bxattr_exit_ok) {
261 retval = bxattr_exit_error;
265 * Free the temporary buffers.
267 free(current_xattr.name);
268 free(current_xattr.value);
271 unser_end(jcr->xattr_data->content, jcr->xattr_data->content_length);
277 * This is a supported OS, See what kind of interface we should use.
278 * Start with the generic interface used by most OS-es.
280 #if defined(HAVE_DARWIN_OS) || \
281 defined(HAVE_LINUX_OS)
283 #if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)) || \
284 (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) || \
285 (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR))
286 #error "Missing either full support for the LXATTR or XATTR functions."
289 #ifdef HAVE_SYS_XATTR_H
290 #include <sys/xattr.h>
292 #error "Missing sys/xattr.h header file"
296 * Define the supported XATTR streams for this OS
298 #if defined(HAVE_DARWIN_OS)
299 static int os_default_xattr_streams[1] = { STREAM_XATTR_DARWIN };
300 static const char *xattr_acl_skiplist[2] = { "com.apple.system.Security", NULL };
301 static const char *xattr_skiplist[3] = { "com.apple.system.extendedsecurity", "com.apple.ResourceFork", NULL };
302 #elif defined(HAVE_LINUX_OS)
303 static int os_default_xattr_streams[1] = { STREAM_XATTR_LINUX };
304 static const char *xattr_acl_skiplist[2] = { "system.posix_acl_access", NULL };
305 static const char *xattr_skiplist[1] = { NULL };
309 * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
310 * listxattr, getxattr and setxattr with an extra options argument
311 * which mimics the l variants of the functions when we specify
312 * XATTR_NOFOLLOW as the options value.
314 #if defined(HAVE_DARWIN_OS)
315 #define llistxattr(path, list, size) listxattr((path), (list), (size), XATTR_NOFOLLOW)
316 #define lgetxattr(path, name, value, size) getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
317 #define lsetxattr(path, name, value, size, flags) setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
320 * Fallback to the non l-functions when those are not available.
322 #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
323 #define lgetxattr getxattr
325 #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
326 #define lsetxattr setxattr
328 #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
329 #define llistxattr listxattr
333 static bxattr_exit_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt)
336 char *xattr_list, *bp;
337 int cnt, xattr_count = 0;
338 int32_t xattr_list_len,
340 uint32_t expected_serialize_len = 0;
341 xattr_t *current_xattr;
342 alist *xattr_value_list = NULL;
343 bxattr_exit_code retval = bxattr_exit_error;
347 * First get the length of the available list with extended attributes.
349 xattr_list_len = llistxattr(jcr->last_fname, NULL, 0);
350 if (xattr_list_len < 0) {
353 return bxattr_exit_ok;
355 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
356 jcr->last_fname, be.bstrerror());
357 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
358 jcr->last_fname, be.bstrerror());
359 return bxattr_exit_error;
361 } else if (xattr_list_len == 0) {
362 return bxattr_exit_ok;
366 * Allocate room for the extented attribute list.
368 xattr_list = (char *)malloc(xattr_list_len + 1);
369 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
372 * Get the actual list of extended attributes names for a file.
374 xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len);
375 if (xattr_list_len < 0) {
378 retval = bxattr_exit_ok;
381 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
382 jcr->last_fname, be.bstrerror());
383 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
384 jcr->last_fname, be.bstrerror());
388 xattr_list[xattr_list_len] = '\0';
390 xattr_value_list = New(alist(10, not_owned_by_alist));
393 * Walk the list of extended attributes names and retrieve the data.
394 * We already count the bytes needed for serializing the stream later on.
397 while ((bp - xattr_list) + 1 < xattr_list_len) {
401 * On some OSes you also get the acls in the extented attribute list.
402 * So we check if we are already backing up acls and if we do we
403 * don't store the extended attribute with the same info.
405 if (ff_pkt->flags & FO_ACL) {
406 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
407 if (!strcmp(bp, xattr_acl_skiplist[cnt])) {
415 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
418 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
419 if (!strcmp(bp, xattr_skiplist[cnt])) {
427 bp = strchr(bp, '\0') + 1;
432 * Each xattr valuepair starts with a magic so we can parse it easier.
434 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
435 current_xattr->magic = XATTR_MAGIC;
436 expected_serialize_len += sizeof(current_xattr->magic);
439 * Allocate space for storing the name.
441 current_xattr->name_length = strlen(bp);
442 current_xattr->name = (char *)malloc(current_xattr->name_length);
443 memcpy((caddr_t)current_xattr->name, (caddr_t)bp, current_xattr->name_length);
445 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
448 * First see how long the value is for the extended attribute.
450 xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0);
451 if (xattr_value_len < 0) {
454 retval = bxattr_exit_ok;
455 free(current_xattr->name);
459 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
460 jcr->last_fname, be.bstrerror());
461 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
462 jcr->last_fname, be.bstrerror());
463 free(current_xattr->name);
470 * Allocate space for storing the value.
472 current_xattr->value = (char *)malloc(xattr_value_len);
473 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
475 xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
476 if (xattr_value_len < 0) {
479 retval = bxattr_exit_ok;
480 free(current_xattr->value);
481 free(current_xattr->name);
485 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
486 jcr->last_fname, be.bstrerror());
487 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
488 jcr->last_fname, be.bstrerror());
489 free(current_xattr->value);
490 free(current_xattr->name);
497 * Store the actual length of the value.
499 current_xattr->value_length = xattr_value_len;
500 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
503 * Protect ourself against things getting out of hand.
505 if (expected_serialize_len >= MAX_XATTR_STREAM) {
506 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
507 jcr->last_fname, MAX_XATTR_STREAM);
508 free(current_xattr->value);
509 free(current_xattr->name);
514 xattr_value_list->append(current_xattr);
516 bp = strchr(bp, '\0') + 1;
520 xattr_list = (char *)NULL;
523 * If we found any xattr send them to the SD.
525 if (xattr_count > 0) {
527 * Serialize the datastream.
529 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
530 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
532 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
537 xattr_drop_internal_table(xattr_value_list);
538 delete xattr_value_list;
539 xattr_value_list = NULL;
542 * Send the datastream to the SD.
544 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
546 xattr_drop_internal_table(xattr_value_list);
547 delete xattr_value_list;
548 xattr_value_list = NULL;
550 return bxattr_exit_ok;
557 if (xattr_value_list) {
558 xattr_drop_internal_table(xattr_value_list);
559 delete xattr_value_list;
560 xattr_value_list = NULL;
566 * This function gets called by the unserialize_xattr_stream function for the OS specific
567 * code to restore an extended attribute on a file.
569 static bxattr_exit_code restore_xattr_on_file(JCR *jcr, xattr_t *xattr)
573 if (lsetxattr(jcr->last_fname, xattr->name, xattr->value, xattr->value_length, 0) != 0) {
578 Mmsg2(jcr->errmsg, _("lsetxattr error on file \"%s\": ERR=%s\n"),
579 jcr->last_fname, be.bstrerror());
580 Dmsg2(100, "lsetxattr error file=%s ERR=%s\n",
581 jcr->last_fname, be.bstrerror());
582 return bxattr_exit_error;
587 return bxattr_exit_ok;
590 static bxattr_exit_code generic_xattr_parse_streams(JCR *jcr, int stream)
592 return unserialize_xattr_stream(jcr);
596 * For all these os-es setup the build and parse function pointer to the generic functions.
598 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_xattr_build_streams;
599 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = generic_xattr_parse_streams;
601 #elif defined(HAVE_FREEBSD_OS) || \
602 defined(HAVE_NETBSD_OS) || \
603 defined(HAVE_OPENBSD_OS)
605 #if !defined(HAVE_EXTATTR_GET_LINK) || \
606 !defined(HAVE_EXTATTR_SET_LINK) || \
607 !defined(HAVE_EXTATTR_LIST_LINK) || \
608 !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \
609 !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE)
610 #error "Missing full support for the extattr functions."
613 #ifdef HAVE_SYS_EXTATTR_H
614 #include <sys/extattr.h>
616 #error "Missing sys/extattr.h header file"
619 #ifdef HAVE_LIBUTIL_H
623 #if defined(HAVE_FREEBSD_OS)
624 static int os_default_xattr_streams[1] = { STREAM_XATTR_FREEBSD };
625 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
626 static const char *xattr_acl_skiplist[1] = { NULL };
627 static const char *xattr_skiplist[1] = { NULL };
628 #elif defined(HAVE_NETBSD_OS)
629 static int os_default_xattr_streams[1] = { STREAM_XATTR_NETBSD };
630 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
631 static const char *xattr_acl_skiplist[1] = { NULL };
632 static const char *xattr_skiplist[1] = { NULL };
633 #elif defined(HAVE_OPENBSD_OS)
634 static int os_default_xattr_streams[1] = { STREAM_XATTR_OPENBSD };
635 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
636 static const char *xattr_acl_skiplist[1] = { NULL };
637 static const char *xattr_skiplist[1] = { NULL };
640 static bxattr_exit_code bsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
644 int cnt, index, xattr_count = 0;
645 int32_t xattr_list_len,
647 uint32_t expected_serialize_len = 0;
648 unsigned int namespace_index;
650 char *current_attrnamespace, current_attrname[BUFSIZ], current_attrtuple[BUFSIZ];
651 xattr_t *current_xattr;
652 alist *xattr_value_list = NULL;
653 bxattr_exit_code retval = bxattr_exit_error;
656 xattr_value_list = New(alist(10, not_owned_by_alist));
659 * Loop over all available xattr namespaces.
661 for (namespace_index = 0; namespace_index < sizeof(os_default_xattr_namespaces) / sizeof(int); namespace_index++) {
662 attrnamespace = os_default_xattr_namespaces[namespace_index];
665 * First get the length of the available list with extended attributes.
667 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, NULL, 0);
668 if (xattr_list_len < 0) {
671 retval = bxattr_exit_ok;
674 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
675 jcr->last_fname, be.bstrerror());
676 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
677 jcr->last_fname, be.bstrerror());
680 } else if (xattr_list_len == 0) {
685 * Allocate room for the extented attribute list.
687 xattr_list = (char *)malloc(xattr_list_len + 1);
688 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
691 * Get the actual list of extended attributes names for a file.
693 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, xattr_list, xattr_list_len);
694 if (xattr_list_len < 0) {
697 retval = bxattr_exit_ok;
700 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
701 jcr->last_fname, be.bstrerror());
702 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
703 jcr->last_fname, be.bstrerror());
707 xattr_list[xattr_list_len] = '\0';
710 * Walk the list of extended attributes names and retrieve the data.
711 * We already count the bytes needed for serializing the stream later on.
713 for (index = 0; index < xattr_list_len; index += xattr_list[index] + 1) {
715 bsnprintf(current_attrname, sizeof(current_attrname), "%*.*s",
716 xattr_list[index], xattr_list[index], xattr_list + (index + 1));
719 * First make a xattr tuple of the current namespace and the name of the xattr.
720 * e.g. something like user.<attrname> or system.<attrname>
722 if (extattr_namespace_to_string(attrnamespace, ¤t_attrnamespace) != 0) {
723 Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"),
724 attrnamespace, jcr->last_fname);
725 Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n",
726 attrnamespace, jcr->last_fname);
731 * print the current name into the buffer as its not null terminated we need to
732 * use the length encoded in the string for copying only the needed bytes.
734 bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s", current_attrnamespace, current_attrname);
737 * On some OSes you also get the acls in the extented attribute list.
738 * So we check if we are already backing up acls and if we do we
739 * don't store the extended attribute with the same info.
741 if (ff_pkt->flags & FO_ACL) {
742 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
743 if (!strcmp(current_attrtuple, xattr_acl_skiplist[cnt])) {
751 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
754 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
755 if (!strcmp(current_attrtuple, xattr_skiplist[cnt])) {
767 * Each xattr valuepair starts with a magic so we can parse it easier.
769 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
770 current_xattr->magic = XATTR_MAGIC;
771 expected_serialize_len += sizeof(current_xattr->magic);
774 * Allocate space for storing the name.
776 current_xattr->name_length = strlen(current_attrtuple);
777 current_xattr->name = (char *)malloc(current_xattr->name_length);
778 memcpy((caddr_t)current_xattr->name, (caddr_t)current_attrtuple, current_xattr->name_length);
780 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
783 * First see how long the value is for the extended attribute.
785 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, NULL, 0);
786 if (xattr_value_len < 0) {
789 retval = bxattr_exit_ok;
790 free(current_xattr->name);
794 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
795 jcr->last_fname, be.bstrerror());
796 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
797 jcr->last_fname, be.bstrerror());
798 free(current_xattr->name);
805 * Allocate space for storing the value.
807 current_xattr->value = (char *)malloc(xattr_value_len);
808 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
810 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, current_xattr->value, xattr_value_len);
811 if (xattr_value_len < 0) {
814 retval = bxattr_exit_ok;
815 free(current_xattr->value);
816 free(current_xattr->name);
820 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
821 jcr->last_fname, be.bstrerror());
822 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
823 jcr->last_fname, be.bstrerror());
824 free(current_xattr->value);
825 free(current_xattr->name);
832 * Store the actual length of the value.
834 current_xattr->value_length = xattr_value_len;
835 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
838 * Protect ourself against things getting out of hand.
840 if (expected_serialize_len >= MAX_XATTR_STREAM) {
841 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
842 jcr->last_fname, MAX_XATTR_STREAM);
843 free(current_xattr->value);
844 free(current_xattr->name);
849 xattr_value_list->append(current_xattr);
854 * We are done with this xattr list.
857 xattr_list = (char *)NULL;
861 * If we found any xattr send them to the SD.
863 if (xattr_count > 0) {
865 * Serialize the datastream.
867 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
868 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
870 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
875 xattr_drop_internal_table(xattr_value_list);
876 delete xattr_value_list;
877 xattr_value_list = NULL;
880 * Send the datastream to the SD.
882 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
884 xattr_drop_internal_table(xattr_value_list);
885 delete xattr_value_list;
886 xattr_value_list = NULL;
888 return bxattr_exit_ok;
895 if (xattr_value_list) {
896 xattr_drop_internal_table(xattr_value_list);
897 delete xattr_value_list;
898 xattr_value_list = NULL;
904 * This function gets called by the unserialize_xattr_stream function for the OS specific
905 * code to restore an extended attribute on a file.
907 static bxattr_exit_code restore_xattr_on_file(JCR *jcr, xattr_t *xattr)
910 int current_attrnamespace, cnt;
911 char *attrnamespace, *attrname;
914 * Try splitting the xattr_name into a namespace and name part.
915 * The splitting character is a .
917 attrnamespace = xattr->name;
918 if ((attrname = strchr(attrnamespace, '.')) == (char *)NULL) {
919 Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"),
920 xattr->name, jcr->last_fname);
921 Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n",
922 xattr->name, jcr->last_fname);
923 return bxattr_exit_error;
928 * Make sure the attrnamespace makes sense.
930 if (extattr_string_to_namespace(attrnamespace, ¤t_attrnamespace) != 0) {
931 Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"),
932 attrnamespace, jcr->last_fname);
933 Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n",
934 attrnamespace, jcr->last_fname);
935 return bxattr_exit_error;
939 * Try restoring the extended attribute.
941 cnt = extattr_set_link(jcr->last_fname, current_attrnamespace,
942 attrname, xattr->value, xattr->value_length);
943 if (cnt < 0 || cnt != xattr->value_length) {
948 Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"),
949 jcr->last_fname, be.bstrerror());
950 Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n",
951 jcr->last_fname, be.bstrerror());
952 return bxattr_exit_error;
957 return bxattr_exit_ok;
960 static bxattr_exit_code bsd_parse_xattr_streams(JCR *jcr, int stream)
962 return unserialize_xattr_stream(jcr);
966 * Function pointers to the build and parse function to use for these xattrs.
968 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = bsd_build_xattr_streams;
969 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = bsd_parse_xattr_streams;
971 #elif defined(HAVE_SUN_OS)
973 * Solaris extended attributes were introduced in Solaris 9
976 * Solaris extensible attributes were introduced in OpenSolaris
977 * by PSARC 2007/315 Solaris extensible attributes are also
978 * sometimes called extended system attributes.
980 * man fsattr(5) on Solaris gives a wealth of info. The most
981 * important bits are:
983 * Attributes are logically supported as files within the file
984 * system. The file system is therefore augmented with an
985 * orthogonal name space of file attributes. Any file (includ-
986 * ing attribute files) can have an arbitrarily deep attribute
987 * tree associated with it. Attribute values are accessed by
988 * file descriptors obtained through a special attribute inter-
989 * face. This logical view of "attributes as files" allows the
990 * leveraging of existing file system interface functionality
991 * to support the construction, deletion, and manipulation of
994 * The special files "." and ".." retain their accustomed
995 * semantics within the attribute hierarchy. The "." attribute
996 * file refers to the current directory and the ".." attribute
997 * file refers to the parent directory. The unnamed directory
998 * at the head of each attribute tree is considered the "child"
999 * of the file it is associated with and the ".." file refers
1000 * to the associated file. For any non-directory file with
1001 * attributes, the ".." entry in the unnamed directory refers
1002 * to a file that is not a directory.
1004 * Conceptually, the attribute model is fully general. Extended
1005 * attributes can be any type of file (doors, links, direc-
1006 * tories, and so forth) and can even have their own attributes
1007 * (fully recursive). As a result, the attributes associated
1008 * with a file could be an arbitrarily deep directory hierarchy
1009 * where each attribute could have an equally complex attribute
1010 * tree associated with it. Not all implementations are able
1011 * to, or want to, support the full model. Implementation are
1012 * therefore permitted to reject operations that are not sup-
1013 * ported. For example, the implementation for the UFS file
1014 * system allows only regular files as attributes (for example,
1015 * no sub-directories) and rejects attempts to place attributes
1018 * The following list details the operations that are rejected
1019 * in the current implementation:
1021 * link Any attempt to create links between
1022 * attribute and non-attribute space
1023 * is rejected to prevent security-
1024 * related or otherwise sensitive
1025 * attributes from being exposed, and
1026 * therefore manipulable, as regular
1029 * rename Any attempt to rename between
1030 * attribute and non-attribute space
1031 * is rejected to prevent an already
1032 * linked file from being renamed and
1033 * thereby circumventing the link res-
1036 * mkdir, symlink, mknod Any attempt to create a "non-
1037 * regular" file in attribute space is
1038 * rejected to reduce the functional-
1039 * ity, and therefore exposure and
1040 * risk, of the initial implementa-
1043 * The entire available name space has been allocated to "gen-
1044 * eral use" to bring the implementation in line with the NFSv4
1045 * draft standard [NFSv4]. That standard defines "named attri-
1046 * butes" (equivalent to Solaris Extended Attributes) with no
1047 * naming restrictions. All Sun applications making use of
1048 * opaque extended attributes will use the prefix "SUNW".
1051 #ifdef HAVE_SYS_ATTR_H
1052 #include <sys/attr.h>
1059 #ifdef HAVE_SYS_NVPAIR_H
1060 #include <sys/nvpair.h>
1063 #ifdef HAVE_SYS_ACL_H
1064 #include <sys/acl.h>
1067 #if !defined(HAVE_OPENAT) ||
1068 !defined(HAVE_UNKINKAT) ||
1069 !defined(HAVE_FCHOWNAT) ||
1070 !defined(HAVE_FUTIMESAT)
1071 #error "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function"
1075 * Define the supported XATTR streams for this OS
1077 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1078 static int os_default_xattr_streams[2] = { STREAM_XATTR_SOLARIS, STREAM_XATTR_SOLARIS_SYS};
1080 static int os_default_xattr_streams[1] = { STREAM_XATTR_SOLARIS };
1081 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
1084 * This code creates a temporary cache with entries for each xattr which has
1085 * a link count > 1 (which indicates it has one or more hard linked counterpart(s))
1087 static xattr_link_cache_entry_t *find_xattr_link_cache_entry(JCR *jcr, ino_t inum)
1089 xattr_link_cache_entry_t *ptr;
1091 foreach_alist(ptr, jcr->xattr_data->link_cache) {
1092 if (ptr && ptr->inum == inum) {
1099 static void add_xattr_link_cache_entry(JCR *jcr, ino_t inum, char *target)
1101 xattr_link_cache_entry_t *ptr;
1103 ptr = (xattr_link_cache_entry_t *)malloc(sizeof(xattr_link_cache_entry_t));
1104 memset((caddr_t)ptr, 0, sizeof(xattr_link_cache_entry_t));
1106 bstrncpy(ptr->target, target, sizeof(ptr->target));
1107 jcr->xattr_data->link_cache->append(ptr);
1110 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1112 * This function returns true if a non default extended system attribute
1113 * list is associated with fd and returns false when an error has occured
1114 * or when only extended system attributes other than archive,
1115 * av_modified or crtime are set.
1117 * The function returns true for the following cases:
1119 * - any extended system attribute other than the default attributes
1120 * ('archive', 'av_modified' and 'crtime') is set
1121 * - nvlist has NULL name string
1122 * - nvpair has data type of 'nvlist'
1123 * - default data type.
1125 static bool solaris_has_non_transient_extensible_attributes(int fd)
1133 bool retval = false;
1135 if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
1140 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
1141 name = nvpair_name(pair);
1144 fattr = name_to_attr(name);
1150 type = nvpair_type(pair);
1152 case DATA_TYPE_BOOLEAN_VALUE:
1153 if (nvpair_value_boolean_value(pair, &value) != 0) {
1156 if (value && fattr != F_ARCHIVE &&
1157 fattr != F_AV_MODIFIED) {
1162 case DATA_TYPE_UINT64_ARRAY:
1163 if (fattr != F_CRTIME) {
1168 case DATA_TYPE_NVLIST:
1176 if (response != NULL) {
1177 nvlist_free(response);
1181 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1183 #if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL)
1185 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1186 * There is no need to store those acls as we already store the stat bits too.
1188 static bool acl_is_trivial(int count, aclent_t *entries)
1193 for (n = 0; n < count; n++) {
1195 if (!(ace->a_type == USER_OBJ ||
1196 ace->a_type == GROUP_OBJ ||
1197 ace->a_type == OTHER_OBJ ||
1198 ace->a_type == CLASS_OBJ))
1203 #endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */
1205 static bxattr_exit_code solaris_save_xattr_acl(JCR *jcr, int fd, const char *attrname, char **acl_text)
1208 #ifdef HAVE_EXTENDED_ACL
1214 * See if this attribute has an ACL
1216 if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0) ||
1217 pathconf(attrname, _PC_ACL_ENABLED) > 0) {
1219 * See if there is a non trivial acl on the file.
1221 if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0) ||
1222 acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) {
1225 return bxattr_exit_ok;
1227 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1228 attrname, jcr->last_fname, be.bstrerror());
1229 Dmsg3(100, "facl_get/acl_get of xattr %s on \"%s\" failed: ERR=%s\n",
1230 attrname, jcr->last_fname, be.bstrerror());
1231 return bxattr_exit_error;
1236 #if defined(ACL_SID_FMT)
1238 * New format flag added in newer Solaris versions.
1240 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1242 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1243 #endif /* ACL_SID_FMT */
1245 *acl_text = acl_totext(aclp, flags);
1253 return bxattr_exit_ok;
1254 #else /* HAVE_EXTENDED_ACL */
1256 aclent_t *acls = NULL;
1260 * See if this attribute has an ACL
1263 n = facl(fd, GETACLCNT, 0, NULL);
1265 n = acl(attrname, GETACLCNT, 0, NULL);
1268 if (n >= MIN_ACL_ENTRIES) {
1269 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1270 if ((fd != -1 && facl(fd, GETACL, n, acls) != n) ||
1271 acl(attrname, GETACL, n, acls) != n) {
1275 return bxattr_exit_ok;
1277 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1278 attrname, jcr->last_fname, be.bstrerror());
1279 Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n",
1280 attrname, jcr->last_fname, be.bstrerror());
1282 return bxattr_exit_error;
1287 * See if there is a non trivial acl on the file.
1289 if (!acl_is_trivial(n, acls)) {
1290 if ((*acl_text = acltotext(acls, n)) == NULL) {
1291 Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"),
1292 attrname, jcr->last_fname, be.bstrerror());
1293 Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n",
1294 attrname, jcr->last_fname, be.bstrerror());
1296 return bxattr_exit_error;
1306 return bxattr_exit_ok;
1307 #endif /* HAVE_EXTENDED_ACL */
1309 #else /* HAVE_ACL */
1310 return bxattr_exit_ok;
1311 #endif /* HAVE_ACL */
1315 * Forward declaration for recursive function call.
1317 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent);
1320 * Save an extended or extensible attribute.
1321 * This is stored as an opaque stream of bytes with the following encoding:
1323 * <xattr_name>\0<stat_buffer>\0<acl_string>\0<actual_xattr_data>
1325 * or for a hardlinked or symlinked attribute
1327 * <xattr_name>\0<stat_buffer>\0<xattr_link_source>\0
1329 * xattr_name can be a subpath relative to the file the xattr is on.
1330 * stat_buffer is the string representation of the stat struct.
1331 * acl_string is an acl text when a non trivial acl is set on the xattr.
1332 * actual_xattr_data is the content of the xattr file.
1334 static bxattr_exit_code solaris_save_xattr(JCR *jcr, int fd, const char *xattr_namespace,
1335 const char *attrname, bool toplevel_hidden_dir, int stream)
1340 xattr_link_cache_entry_t *xlce;
1341 char target_attrname[PATH_MAX];
1342 char link_source[PATH_MAX];
1343 char *acl_text = NULL;
1344 char attribs[MAXSTRING];
1345 char buffer[BUFSIZ];
1346 bxattr_exit_code retval = bxattr_exit_error;
1349 bsnprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace, attrname);
1352 * Get the stats of the extended or extensible attribute.
1354 if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) {
1357 retval = bxattr_exit_ok;
1360 Mmsg3(jcr->errmsg, _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"),
1361 target_attrname, jcr->last_fname, be.bstrerror());
1362 Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n",
1363 target_attrname, jcr->last_fname, be.bstrerror());
1369 * Based on the filetype perform the correct action. We support most filetypes here, more
1370 * then the actual implementation on Solaris supports so some code may never get executed
1371 * due to limitations in the implementation.
1373 switch (st.st_mode & S_IFMT) {
1378 * Get any acl on the xattr.
1380 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1384 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1385 * Encode the stat struct into an ASCII representation.
1387 encode_stat(attribs, &st, 0, stream);
1388 cnt = bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c",
1389 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1393 * Get any acl on the xattr.
1395 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1399 * See if this is the toplevel_hidden_dir being saved.
1401 if (toplevel_hidden_dir) {
1403 * Save the data for later storage when we encounter a real xattr. We store the data
1404 * in the jcr->xattr_data->content buffer and flush that just before sending out the
1405 * first real xattr. Encode the stat struct into an ASCII representation and jump
1406 * out of the function.
1408 encode_stat(attribs, &st, 0, stream);
1409 cnt = bsnprintf(buffer, sizeof(buffer),
1411 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1412 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1413 jcr->xattr_data->content_length = cnt;
1417 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1418 * Encode the stat struct into an ASCII representation.
1420 encode_stat(attribs, &st, 0, stream);
1421 cnt = bsnprintf(buffer, sizeof(buffer),
1423 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1428 * If this is a hardlinked file check the inode cache for a hit.
1430 if (st.st_nlink > 1) {
1432 * See if the cache already knows this inode number.
1434 if ((xlce = find_xattr_link_cache_entry(jcr, st.st_ino)) != NULL) {
1436 * Generate a xattr encoding with the reference to the target in there.
1438 encode_stat(attribs, &st, st.st_ino, stream);
1439 cnt = bsnprintf(buffer, sizeof(buffer),
1441 target_attrname, 0, attribs, 0, xlce->target, 0);
1442 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1443 jcr->xattr_data->content_length = cnt;
1444 retval = send_xattr_stream(jcr, stream);
1447 * For a hard linked file we are ready now, no need to recursively save the attributes.
1453 * Store this hard linked file in the cache.
1454 * Store the name relative to the top level xattr space.
1456 add_xattr_link_cache_entry(jcr, st.st_ino, target_attrname + 1);
1460 * Get any acl on the xattr.
1462 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok) {
1467 * Encode the stat struct into an ASCII representation.
1469 encode_stat(attribs, &st, 0, stream);
1470 cnt = bsnprintf(buffer, sizeof(buffer),
1472 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1475 * Open the extended or extensible attribute file.
1477 if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) {
1480 retval = bxattr_exit_ok;
1483 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on \"%s\": ERR=%s\n"),
1484 target_attrname, jcr->last_fname, be.bstrerror());
1485 Dmsg3(100, "openat of xattr %s on \"%s\" failed: ERR=%s\n",
1486 target_attrname, jcr->last_fname, be.bstrerror());
1493 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1494 * Encode the stat struct into an ASCII representation.
1496 if (readlink(attrname, link_source, sizeof(link_source)) < 0) {
1499 retval = bxattr_exit_ok;
1502 Mmsg3(jcr->errmsg, _("Unable to read symlin %s on \"%s\": ERR=%s\n"),
1503 target_attrname, jcr->last_fname, be.bstrerror());
1504 Dmsg3(100, "readlink of xattr %s on \"%s\" failed: ERR=%s\n",
1505 target_attrname, jcr->last_fname, be.bstrerror());
1511 * Generate a xattr encoding with the reference to the target in there.
1513 encode_stat(attribs, &st, st.st_ino, stream);
1514 cnt = bsnprintf(buffer, sizeof(buffer),
1516 target_attrname, 0, attribs, 0, link_source, 0);
1517 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1518 jcr->xattr_data->content_length = cnt;
1519 retval = send_xattr_stream(jcr, stream);
1521 if (retval == bxattr_exit_ok) {
1522 jcr->xattr_data->nr_saved++;
1526 * For a soft linked file we are ready now, no need to recursively save the attributes.
1534 * See if this is the first real xattr being saved.
1535 * If it is save the toplevel_hidden_dir attributes first.
1536 * This is easy as its stored already in the jcr->xattr_data->content buffer.
1538 if (jcr->xattr_data->nr_saved == 0) {
1539 retval = send_xattr_stream(jcr, STREAM_XATTR_SOLARIS);
1540 if (retval != bxattr_exit_ok) {
1543 jcr->xattr_data->nr_saved++;
1546 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1547 jcr->xattr_data->content_length = cnt;
1550 * Only dump the content of regular files.
1552 switch (st.st_mode & S_IFMT) {
1554 if (st.st_size > 0) {
1556 * Protect ourself against things getting out of hand.
1558 if (st.st_size >= MAX_XATTR_STREAM) {
1559 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
1560 jcr->last_fname, MAX_XATTR_STREAM);
1564 while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) {
1565 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, jcr->xattr_data->content_length + cnt);
1566 memcpy(jcr->xattr_data->content + jcr->xattr_data->content_length, buffer, cnt);
1567 jcr->xattr_data->content_length += cnt;
1571 Mmsg2(jcr->errmsg, _("Unable to read content of xattr %s on file \"%s\"\n"),
1572 target_attrname, jcr->last_fname);
1573 Dmsg2(100, "read of data from xattr %s on \"%s\" failed\n",
1574 target_attrname, jcr->last_fname);
1585 retval = send_xattr_stream(jcr, stream);
1586 if (retval == bxattr_exit_ok) {
1587 jcr->xattr_data->nr_saved++;
1592 * Recursivly call solaris_save_extended_attributes for archiving the attributes
1593 * available on this extended attribute.
1596 retval = solaris_save_xattrs(jcr, xattr_namespace, attrname);
1599 * The recursive call could change our working dir so change back to the wanted workdir.
1601 if (fchdir(fd) < 0) {
1604 retval = bxattr_exit_ok;
1607 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"),
1608 jcr->last_fname, be.bstrerror());
1609 Dmsg3(100, "Unable to fchdir to xattr space of file \"%s\" using fd %d: ERR=%s\n",
1610 jcr->last_fname, fd, be.bstrerror());
1626 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent)
1629 int fd, filefd = -1, attrdirfd = -1;
1632 char current_xattr_namespace[PATH_MAX];
1633 bxattr_exit_code retval = bxattr_exit_error;
1637 * Determine what argument to use. Use attr_parent when set
1638 * (recursive call) or jcr->last_fname for first call. Also save
1639 * the current depth of the xattr_space we are in.
1643 if (xattr_namespace) {
1644 bsnprintf(current_xattr_namespace, sizeof(current_xattr_namespace), "%s%s/",
1645 xattr_namespace, attr_parent);
1647 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1650 name = jcr->last_fname;
1651 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1655 * Open the file on which to save the xattrs read-only.
1657 if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
1660 retval = bxattr_exit_ok;
1663 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1664 jcr->last_fname, be.bstrerror());
1665 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1666 jcr->last_fname, be.bstrerror());
1672 * Open the xattr naming space.
1674 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1678 * Gentile way of the system saying this type of xattr layering is not supported.
1679 * Which is not problem we just forget about this this xattr.
1680 * But as this is not an error we return a positive return value.
1682 retval = bxattr_exit_ok;
1685 retval = bxattr_exit_ok;
1688 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1689 name, jcr->last_fname, be.bstrerror());
1690 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1691 name, jcr->last_fname, be.bstrerror());
1697 * We need to change into the attribute directory to determine if each of the
1698 * attributes should be saved.
1700 if (fchdir(attrdirfd) < 0) {
1701 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1702 jcr->last_fname, be.bstrerror());
1703 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1704 jcr->last_fname, attrdirfd, be.bstrerror());
1709 * Save the data of the toplevel xattr hidden_dir. We save this one before anything
1710 * else because the readdir returns "." entry after the extensible attr entry.
1711 * And as we want this entry before anything else we better just save its data.
1714 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, ".",
1715 true, STREAM_XATTR_SOLARIS);
1717 if ((fd = dup(attrdirfd)) == -1 ||
1718 (dirp = fdopendir(fd)) == (DIR *)NULL) {
1719 Mmsg2(jcr->errmsg, _("Unable to list the xattr space on file \"%s\": ERR=%s\n"),
1720 jcr->last_fname, be.bstrerror());
1721 Dmsg3(100, "Unable to fdopendir xattr space on file \"%s\" using fd %d: ERR=%s\n",
1722 jcr->last_fname, fd, be.bstrerror());
1728 * Walk the namespace.
1730 while (dp = readdir(dirp)) {
1732 * Skip only the toplevel . dir.
1734 if (!attr_parent && !strcmp(dp->d_name, "."))
1738 * Skip all .. directories
1740 if (!strcmp(dp->d_name, ".."))
1743 Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n",
1744 current_xattr_namespace, dp->d_name, jcr->last_fname);
1746 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1748 * We are not interested in read-only extensible attributes.
1750 if (!strcmp(dp->d_name, VIEW_READONLY)) {
1751 Dmsg3(400, "Skipping readonly extensible attributes %s%s on file \"%s\"\n",
1752 current_xattr_namespace, dp->d_name, jcr->last_fname);
1758 * We are only interested in read-write extensible attributes
1759 * when they contain non-transient values.
1761 if (!strcmp(dp->d_name, VIEW_READWRITE)) {
1763 * Determine if there are non-transient system attributes at the toplevel.
1764 * We need to provide a fd to the open file.
1766 if (!solaris_has_non_transient_extensible_attributes(filefd)) {
1767 Dmsg3(400, "Skipping transient extensible attributes %s%s on file \"%s\"\n",
1768 current_xattr_namespace, dp->d_name, jcr->last_fname);
1775 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1776 false, STREAM_XATTR_SOLARIS_SYS);
1779 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1784 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1785 false, STREAM_XATTR_SOLARIS);
1789 retval = bxattr_exit_ok;
1792 if (attrdirfd != -1)
1800 static bxattr_exit_code solaris_restore_xattr_acl(JCR *jcr, int fd, const char *attrname, char *acl_text)
1802 #ifdef HAVE_EXTENDED_ACL
1807 if ((error = acl_fromtext(acl_text, &aclp)) != 0) {
1808 Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"),
1810 return bxattr_exit_error;
1813 if ((fd != -1 && facl_set(fd, aclp) != 0) ||
1814 acl_set(attrname, aclp) != 0) {
1815 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1816 attrname, jcr->last_fname, be.bstrerror());
1817 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1818 attrname, jcr->last_fname, be.bstrerror());
1819 return bxattr_exit_error;
1825 return bxattr_exit_ok;
1827 #else /* HAVE_EXTENDED_ACL */
1829 aclent_t *acls = NULL;
1832 acls = aclfromtext(acl_text, &n);
1834 if ((fd != -1 && facl(fd, SETACL, n, acls) != 0) ||
1835 acl(attrname, SETACL, n, acls) != 0) {
1836 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1837 attrname, jcr->last_fname, be.bstrerror());
1838 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1839 attrname, jcr->last_fname, be.bstrerror());
1840 return bxattr_exit_error;
1847 return bxattr_exit_ok;
1849 #endif /* HAVE_EXTENDED_ACL */
1852 #endif /* HAVE_ACL */
1854 static bxattr_exit_code solaris_restore_xattrs(JCR *jcr, bool is_extensible)
1856 int fd, filefd = -1, attrdirfd = -1, attrfd = -1;
1857 int used_bytes, total_bytes, cnt;
1858 char *bp, *target_attrname, *attribs;
1859 char *linked_target = NULL;
1860 char *acl_text = NULL;
1864 struct timeval times[2];
1865 bxattr_exit_code retval = bxattr_exit_error;
1869 * Parse the xattr stream. First the part that is the same for all xattrs.
1872 total_bytes = jcr->xattr_data->content_length;
1875 * The name of the target xattr has a leading / we are not interested
1876 * in that so skip it when decoding the string. We always start a the /
1877 * of the xattr space anyway.
1879 target_attrname = jcr->xattr_data->content + 1;
1880 if ((bp = strchr(target_attrname, '\0')) == (char *)NULL ||
1881 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1887 * Open the file on which to restore the xattrs read-only.
1889 if ((filefd = open(jcr->last_fname, O_RDONLY | O_NONBLOCK)) < 0) {
1890 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1891 jcr->last_fname, be.bstrerror());
1892 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1893 jcr->last_fname, be.bstrerror());
1898 * Open the xattr naming space and make it the current working dir.
1900 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1901 Mmsg2(jcr->errmsg, _("Unable to open xattr space on file \"%s\": ERR=%s\n"),
1902 jcr->last_fname, be.bstrerror());
1903 Dmsg2(100, "Unable to open xattr space on file \"%s\": ERR=%s\n",
1904 jcr->last_fname, be.bstrerror());
1908 if (fchdir(attrdirfd) < 0) {
1909 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1910 jcr->last_fname, be.bstrerror());
1911 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1912 jcr->last_fname, attrdirfd, be.bstrerror());
1917 * Try to open the correct xattr subdir based on the target_attrname given.
1918 * e.g. check if its a subdir attrname. Each / in the string makes us go
1921 while ((bp = strchr(target_attrname, '/')) != (char *)NULL) {
1924 if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) {
1925 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
1926 target_attrname, jcr->last_fname, be.bstrerror());
1927 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
1928 target_attrname, jcr->last_fname, be.bstrerror());
1936 * Open the xattr naming space.
1938 if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1939 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1940 target_attrname, jcr->last_fname, be.bstrerror());
1941 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1942 target_attrname, jcr->last_fname, be.bstrerror());
1950 * Make the xattr space our current workingdir.
1952 if (fchdir(attrdirfd) < 0) {
1953 Mmsg3(jcr->errmsg, _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"),
1954 target_attrname, jcr->last_fname, be.bstrerror());
1955 Dmsg4(100, "Unable to fchdir to xattr space %s on file \"%s\" using fd %d: ERR=%s\n",
1956 target_attrname, jcr->last_fname, attrdirfd, be.bstrerror());
1960 target_attrname = ++bp;
1964 * Decode the attributes from the stream.
1966 decode_stat(attribs, &st, &inum);
1969 * Decode the next field (acl_text).
1971 if ((bp = strchr(attribs, '\0')) == (char *)NULL ||
1972 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1978 * Based on the filetype perform the correct action. We support most filetypes here, more
1979 * then the actual implementation on Solaris supports so some code may never get executed
1980 * due to limitations in the implementation.
1982 switch (st.st_mode & S_IFMT) {
1985 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1987 unlinkat(attrdirfd, target_attrname, 0);
1988 if (mkfifo(target_attrname, st.st_mode) < 0) {
1989 Mmsg3(jcr->errmsg, _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"),
1990 target_attrname, jcr->last_fname, be.bstrerror());
1991 Dmsg3(100, "Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n",
1992 target_attrname, jcr->last_fname, be.bstrerror());
1999 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2001 unlinkat(attrdirfd, target_attrname, 0);
2002 if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) {
2003 Mmsg3(jcr->errmsg, _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"),
2004 target_attrname, jcr->last_fname, be.bstrerror());
2005 Dmsg3(100, "Unable to mknod xattr %s on file \"%s\": ERR=%s\n",
2006 target_attrname, jcr->last_fname, be.bstrerror());
2012 * If its not the hidden_dir create the entry.
2013 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2015 if (strcmp(target_attrname, ".")) {
2016 unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR);
2017 if (mkdir(target_attrname, st.st_mode) < 0) {
2018 Jmsg3(jcr, M_WARNING, 0, _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"),
2019 target_attrname, jcr->last_fname, be.bstrerror());
2020 Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n",
2021 target_attrname, jcr->last_fname, be.bstrerror());
2028 * See if this is a hard linked file. e.g. inum != 0
2033 unlinkat(attrdirfd, target_attrname, 0);
2034 if (link(linked_target, target_attrname) < 0) {
2035 Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"),
2036 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2037 Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n",
2038 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2043 * Successfully restored xattr.
2045 retval = bxattr_exit_ok;
2048 if ((bp = strchr(acl_text, '\0')) == (char *)NULL ||
2049 (used_bytes = (bp - jcr->xattr_data->content)) >= total_bytes) {
2053 if (used_bytes < (total_bytes - 1))
2057 * Restore the actual xattr.
2059 if (!is_extensible) {
2060 unlinkat(attrdirfd, target_attrname, 0);
2063 if ((attrfd = openat(attrdirfd, target_attrname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) {
2064 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
2065 target_attrname, jcr->last_fname, be.bstrerror());
2066 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
2067 target_attrname, jcr->last_fname, be.bstrerror());
2073 * Restore the actual data.
2075 if (st.st_size > 0) {
2076 used_bytes = (data - jcr->xattr_data->content);
2077 cnt = total_bytes - used_bytes;
2080 * Do a sanity check, the st.st_size should be the same as the number of bytes
2081 * we have available as data of the stream.
2083 if (cnt != st.st_size) {
2084 Mmsg2(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n"),
2085 target_attrname, jcr->last_fname);
2086 Dmsg2(100, "Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n",
2087 target_attrname, jcr->last_fname);
2092 cnt = write(attrfd, data, cnt);
2094 Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"),
2095 target_attrname, jcr->last_fname, be.bstrerror());
2096 Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n",
2097 target_attrname, jcr->last_fname, be.bstrerror());
2103 cnt = total_bytes - used_bytes;
2109 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2113 if (symlink(linked_target, target_attrname) < 0) {
2114 Mmsg4(jcr->errmsg, _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"),
2115 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2116 Dmsg4(100, "Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n",
2117 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2122 * Successfully restored xattr.
2124 retval = bxattr_exit_ok;
2131 * Restore owner and acl for non extensible attributes.
2133 if (!is_extensible) {
2134 if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0) {
2138 * Gentile way of the system saying this type of xattr layering is not supported.
2139 * But as this is not an error we return a positive return value.
2141 retval = bxattr_exit_ok;
2144 retval = bxattr_exit_ok;
2147 Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"),
2148 target_attrname, jcr->last_fname, be.bstrerror());
2149 Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n",
2150 target_attrname, jcr->last_fname, be.bstrerror());
2157 if (acl_text && *acl_text)
2158 if (solaris_restore_xattr_acl(jcr, attrfd, target_attrname, acl_text) != bxattr_exit_ok)
2160 #endif /* HAVE_ACL */
2163 * For a non extensible attribute restore access and modification time on the xattr.
2165 if (!is_extensible) {
2166 times[0].tv_sec = st.st_atime;
2167 times[0].tv_usec = 0;
2168 times[1].tv_sec = st.st_mtime;
2169 times[1].tv_usec = 0;
2171 if (futimesat(attrdirfd, target_attrname, times) < 0) {
2172 Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"),
2173 target_attrname, jcr->last_fname, be.bstrerror());
2174 Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n",
2175 target_attrname, jcr->last_fname, be.bstrerror());
2181 * Successfully restored xattr.
2183 retval = bxattr_exit_ok;
2187 Mmsg1(jcr->errmsg, _("Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n"),
2189 Dmsg1(100, "Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n",
2196 if (attrdirfd != -1) {
2205 static bxattr_exit_code solaris_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2208 bxattr_exit_code retval = bxattr_exit_ok;
2211 * First see if extended attributes or extensible attributes are present.
2212 * If not just pretend things went ok.
2214 if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0) {
2215 jcr->xattr_data->nr_saved = 0;
2216 jcr->xattr_data->link_cache = New(alist(10, not_owned_by_alist));
2219 * As we change the cwd in the save function save the current cwd
2220 * for restore after return from the solaris_save_xattrs function.
2222 getcwd(cwd, sizeof(cwd));
2223 retval = solaris_save_xattrs(jcr, NULL, NULL);
2225 delete jcr->xattr_data->link_cache;
2226 jcr->xattr_data->link_cache = NULL;
2231 static bxattr_exit_code solaris_parse_xattr_streams(JCR *jcr, int stream)
2234 bool is_extensible = false;
2235 bxattr_exit_code retval;
2238 * First make sure we can restore xattr on the filesystem.
2241 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
2242 case STREAM_XATTR_SOLARIS_SYS:
2243 if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0) {
2244 Qmsg1(jcr, M_WARNING, 0,
2245 _("Failed to restore extensible attributes on file \"%s\"\n"),
2247 Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n",
2249 return bxattr_exit_error;
2252 is_extensible = true;
2255 case STREAM_XATTR_SOLARIS:
2256 if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0) {
2257 Qmsg1(jcr, M_WARNING, 0,
2258 _("Failed to restore extended attributes on file \"%s\"\n"),
2260 Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n",
2262 return bxattr_exit_error;
2266 return bxattr_exit_error;
2270 * As we change the cwd in the restore function save the current cwd
2271 * for restore after return from the solaris_restore_xattrs function.
2273 getcwd(cwd, sizeof(cwd));
2274 retval = solaris_restore_xattrs(jcr, is_extensible);
2281 * Function pointers to the build and parse function to use for these xattrs.
2283 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_xattr_streams;
2284 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = solaris_parse_xattr_streams;
2286 #endif /* defined(HAVE_SUN_OS) */
2289 * Entry points when compiled with support for XATTRs on a supported platform.
2291 bxattr_exit_code build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2293 if (os_build_xattr_streams) {
2294 return (*os_build_xattr_streams)(jcr, ff_pkt);
2296 return bxattr_exit_error;
2299 bxattr_exit_code parse_xattr_streams(JCR *jcr, int stream)
2303 if (os_parse_xattr_streams) {
2305 * See if we can parse this stream, and ifso give it a try.
2307 for (cnt = 0; cnt < sizeof(os_default_xattr_streams) / sizeof(int); cnt++) {
2308 if (os_default_xattr_streams[cnt] == stream) {
2309 return (*os_parse_xattr_streams)(jcr, stream);
2314 * Issue a warning and discard the message. But pretend the restore was ok.
2316 Jmsg2(jcr, M_WARNING, 0,
2317 _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"),
2318 jcr->last_fname, stream);
2319 return bxattr_exit_error;