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);
145 delete xattr_value_list;
149 * The xattr stream for OSX, FreeBSD, Linux and NetBSD is a serialized stream of bytes
150 * which encodes one or more xattr_t structures.
152 * The Serialized stream consists of the following elements:
153 * magic - A magic string which makes it easy to detect any binary incompatabilites
154 * name_length - The length of the following xattr name
155 * name - The name of the extended attribute
156 * value_length - The length of the following xattr data
157 * value - The actual content of the extended attribute
159 * This is repeated 1 or more times.
162 static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len, alist *xattr_value_list)
164 xattr_t *current_xattr;
168 * Make sure the serialized stream fits in the poolmem buffer.
169 * We allocate some more to be sure the stream is gonna fit.
171 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, expected_serialize_len + 10);
172 ser_begin(jcr->xattr_data->content, expected_serialize_len + 10);
175 * Walk the list of xattrs and serialize the data.
177 foreach_alist(current_xattr, xattr_value_list) {
179 * See if we can shortcut.
181 if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC)
184 ser_uint32(current_xattr->magic);
185 ser_uint32(current_xattr->name_length);
186 ser_bytes(current_xattr->name, current_xattr->name_length);
188 ser_uint32(current_xattr->value_length);
189 ser_bytes(current_xattr->value, current_xattr->value_length);
192 ser_end(jcr->xattr_data->content, expected_serialize_len + 10);
193 jcr->xattr_data->content_length = ser_length(jcr->xattr_data->content);
195 return jcr->xattr_data->content_length;
199 * Forward declaration for restore function.
201 static bxattr_exit_code restore_xattr_on_file(JCR *jcr, xattr_t *xattr);
203 static bxattr_exit_code unserialize_xattr_stream(JCR *jcr)
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.
221 unser_uint32(current_xattr.magic);
222 if (current_xattr.magic != XATTR_MAGIC) {
223 Mmsg1(jcr->errmsg, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"),
225 Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n",
227 return bxattr_exit_error;
231 * Decode the valuepair. First decode the length of the name.
233 unser_uint32(current_xattr.name_length);
236 * Allocate room for the name and decode its content.
238 current_xattr.name = (char *)malloc(current_xattr.name_length + 1);
239 unser_bytes(current_xattr.name, current_xattr.name_length);
242 * The xattr_name needs to be null terminated for lsetxattr.
244 current_xattr.name[current_xattr.name_length] = '\0';
247 * Decode the value length.
249 unser_uint32(current_xattr.value_length);
252 * Allocate room for the value and decode its content.
254 current_xattr.value = (char *)malloc(current_xattr.value_length);
255 unser_bytes(current_xattr.value, current_xattr.value_length);
258 * Try to set the extended attribute on the file.
259 * If we fail to set this attribute we flag the error but its not fatal,
260 * we try to restore the other extended attributes too.
262 if (restore_xattr_on_file(jcr, ¤t_xattr) != bxattr_exit_ok) {
263 retval = bxattr_exit_error;
267 * Free the temporary buffers.
269 free(current_xattr.name);
270 free(current_xattr.value);
273 unser_end(jcr->xattr_data->content, jcr->xattr_data->content_length);
279 * This is a supported OS, See what kind of interface we should use.
280 * Start with the generic interface used by most OS-es.
282 #if defined(HAVE_DARWIN_OS) || \
283 defined(HAVE_LINUX_OS)
285 #if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)) || \
286 (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) || \
287 (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR))
288 #error "Missing either full support for the LXATTR or XATTR functions."
291 #ifdef HAVE_SYS_XATTR_H
292 #include <sys/xattr.h>
294 #error "Missing sys/xattr.h header file"
298 * Define the supported XATTR streams for this OS
300 #if defined(HAVE_DARWIN_OS)
301 static int os_default_xattr_streams[1] = { STREAM_XATTR_DARWIN };
302 static const char *xattr_acl_skiplist[2] = { "com.apple.system.Security", NULL };
303 static const char *xattr_skiplist[3] = { "com.apple.system.extendedsecurity", "com.apple.ResourceFork", NULL };
304 #elif defined(HAVE_LINUX_OS)
305 static int os_default_xattr_streams[1] = { STREAM_XATTR_LINUX };
306 static const char *xattr_acl_skiplist[2] = { "system.posix_acl_access", NULL };
307 static const char *xattr_skiplist[1] = { NULL };
311 * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
312 * listxattr, getxattr and setxattr with an extra options argument
313 * which mimics the l variants of the functions when we specify
314 * XATTR_NOFOLLOW as the options value.
316 #if defined(HAVE_DARWIN_OS)
317 #define llistxattr(path, list, size) listxattr((path), (list), (size), XATTR_NOFOLLOW)
318 #define lgetxattr(path, name, value, size) getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
319 #define lsetxattr(path, name, value, size, flags) setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
322 * Fallback to the non l-functions when those are not available.
324 #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
325 #define lgetxattr getxattr
327 #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
328 #define lsetxattr setxattr
330 #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
331 #define llistxattr listxattr
335 static bxattr_exit_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt)
338 char *xattr_list, *bp;
339 int cnt, xattr_count = 0;
340 int32_t xattr_list_len,
342 uint32_t expected_serialize_len = 0;
343 xattr_t *current_xattr;
344 alist *xattr_value_list = NULL;
345 bxattr_exit_code retval = bxattr_exit_error;
349 * First get the length of the available list with extended attributes.
351 xattr_list_len = llistxattr(jcr->last_fname, NULL, 0);
352 if (xattr_list_len < 0) {
355 return bxattr_exit_ok;
357 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
358 jcr->last_fname, be.bstrerror());
359 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
360 jcr->last_fname, be.bstrerror());
361 return bxattr_exit_error;
363 } else if (xattr_list_len == 0) {
364 return bxattr_exit_ok;
368 * Allocate room for the extented attribute list.
370 xattr_list = (char *)malloc(xattr_list_len + 1);
371 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
374 * Get the actual list of extended attributes names for a file.
376 xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len);
377 if (xattr_list_len < 0) {
380 retval = bxattr_exit_ok;
383 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
384 jcr->last_fname, be.bstrerror());
385 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
386 jcr->last_fname, be.bstrerror());
390 xattr_list[xattr_list_len] = '\0';
392 xattr_value_list = New(alist(10, not_owned_by_alist));
395 * Walk the list of extended attributes names and retrieve the data.
396 * We already count the bytes needed for serializing the stream later on.
399 while ((bp - xattr_list) + 1 < xattr_list_len) {
403 * On some OSes you also get the acls in the extented attribute list.
404 * So we check if we are already backing up acls and if we do we
405 * don't store the extended attribute with the same info.
407 if (ff_pkt->flags & FO_ACL) {
408 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
409 if (!strcmp(bp, xattr_acl_skiplist[cnt])) {
417 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
420 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
421 if (!strcmp(bp, xattr_skiplist[cnt])) {
429 bp = strchr(bp, '\0') + 1;
434 * Each xattr valuepair starts with a magic so we can parse it easier.
436 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
437 current_xattr->magic = XATTR_MAGIC;
438 expected_serialize_len += sizeof(current_xattr->magic);
441 * Allocate space for storing the name.
443 current_xattr->name_length = strlen(bp);
444 current_xattr->name = (char *)malloc(current_xattr->name_length);
445 memcpy((caddr_t)current_xattr->name, (caddr_t)bp, current_xattr->name_length);
447 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
450 * First see how long the value is for the extended attribute.
452 xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0);
453 if (xattr_value_len < 0) {
456 retval = bxattr_exit_ok;
457 free(current_xattr->name);
461 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
462 jcr->last_fname, be.bstrerror());
463 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
464 jcr->last_fname, be.bstrerror());
465 free(current_xattr->name);
472 * Allocate space for storing the value.
474 current_xattr->value = (char *)malloc(xattr_value_len);
475 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
477 xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
478 if (xattr_value_len < 0) {
481 retval = bxattr_exit_ok;
482 free(current_xattr->value);
483 free(current_xattr->name);
487 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
488 jcr->last_fname, be.bstrerror());
489 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
490 jcr->last_fname, be.bstrerror());
491 free(current_xattr->value);
492 free(current_xattr->name);
499 * Store the actual length of the value.
501 current_xattr->value_length = xattr_value_len;
502 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
505 * Protect ourself against things getting out of hand.
507 if (expected_serialize_len >= MAX_XATTR_STREAM) {
508 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
509 jcr->last_fname, MAX_XATTR_STREAM);
510 free(current_xattr->value);
511 free(current_xattr->name);
516 xattr_value_list->append(current_xattr);
518 bp = strchr(bp, '\0') + 1;
522 xattr_list = (char *)NULL;
525 * If we found any xattr send them to the SD.
527 if (xattr_count > 0) {
529 * Serialize the datastream.
531 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
532 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
534 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
539 xattr_drop_internal_table(xattr_value_list);
540 xattr_value_list = NULL;
543 * Send the datastream to the SD.
545 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
547 xattr_drop_internal_table(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 xattr_value_list = NULL;
565 * This function gets called by the unserialize_xattr_stream function for the OS specific
566 * code to restore an extended attribute on a file.
568 static bxattr_exit_code restore_xattr_on_file(JCR *jcr, xattr_t *xattr)
572 if (lsetxattr(jcr->last_fname, xattr->name, xattr->value, xattr->value_length, 0) != 0) {
577 Mmsg2(jcr->errmsg, _("lsetxattr error on file \"%s\": ERR=%s\n"),
578 jcr->last_fname, be.bstrerror());
579 Dmsg2(100, "lsetxattr error file=%s ERR=%s\n",
580 jcr->last_fname, be.bstrerror());
581 return bxattr_exit_error;
586 return bxattr_exit_ok;
589 static bxattr_exit_code generic_xattr_parse_streams(JCR *jcr, int stream)
591 return unserialize_xattr_stream(jcr);
595 * For all these os-es setup the build and parse function pointer to the generic functions.
597 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_xattr_build_streams;
598 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = generic_xattr_parse_streams;
600 #elif defined(HAVE_FREEBSD_OS) || \
601 defined(HAVE_NETBSD_OS) || \
602 defined(HAVE_OPENBSD_OS)
604 #if !defined(HAVE_EXTATTR_GET_LINK) || \
605 !defined(HAVE_EXTATTR_SET_LINK) || \
606 !defined(HAVE_EXTATTR_LIST_LINK) || \
607 !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \
608 !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE)
609 #error "Missing full support for the extattr functions."
612 #ifdef HAVE_SYS_EXTATTR_H
613 #include <sys/extattr.h>
615 #error "Missing sys/extattr.h header file"
618 #ifdef HAVE_LIBUTIL_H
622 #if defined(HAVE_FREEBSD_OS)
623 static int os_default_xattr_streams[1] = { STREAM_XATTR_FREEBSD };
624 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
625 static const char *xattr_acl_skiplist[1] = { NULL };
626 static const char *xattr_skiplist[1] = { NULL };
627 #elif defined(HAVE_NETBSD_OS)
628 static int os_default_xattr_streams[1] = { STREAM_XATTR_NETBSD };
629 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
630 static const char *xattr_acl_skiplist[1] = { NULL };
631 static const char *xattr_skiplist[1] = { NULL };
632 #elif defined(HAVE_OPENBSD_OS)
633 static int os_default_xattr_streams[1] = { STREAM_XATTR_OPENBSD };
634 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
635 static const char *xattr_acl_skiplist[1] = { NULL };
636 static const char *xattr_skiplist[1] = { NULL };
639 static bxattr_exit_code bsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
643 int cnt, index, xattr_count = 0;
644 int32_t xattr_list_len,
646 uint32_t expected_serialize_len = 0;
647 unsigned int namespace_index;
649 char *current_attrnamespace, current_attrname[BUFSIZ], current_attrtuple[BUFSIZ];
650 xattr_t *current_xattr;
651 alist *xattr_value_list = NULL;
652 bxattr_exit_code retval = bxattr_exit_error;
655 xattr_value_list = New(alist(10, not_owned_by_alist));
658 * Loop over all available xattr namespaces.
660 for (namespace_index = 0; namespace_index < sizeof(os_default_xattr_namespaces) / sizeof(int); namespace_index++) {
661 attrnamespace = os_default_xattr_namespaces[namespace_index];
664 * First get the length of the available list with extended attributes.
666 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, NULL, 0);
667 if (xattr_list_len < 0) {
670 retval = bxattr_exit_ok;
673 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
674 jcr->last_fname, be.bstrerror());
675 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
676 jcr->last_fname, be.bstrerror());
679 } else if (xattr_list_len == 0) {
684 * Allocate room for the extented attribute list.
686 xattr_list = (char *)malloc(xattr_list_len + 1);
687 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
690 * Get the actual list of extended attributes names for a file.
692 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, xattr_list, xattr_list_len);
693 if (xattr_list_len < 0) {
696 retval = bxattr_exit_ok;
699 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
700 jcr->last_fname, be.bstrerror());
701 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
702 jcr->last_fname, be.bstrerror());
706 xattr_list[xattr_list_len] = '\0';
709 * Walk the list of extended attributes names and retrieve the data.
710 * We already count the bytes needed for serializing the stream later on.
712 for (index = 0; index < xattr_list_len; index += xattr_list[index] + 1) {
714 bsnprintf(current_attrname, sizeof(current_attrname), "%*.*s",
715 xattr_list[index], xattr_list[index], xattr_list + (index + 1));
718 * First make a xattr tuple of the current namespace and the name of the xattr.
719 * e.g. something like user.<attrname> or system.<attrname>
721 if (extattr_namespace_to_string(attrnamespace, ¤t_attrnamespace) != 0) {
722 Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"),
723 attrnamespace, jcr->last_fname);
724 Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n",
725 attrnamespace, jcr->last_fname);
730 * print the current name into the buffer as its not null terminated we need to
731 * use the length encoded in the string for copying only the needed bytes.
733 bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s", current_attrnamespace, current_attrname);
736 * On some OSes you also get the acls in the extented attribute list.
737 * So we check if we are already backing up acls and if we do we
738 * don't store the extended attribute with the same info.
740 if (ff_pkt->flags & FO_ACL) {
741 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
742 if (!strcmp(current_attrtuple, xattr_acl_skiplist[cnt])) {
750 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
753 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
754 if (!strcmp(current_attrtuple, xattr_skiplist[cnt])) {
766 * Each xattr valuepair starts with a magic so we can parse it easier.
768 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
769 current_xattr->magic = XATTR_MAGIC;
770 expected_serialize_len += sizeof(current_xattr->magic);
773 * Allocate space for storing the name.
775 current_xattr->name_length = strlen(current_attrtuple);
776 current_xattr->name = (char *)malloc(current_xattr->name_length);
777 memcpy((caddr_t)current_xattr->name, (caddr_t)current_attrtuple, current_xattr->name_length);
779 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
782 * First see how long the value is for the extended attribute.
784 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, NULL, 0);
785 if (xattr_value_len < 0) {
788 retval = bxattr_exit_ok;
789 free(current_xattr->name);
793 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
794 jcr->last_fname, be.bstrerror());
795 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
796 jcr->last_fname, be.bstrerror());
797 free(current_xattr->name);
804 * Allocate space for storing the value.
806 current_xattr->value = (char *)malloc(xattr_value_len);
807 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
809 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, current_xattr->value, xattr_value_len);
810 if (xattr_value_len < 0) {
813 retval = bxattr_exit_ok;
814 free(current_xattr->value);
815 free(current_xattr->name);
819 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
820 jcr->last_fname, be.bstrerror());
821 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
822 jcr->last_fname, be.bstrerror());
823 free(current_xattr->value);
824 free(current_xattr->name);
831 * Store the actual length of the value.
833 current_xattr->value_length = xattr_value_len;
834 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
837 * Protect ourself against things getting out of hand.
839 if (expected_serialize_len >= MAX_XATTR_STREAM) {
840 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
841 jcr->last_fname, MAX_XATTR_STREAM);
842 free(current_xattr->value);
843 free(current_xattr->name);
848 xattr_value_list->append(current_xattr);
853 * We are done with this xattr list.
856 xattr_list = (char *)NULL;
860 * If we found any xattr send them to the SD.
862 if (xattr_count > 0) {
864 * Serialize the datastream.
866 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
867 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
869 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
874 xattr_drop_internal_table(xattr_value_list);
875 xattr_value_list = NULL;
878 * Send the datastream to the SD.
880 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
882 xattr_drop_internal_table(xattr_value_list);
883 xattr_value_list = NULL;
885 return bxattr_exit_ok;
892 if (xattr_value_list) {
893 xattr_drop_internal_table(xattr_value_list);
894 xattr_value_list = NULL;
900 * This function gets called by the unserialize_xattr_stream function for the OS specific
901 * code to restore an extended attribute on a file.
903 static bxattr_exit_code restore_xattr_on_file(JCR *jcr, xattr_t *xattr)
906 int current_attrnamespace, cnt;
907 char *attrnamespace, *attrname;
910 * Try splitting the xattr_name into a namespace and name part.
911 * The splitting character is a .
913 attrnamespace = xattr->name;
914 if ((attrname = strchr(attrnamespace, '.')) == (char *)NULL) {
915 Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"),
916 xattr->name, jcr->last_fname);
917 Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n",
918 xattr->name, jcr->last_fname);
919 return bxattr_exit_error;
924 * Make sure the attrnamespace makes sense.
926 if (extattr_string_to_namespace(attrnamespace, ¤t_attrnamespace) != 0) {
927 Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"),
928 attrnamespace, jcr->last_fname);
929 Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n",
930 attrnamespace, jcr->last_fname);
931 return bxattr_exit_error;
935 * Try restoring the extended attribute.
937 cnt = extattr_set_link(jcr->last_fname, current_attrnamespace,
938 attrname, xattr->value, xattr->value_length);
939 if (cnt < 0 || cnt != xattr->value_length) {
944 Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"),
945 jcr->last_fname, be.bstrerror());
946 Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n",
947 jcr->last_fname, be.bstrerror());
948 return bxattr_exit_error;
953 return bxattr_exit_ok;
956 static bxattr_exit_code bsd_parse_xattr_streams(JCR *jcr, int stream)
958 return unserialize_xattr_stream(jcr);
962 * Function pointers to the build and parse function to use for these xattrs.
964 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = bsd_build_xattr_streams;
965 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = bsd_parse_xattr_streams;
967 #elif defined(HAVE_SUN_OS)
969 * Solaris extended attributes were introduced in Solaris 9
972 * Solaris extensible attributes were introduced in OpenSolaris
973 * by PSARC 2007/315 Solaris extensible attributes are also
974 * sometimes called extended system attributes.
976 * man fsattr(5) on Solaris gives a wealth of info. The most
977 * important bits are:
979 * Attributes are logically supported as files within the file
980 * system. The file system is therefore augmented with an
981 * orthogonal name space of file attributes. Any file (includ-
982 * ing attribute files) can have an arbitrarily deep attribute
983 * tree associated with it. Attribute values are accessed by
984 * file descriptors obtained through a special attribute inter-
985 * face. This logical view of "attributes as files" allows the
986 * leveraging of existing file system interface functionality
987 * to support the construction, deletion, and manipulation of
990 * The special files "." and ".." retain their accustomed
991 * semantics within the attribute hierarchy. The "." attribute
992 * file refers to the current directory and the ".." attribute
993 * file refers to the parent directory. The unnamed directory
994 * at the head of each attribute tree is considered the "child"
995 * of the file it is associated with and the ".." file refers
996 * to the associated file. For any non-directory file with
997 * attributes, the ".." entry in the unnamed directory refers
998 * to a file that is not a directory.
1000 * Conceptually, the attribute model is fully general. Extended
1001 * attributes can be any type of file (doors, links, direc-
1002 * tories, and so forth) and can even have their own attributes
1003 * (fully recursive). As a result, the attributes associated
1004 * with a file could be an arbitrarily deep directory hierarchy
1005 * where each attribute could have an equally complex attribute
1006 * tree associated with it. Not all implementations are able
1007 * to, or want to, support the full model. Implementation are
1008 * therefore permitted to reject operations that are not sup-
1009 * ported. For example, the implementation for the UFS file
1010 * system allows only regular files as attributes (for example,
1011 * no sub-directories) and rejects attempts to place attributes
1014 * The following list details the operations that are rejected
1015 * in the current implementation:
1017 * link Any attempt to create links between
1018 * attribute and non-attribute space
1019 * is rejected to prevent security-
1020 * related or otherwise sensitive
1021 * attributes from being exposed, and
1022 * therefore manipulable, as regular
1025 * rename Any attempt to rename between
1026 * attribute and non-attribute space
1027 * is rejected to prevent an already
1028 * linked file from being renamed and
1029 * thereby circumventing the link res-
1032 * mkdir, symlink, mknod Any attempt to create a "non-
1033 * regular" file in attribute space is
1034 * rejected to reduce the functional-
1035 * ity, and therefore exposure and
1036 * risk, of the initial implementa-
1039 * The entire available name space has been allocated to "gen-
1040 * eral use" to bring the implementation in line with the NFSv4
1041 * draft standard [NFSv4]. That standard defines "named attri-
1042 * butes" (equivalent to Solaris Extended Attributes) with no
1043 * naming restrictions. All Sun applications making use of
1044 * opaque extended attributes will use the prefix "SUNW".
1047 #ifdef HAVE_SYS_ATTR_H
1048 #include <sys/attr.h>
1055 #ifdef HAVE_SYS_NVPAIR_H
1056 #include <sys/nvpair.h>
1059 #ifdef HAVE_SYS_ACL_H
1060 #include <sys/acl.h>
1063 #if !defined(HAVE_OPENAT) ||
1064 !defined(HAVE_UNKINKAT) ||
1065 !defined(HAVE_FCHOWNAT) ||
1066 !defined(HAVE_FUTIMESAT)
1067 #error "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function"
1071 * Define the supported XATTR streams for this OS
1073 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1074 static int os_default_xattr_streams[2] = { STREAM_XATTR_SOLARIS, STREAM_XATTR_SOLARIS_SYS};
1076 static int os_default_xattr_streams[1] = { STREAM_XATTR_SOLARIS };
1077 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
1080 * This code creates a temporary cache with entries for each xattr which has
1081 * a link count > 1 (which indicates it has one or more hard linked counterpart(s))
1083 static xattr_link_cache_entry_t *find_xattr_link_cache_entry(JCR *jcr, ino_t inum)
1085 xattr_link_cache_entry_t *ptr;
1087 foreach_alist(ptr, jcr->xattr_data->link_cache) {
1088 if (ptr && ptr->inum == inum) {
1095 static void add_xattr_link_cache_entry(JCR *jcr, ino_t inum, char *target)
1097 xattr_link_cache_entry_t *ptr;
1099 ptr = (xattr_link_cache_entry_t *)malloc(sizeof(xattr_link_cache_entry_t));
1100 memset((caddr_t)ptr, 0, sizeof(xattr_link_cache_entry_t));
1102 bstrncpy(ptr->target, target, sizeof(ptr->target));
1103 jcr->xattr_data->link_cache->append(ptr);
1106 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1108 * This function returns true if a non default extended system attribute
1109 * list is associated with fd and returns false when an error has occured
1110 * or when only extended system attributes other than archive,
1111 * av_modified or crtime are set.
1113 * The function returns true for the following cases:
1115 * - any extended system attribute other than the default attributes
1116 * ('archive', 'av_modified' and 'crtime') is set
1117 * - nvlist has NULL name string
1118 * - nvpair has data type of 'nvlist'
1119 * - default data type.
1121 static bool solaris_has_non_transient_extensible_attributes(int fd)
1129 bool retval = false;
1131 if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
1136 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
1137 name = nvpair_name(pair);
1140 fattr = name_to_attr(name);
1146 type = nvpair_type(pair);
1148 case DATA_TYPE_BOOLEAN_VALUE:
1149 if (nvpair_value_boolean_value(pair, &value) != 0) {
1152 if (value && fattr != F_ARCHIVE &&
1153 fattr != F_AV_MODIFIED) {
1158 case DATA_TYPE_UINT64_ARRAY:
1159 if (fattr != F_CRTIME) {
1164 case DATA_TYPE_NVLIST:
1172 if (response != NULL) {
1173 nvlist_free(response);
1177 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1179 #if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL)
1181 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1182 * There is no need to store those acls as we already store the stat bits too.
1184 static bool acl_is_trivial(int count, aclent_t *entries)
1189 for (n = 0; n < count; n++) {
1191 if (!(ace->a_type == USER_OBJ ||
1192 ace->a_type == GROUP_OBJ ||
1193 ace->a_type == OTHER_OBJ ||
1194 ace->a_type == CLASS_OBJ))
1199 #endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */
1201 static bxattr_exit_code solaris_save_xattr_acl(JCR *jcr, int fd, const char *attrname, char **acl_text)
1204 #ifdef HAVE_EXTENDED_ACL
1210 * See if this attribute has an ACL
1212 if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0) ||
1213 pathconf(attrname, _PC_ACL_ENABLED) > 0) {
1215 * See if there is a non trivial acl on the file.
1217 if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0) ||
1218 acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) {
1221 return bxattr_exit_ok;
1223 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1224 attrname, jcr->last_fname, be.bstrerror());
1225 Dmsg3(100, "facl_get/acl_get of xattr %s on \"%s\" failed: ERR=%s\n",
1226 attrname, jcr->last_fname, be.bstrerror());
1227 return bxattr_exit_error;
1232 #if defined(ACL_SID_FMT)
1234 * New format flag added in newer Solaris versions.
1236 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1238 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1239 #endif /* ACL_SID_FMT */
1241 *acl_text = acl_totext(aclp, flags);
1249 return bxattr_exit_ok;
1250 #else /* HAVE_EXTENDED_ACL */
1252 aclent_t *acls = NULL;
1256 * See if this attribute has an ACL
1259 n = facl(fd, GETACLCNT, 0, NULL);
1261 n = acl(attrname, GETACLCNT, 0, NULL);
1264 if (n >= MIN_ACL_ENTRIES) {
1265 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1266 if ((fd != -1 && facl(fd, GETACL, n, acls) != n) ||
1267 acl(attrname, GETACL, n, acls) != n) {
1271 return bxattr_exit_ok;
1273 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1274 attrname, jcr->last_fname, be.bstrerror());
1275 Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n",
1276 attrname, jcr->last_fname, be.bstrerror());
1278 return bxattr_exit_error;
1283 * See if there is a non trivial acl on the file.
1285 if (!acl_is_trivial(n, acls)) {
1286 if ((*acl_text = acltotext(acls, n)) == NULL) {
1287 Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"),
1288 attrname, jcr->last_fname, be.bstrerror());
1289 Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n",
1290 attrname, jcr->last_fname, be.bstrerror());
1292 return bxattr_exit_error;
1302 return bxattr_exit_ok;
1303 #endif /* HAVE_EXTENDED_ACL */
1305 #else /* HAVE_ACL */
1306 return bxattr_exit_ok;
1307 #endif /* HAVE_ACL */
1311 * Forward declaration for recursive function call.
1313 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent);
1316 * Save an extended or extensible attribute.
1317 * This is stored as an opaque stream of bytes with the following encoding:
1319 * <xattr_name>\0<stat_buffer>\0<acl_string>\0<actual_xattr_data>
1321 * or for a hardlinked or symlinked attribute
1323 * <xattr_name>\0<stat_buffer>\0<xattr_link_source>\0
1325 * xattr_name can be a subpath relative to the file the xattr is on.
1326 * stat_buffer is the string representation of the stat struct.
1327 * acl_string is an acl text when a non trivial acl is set on the xattr.
1328 * actual_xattr_data is the content of the xattr file.
1330 static bxattr_exit_code solaris_save_xattr(JCR *jcr, int fd, const char *xattr_namespace,
1331 const char *attrname, bool toplevel_hidden_dir, int stream)
1336 xattr_link_cache_entry_t *xlce;
1337 char target_attrname[PATH_MAX];
1338 char link_source[PATH_MAX];
1339 char *acl_text = NULL;
1340 char attribs[MAXSTRING];
1341 char buffer[BUFSIZ];
1342 bxattr_exit_code retval = bxattr_exit_error;
1345 bsnprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace, attrname);
1348 * Get the stats of the extended or extensible attribute.
1350 if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) {
1353 retval = bxattr_exit_ok;
1356 Mmsg3(jcr->errmsg, _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"),
1357 target_attrname, jcr->last_fname, be.bstrerror());
1358 Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n",
1359 target_attrname, jcr->last_fname, be.bstrerror());
1365 * Based on the filetype perform the correct action. We support most filetypes here, more
1366 * then the actual implementation on Solaris supports so some code may never get executed
1367 * due to limitations in the implementation.
1369 switch (st.st_mode & S_IFMT) {
1374 * Get any acl on the xattr.
1376 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1380 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1381 * Encode the stat struct into an ASCII representation.
1383 encode_stat(attribs, &st, 0, stream);
1384 cnt = bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c",
1385 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1389 * Get any acl on the xattr.
1391 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1395 * See if this is the toplevel_hidden_dir being saved.
1397 if (toplevel_hidden_dir) {
1399 * Save the data for later storage when we encounter a real xattr. We store the data
1400 * in the jcr->xattr_data->content buffer and flush that just before sending out the
1401 * first real xattr. Encode the stat struct into an ASCII representation and jump
1402 * out of the function.
1404 encode_stat(attribs, &st, 0, stream);
1405 cnt = bsnprintf(buffer, sizeof(buffer),
1407 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1408 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1409 jcr->xattr_data->content_length = cnt;
1413 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1414 * Encode the stat struct into an ASCII representation.
1416 encode_stat(attribs, &st, 0, stream);
1417 cnt = bsnprintf(buffer, sizeof(buffer),
1419 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1424 * If this is a hardlinked file check the inode cache for a hit.
1426 if (st.st_nlink > 1) {
1428 * See if the cache already knows this inode number.
1430 if ((xlce = find_xattr_link_cache_entry(jcr, st.st_ino)) != NULL) {
1432 * Generate a xattr encoding with the reference to the target in there.
1434 encode_stat(attribs, &st, st.st_ino, stream);
1435 cnt = bsnprintf(buffer, sizeof(buffer),
1437 target_attrname, 0, attribs, 0, xlce->target, 0);
1438 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1439 jcr->xattr_data->content_length = cnt;
1440 retval = send_xattr_stream(jcr, stream);
1443 * For a hard linked file we are ready now, no need to recursively save the attributes.
1449 * Store this hard linked file in the cache.
1450 * Store the name relative to the top level xattr space.
1452 add_xattr_link_cache_entry(jcr, st.st_ino, target_attrname + 1);
1456 * Get any acl on the xattr.
1458 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok) {
1463 * Encode the stat struct into an ASCII representation.
1465 encode_stat(attribs, &st, 0, stream);
1466 cnt = bsnprintf(buffer, sizeof(buffer),
1468 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1471 * Open the extended or extensible attribute file.
1473 if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) {
1476 retval = bxattr_exit_ok;
1479 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on \"%s\": ERR=%s\n"),
1480 target_attrname, jcr->last_fname, be.bstrerror());
1481 Dmsg3(100, "openat of xattr %s on \"%s\" failed: ERR=%s\n",
1482 target_attrname, jcr->last_fname, be.bstrerror());
1489 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1490 * Encode the stat struct into an ASCII representation.
1492 if (readlink(attrname, link_source, sizeof(link_source)) < 0) {
1495 retval = bxattr_exit_ok;
1498 Mmsg3(jcr->errmsg, _("Unable to read symlin %s on \"%s\": ERR=%s\n"),
1499 target_attrname, jcr->last_fname, be.bstrerror());
1500 Dmsg3(100, "readlink of xattr %s on \"%s\" failed: ERR=%s\n",
1501 target_attrname, jcr->last_fname, be.bstrerror());
1507 * Generate a xattr encoding with the reference to the target in there.
1509 encode_stat(attribs, &st, st.st_ino, stream);
1510 cnt = bsnprintf(buffer, sizeof(buffer),
1512 target_attrname, 0, attribs, 0, link_source, 0);
1513 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1514 jcr->xattr_data->content_length = cnt;
1515 retval = send_xattr_stream(jcr, stream);
1517 if (retval == bxattr_exit_ok) {
1518 jcr->xattr_data->nr_saved++;
1522 * For a soft linked file we are ready now, no need to recursively save the attributes.
1530 * See if this is the first real xattr being saved.
1531 * If it is save the toplevel_hidden_dir attributes first.
1532 * This is easy as its stored already in the jcr->xattr_data->content buffer.
1534 if (jcr->xattr_data->nr_saved == 0) {
1535 retval = send_xattr_stream(jcr, STREAM_XATTR_SOLARIS);
1536 if (retval != bxattr_exit_ok) {
1539 jcr->xattr_data->nr_saved++;
1542 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1543 jcr->xattr_data->content_length = cnt;
1546 * Only dump the content of regular files.
1548 switch (st.st_mode & S_IFMT) {
1550 if (st.st_size > 0) {
1552 * Protect ourself against things getting out of hand.
1554 if (st.st_size >= MAX_XATTR_STREAM) {
1555 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
1556 jcr->last_fname, MAX_XATTR_STREAM);
1560 while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) {
1561 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, jcr->xattr_data->content_length + cnt);
1562 memcpy(jcr->xattr_data->content + jcr->xattr_data->content_length, buffer, cnt);
1563 jcr->xattr_data->content_length += cnt;
1567 Mmsg2(jcr->errmsg, _("Unable to read content of xattr %s on file \"%s\"\n"),
1568 target_attrname, jcr->last_fname);
1569 Dmsg2(100, "read of data from xattr %s on \"%s\" failed\n",
1570 target_attrname, jcr->last_fname);
1581 retval = send_xattr_stream(jcr, stream);
1582 if (retval == bxattr_exit_ok) {
1583 jcr->xattr_data->nr_saved++;
1588 * Recursivly call solaris_save_extended_attributes for archiving the attributes
1589 * available on this extended attribute.
1592 retval = solaris_save_xattrs(jcr, xattr_namespace, attrname);
1595 * The recursive call could change our working dir so change back to the wanted workdir.
1597 if (fchdir(fd) < 0) {
1600 retval = bxattr_exit_ok;
1603 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"),
1604 jcr->last_fname, be.bstrerror());
1605 Dmsg3(100, "Unable to fchdir to xattr space of file \"%s\" using fd %d: ERR=%s\n",
1606 jcr->last_fname, fd, be.bstrerror());
1622 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent)
1625 int fd, filefd = -1, attrdirfd = -1;
1628 char current_xattr_namespace[PATH_MAX];
1629 bxattr_exit_code retval = bxattr_exit_error;
1633 * Determine what argument to use. Use attr_parent when set
1634 * (recursive call) or jcr->last_fname for first call. Also save
1635 * the current depth of the xattr_space we are in.
1639 if (xattr_namespace) {
1640 bsnprintf(current_xattr_namespace, sizeof(current_xattr_namespace), "%s%s/",
1641 xattr_namespace, attr_parent);
1643 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1646 name = jcr->last_fname;
1647 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1651 * Open the file on which to save the xattrs read-only.
1653 if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
1656 retval = bxattr_exit_ok;
1659 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1660 jcr->last_fname, be.bstrerror());
1661 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1662 jcr->last_fname, be.bstrerror());
1668 * Open the xattr naming space.
1670 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1674 * Gentile way of the system saying this type of xattr layering is not supported.
1675 * Which is not problem we just forget about this this xattr.
1676 * But as this is not an error we return a positive return value.
1678 retval = bxattr_exit_ok;
1681 retval = bxattr_exit_ok;
1684 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1685 name, jcr->last_fname, be.bstrerror());
1686 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1687 name, jcr->last_fname, be.bstrerror());
1693 * We need to change into the attribute directory to determine if each of the
1694 * attributes should be saved.
1696 if (fchdir(attrdirfd) < 0) {
1697 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1698 jcr->last_fname, be.bstrerror());
1699 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1700 jcr->last_fname, attrdirfd, be.bstrerror());
1705 * Save the data of the toplevel xattr hidden_dir. We save this one before anything
1706 * else because the readdir returns "." entry after the extensible attr entry.
1707 * And as we want this entry before anything else we better just save its data.
1710 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, ".",
1711 true, STREAM_XATTR_SOLARIS);
1713 if ((fd = dup(attrdirfd)) == -1 ||
1714 (dirp = fdopendir(fd)) == (DIR *)NULL) {
1715 Mmsg2(jcr->errmsg, _("Unable to list the xattr space on file \"%s\": ERR=%s\n"),
1716 jcr->last_fname, be.bstrerror());
1717 Dmsg3(100, "Unable to fdopendir xattr space on file \"%s\" using fd %d: ERR=%s\n",
1718 jcr->last_fname, fd, be.bstrerror());
1724 * Walk the namespace.
1726 while (dp = readdir(dirp)) {
1728 * Skip only the toplevel . dir.
1730 if (!attr_parent && !strcmp(dp->d_name, "."))
1734 * Skip all .. directories
1736 if (!strcmp(dp->d_name, ".."))
1739 Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n",
1740 current_xattr_namespace, dp->d_name, jcr->last_fname);
1742 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1744 * We are not interested in read-only extensible attributes.
1746 if (!strcmp(dp->d_name, VIEW_READONLY)) {
1747 Dmsg3(400, "Skipping readonly extensible attributes %s%s on file \"%s\"\n",
1748 current_xattr_namespace, dp->d_name, jcr->last_fname);
1754 * We are only interested in read-write extensible attributes
1755 * when they contain non-transient values.
1757 if (!strcmp(dp->d_name, VIEW_READWRITE)) {
1759 * Determine if there are non-transient system attributes at the toplevel.
1760 * We need to provide a fd to the open file.
1762 if (!solaris_has_non_transient_extensible_attributes(filefd)) {
1763 Dmsg3(400, "Skipping transient extensible attributes %s%s on file \"%s\"\n",
1764 current_xattr_namespace, dp->d_name, jcr->last_fname);
1771 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1772 false, STREAM_XATTR_SOLARIS_SYS);
1775 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1780 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1781 false, STREAM_XATTR_SOLARIS);
1785 retval = bxattr_exit_ok;
1788 if (attrdirfd != -1)
1796 static bxattr_exit_code solaris_restore_xattr_acl(JCR *jcr, int fd, const char *attrname, char *acl_text)
1798 #ifdef HAVE_EXTENDED_ACL
1803 if ((error = acl_fromtext(acl_text, &aclp)) != 0) {
1804 Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"),
1806 return bxattr_exit_error;
1809 if ((fd != -1 && facl_set(fd, aclp) != 0) ||
1810 acl_set(attrname, aclp) != 0) {
1811 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1812 attrname, jcr->last_fname, be.bstrerror());
1813 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1814 attrname, jcr->last_fname, be.bstrerror());
1815 return bxattr_exit_error;
1821 return bxattr_exit_ok;
1823 #else /* HAVE_EXTENDED_ACL */
1825 aclent_t *acls = NULL;
1828 acls = aclfromtext(acl_text, &n);
1830 if ((fd != -1 && facl(fd, SETACL, n, acls) != 0) ||
1831 acl(attrname, SETACL, n, acls) != 0) {
1832 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1833 attrname, jcr->last_fname, be.bstrerror());
1834 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1835 attrname, jcr->last_fname, be.bstrerror());
1836 return bxattr_exit_error;
1843 return bxattr_exit_ok;
1845 #endif /* HAVE_EXTENDED_ACL */
1848 #endif /* HAVE_ACL */
1850 static bxattr_exit_code solaris_restore_xattrs(JCR *jcr, bool is_extensible)
1852 int fd, filefd = -1, attrdirfd = -1, attrfd = -1;
1853 int used_bytes, total_bytes, cnt;
1854 char *bp, *target_attrname, *attribs;
1855 char *linked_target = NULL;
1856 char *acl_text = NULL;
1860 struct timeval times[2];
1861 bxattr_exit_code retval = bxattr_exit_error;
1865 * Parse the xattr stream. First the part that is the same for all xattrs.
1868 total_bytes = jcr->xattr_data->content_length;
1871 * The name of the target xattr has a leading / we are not interested
1872 * in that so skip it when decoding the string. We always start a the /
1873 * of the xattr space anyway.
1875 target_attrname = jcr->xattr_data->content + 1;
1876 if ((bp = strchr(target_attrname, '\0')) == (char *)NULL ||
1877 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1883 * Open the file on which to restore the xattrs read-only.
1885 if ((filefd = open(jcr->last_fname, O_RDONLY | O_NONBLOCK)) < 0) {
1886 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1887 jcr->last_fname, be.bstrerror());
1888 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1889 jcr->last_fname, be.bstrerror());
1894 * Open the xattr naming space and make it the current working dir.
1896 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1897 Mmsg2(jcr->errmsg, _("Unable to open xattr space on file \"%s\": ERR=%s\n"),
1898 jcr->last_fname, be.bstrerror());
1899 Dmsg2(100, "Unable to open xattr space on file \"%s\": ERR=%s\n",
1900 jcr->last_fname, be.bstrerror());
1904 if (fchdir(attrdirfd) < 0) {
1905 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1906 jcr->last_fname, be.bstrerror());
1907 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1908 jcr->last_fname, attrdirfd, be.bstrerror());
1913 * Try to open the correct xattr subdir based on the target_attrname given.
1914 * e.g. check if its a subdir attrname. Each / in the string makes us go
1917 while ((bp = strchr(target_attrname, '/')) != (char *)NULL) {
1920 if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) {
1921 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
1922 target_attrname, jcr->last_fname, be.bstrerror());
1923 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
1924 target_attrname, jcr->last_fname, be.bstrerror());
1932 * Open the xattr naming space.
1934 if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1935 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1936 target_attrname, jcr->last_fname, be.bstrerror());
1937 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1938 target_attrname, jcr->last_fname, be.bstrerror());
1946 * Make the xattr space our current workingdir.
1948 if (fchdir(attrdirfd) < 0) {
1949 Mmsg3(jcr->errmsg, _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"),
1950 target_attrname, jcr->last_fname, be.bstrerror());
1951 Dmsg4(100, "Unable to fchdir to xattr space %s on file \"%s\" using fd %d: ERR=%s\n",
1952 target_attrname, jcr->last_fname, attrdirfd, be.bstrerror());
1956 target_attrname = ++bp;
1960 * Decode the attributes from the stream.
1962 decode_stat(attribs, &st, &inum);
1965 * Decode the next field (acl_text).
1967 if ((bp = strchr(attribs, '\0')) == (char *)NULL ||
1968 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1974 * Based on the filetype perform the correct action. We support most filetypes here, more
1975 * then the actual implementation on Solaris supports so some code may never get executed
1976 * due to limitations in the implementation.
1978 switch (st.st_mode & S_IFMT) {
1981 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1983 unlinkat(attrdirfd, target_attrname, 0);
1984 if (mkfifo(target_attrname, st.st_mode) < 0) {
1985 Mmsg3(jcr->errmsg, _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"),
1986 target_attrname, jcr->last_fname, be.bstrerror());
1987 Dmsg3(100, "Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n",
1988 target_attrname, jcr->last_fname, be.bstrerror());
1995 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1997 unlinkat(attrdirfd, target_attrname, 0);
1998 if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) {
1999 Mmsg3(jcr->errmsg, _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"),
2000 target_attrname, jcr->last_fname, be.bstrerror());
2001 Dmsg3(100, "Unable to mknod xattr %s on file \"%s\": ERR=%s\n",
2002 target_attrname, jcr->last_fname, be.bstrerror());
2008 * If its not the hidden_dir create the entry.
2009 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2011 if (strcmp(target_attrname, ".")) {
2012 unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR);
2013 if (mkdir(target_attrname, st.st_mode) < 0) {
2014 Jmsg3(jcr, M_WARNING, 0, _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"),
2015 target_attrname, jcr->last_fname, be.bstrerror());
2016 Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n",
2017 target_attrname, jcr->last_fname, be.bstrerror());
2024 * See if this is a hard linked file. e.g. inum != 0
2029 unlinkat(attrdirfd, target_attrname, 0);
2030 if (link(linked_target, target_attrname) < 0) {
2031 Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"),
2032 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2033 Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n",
2034 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2039 * Successfully restored xattr.
2041 retval = bxattr_exit_ok;
2044 if ((bp = strchr(acl_text, '\0')) == (char *)NULL ||
2045 (used_bytes = (bp - jcr->xattr_data->content)) >= total_bytes) {
2049 if (used_bytes < (total_bytes - 1))
2053 * Restore the actual xattr.
2055 if (!is_extensible) {
2056 unlinkat(attrdirfd, target_attrname, 0);
2059 if ((attrfd = openat(attrdirfd, target_attrname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) {
2060 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
2061 target_attrname, jcr->last_fname, be.bstrerror());
2062 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
2063 target_attrname, jcr->last_fname, be.bstrerror());
2069 * Restore the actual data.
2071 if (st.st_size > 0) {
2072 used_bytes = (data - jcr->xattr_data->content);
2073 cnt = total_bytes - used_bytes;
2076 * Do a sanity check, the st.st_size should be the same as the number of bytes
2077 * we have available as data of the stream.
2079 if (cnt != st.st_size) {
2080 Mmsg2(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n"),
2081 target_attrname, jcr->last_fname);
2082 Dmsg2(100, "Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n",
2083 target_attrname, jcr->last_fname);
2088 cnt = write(attrfd, data, cnt);
2090 Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"),
2091 target_attrname, jcr->last_fname, be.bstrerror());
2092 Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n",
2093 target_attrname, jcr->last_fname, be.bstrerror());
2099 cnt = total_bytes - used_bytes;
2105 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2109 if (symlink(linked_target, target_attrname) < 0) {
2110 Mmsg4(jcr->errmsg, _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"),
2111 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2112 Dmsg4(100, "Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n",
2113 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2118 * Successfully restored xattr.
2120 retval = bxattr_exit_ok;
2127 * Restore owner and acl for non extensible attributes.
2129 if (!is_extensible) {
2130 if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0) {
2134 * Gentile way of the system saying this type of xattr layering is not supported.
2135 * But as this is not an error we return a positive return value.
2137 retval = bxattr_exit_ok;
2140 retval = bxattr_exit_ok;
2143 Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"),
2144 target_attrname, jcr->last_fname, be.bstrerror());
2145 Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n",
2146 target_attrname, jcr->last_fname, be.bstrerror());
2153 if (acl_text && *acl_text)
2154 if (solaris_restore_xattr_acl(jcr, attrfd, target_attrname, acl_text) != bxattr_exit_ok)
2156 #endif /* HAVE_ACL */
2159 * For a non extensible attribute restore access and modification time on the xattr.
2161 if (!is_extensible) {
2162 times[0].tv_sec = st.st_atime;
2163 times[0].tv_usec = 0;
2164 times[1].tv_sec = st.st_mtime;
2165 times[1].tv_usec = 0;
2167 if (futimesat(attrdirfd, target_attrname, times) < 0) {
2168 Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"),
2169 target_attrname, jcr->last_fname, be.bstrerror());
2170 Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n",
2171 target_attrname, jcr->last_fname, be.bstrerror());
2177 * Successfully restored xattr.
2179 retval = bxattr_exit_ok;
2183 Mmsg1(jcr->errmsg, _("Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n"),
2185 Dmsg1(100, "Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n",
2192 if (attrdirfd != -1) {
2201 static bxattr_exit_code solaris_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2204 bxattr_exit_code retval = bxattr_exit_ok;
2207 * First see if extended attributes or extensible attributes are present.
2208 * If not just pretend things went ok.
2210 if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0) {
2211 jcr->xattr_data->nr_saved = 0;
2212 jcr->xattr_data->link_cache = New(alist(10, not_owned_by_alist));
2215 * As we change the cwd in the save function save the current cwd
2216 * for restore after return from the solaris_save_xattrs function.
2218 getcwd(cwd, sizeof(cwd));
2219 retval = solaris_save_xattrs(jcr, NULL, NULL);
2221 delete jcr->xattr_data->link_cache;
2222 jcr->xattr_data->link_cache = NULL;
2227 static bxattr_exit_code solaris_parse_xattr_streams(JCR *jcr, int stream)
2230 bool is_extensible = false;
2231 bxattr_exit_code retval;
2234 * First make sure we can restore xattr on the filesystem.
2237 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
2238 case STREAM_XATTR_SOLARIS_SYS:
2239 if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0) {
2240 Qmsg1(jcr, M_WARNING, 0,
2241 _("Failed to restore extensible attributes on file \"%s\"\n"),
2243 Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n",
2245 return bxattr_exit_error;
2248 is_extensible = true;
2251 case STREAM_XATTR_SOLARIS:
2252 if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0) {
2253 Qmsg1(jcr, M_WARNING, 0,
2254 _("Failed to restore extended attributes on file \"%s\"\n"),
2256 Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n",
2258 return bxattr_exit_error;
2262 return bxattr_exit_error;
2266 * As we change the cwd in the restore function save the current cwd
2267 * for restore after return from the solaris_restore_xattrs function.
2269 getcwd(cwd, sizeof(cwd));
2270 retval = solaris_restore_xattrs(jcr, is_extensible);
2277 * Function pointers to the build and parse function to use for these xattrs.
2279 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_xattr_streams;
2280 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = solaris_parse_xattr_streams;
2282 #endif /* defined(HAVE_SUN_OS) */
2285 * Entry points when compiled with support for XATTRs on a supported platform.
2287 bxattr_exit_code build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2289 if (os_build_xattr_streams) {
2290 return (*os_build_xattr_streams)(jcr, ff_pkt);
2292 return bxattr_exit_error;
2295 bxattr_exit_code parse_xattr_streams(JCR *jcr, int stream)
2299 if (os_parse_xattr_streams) {
2301 * See if we can parse this stream, and ifso give it a try.
2303 for (cnt = 0; cnt < sizeof(os_default_xattr_streams) / sizeof(int); cnt++) {
2304 if (os_default_xattr_streams[cnt] == stream) {
2305 return (*os_parse_xattr_streams)(jcr, stream);
2310 * Issue a warning and discard the message. But pretend the restore was ok.
2312 Jmsg2(jcr, M_WARNING, 0,
2313 _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"),
2314 jcr->last_fname, stream);
2315 return bxattr_exit_error;