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;
198 static bxattr_exit_code unserialize_xattr_stream(JCR *jcr, alist *xattr_value_list)
201 xattr_t *current_xattr;
202 bxattr_exit_code retval = bxattr_exit_ok;
205 * Parse the stream and call restore_xattr_on_file for each extended attribute.
207 * Start unserializing the data. We keep on looping while we have not
208 * unserialized all bytes in the stream.
210 unser_begin(jcr->xattr_data->content, jcr->xattr_data->content_length);
211 while (unser_length(jcr->xattr_data->content) < jcr->xattr_data->content_length) {
213 * First make sure the magic is present. This way we can easily catch corruption.
214 * Any missing MAGIC is fatal we do NOT try to continue.
217 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
218 unser_uint32(current_xattr->magic);
219 if (current_xattr->magic != XATTR_MAGIC) {
220 Mmsg1(jcr->errmsg, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"),
222 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);
255 xattr_value_list->append(current_xattr);
258 unser_end(jcr->xattr_data->content, jcr->xattr_data->content_length);
264 * This is a supported OS, See what kind of interface we should use.
265 * Start with the generic interface used by most OS-es.
267 #if defined(HAVE_DARWIN_OS) || \
268 defined(HAVE_LINUX_OS)
270 #if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)) || \
271 (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) || \
272 (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR))
273 #error "Missing either full support for the LXATTR or XATTR functions."
276 #ifdef HAVE_SYS_XATTR_H
277 #include <sys/xattr.h>
279 #error "Missing sys/xattr.h header file"
283 * Define the supported XATTR streams for this OS
285 #if defined(HAVE_DARWIN_OS)
286 static int os_default_xattr_streams[1] = { STREAM_XATTR_DARWIN };
287 static const char *xattr_acl_skiplist[2] = { "com.apple.system.Security", NULL };
288 static const char *xattr_skiplist[3] = { "com.apple.system.extendedsecurity", "com.apple.ResourceFork", NULL };
289 #elif defined(HAVE_LINUX_OS)
290 static int os_default_xattr_streams[1] = { STREAM_XATTR_LINUX };
291 static const char *xattr_acl_skiplist[2] = { "system.posix_acl_access", NULL };
292 static const char *xattr_skiplist[1] = { NULL };
296 * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
297 * listxattr, getxattr and setxattr with an extra options argument
298 * which mimics the l variants of the functions when we specify
299 * XATTR_NOFOLLOW as the options value.
301 #if defined(HAVE_DARWIN_OS)
302 #define llistxattr(path, list, size) listxattr((path), (list), (size), XATTR_NOFOLLOW)
303 #define lgetxattr(path, name, value, size) getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
304 #define lsetxattr(path, name, value, size, flags) setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
307 * Fallback to the non l-functions when those are not available.
309 #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
310 #define lgetxattr getxattr
312 #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
313 #define lsetxattr setxattr
315 #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
316 #define llistxattr listxattr
320 static bxattr_exit_code generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt)
323 char *xattr_list, *bp;
324 int cnt, xattr_count = 0;
325 int32_t xattr_list_len,
327 uint32_t expected_serialize_len = 0;
328 xattr_t *current_xattr;
329 alist *xattr_value_list = NULL;
330 bxattr_exit_code retval = bxattr_exit_error;
334 * First get the length of the available list with extended attributes.
336 xattr_list_len = llistxattr(jcr->last_fname, NULL, 0);
337 if (xattr_list_len < 0) {
340 return bxattr_exit_ok;
342 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
343 jcr->last_fname, be.bstrerror());
344 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
345 jcr->last_fname, be.bstrerror());
346 return bxattr_exit_error;
348 } else if (xattr_list_len == 0) {
349 return bxattr_exit_ok;
353 * Allocate room for the extented attribute list.
355 xattr_list = (char *)malloc(xattr_list_len + 1);
356 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
359 * Get the actual list of extended attributes names for a file.
361 xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len);
362 if (xattr_list_len < 0) {
365 retval = bxattr_exit_ok;
368 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
369 jcr->last_fname, be.bstrerror());
370 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
371 jcr->last_fname, be.bstrerror());
375 xattr_list[xattr_list_len] = '\0';
377 xattr_value_list = New(alist(10, not_owned_by_alist));
380 * Walk the list of extended attributes names and retrieve the data.
381 * We already count the bytes needed for serializing the stream later on.
384 while ((bp - xattr_list) + 1 < xattr_list_len) {
388 * On some OSes you also get the acls in the extented attribute list.
389 * So we check if we are already backing up acls and if we do we
390 * don't store the extended attribute with the same info.
392 if (ff_pkt->flags & FO_ACL) {
393 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
394 if (!strcmp(bp, xattr_acl_skiplist[cnt])) {
402 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
405 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
406 if (!strcmp(bp, xattr_skiplist[cnt])) {
414 bp = strchr(bp, '\0') + 1;
419 * Each xattr valuepair starts with a magic so we can parse it easier.
421 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
422 current_xattr->magic = XATTR_MAGIC;
423 expected_serialize_len += sizeof(current_xattr->magic);
426 * Allocate space for storing the name.
428 current_xattr->name_length = strlen(bp);
429 current_xattr->name = (char *)malloc(current_xattr->name_length);
430 memcpy((caddr_t)current_xattr->name, (caddr_t)bp, current_xattr->name_length);
432 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
435 * First see how long the value is for the extended attribute.
437 xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0);
438 if (xattr_value_len < 0) {
441 retval = bxattr_exit_ok;
442 free(current_xattr->name);
446 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
447 jcr->last_fname, be.bstrerror());
448 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
449 jcr->last_fname, be.bstrerror());
450 free(current_xattr->name);
457 * Allocate space for storing the value.
459 current_xattr->value = (char *)malloc(xattr_value_len);
460 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
462 xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
463 if (xattr_value_len < 0) {
466 retval = bxattr_exit_ok;
467 free(current_xattr->value);
468 free(current_xattr->name);
472 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
473 jcr->last_fname, be.bstrerror());
474 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
475 jcr->last_fname, be.bstrerror());
476 free(current_xattr->value);
477 free(current_xattr->name);
484 * Store the actual length of the value.
486 current_xattr->value_length = xattr_value_len;
487 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
490 * Protect ourself against things getting out of hand.
492 if (expected_serialize_len >= MAX_XATTR_STREAM) {
493 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
494 jcr->last_fname, MAX_XATTR_STREAM);
495 free(current_xattr->value);
496 free(current_xattr->name);
501 xattr_value_list->append(current_xattr);
503 bp = strchr(bp, '\0') + 1;
507 xattr_list = (char *)NULL;
510 * If we found any xattr send them to the SD.
512 if (xattr_count > 0) {
514 * Serialize the datastream.
516 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
517 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
519 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
524 xattr_drop_internal_table(xattr_value_list);
527 * Send the datastream to the SD.
529 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
531 xattr_drop_internal_table(xattr_value_list);
533 return bxattr_exit_ok;
540 if (xattr_value_list) {
541 xattr_drop_internal_table(xattr_value_list);
546 static bxattr_exit_code generic_xattr_parse_streams(JCR *jcr, int stream)
548 xattr_t *current_xattr;
549 alist *xattr_value_list;
552 xattr_value_list = New(alist(10, not_owned_by_alist));
554 if (unserialize_xattr_stream(jcr, xattr_value_list) != bxattr_exit_ok) {
555 xattr_drop_internal_table(xattr_value_list);
556 return bxattr_exit_error;
559 foreach_alist(current_xattr, xattr_value_list) {
560 if (lsetxattr(jcr->last_fname, current_xattr->name, current_xattr->value, current_xattr->value_length, 0) != 0) {
565 Mmsg2(jcr->errmsg, _("lsetxattr error on file \"%s\": ERR=%s\n"),
566 jcr->last_fname, be.bstrerror());
567 Dmsg2(100, "lsetxattr error file=%s ERR=%s\n",
568 jcr->last_fname, be.bstrerror());
574 xattr_drop_internal_table(xattr_value_list);
575 return bxattr_exit_ok;
579 xattr_drop_internal_table(xattr_value_list);
580 return bxattr_exit_error;
584 * For all these os-es setup the build and parse function pointer to the generic functions.
586 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = generic_xattr_build_streams;
587 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = generic_xattr_parse_streams;
589 #elif defined(HAVE_FREEBSD_OS) || \
590 defined(HAVE_NETBSD_OS) || \
591 defined(HAVE_OPENBSD_OS)
593 #if !defined(HAVE_EXTATTR_GET_LINK) || \
594 !defined(HAVE_EXTATTR_SET_LINK) || \
595 !defined(HAVE_EXTATTR_LIST_LINK) || \
596 !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \
597 !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE)
598 #error "Missing full support for the extattr functions."
601 #ifdef HAVE_SYS_EXTATTR_H
602 #include <sys/extattr.h>
604 #error "Missing sys/extattr.h header file"
607 #ifdef HAVE_LIBUTIL_H
611 #if defined(HAVE_FREEBSD_OS)
612 static int os_default_xattr_streams[1] = { STREAM_XATTR_FREEBSD };
613 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
614 static const char *xattr_acl_skiplist[1] = { NULL };
615 static const char *xattr_skiplist[1] = { NULL };
616 #elif defined(HAVE_NETBSD_OS)
617 static int os_default_xattr_streams[1] = { STREAM_XATTR_NETBSD };
618 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
619 static const char *xattr_acl_skiplist[1] = { NULL };
620 static const char *xattr_skiplist[1] = { NULL };
621 #elif defined(HAVE_OPENBSD_OS)
622 static int os_default_xattr_streams[1] = { STREAM_XATTR_OPENBSD };
623 static int os_default_xattr_namespaces[2] = { EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM };
624 static const char *xattr_acl_skiplist[1] = { NULL };
625 static const char *xattr_skiplist[1] = { NULL };
628 static bxattr_exit_code bsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
632 int cnt, index, xattr_count = 0;
633 int32_t xattr_list_len,
635 uint32_t expected_serialize_len = 0;
636 unsigned int namespace_index;
638 char *current_attrnamespace, current_attrname[BUFSIZ], current_attrtuple[BUFSIZ];
639 xattr_t *current_xattr;
640 alist *xattr_value_list = NULL;
641 bxattr_exit_code retval = bxattr_exit_error;
644 xattr_value_list = New(alist(10, not_owned_by_alist));
647 * Loop over all available xattr namespaces.
649 for (namespace_index = 0; namespace_index < sizeof(os_default_xattr_namespaces) / sizeof(int); namespace_index++) {
650 attrnamespace = os_default_xattr_namespaces[namespace_index];
653 * First get the length of the available list with extended attributes.
655 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, NULL, 0);
656 if (xattr_list_len < 0) {
659 retval = bxattr_exit_ok;
662 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
663 jcr->last_fname, be.bstrerror());
664 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
665 jcr->last_fname, be.bstrerror());
668 } else if (xattr_list_len == 0) {
673 * Allocate room for the extented attribute list.
675 xattr_list = (char *)malloc(xattr_list_len + 1);
676 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
679 * Get the actual list of extended attributes names for a file.
681 xattr_list_len = extattr_list_link(jcr->last_fname, attrnamespace, xattr_list, xattr_list_len);
682 if (xattr_list_len < 0) {
685 retval = bxattr_exit_ok;
688 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"),
689 jcr->last_fname, be.bstrerror());
690 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
691 jcr->last_fname, be.bstrerror());
695 xattr_list[xattr_list_len] = '\0';
698 * Walk the list of extended attributes names and retrieve the data.
699 * We already count the bytes needed for serializing the stream later on.
701 for (index = 0; index < xattr_list_len; index += xattr_list[index] + 1) {
703 bsnprintf(current_attrname, sizeof(current_attrname), "%*.*s",
704 xattr_list[index], xattr_list[index], xattr_list + (index + 1));
707 * First make a xattr tuple of the current namespace and the name of the xattr.
708 * e.g. something like user.<attrname> or system.<attrname>
710 if (extattr_namespace_to_string(attrnamespace, ¤t_attrnamespace) != 0) {
711 Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"),
712 attrnamespace, jcr->last_fname);
713 Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n",
714 attrnamespace, jcr->last_fname);
719 * print the current name into the buffer as its not null terminated we need to
720 * use the length encoded in the string for copying only the needed bytes.
722 bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s", current_attrnamespace, current_attrname);
725 * On some OSes you also get the acls in the extented attribute list.
726 * So we check if we are already backing up acls and if we do we
727 * don't store the extended attribute with the same info.
729 if (ff_pkt->flags & FO_ACL) {
730 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
731 if (!strcmp(current_attrtuple, xattr_acl_skiplist[cnt])) {
739 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
742 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
743 if (!strcmp(current_attrtuple, xattr_skiplist[cnt])) {
755 * Each xattr valuepair starts with a magic so we can parse it easier.
757 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
758 current_xattr->magic = XATTR_MAGIC;
759 expected_serialize_len += sizeof(current_xattr->magic);
762 * Allocate space for storing the name.
764 current_xattr->name_length = strlen(current_attrtuple);
765 current_xattr->name = (char *)malloc(current_xattr->name_length);
766 memcpy((caddr_t)current_xattr->name, (caddr_t)current_attrtuple, current_xattr->name_length);
768 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
771 * First see how long the value is for the extended attribute.
773 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, NULL, 0);
774 if (xattr_value_len < 0) {
777 retval = bxattr_exit_ok;
778 free(current_xattr->name);
782 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
783 jcr->last_fname, be.bstrerror());
784 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
785 jcr->last_fname, be.bstrerror());
786 free(current_xattr->name);
793 * Allocate space for storing the value.
795 current_xattr->value = (char *)malloc(xattr_value_len);
796 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
798 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, current_xattr->value, xattr_value_len);
799 if (xattr_value_len < 0) {
802 retval = bxattr_exit_ok;
803 free(current_xattr->value);
804 free(current_xattr->name);
808 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
809 jcr->last_fname, be.bstrerror());
810 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
811 jcr->last_fname, be.bstrerror());
812 free(current_xattr->value);
813 free(current_xattr->name);
820 * Store the actual length of the value.
822 current_xattr->value_length = xattr_value_len;
823 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
826 * Protect ourself against things getting out of hand.
828 if (expected_serialize_len >= MAX_XATTR_STREAM) {
829 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
830 jcr->last_fname, MAX_XATTR_STREAM);
831 free(current_xattr->value);
832 free(current_xattr->name);
837 xattr_value_list->append(current_xattr);
842 * We are done with this xattr list.
845 xattr_list = (char *)NULL;
849 * If we found any xattr send them to the SD.
851 if (xattr_count > 0) {
853 * Serialize the datastream.
855 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
856 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
858 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
863 xattr_drop_internal_table(xattr_value_list);
864 xattr_value_list = NULL;
867 * Send the datastream to the SD.
869 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
871 xattr_drop_internal_table(xattr_value_list);
872 xattr_value_list = NULL;
874 return bxattr_exit_ok;
881 if (xattr_value_list) {
882 xattr_drop_internal_table(xattr_value_list);
883 xattr_value_list = NULL;
888 static bxattr_exit_code bsd_parse_xattr_streams(JCR *jcr, int stream)
890 xattr_t *current_xattr;
891 alist *xattr_value_list;
892 int current_attrnamespace, cnt;
893 char *attrnamespace, *attrname;
896 xattr_value_list = New(alist(10, not_owned_by_alist));
898 if (unserialize_xattr_stream(jcr, xattr_value_list) != bxattr_exit_ok) {
899 xattr_drop_internal_table(xattr_value_list);
900 return bxattr_exit_error;
903 foreach_alist(current_xattr, xattr_value_list) {
905 * Try splitting the xattr_name into a namespace and name part.
906 * The splitting character is a .
908 attrnamespace = current_xattr->name;
909 if ((attrname = strchr(attrnamespace, '.')) == (char *)NULL) {
910 Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"),
911 current_xattr->name, jcr->last_fname);
912 Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n",
913 current_xattr->name, jcr->last_fname);
919 * Make sure the attrnamespace makes sense.
921 if (extattr_string_to_namespace(attrnamespace, ¤t_attrnamespace) != 0) {
922 Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"),
923 attrnamespace, jcr->last_fname);
924 Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n",
925 attrnamespace, jcr->last_fname);
930 * Try restoring the extended attribute.
932 cnt = extattr_set_link(jcr->last_fname, current_attrnamespace,
933 attrname, current_xattr->value, current_xattr->value_length);
934 if (cnt < 0 || cnt != current_xattr->value_length) {
940 Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"),
941 jcr->last_fname, be.bstrerror());
942 Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n",
943 jcr->last_fname, be.bstrerror());
950 xattr_drop_internal_table(xattr_value_list);
951 return bxattr_exit_ok;
955 xattr_drop_internal_table(xattr_value_list);
956 return bxattr_exit_error;
960 * Function pointers to the build and parse function to use for these xattrs.
962 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = bsd_build_xattr_streams;
963 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = bsd_parse_xattr_streams;
965 #elif defined(HAVE_SUN_OS)
967 * Solaris extended attributes were introduced in Solaris 9
970 * Solaris extensible attributes were introduced in OpenSolaris
971 * by PSARC 2007/315 Solaris extensible attributes are also
972 * sometimes called extended system attributes.
974 * man fsattr(5) on Solaris gives a wealth of info. The most
975 * important bits are:
977 * Attributes are logically supported as files within the file
978 * system. The file system is therefore augmented with an
979 * orthogonal name space of file attributes. Any file (includ-
980 * ing attribute files) can have an arbitrarily deep attribute
981 * tree associated with it. Attribute values are accessed by
982 * file descriptors obtained through a special attribute inter-
983 * face. This logical view of "attributes as files" allows the
984 * leveraging of existing file system interface functionality
985 * to support the construction, deletion, and manipulation of
988 * The special files "." and ".." retain their accustomed
989 * semantics within the attribute hierarchy. The "." attribute
990 * file refers to the current directory and the ".." attribute
991 * file refers to the parent directory. The unnamed directory
992 * at the head of each attribute tree is considered the "child"
993 * of the file it is associated with and the ".." file refers
994 * to the associated file. For any non-directory file with
995 * attributes, the ".." entry in the unnamed directory refers
996 * to a file that is not a directory.
998 * Conceptually, the attribute model is fully general. Extended
999 * attributes can be any type of file (doors, links, direc-
1000 * tories, and so forth) and can even have their own attributes
1001 * (fully recursive). As a result, the attributes associated
1002 * with a file could be an arbitrarily deep directory hierarchy
1003 * where each attribute could have an equally complex attribute
1004 * tree associated with it. Not all implementations are able
1005 * to, or want to, support the full model. Implementation are
1006 * therefore permitted to reject operations that are not sup-
1007 * ported. For example, the implementation for the UFS file
1008 * system allows only regular files as attributes (for example,
1009 * no sub-directories) and rejects attempts to place attributes
1012 * The following list details the operations that are rejected
1013 * in the current implementation:
1015 * link Any attempt to create links between
1016 * attribute and non-attribute space
1017 * is rejected to prevent security-
1018 * related or otherwise sensitive
1019 * attributes from being exposed, and
1020 * therefore manipulable, as regular
1023 * rename Any attempt to rename between
1024 * attribute and non-attribute space
1025 * is rejected to prevent an already
1026 * linked file from being renamed and
1027 * thereby circumventing the link res-
1030 * mkdir, symlink, mknod Any attempt to create a "non-
1031 * regular" file in attribute space is
1032 * rejected to reduce the functional-
1033 * ity, and therefore exposure and
1034 * risk, of the initial implementa-
1037 * The entire available name space has been allocated to "gen-
1038 * eral use" to bring the implementation in line with the NFSv4
1039 * draft standard [NFSv4]. That standard defines "named attri-
1040 * butes" (equivalent to Solaris Extended Attributes) with no
1041 * naming restrictions. All Sun applications making use of
1042 * opaque extended attributes will use the prefix "SUNW".
1045 #ifdef HAVE_SYS_ATTR_H
1046 #include <sys/attr.h>
1053 #ifdef HAVE_SYS_NVPAIR_H
1054 #include <sys/nvpair.h>
1057 #ifdef HAVE_SYS_ACL_H
1058 #include <sys/acl.h>
1061 #if !defined(HAVE_OPENAT) ||
1062 !defined(HAVE_UNKINKAT) ||
1063 !defined(HAVE_FCHOWNAT) ||
1064 !defined(HAVE_FUTIMESAT)
1065 #error "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function"
1069 * Define the supported XATTR streams for this OS
1071 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1072 static int os_default_xattr_streams[2] = { STREAM_XATTR_SOLARIS, STREAM_XATTR_SOLARIS_SYS};
1074 static int os_default_xattr_streams[1] = { STREAM_XATTR_SOLARIS };
1075 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
1078 * This code creates a temporary cache with entries for each xattr which has
1079 * a link count > 1 (which indicates it has one or more hard linked counterpart(s))
1081 static xattr_link_cache_entry_t *find_xattr_link_cache_entry(JCR *jcr, ino_t inum)
1083 xattr_link_cache_entry_t *ptr;
1085 foreach_alist(ptr, jcr->xattr_data->link_cache) {
1086 if (ptr && ptr->inum == inum) {
1093 static void add_xattr_link_cache_entry(JCR *jcr, ino_t inum, char *target)
1095 xattr_link_cache_entry_t *ptr;
1097 ptr = (xattr_link_cache_entry_t *)malloc(sizeof(xattr_link_cache_entry_t));
1098 memset((caddr_t)ptr, 0, sizeof(xattr_link_cache_entry_t));
1100 bstrncpy(ptr->target, target, sizeof(ptr->target));
1101 jcr->xattr_data->link_cache->append(ptr);
1104 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1106 * This function returns true if a non default extended system attribute
1107 * list is associated with fd and returns false when an error has occured
1108 * or when only extended system attributes other than archive,
1109 * av_modified or crtime are set.
1111 * The function returns true for the following cases:
1113 * - any extended system attribute other than the default attributes
1114 * ('archive', 'av_modified' and 'crtime') is set
1115 * - nvlist has NULL name string
1116 * - nvpair has data type of 'nvlist'
1117 * - default data type.
1119 static bool solaris_has_non_transient_extensible_attributes(int fd)
1127 bool retval = false;
1129 if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
1134 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
1135 name = nvpair_name(pair);
1138 fattr = name_to_attr(name);
1144 type = nvpair_type(pair);
1146 case DATA_TYPE_BOOLEAN_VALUE:
1147 if (nvpair_value_boolean_value(pair, &value) != 0) {
1150 if (value && fattr != F_ARCHIVE &&
1151 fattr != F_AV_MODIFIED) {
1156 case DATA_TYPE_UINT64_ARRAY:
1157 if (fattr != F_CRTIME) {
1162 case DATA_TYPE_NVLIST:
1170 if (response != NULL) {
1171 nvlist_free(response);
1175 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1177 #if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL)
1179 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1180 * There is no need to store those acls as we already store the stat bits too.
1182 static bool acl_is_trivial(int count, aclent_t *entries)
1187 for (n = 0; n < count; n++) {
1189 if (!(ace->a_type == USER_OBJ ||
1190 ace->a_type == GROUP_OBJ ||
1191 ace->a_type == OTHER_OBJ ||
1192 ace->a_type == CLASS_OBJ))
1197 #endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */
1199 static bxattr_exit_code solaris_save_xattr_acl(JCR *jcr, int fd, const char *attrname, char **acl_text)
1202 #ifdef HAVE_EXTENDED_ACL
1208 * See if this attribute has an ACL
1210 if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0) ||
1211 pathconf(attrname, _PC_ACL_ENABLED) > 0) {
1213 * See if there is a non trivial acl on the file.
1215 if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0) ||
1216 acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) {
1219 return bxattr_exit_ok;
1221 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1222 attrname, jcr->last_fname, be.bstrerror());
1223 Dmsg3(100, "facl_get/acl_get of xattr %s on \"%s\" failed: ERR=%s\n",
1224 attrname, jcr->last_fname, be.bstrerror());
1225 return bxattr_exit_error;
1230 #if defined(ACL_SID_FMT)
1232 * New format flag added in newer Solaris versions.
1234 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1236 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1237 #endif /* ACL_SID_FMT */
1239 *acl_text = acl_totext(aclp, flags);
1247 return bxattr_exit_ok;
1248 #else /* HAVE_EXTENDED_ACL */
1250 aclent_t *acls = NULL;
1254 * See if this attribute has an ACL
1257 n = facl(fd, GETACLCNT, 0, NULL);
1259 n = acl(attrname, GETACLCNT, 0, NULL);
1262 if (n >= MIN_ACL_ENTRIES) {
1263 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1264 if ((fd != -1 && facl(fd, GETACL, n, acls) != n) ||
1265 acl(attrname, GETACL, n, acls) != n) {
1269 return bxattr_exit_ok;
1271 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1272 attrname, jcr->last_fname, be.bstrerror());
1273 Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n",
1274 attrname, jcr->last_fname, be.bstrerror());
1276 return bxattr_exit_error;
1281 * See if there is a non trivial acl on the file.
1283 if (!acl_is_trivial(n, acls)) {
1284 if ((*acl_text = acltotext(acls, n)) == NULL) {
1285 Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"),
1286 attrname, jcr->last_fname, be.bstrerror());
1287 Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n",
1288 attrname, jcr->last_fname, be.bstrerror());
1290 return bxattr_exit_error;
1300 return bxattr_exit_ok;
1301 #endif /* HAVE_EXTENDED_ACL */
1303 #else /* HAVE_ACL */
1304 return bxattr_exit_ok;
1305 #endif /* HAVE_ACL */
1309 * Forward declaration for recursive function call.
1311 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent);
1314 * Save an extended or extensible attribute.
1315 * This is stored as an opaque stream of bytes with the following encoding:
1317 * <xattr_name>\0<stat_buffer>\0<acl_string>\0<actual_xattr_data>
1319 * or for a hardlinked or symlinked attribute
1321 * <xattr_name>\0<stat_buffer>\0<xattr_link_source>\0
1323 * xattr_name can be a subpath relative to the file the xattr is on.
1324 * stat_buffer is the string representation of the stat struct.
1325 * acl_string is an acl text when a non trivial acl is set on the xattr.
1326 * actual_xattr_data is the content of the xattr file.
1328 static bxattr_exit_code solaris_save_xattr(JCR *jcr, int fd, const char *xattr_namespace,
1329 const char *attrname, bool toplevel_hidden_dir, int stream)
1334 xattr_link_cache_entry_t *xlce;
1335 char target_attrname[PATH_MAX];
1336 char link_source[PATH_MAX];
1337 char *acl_text = NULL;
1338 char attribs[MAXSTRING];
1339 char buffer[BUFSIZ];
1340 bxattr_exit_code retval = bxattr_exit_error;
1343 bsnprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace, attrname);
1346 * Get the stats of the extended or extensible attribute.
1348 if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) {
1351 retval = bxattr_exit_ok;
1354 Mmsg3(jcr->errmsg, _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"),
1355 target_attrname, jcr->last_fname, be.bstrerror());
1356 Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n",
1357 target_attrname, jcr->last_fname, be.bstrerror());
1363 * Based on the filetype perform the correct action. We support most filetypes here, more
1364 * then the actual implementation on Solaris supports so some code may never get executed
1365 * due to limitations in the implementation.
1367 switch (st.st_mode & S_IFMT) {
1372 * Get any acl on the xattr.
1374 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1378 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1379 * Encode the stat struct into an ASCII representation.
1381 encode_stat(attribs, &st, 0, stream);
1382 cnt = bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c",
1383 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1387 * Get any acl on the xattr.
1389 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1393 * See if this is the toplevel_hidden_dir being saved.
1395 if (toplevel_hidden_dir) {
1397 * Save the data for later storage when we encounter a real xattr. We store the data
1398 * in the jcr->xattr_data->content buffer and flush that just before sending out the
1399 * first real xattr. Encode the stat struct into an ASCII representation and jump
1400 * out of the function.
1402 encode_stat(attribs, &st, 0, stream);
1403 cnt = bsnprintf(buffer, sizeof(buffer),
1405 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1406 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1407 jcr->xattr_data->content_length = cnt;
1411 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1412 * Encode the stat struct into an ASCII representation.
1414 encode_stat(attribs, &st, 0, stream);
1415 cnt = bsnprintf(buffer, sizeof(buffer),
1417 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1422 * If this is a hardlinked file check the inode cache for a hit.
1424 if (st.st_nlink > 1) {
1426 * See if the cache already knows this inode number.
1428 if ((xlce = find_xattr_link_cache_entry(jcr, st.st_ino)) != NULL) {
1430 * Generate a xattr encoding with the reference to the target in there.
1432 encode_stat(attribs, &st, st.st_ino, stream);
1433 cnt = bsnprintf(buffer, sizeof(buffer),
1435 target_attrname, 0, attribs, 0, xlce->target, 0);
1436 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1437 jcr->xattr_data->content_length = cnt;
1438 retval = send_xattr_stream(jcr, stream);
1441 * For a hard linked file we are ready now, no need to recursively save the attributes.
1447 * Store this hard linked file in the cache.
1448 * Store the name relative to the top level xattr space.
1450 add_xattr_link_cache_entry(jcr, st.st_ino, target_attrname + 1);
1454 * Get any acl on the xattr.
1456 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok) {
1461 * Encode the stat struct into an ASCII representation.
1463 encode_stat(attribs, &st, 0, stream);
1464 cnt = bsnprintf(buffer, sizeof(buffer),
1466 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1469 * Open the extended or extensible attribute file.
1471 if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) {
1474 retval = bxattr_exit_ok;
1477 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on \"%s\": ERR=%s\n"),
1478 target_attrname, jcr->last_fname, be.bstrerror());
1479 Dmsg3(100, "openat of xattr %s on \"%s\" failed: ERR=%s\n",
1480 target_attrname, jcr->last_fname, be.bstrerror());
1487 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1488 * Encode the stat struct into an ASCII representation.
1490 if (readlink(attrname, link_source, sizeof(link_source)) < 0) {
1493 retval = bxattr_exit_ok;
1496 Mmsg3(jcr->errmsg, _("Unable to read symlin %s on \"%s\": ERR=%s\n"),
1497 target_attrname, jcr->last_fname, be.bstrerror());
1498 Dmsg3(100, "readlink of xattr %s on \"%s\" failed: ERR=%s\n",
1499 target_attrname, jcr->last_fname, be.bstrerror());
1505 * Generate a xattr encoding with the reference to the target in there.
1507 encode_stat(attribs, &st, st.st_ino, stream);
1508 cnt = bsnprintf(buffer, sizeof(buffer),
1510 target_attrname, 0, attribs, 0, link_source, 0);
1511 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1512 jcr->xattr_data->content_length = cnt;
1513 retval = send_xattr_stream(jcr, stream);
1515 if (retval == bxattr_exit_ok) {
1516 jcr->xattr_data->nr_saved++;
1520 * For a soft linked file we are ready now, no need to recursively save the attributes.
1528 * See if this is the first real xattr being saved.
1529 * If it is save the toplevel_hidden_dir attributes first.
1530 * This is easy as its stored already in the jcr->xattr_data->content buffer.
1532 if (jcr->xattr_data->nr_saved == 0) {
1533 retval = send_xattr_stream(jcr, STREAM_XATTR_SOLARIS);
1534 if (retval != bxattr_exit_ok) {
1537 jcr->xattr_data->nr_saved++;
1540 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1541 jcr->xattr_data->content_length = cnt;
1544 * Only dump the content of regular files.
1546 switch (st.st_mode & S_IFMT) {
1548 if (st.st_size > 0) {
1550 * Protect ourself against things getting out of hand.
1552 if (st.st_size >= MAX_XATTR_STREAM) {
1553 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
1554 jcr->last_fname, MAX_XATTR_STREAM);
1558 while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) {
1559 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, jcr->xattr_data->content_length + cnt);
1560 memcpy(jcr->xattr_data->content + jcr->xattr_data->content_length, buffer, cnt);
1561 jcr->xattr_data->content_length += cnt;
1565 Mmsg2(jcr->errmsg, _("Unable to read content of xattr %s on file \"%s\"\n"),
1566 target_attrname, jcr->last_fname);
1567 Dmsg2(100, "read of data from xattr %s on \"%s\" failed\n",
1568 target_attrname, jcr->last_fname);
1579 retval = send_xattr_stream(jcr, stream);
1580 if (retval == bxattr_exit_ok) {
1581 jcr->xattr_data->nr_saved++;
1586 * Recursivly call solaris_save_extended_attributes for archiving the attributes
1587 * available on this extended attribute.
1590 retval = solaris_save_xattrs(jcr, xattr_namespace, attrname);
1593 * The recursive call could change our working dir so change back to the wanted workdir.
1595 if (fchdir(fd) < 0) {
1598 retval = bxattr_exit_ok;
1601 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"),
1602 jcr->last_fname, be.bstrerror());
1603 Dmsg3(100, "Unable to fchdir to xattr space of file \"%s\" using fd %d: ERR=%s\n",
1604 jcr->last_fname, fd, be.bstrerror());
1620 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent)
1623 int fd, filefd = -1, attrdirfd = -1;
1626 char current_xattr_namespace[PATH_MAX];
1627 bxattr_exit_code retval = bxattr_exit_error;
1631 * Determine what argument to use. Use attr_parent when set
1632 * (recursive call) or jcr->last_fname for first call. Also save
1633 * the current depth of the xattr_space we are in.
1637 if (xattr_namespace) {
1638 bsnprintf(current_xattr_namespace, sizeof(current_xattr_namespace), "%s%s/",
1639 xattr_namespace, attr_parent);
1641 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1644 name = jcr->last_fname;
1645 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1649 * Open the file on which to save the xattrs read-only.
1651 if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
1654 retval = bxattr_exit_ok;
1657 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1658 jcr->last_fname, be.bstrerror());
1659 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1660 jcr->last_fname, be.bstrerror());
1666 * Open the xattr naming space.
1668 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1672 * Gentile way of the system saying this type of xattr layering is not supported.
1673 * Which is not problem we just forget about this this xattr.
1674 * But as this is not an error we return a positive return value.
1676 retval = bxattr_exit_ok;
1679 retval = bxattr_exit_ok;
1682 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1683 name, jcr->last_fname, be.bstrerror());
1684 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1685 name, jcr->last_fname, be.bstrerror());
1691 * We need to change into the attribute directory to determine if each of the
1692 * attributes should be saved.
1694 if (fchdir(attrdirfd) < 0) {
1695 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1696 jcr->last_fname, be.bstrerror());
1697 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1698 jcr->last_fname, attrdirfd, be.bstrerror());
1703 * Save the data of the toplevel xattr hidden_dir. We save this one before anything
1704 * else because the readdir returns "." entry after the extensible attr entry.
1705 * And as we want this entry before anything else we better just save its data.
1708 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, ".",
1709 true, STREAM_XATTR_SOLARIS);
1711 if ((fd = dup(attrdirfd)) == -1 ||
1712 (dirp = fdopendir(fd)) == (DIR *)NULL) {
1713 Mmsg2(jcr->errmsg, _("Unable to list the xattr space on file \"%s\": ERR=%s\n"),
1714 jcr->last_fname, be.bstrerror());
1715 Dmsg3(100, "Unable to fdopendir xattr space on file \"%s\" using fd %d: ERR=%s\n",
1716 jcr->last_fname, fd, be.bstrerror());
1722 * Walk the namespace.
1724 while (dp = readdir(dirp)) {
1726 * Skip only the toplevel . dir.
1728 if (!attr_parent && !strcmp(dp->d_name, "."))
1732 * Skip all .. directories
1734 if (!strcmp(dp->d_name, ".."))
1737 Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n",
1738 current_xattr_namespace, dp->d_name, jcr->last_fname);
1740 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1742 * We are not interested in read-only extensible attributes.
1744 if (!strcmp(dp->d_name, VIEW_READONLY)) {
1745 Dmsg3(400, "Skipping readonly extensible attributes %s%s on file \"%s\"\n",
1746 current_xattr_namespace, dp->d_name, jcr->last_fname);
1752 * We are only interested in read-write extensible attributes
1753 * when they contain non-transient values.
1755 if (!strcmp(dp->d_name, VIEW_READWRITE)) {
1757 * Determine if there are non-transient system attributes at the toplevel.
1758 * We need to provide a fd to the open file.
1760 if (!solaris_has_non_transient_extensible_attributes(filefd)) {
1761 Dmsg3(400, "Skipping transient extensible attributes %s%s on file \"%s\"\n",
1762 current_xattr_namespace, dp->d_name, jcr->last_fname);
1769 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1770 false, STREAM_XATTR_SOLARIS_SYS);
1773 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1778 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1779 false, STREAM_XATTR_SOLARIS);
1783 retval = bxattr_exit_ok;
1786 if (attrdirfd != -1)
1794 static bxattr_exit_code solaris_restore_xattr_acl(JCR *jcr, int fd, const char *attrname, char *acl_text)
1796 #ifdef HAVE_EXTENDED_ACL
1801 if ((error = acl_fromtext(acl_text, &aclp)) != 0) {
1802 Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"),
1804 return bxattr_exit_error;
1807 if ((fd != -1 && facl_set(fd, aclp) != 0) ||
1808 acl_set(attrname, aclp) != 0) {
1809 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1810 attrname, jcr->last_fname, be.bstrerror());
1811 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1812 attrname, jcr->last_fname, be.bstrerror());
1813 return bxattr_exit_error;
1819 return bxattr_exit_ok;
1821 #else /* HAVE_EXTENDED_ACL */
1823 aclent_t *acls = NULL;
1826 acls = aclfromtext(acl_text, &n);
1828 if ((fd != -1 && facl(fd, SETACL, n, acls) != 0) ||
1829 acl(attrname, SETACL, n, acls) != 0) {
1830 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1831 attrname, jcr->last_fname, be.bstrerror());
1832 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1833 attrname, jcr->last_fname, be.bstrerror());
1834 return bxattr_exit_error;
1841 return bxattr_exit_ok;
1843 #endif /* HAVE_EXTENDED_ACL */
1846 #endif /* HAVE_ACL */
1848 static bxattr_exit_code solaris_restore_xattrs(JCR *jcr, bool is_extensible)
1850 int fd, filefd = -1, attrdirfd = -1, attrfd = -1;
1851 int used_bytes, total_bytes, cnt;
1852 char *bp, *target_attrname, *attribs;
1853 char *linked_target = NULL;
1854 char *acl_text = NULL;
1858 struct timeval times[2];
1859 bxattr_exit_code retval = bxattr_exit_error;
1863 * Parse the xattr stream. First the part that is the same for all xattrs.
1866 total_bytes = jcr->xattr_data->content_length;
1869 * The name of the target xattr has a leading / we are not interested
1870 * in that so skip it when decoding the string. We always start a the /
1871 * of the xattr space anyway.
1873 target_attrname = jcr->xattr_data->content + 1;
1874 if ((bp = strchr(target_attrname, '\0')) == (char *)NULL ||
1875 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1881 * Open the file on which to restore the xattrs read-only.
1883 if ((filefd = open(jcr->last_fname, O_RDONLY | O_NONBLOCK)) < 0) {
1884 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1885 jcr->last_fname, be.bstrerror());
1886 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1887 jcr->last_fname, be.bstrerror());
1892 * Open the xattr naming space and make it the current working dir.
1894 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1895 Mmsg2(jcr->errmsg, _("Unable to open xattr space on file \"%s\": ERR=%s\n"),
1896 jcr->last_fname, be.bstrerror());
1897 Dmsg2(100, "Unable to open xattr space on file \"%s\": ERR=%s\n",
1898 jcr->last_fname, be.bstrerror());
1902 if (fchdir(attrdirfd) < 0) {
1903 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1904 jcr->last_fname, be.bstrerror());
1905 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1906 jcr->last_fname, attrdirfd, be.bstrerror());
1911 * Try to open the correct xattr subdir based on the target_attrname given.
1912 * e.g. check if its a subdir attrname. Each / in the string makes us go
1915 while ((bp = strchr(target_attrname, '/')) != (char *)NULL) {
1918 if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) {
1919 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
1920 target_attrname, jcr->last_fname, be.bstrerror());
1921 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
1922 target_attrname, jcr->last_fname, be.bstrerror());
1930 * Open the xattr naming space.
1932 if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1933 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1934 target_attrname, jcr->last_fname, be.bstrerror());
1935 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1936 target_attrname, jcr->last_fname, be.bstrerror());
1944 * Make the xattr space our current workingdir.
1946 if (fchdir(attrdirfd) < 0) {
1947 Mmsg3(jcr->errmsg, _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"),
1948 target_attrname, jcr->last_fname, be.bstrerror());
1949 Dmsg4(100, "Unable to fchdir to xattr space %s on file \"%s\" using fd %d: ERR=%s\n",
1950 target_attrname, jcr->last_fname, attrdirfd, be.bstrerror());
1954 target_attrname = ++bp;
1958 * Decode the attributes from the stream.
1960 decode_stat(attribs, &st, &inum);
1963 * Decode the next field (acl_text).
1965 if ((bp = strchr(attribs, '\0')) == (char *)NULL ||
1966 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1972 * Based on the filetype perform the correct action. We support most filetypes here, more
1973 * then the actual implementation on Solaris supports so some code may never get executed
1974 * due to limitations in the implementation.
1976 switch (st.st_mode & S_IFMT) {
1979 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1981 unlinkat(attrdirfd, target_attrname, 0);
1982 if (mkfifo(target_attrname, st.st_mode) < 0) {
1983 Mmsg3(jcr->errmsg, _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"),
1984 target_attrname, jcr->last_fname, be.bstrerror());
1985 Dmsg3(100, "Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n",
1986 target_attrname, jcr->last_fname, be.bstrerror());
1993 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1995 unlinkat(attrdirfd, target_attrname, 0);
1996 if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) {
1997 Mmsg3(jcr->errmsg, _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"),
1998 target_attrname, jcr->last_fname, be.bstrerror());
1999 Dmsg3(100, "Unable to mknod xattr %s on file \"%s\": ERR=%s\n",
2000 target_attrname, jcr->last_fname, be.bstrerror());
2006 * If its not the hidden_dir create the entry.
2007 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2009 if (strcmp(target_attrname, ".")) {
2010 unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR);
2011 if (mkdir(target_attrname, st.st_mode) < 0) {
2012 Jmsg3(jcr, M_WARNING, 0, _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"),
2013 target_attrname, jcr->last_fname, be.bstrerror());
2014 Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n",
2015 target_attrname, jcr->last_fname, be.bstrerror());
2022 * See if this is a hard linked file. e.g. inum != 0
2027 unlinkat(attrdirfd, target_attrname, 0);
2028 if (link(linked_target, target_attrname) < 0) {
2029 Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"),
2030 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2031 Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n",
2032 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2037 * Successfully restored xattr.
2039 retval = bxattr_exit_ok;
2042 if ((bp = strchr(acl_text, '\0')) == (char *)NULL ||
2043 (used_bytes = (bp - jcr->xattr_data->content)) >= total_bytes) {
2047 if (used_bytes < (total_bytes - 1))
2051 * Restore the actual xattr.
2053 if (!is_extensible) {
2054 unlinkat(attrdirfd, target_attrname, 0);
2057 if ((attrfd = openat(attrdirfd, target_attrname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) {
2058 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
2059 target_attrname, jcr->last_fname, be.bstrerror());
2060 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
2061 target_attrname, jcr->last_fname, be.bstrerror());
2067 * Restore the actual data.
2069 if (st.st_size > 0) {
2070 used_bytes = (data - jcr->xattr_data->content);
2071 cnt = total_bytes - used_bytes;
2074 * Do a sanity check, the st.st_size should be the same as the number of bytes
2075 * we have available as data of the stream.
2077 if (cnt != st.st_size) {
2078 Mmsg2(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n"),
2079 target_attrname, jcr->last_fname);
2080 Dmsg2(100, "Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n",
2081 target_attrname, jcr->last_fname);
2086 cnt = write(attrfd, data, cnt);
2088 Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"),
2089 target_attrname, jcr->last_fname, be.bstrerror());
2090 Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n",
2091 target_attrname, jcr->last_fname, be.bstrerror());
2097 cnt = total_bytes - used_bytes;
2103 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2107 if (symlink(linked_target, target_attrname) < 0) {
2108 Mmsg4(jcr->errmsg, _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"),
2109 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2110 Dmsg4(100, "Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n",
2111 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2116 * Successfully restored xattr.
2118 retval = bxattr_exit_ok;
2125 * Restore owner and acl for non extensible attributes.
2127 if (!is_extensible) {
2128 if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0) {
2132 * Gentile way of the system saying this type of xattr layering is not supported.
2133 * But as this is not an error we return a positive return value.
2135 retval = bxattr_exit_ok;
2138 retval = bxattr_exit_ok;
2141 Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"),
2142 target_attrname, jcr->last_fname, be.bstrerror());
2143 Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n",
2144 target_attrname, jcr->last_fname, be.bstrerror());
2151 if (acl_text && *acl_text)
2152 if (solaris_restore_xattr_acl(jcr, attrfd, target_attrname, acl_text) != bxattr_exit_ok)
2154 #endif /* HAVE_ACL */
2157 * For a non extensible attribute restore access and modification time on the xattr.
2159 if (!is_extensible) {
2160 times[0].tv_sec = st.st_atime;
2161 times[0].tv_usec = 0;
2162 times[1].tv_sec = st.st_mtime;
2163 times[1].tv_usec = 0;
2165 if (futimesat(attrdirfd, target_attrname, times) < 0) {
2166 Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"),
2167 target_attrname, jcr->last_fname, be.bstrerror());
2168 Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n",
2169 target_attrname, jcr->last_fname, be.bstrerror());
2175 * Successfully restored xattr.
2177 retval = bxattr_exit_ok;
2181 Mmsg1(jcr->errmsg, _("Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n"),
2183 Dmsg1(100, "Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n",
2190 if (attrdirfd != -1) {
2199 static bxattr_exit_code solaris_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2202 bxattr_exit_code retval = bxattr_exit_ok;
2205 * First see if extended attributes or extensible attributes are present.
2206 * If not just pretend things went ok.
2208 if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0) {
2209 jcr->xattr_data->nr_saved = 0;
2210 jcr->xattr_data->link_cache = New(alist(10, not_owned_by_alist));
2213 * As we change the cwd in the save function save the current cwd
2214 * for restore after return from the solaris_save_xattrs function.
2216 getcwd(cwd, sizeof(cwd));
2217 retval = solaris_save_xattrs(jcr, NULL, NULL);
2219 delete jcr->xattr_data->link_cache;
2220 jcr->xattr_data->link_cache = NULL;
2225 static bxattr_exit_code solaris_parse_xattr_streams(JCR *jcr, int stream)
2228 bool is_extensible = false;
2229 bxattr_exit_code retval;
2232 * First make sure we can restore xattr on the filesystem.
2235 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
2236 case STREAM_XATTR_SOLARIS_SYS:
2237 if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0) {
2238 Qmsg1(jcr, M_WARNING, 0,
2239 _("Failed to restore extensible attributes on file \"%s\"\n"),
2241 Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n",
2243 return bxattr_exit_error;
2246 is_extensible = true;
2249 case STREAM_XATTR_SOLARIS:
2250 if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0) {
2251 Qmsg1(jcr, M_WARNING, 0,
2252 _("Failed to restore extended attributes on file \"%s\"\n"),
2254 Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n",
2256 return bxattr_exit_error;
2260 return bxattr_exit_error;
2264 * As we change the cwd in the restore function save the current cwd
2265 * for restore after return from the solaris_restore_xattrs function.
2267 getcwd(cwd, sizeof(cwd));
2268 retval = solaris_restore_xattrs(jcr, is_extensible);
2275 * Function pointers to the build and parse function to use for these xattrs.
2277 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_xattr_streams;
2278 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = solaris_parse_xattr_streams;
2280 #endif /* defined(HAVE_SUN_OS) */
2283 * Entry points when compiled with support for XATTRs on a supported platform.
2285 bxattr_exit_code build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2287 if (os_build_xattr_streams) {
2288 return (*os_build_xattr_streams)(jcr, ff_pkt);
2290 return bxattr_exit_error;
2293 bxattr_exit_code parse_xattr_streams(JCR *jcr, int stream)
2297 if (os_parse_xattr_streams) {
2299 * See if we can parse this stream, and ifso give it a try.
2301 for (cnt = 0; cnt < sizeof(os_default_xattr_streams) / sizeof(int); cnt++) {
2302 if (os_default_xattr_streams[cnt] == stream) {
2303 return (*os_parse_xattr_streams)(jcr, stream);
2308 * Issue a warning and discard the message. But pretend the restore was ok.
2310 Jmsg2(jcr, M_WARNING, 0,
2311 _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"),
2312 jcr->last_fname, stream);
2313 return bxattr_exit_error;