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.
266 #if defined(HAVE_DARWIN_OS) || \
267 defined(HAVE_LINUX_OS)
269 #if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)) || \
270 (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) || \
271 (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR))
272 #error "Missing either full support for the LXATTR or XATTR functions."
275 #ifdef HAVE_SYS_XATTR_H
276 #include <sys/xattr.h>
278 #error "Missing sys/xattr.h header file"
282 * Define the supported XATTR streams for this OS
284 #if defined(HAVE_DARWIN_OS)
285 static int os_default_xattr_streams[1] = { STREAM_XATTR_DARWIN };
286 static const char *xattr_acl_skiplist[2] = { "com.apple.system.Security", NULL };
287 static const char *xattr_skiplist[3] = { "com.apple.system.extendedsecurity", "com.apple.ResourceFork", NULL };
288 #elif defined(HAVE_LINUX_OS)
289 static int os_default_xattr_streams[1] = { STREAM_XATTR_LINUX };
290 static const char *xattr_acl_skiplist[2] = { "system.posix_acl_access", NULL };
291 static const char *xattr_skiplist[1] = { NULL };
295 * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
296 * listxattr, getxattr and setxattr with an extra options argument
297 * which mimics the l variants of the functions when we specify
298 * XATTR_NOFOLLOW as the options value.
300 #if defined(HAVE_DARWIN_OS)
301 #define llistxattr(path, list, size) listxattr((path), (list), (size), XATTR_NOFOLLOW)
302 #define lgetxattr(path, name, value, size) getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
303 #define lsetxattr(path, name, value, size, flags) setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
306 * Fallback to the non l-functions when those are not available.
308 #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
309 #define lgetxattr getxattr
311 #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
312 #define lsetxattr setxattr
314 #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
315 #define llistxattr listxattr
319 static bxattr_exit_code linux_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt)
322 char *xattr_list, *bp;
323 int cnt, xattr_count = 0;
324 int32_t xattr_list_len,
326 uint32_t expected_serialize_len = 0;
327 xattr_t *current_xattr;
328 alist *xattr_value_list = NULL;
329 bxattr_exit_code retval = bxattr_exit_error;
333 * First get the length of the available list with extended attributes.
335 xattr_list_len = llistxattr(jcr->last_fname, NULL, 0);
336 if (xattr_list_len < 0) {
339 return bxattr_exit_ok;
341 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
342 jcr->last_fname, be.bstrerror());
343 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
344 jcr->last_fname, be.bstrerror());
345 return bxattr_exit_error;
347 } else if (xattr_list_len == 0) {
348 return bxattr_exit_ok;
352 * Allocate room for the extented attribute list.
354 xattr_list = (char *)malloc(xattr_list_len + 1);
355 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
358 * Get the actual list of extended attributes names for a file.
360 xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len);
361 if (xattr_list_len < 0) {
364 retval = bxattr_exit_ok;
367 Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
368 jcr->last_fname, be.bstrerror());
369 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
370 jcr->last_fname, be.bstrerror());
374 xattr_list[xattr_list_len] = '\0';
376 xattr_value_list = New(alist(10, not_owned_by_alist));
379 * Walk the list of extended attributes names and retrieve the data.
380 * We already count the bytes needed for serializing the stream later on.
383 while ((bp - xattr_list) + 1 < xattr_list_len) {
387 * On some OSes you also get the acls in the extented attribute list.
388 * So we check if we are already backing up acls and if we do we
389 * don't store the extended attribute with the same info.
391 if (ff_pkt->flags & FO_ACL) {
392 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
393 if (bstrcmp(bp, xattr_acl_skiplist[cnt])) {
401 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
404 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
405 if (bstrcmp(bp, xattr_skiplist[cnt])) {
413 bp = strchr(bp, '\0') + 1;
418 * Each xattr valuepair starts with a magic so we can parse it easier.
420 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
421 current_xattr->magic = XATTR_MAGIC;
422 expected_serialize_len += sizeof(current_xattr->magic);
425 * Allocate space for storing the name.
427 current_xattr->name_length = strlen(bp);
428 current_xattr->name = (char *)malloc(current_xattr->name_length);
429 memcpy((caddr_t)current_xattr->name, (caddr_t)bp, current_xattr->name_length);
431 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
434 * First see how long the value is for the extended attribute.
436 xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0);
437 if (xattr_value_len < 0) {
440 retval = bxattr_exit_ok;
441 free(current_xattr->name);
445 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
446 jcr->last_fname, be.bstrerror());
447 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
448 jcr->last_fname, be.bstrerror());
449 free(current_xattr->name);
456 * Allocate space for storing the value.
458 current_xattr->value = (char *)malloc(xattr_value_len);
459 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
461 xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
462 if (xattr_value_len < 0) {
465 retval = bxattr_exit_ok;
466 free(current_xattr->value);
467 free(current_xattr->name);
471 Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
472 jcr->last_fname, be.bstrerror());
473 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
474 jcr->last_fname, be.bstrerror());
475 free(current_xattr->value);
476 free(current_xattr->name);
483 * Store the actual length of the value.
485 current_xattr->value_length = xattr_value_len;
486 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
489 * Protect ourself against things getting out of hand.
491 if (expected_serialize_len >= MAX_XATTR_STREAM) {
492 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
493 jcr->last_fname, MAX_XATTR_STREAM);
494 free(current_xattr->value);
495 free(current_xattr->name);
500 xattr_value_list->append(current_xattr);
502 bp = strchr(bp, '\0') + 1;
506 xattr_list = (char *)NULL;
509 * If we found any xattr send them to the SD.
511 if (xattr_count > 0) {
513 * Serialize the datastream.
515 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
516 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
518 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
523 xattr_drop_internal_table(xattr_value_list);
526 * Send the datastream to the SD.
528 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
530 xattr_drop_internal_table(xattr_value_list);
532 return bxattr_exit_ok;
539 if (xattr_value_list) {
540 xattr_drop_internal_table(xattr_value_list);
545 static bxattr_exit_code linux_xattr_parse_streams(JCR *jcr, int stream)
547 xattr_t *current_xattr;
548 alist *xattr_value_list;
551 xattr_value_list = New(alist(10, not_owned_by_alist));
553 if (unserialize_xattr_stream(jcr, xattr_value_list) != bxattr_exit_ok) {
554 xattr_drop_internal_table(xattr_value_list);
555 return bxattr_exit_error;
558 foreach_alist(current_xattr, xattr_value_list) {
559 if (lsetxattr(jcr->last_fname, current_xattr->name, current_xattr->value, current_xattr->value_length, 0) != 0) {
564 Mmsg2(jcr->errmsg, _("lsetxattr error on file \"%s\": ERR=%s\n"),
565 jcr->last_fname, be.bstrerror());
566 Dmsg2(100, "lsetxattr error file=%s ERR=%s\n",
567 jcr->last_fname, be.bstrerror());
573 xattr_drop_internal_table(xattr_value_list);
574 return bxattr_exit_ok;
577 xattr_drop_internal_table(xattr_value_list);
578 return bxattr_exit_error;
582 * Function pointers to the build and parse function to use for these xattrs.
584 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = linux_xattr_build_streams;
585 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = linux_xattr_parse_streams;
587 #elif defined(HAVE_FREEBSD_OS) || \
588 defined(HAVE_NETBSD_OS) || \
589 defined(HAVE_OPENBSD_OS)
591 #if !defined(HAVE_EXTATTR_GET_LINK) || \
592 !defined(HAVE_EXTATTR_SET_LINK) || \
593 !defined(HAVE_EXTATTR_LIST_LINK) || \
594 !defined(HAVE_EXTATTR_GET_FILE) || \
595 !defined(HAVE_EXTATTR_SET_FILE) || \
596 !defined(HAVE_EXTATTR_LIST_FILE) || \
597 !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING) || \
598 !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE)
599 #error "Missing full support for the extattr functions."
602 #ifdef HAVE_SYS_EXTATTR_H
603 #include <sys/extattr.h>
605 #error "Missing sys/extattr.h header file"
608 #ifdef HAVE_LIBUTIL_H
612 #if !defined(HAVE_EXTATTR_GET_LINK) && defined(HAVE_EXTATTR_GET_FILE)
613 #define extattr_get_link extattr_get_file
615 #if !defined(HAVE_EXTATTR_SET_LINK) && defined(HAVE_EXTATTR_SET_FILE)
616 #define extattr_set_link extattr_set_file
618 #if !defined(HAVE_EXTATTR_LIST_LINK) && defined(HAVE_EXTATTR_LIST_FILE)
619 #define extattr_list_link extattr_list_file
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) {
716 * print the current name into the buffer as its not null terminated we need to
717 * use the length encoded in the string for copying only the needed bytes.
719 bsnprintf(current_attrname, sizeof(current_attrname), "%*.*s",
720 xattr_list[index], xattr_list[index], xattr_list + (index + 1));
723 * First make a xattr tuple of the current namespace and the name of the xattr.
724 * e.g. something like user.<attrname> or system.<attrname>
726 if (extattr_namespace_to_string(attrnamespace, ¤t_attrnamespace) != 0) {
727 Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"),
728 attrnamespace, jcr->last_fname);
729 Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n",
730 attrnamespace, jcr->last_fname);
735 * Create a tupple of the current attrnamespace and attrname.
737 bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s", current_attrnamespace, current_attrname);
740 * On some OSes you also get the acls in the extented attribute list.
741 * So we check if we are already backing up acls and if we do we
742 * don't store the extended attribute with the same info.
744 if (ff_pkt->flags & FO_ACL) {
745 for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
746 if (bstrcmp(current_attrtuple, xattr_acl_skiplist[cnt])) {
754 * On some OSes we want to skip certain xattrs which are in the xattr_skiplist array.
757 for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
758 if (bstrcmp(current_attrtuple, xattr_skiplist[cnt])) {
770 * Each xattr valuepair starts with a magic so we can parse it easier.
772 current_xattr = (xattr_t *)malloc(sizeof(xattr_t));
773 current_xattr->magic = XATTR_MAGIC;
774 expected_serialize_len += sizeof(current_xattr->magic);
777 * Allocate space for storing the name.
779 current_xattr->name_length = strlen(current_attrtuple);
780 current_xattr->name = (char *)malloc(current_xattr->name_length);
781 memcpy((caddr_t)current_xattr->name, (caddr_t)current_attrtuple, current_xattr->name_length);
783 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
786 * First see how long the value is for the extended attribute.
788 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, NULL, 0);
789 if (xattr_value_len < 0) {
792 retval = bxattr_exit_ok;
793 free(current_xattr->name);
797 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
798 jcr->last_fname, be.bstrerror());
799 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
800 jcr->last_fname, be.bstrerror());
801 free(current_xattr->name);
808 * Allocate space for storing the value.
810 current_xattr->value = (char *)malloc(xattr_value_len);
811 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
813 xattr_value_len = extattr_get_link(jcr->last_fname, attrnamespace, current_attrname, current_xattr->value, xattr_value_len);
814 if (xattr_value_len < 0) {
817 retval = bxattr_exit_ok;
818 free(current_xattr->value);
819 free(current_xattr->name);
823 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"),
824 jcr->last_fname, be.bstrerror());
825 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
826 jcr->last_fname, be.bstrerror());
827 free(current_xattr->value);
828 free(current_xattr->name);
835 * Store the actual length of the value.
837 current_xattr->value_length = xattr_value_len;
838 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
841 * Protect ourself against things getting out of hand.
843 if (expected_serialize_len >= MAX_XATTR_STREAM) {
844 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
845 jcr->last_fname, MAX_XATTR_STREAM);
846 free(current_xattr->value);
847 free(current_xattr->name);
852 xattr_value_list->append(current_xattr);
857 * We are done with this xattr list.
860 xattr_list = (char *)NULL;
864 * If we found any xattr send them to the SD.
866 if (xattr_count > 0) {
868 * Serialize the datastream.
870 if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
871 Mmsg1(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"),
873 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
878 xattr_drop_internal_table(xattr_value_list);
879 xattr_value_list = NULL;
882 * Send the datastream to the SD.
884 return send_xattr_stream(jcr, os_default_xattr_streams[0]);
886 xattr_drop_internal_table(xattr_value_list);
887 xattr_value_list = NULL;
889 return bxattr_exit_ok;
896 if (xattr_value_list) {
897 xattr_drop_internal_table(xattr_value_list);
898 xattr_value_list = NULL;
903 static bxattr_exit_code bsd_parse_xattr_streams(JCR *jcr, int stream)
905 xattr_t *current_xattr;
906 alist *xattr_value_list;
907 int current_attrnamespace, cnt;
908 char *attrnamespace, *attrname;
911 xattr_value_list = New(alist(10, not_owned_by_alist));
913 if (unserialize_xattr_stream(jcr, xattr_value_list) != bxattr_exit_ok) {
914 xattr_drop_internal_table(xattr_value_list);
915 return bxattr_exit_error;
918 foreach_alist(current_xattr, xattr_value_list) {
920 * Try splitting the xattr_name into a namespace and name part.
921 * The splitting character is a .
923 attrnamespace = current_xattr->name;
924 if ((attrname = strchr(attrnamespace, '.')) == (char *)NULL) {
925 Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"),
926 current_xattr->name, jcr->last_fname);
927 Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n",
928 current_xattr->name, jcr->last_fname);
934 * Make sure the attrnamespace makes sense.
936 if (extattr_string_to_namespace(attrnamespace, ¤t_attrnamespace) != 0) {
937 Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"),
938 attrnamespace, jcr->last_fname);
939 Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n",
940 attrnamespace, jcr->last_fname);
945 * Try restoring the extended attribute.
947 cnt = extattr_set_link(jcr->last_fname, current_attrnamespace,
948 attrname, current_xattr->value, current_xattr->value_length);
949 if (cnt < 0 || cnt != current_xattr->value_length) {
955 Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"),
956 jcr->last_fname, be.bstrerror());
957 Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n",
958 jcr->last_fname, be.bstrerror());
965 xattr_drop_internal_table(xattr_value_list);
966 return bxattr_exit_ok;
969 xattr_drop_internal_table(xattr_value_list);
970 return bxattr_exit_error;
974 * Function pointers to the build and parse function to use for these xattrs.
976 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = bsd_build_xattr_streams;
977 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = bsd_parse_xattr_streams;
979 #elif defined(HAVE_SUN_OS)
981 * Solaris extended attributes were introduced in Solaris 9
984 * Solaris extensible attributes were introduced in OpenSolaris
985 * by PSARC 2007/315 Solaris extensible attributes are also
986 * sometimes called extended system attributes.
988 * man fsattr(5) on Solaris gives a wealth of info. The most
989 * important bits are:
991 * Attributes are logically supported as files within the file
992 * system. The file system is therefore augmented with an
993 * orthogonal name space of file attributes. Any file (includ-
994 * ing attribute files) can have an arbitrarily deep attribute
995 * tree associated with it. Attribute values are accessed by
996 * file descriptors obtained through a special attribute inter-
997 * face. This logical view of "attributes as files" allows the
998 * leveraging of existing file system interface functionality
999 * to support the construction, deletion, and manipulation of
1002 * The special files "." and ".." retain their accustomed
1003 * semantics within the attribute hierarchy. The "." attribute
1004 * file refers to the current directory and the ".." attribute
1005 * file refers to the parent directory. The unnamed directory
1006 * at the head of each attribute tree is considered the "child"
1007 * of the file it is associated with and the ".." file refers
1008 * to the associated file. For any non-directory file with
1009 * attributes, the ".." entry in the unnamed directory refers
1010 * to a file that is not a directory.
1012 * Conceptually, the attribute model is fully general. Extended
1013 * attributes can be any type of file (doors, links, direc-
1014 * tories, and so forth) and can even have their own attributes
1015 * (fully recursive). As a result, the attributes associated
1016 * with a file could be an arbitrarily deep directory hierarchy
1017 * where each attribute could have an equally complex attribute
1018 * tree associated with it. Not all implementations are able
1019 * to, or want to, support the full model. Implementation are
1020 * therefore permitted to reject operations that are not sup-
1021 * ported. For example, the implementation for the UFS file
1022 * system allows only regular files as attributes (for example,
1023 * no sub-directories) and rejects attempts to place attributes
1026 * The following list details the operations that are rejected
1027 * in the current implementation:
1029 * link Any attempt to create links between
1030 * attribute and non-attribute space
1031 * is rejected to prevent security-
1032 * related or otherwise sensitive
1033 * attributes from being exposed, and
1034 * therefore manipulable, as regular
1037 * rename Any attempt to rename between
1038 * attribute and non-attribute space
1039 * is rejected to prevent an already
1040 * linked file from being renamed and
1041 * thereby circumventing the link res-
1044 * mkdir, symlink, mknod Any attempt to create a "non-
1045 * regular" file in attribute space is
1046 * rejected to reduce the functional-
1047 * ity, and therefore exposure and
1048 * risk, of the initial implementa-
1051 * The entire available name space has been allocated to "gen-
1052 * eral use" to bring the implementation in line with the NFSv4
1053 * draft standard [NFSv4]. That standard defines "named attri-
1054 * butes" (equivalent to Solaris Extended Attributes) with no
1055 * naming restrictions. All Sun applications making use of
1056 * opaque extended attributes will use the prefix "SUNW".
1059 #ifdef HAVE_SYS_ATTR_H
1060 #include <sys/attr.h>
1067 #ifdef HAVE_SYS_NVPAIR_H
1068 #include <sys/nvpair.h>
1071 #ifdef HAVE_SYS_ACL_H
1072 #include <sys/acl.h>
1075 #if !defined(HAVE_OPENAT) ||
1076 !defined(HAVE_UNKINKAT) ||
1077 !defined(HAVE_FCHOWNAT) ||
1078 !defined(HAVE_FUTIMESAT)
1079 #error "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function"
1083 * Define the supported XATTR streams for this OS
1085 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1086 static int os_default_xattr_streams[2] = { STREAM_XATTR_SOLARIS, STREAM_XATTR_SOLARIS_SYS};
1088 static int os_default_xattr_streams[1] = { STREAM_XATTR_SOLARIS };
1089 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
1092 * This code creates a temporary cache with entries for each xattr which has
1093 * a link count > 1 (which indicates it has one or more hard linked counterpart(s))
1095 static xattr_link_cache_entry_t *find_xattr_link_cache_entry(JCR *jcr, ino_t inum)
1097 xattr_link_cache_entry_t *ptr;
1099 foreach_alist(ptr, jcr->xattr_data->link_cache) {
1100 if (ptr && ptr->inum == inum) {
1107 static void add_xattr_link_cache_entry(JCR *jcr, ino_t inum, char *target)
1109 xattr_link_cache_entry_t *ptr;
1111 ptr = (xattr_link_cache_entry_t *)malloc(sizeof(xattr_link_cache_entry_t));
1112 memset((caddr_t)ptr, 0, sizeof(xattr_link_cache_entry_t));
1114 bstrncpy(ptr->target, target, sizeof(ptr->target));
1115 jcr->xattr_data->link_cache->append(ptr);
1118 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1120 * This function returns true if a non default extended system attribute
1121 * list is associated with fd and returns false when an error has occured
1122 * or when only extended system attributes other than archive,
1123 * av_modified or crtime are set.
1125 * The function returns true for the following cases:
1127 * - any extended system attribute other than the default attributes
1128 * ('archive', 'av_modified' and 'crtime') is set
1129 * - nvlist has NULL name string
1130 * - nvpair has data type of 'nvlist'
1131 * - default data type.
1133 static bool solaris_has_non_transient_extensible_attributes(int fd)
1141 bool retval = false;
1143 if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
1148 while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
1149 name = nvpair_name(pair);
1152 fattr = name_to_attr(name);
1158 type = nvpair_type(pair);
1160 case DATA_TYPE_BOOLEAN_VALUE:
1161 if (nvpair_value_boolean_value(pair, &value) != 0) {
1164 if (value && fattr != F_ARCHIVE &&
1165 fattr != F_AV_MODIFIED) {
1170 case DATA_TYPE_UINT64_ARRAY:
1171 if (fattr != F_CRTIME) {
1176 case DATA_TYPE_NVLIST:
1184 if (response != NULL) {
1185 nvlist_free(response);
1189 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1191 #if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL)
1193 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1194 * There is no need to store those acls as we already store the stat bits too.
1196 static bool acl_is_trivial(int count, aclent_t *entries)
1201 for (n = 0; n < count; n++) {
1203 if (!(ace->a_type == USER_OBJ ||
1204 ace->a_type == GROUP_OBJ ||
1205 ace->a_type == OTHER_OBJ ||
1206 ace->a_type == CLASS_OBJ))
1211 #endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */
1213 static bxattr_exit_code solaris_save_xattr_acl(JCR *jcr, int fd, const char *attrname, char **acl_text)
1216 #ifdef HAVE_EXTENDED_ACL
1222 * See if this attribute has an ACL
1224 if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0) ||
1225 pathconf(attrname, _PC_ACL_ENABLED) > 0) {
1227 * See if there is a non trivial acl on the file.
1229 if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0) ||
1230 acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) {
1233 return bxattr_exit_ok;
1235 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1236 attrname, jcr->last_fname, be.bstrerror());
1237 Dmsg3(100, "facl_get/acl_get of xattr %s on \"%s\" failed: ERR=%s\n",
1238 attrname, jcr->last_fname, be.bstrerror());
1239 return bxattr_exit_error;
1244 #if defined(ACL_SID_FMT)
1246 * New format flag added in newer Solaris versions.
1248 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1250 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1251 #endif /* ACL_SID_FMT */
1253 *acl_text = acl_totext(aclp, flags);
1261 return bxattr_exit_ok;
1262 #else /* HAVE_EXTENDED_ACL */
1264 aclent_t *acls = NULL;
1268 * See if this attribute has an ACL
1271 n = facl(fd, GETACLCNT, 0, NULL);
1273 n = acl(attrname, GETACLCNT, 0, NULL);
1276 if (n >= MIN_ACL_ENTRIES) {
1277 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
1278 if ((fd != -1 && facl(fd, GETACL, n, acls) != n) ||
1279 acl(attrname, GETACL, n, acls) != n) {
1283 return bxattr_exit_ok;
1285 Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
1286 attrname, jcr->last_fname, be.bstrerror());
1287 Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n",
1288 attrname, jcr->last_fname, be.bstrerror());
1290 return bxattr_exit_error;
1295 * See if there is a non trivial acl on the file.
1297 if (!acl_is_trivial(n, acls)) {
1298 if ((*acl_text = acltotext(acls, n)) == NULL) {
1299 Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"),
1300 attrname, jcr->last_fname, be.bstrerror());
1301 Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n",
1302 attrname, jcr->last_fname, be.bstrerror());
1304 return bxattr_exit_error;
1314 return bxattr_exit_ok;
1315 #endif /* HAVE_EXTENDED_ACL */
1317 #else /* HAVE_ACL */
1318 return bxattr_exit_ok;
1319 #endif /* HAVE_ACL */
1323 * Forward declaration for recursive function call.
1325 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent);
1328 * Save an extended or extensible attribute.
1329 * This is stored as an opaque stream of bytes with the following encoding:
1331 * <xattr_name>\0<stat_buffer>\0<acl_string>\0<actual_xattr_data>
1333 * or for a hardlinked or symlinked attribute
1335 * <xattr_name>\0<stat_buffer>\0<xattr_link_source>\0
1337 * xattr_name can be a subpath relative to the file the xattr is on.
1338 * stat_buffer is the string representation of the stat struct.
1339 * acl_string is an acl text when a non trivial acl is set on the xattr.
1340 * actual_xattr_data is the content of the xattr file.
1342 static bxattr_exit_code solaris_save_xattr(JCR *jcr, int fd, const char *xattr_namespace,
1343 const char *attrname, bool toplevel_hidden_dir, int stream)
1348 xattr_link_cache_entry_t *xlce;
1349 char target_attrname[PATH_MAX];
1350 char link_source[PATH_MAX];
1351 char *acl_text = NULL;
1352 char attribs[MAXSTRING];
1353 char buffer[BUFSIZ];
1354 bxattr_exit_code retval = bxattr_exit_error;
1357 bsnprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace, attrname);
1360 * Get the stats of the extended or extensible attribute.
1362 if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) {
1365 retval = bxattr_exit_ok;
1368 Mmsg3(jcr->errmsg, _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"),
1369 target_attrname, jcr->last_fname, be.bstrerror());
1370 Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n",
1371 target_attrname, jcr->last_fname, be.bstrerror());
1377 * Based on the filetype perform the correct action. We support most filetypes here, more
1378 * then the actual implementation on Solaris supports so some code may never get executed
1379 * due to limitations in the implementation.
1381 switch (st.st_mode & S_IFMT) {
1386 * Get any acl on the xattr.
1388 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1392 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1393 * Encode the stat struct into an ASCII representation.
1395 encode_stat(attribs, &st, 0, stream);
1396 cnt = bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c",
1397 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1401 * Get any acl on the xattr.
1403 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok)
1407 * See if this is the toplevel_hidden_dir being saved.
1409 if (toplevel_hidden_dir) {
1411 * Save the data for later storage when we encounter a real xattr. We store the data
1412 * in the jcr->xattr_data->content buffer and flush that just before sending out the
1413 * first real xattr. Encode the stat struct into an ASCII representation and jump
1414 * out of the function.
1416 encode_stat(attribs, &st, 0, stream);
1417 cnt = bsnprintf(buffer, sizeof(buffer),
1419 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1420 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1421 jcr->xattr_data->content_length = cnt;
1425 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1426 * Encode the stat struct into an ASCII representation.
1428 encode_stat(attribs, &st, 0, stream);
1429 cnt = bsnprintf(buffer, sizeof(buffer),
1431 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1436 * If this is a hardlinked file check the inode cache for a hit.
1438 if (st.st_nlink > 1) {
1440 * See if the cache already knows this inode number.
1442 if ((xlce = find_xattr_link_cache_entry(jcr, st.st_ino)) != NULL) {
1444 * Generate a xattr encoding with the reference to the target in there.
1446 encode_stat(attribs, &st, st.st_ino, stream);
1447 cnt = bsnprintf(buffer, sizeof(buffer),
1449 target_attrname, 0, attribs, 0, xlce->target, 0);
1450 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1451 jcr->xattr_data->content_length = cnt;
1452 retval = send_xattr_stream(jcr, stream);
1455 * For a hard linked file we are ready now, no need to recursively save the attributes.
1461 * Store this hard linked file in the cache.
1462 * Store the name relative to the top level xattr space.
1464 add_xattr_link_cache_entry(jcr, st.st_ino, target_attrname + 1);
1468 * Get any acl on the xattr.
1470 if (solaris_save_xattr_acl(jcr, attrfd, attrname, &acl_text) != bxattr_exit_ok) {
1475 * Encode the stat struct into an ASCII representation.
1477 encode_stat(attribs, &st, 0, stream);
1478 cnt = bsnprintf(buffer, sizeof(buffer),
1480 target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1483 * Open the extended or extensible attribute file.
1485 if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) {
1488 retval = bxattr_exit_ok;
1491 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on \"%s\": ERR=%s\n"),
1492 target_attrname, jcr->last_fname, be.bstrerror());
1493 Dmsg3(100, "openat of xattr %s on \"%s\" failed: ERR=%s\n",
1494 target_attrname, jcr->last_fname, be.bstrerror());
1501 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1502 * Encode the stat struct into an ASCII representation.
1504 if (readlink(attrname, link_source, sizeof(link_source)) < 0) {
1507 retval = bxattr_exit_ok;
1510 Mmsg3(jcr->errmsg, _("Unable to read symlin %s on \"%s\": ERR=%s\n"),
1511 target_attrname, jcr->last_fname, be.bstrerror());
1512 Dmsg3(100, "readlink of xattr %s on \"%s\" failed: ERR=%s\n",
1513 target_attrname, jcr->last_fname, be.bstrerror());
1519 * Generate a xattr encoding with the reference to the target in there.
1521 encode_stat(attribs, &st, st.st_ino, stream);
1522 cnt = bsnprintf(buffer, sizeof(buffer),
1524 target_attrname, 0, attribs, 0, link_source, 0);
1525 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1526 jcr->xattr_data->content_length = cnt;
1527 retval = send_xattr_stream(jcr, stream);
1529 if (retval == bxattr_exit_ok) {
1530 jcr->xattr_data->nr_saved++;
1534 * For a soft linked file we are ready now, no need to recursively save the attributes.
1542 * See if this is the first real xattr being saved.
1543 * If it is save the toplevel_hidden_dir attributes first.
1544 * This is easy as its stored already in the jcr->xattr_data->content buffer.
1546 if (jcr->xattr_data->nr_saved == 0) {
1547 retval = send_xattr_stream(jcr, STREAM_XATTR_SOLARIS);
1548 if (retval != bxattr_exit_ok) {
1551 jcr->xattr_data->nr_saved++;
1554 pm_memcpy(jcr->xattr_data->content, buffer, cnt);
1555 jcr->xattr_data->content_length = cnt;
1558 * Only dump the content of regular files.
1560 switch (st.st_mode & S_IFMT) {
1562 if (st.st_size > 0) {
1564 * Protect ourself against things getting out of hand.
1566 if (st.st_size >= MAX_XATTR_STREAM) {
1567 Mmsg2(jcr->errmsg, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
1568 jcr->last_fname, MAX_XATTR_STREAM);
1572 while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) {
1573 jcr->xattr_data->content = check_pool_memory_size(jcr->xattr_data->content, jcr->xattr_data->content_length + cnt);
1574 memcpy(jcr->xattr_data->content + jcr->xattr_data->content_length, buffer, cnt);
1575 jcr->xattr_data->content_length += cnt;
1579 Mmsg2(jcr->errmsg, _("Unable to read content of xattr %s on file \"%s\"\n"),
1580 target_attrname, jcr->last_fname);
1581 Dmsg2(100, "read of data from xattr %s on \"%s\" failed\n",
1582 target_attrname, jcr->last_fname);
1593 retval = send_xattr_stream(jcr, stream);
1594 if (retval == bxattr_exit_ok) {
1595 jcr->xattr_data->nr_saved++;
1600 * Recursivly call solaris_save_extended_attributes for archiving the attributes
1601 * available on this extended attribute.
1604 retval = solaris_save_xattrs(jcr, xattr_namespace, attrname);
1607 * The recursive call could change our working dir so change back to the wanted workdir.
1609 if (fchdir(fd) < 0) {
1612 retval = bxattr_exit_ok;
1615 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"),
1616 jcr->last_fname, be.bstrerror());
1617 Dmsg3(100, "Unable to fchdir to xattr space of file \"%s\" using fd %d: ERR=%s\n",
1618 jcr->last_fname, fd, be.bstrerror());
1634 static bxattr_exit_code solaris_save_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent)
1637 int fd, filefd = -1, attrdirfd = -1;
1640 char current_xattr_namespace[PATH_MAX];
1641 bxattr_exit_code retval = bxattr_exit_error;
1645 * Determine what argument to use. Use attr_parent when set
1646 * (recursive call) or jcr->last_fname for first call. Also save
1647 * the current depth of the xattr_space we are in.
1651 if (xattr_namespace) {
1652 bsnprintf(current_xattr_namespace, sizeof(current_xattr_namespace), "%s%s/",
1653 xattr_namespace, attr_parent);
1655 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1658 name = jcr->last_fname;
1659 bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
1663 * Open the file on which to save the xattrs read-only.
1665 if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
1668 retval = bxattr_exit_ok;
1671 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1672 jcr->last_fname, be.bstrerror());
1673 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1674 jcr->last_fname, be.bstrerror());
1680 * Open the xattr naming space.
1682 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1686 * Gentile way of the system saying this type of xattr layering is not supported.
1687 * Which is not problem we just forget about this this xattr.
1688 * But as this is not an error we return a positive return value.
1690 retval = bxattr_exit_ok;
1693 retval = bxattr_exit_ok;
1696 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1697 name, jcr->last_fname, be.bstrerror());
1698 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1699 name, jcr->last_fname, be.bstrerror());
1705 * We need to change into the attribute directory to determine if each of the
1706 * attributes should be saved.
1708 if (fchdir(attrdirfd) < 0) {
1709 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1710 jcr->last_fname, be.bstrerror());
1711 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1712 jcr->last_fname, attrdirfd, be.bstrerror());
1717 * Save the data of the toplevel xattr hidden_dir. We save this one before anything
1718 * else because the readdir returns "." entry after the extensible attr entry.
1719 * And as we want this entry before anything else we better just save its data.
1722 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, ".",
1723 true, STREAM_XATTR_SOLARIS);
1725 if ((fd = dup(attrdirfd)) == -1 ||
1726 (dirp = fdopendir(fd)) == (DIR *)NULL) {
1727 Mmsg2(jcr->errmsg, _("Unable to list the xattr space on file \"%s\": ERR=%s\n"),
1728 jcr->last_fname, be.bstrerror());
1729 Dmsg3(100, "Unable to fdopendir xattr space on file \"%s\" using fd %d: ERR=%s\n",
1730 jcr->last_fname, fd, be.bstrerror());
1736 * Walk the namespace.
1738 while (dp = readdir(dirp)) {
1740 * Skip only the toplevel . dir.
1742 if (!attr_parent && bstrcmp(dp->d_name, "."))
1746 * Skip all .. directories
1748 if (bstrcmp(dp->d_name, ".."))
1751 Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n",
1752 current_xattr_namespace, dp->d_name, jcr->last_fname);
1754 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1756 * We are not interested in read-only extensible attributes.
1758 if (bstrcmp(dp->d_name, VIEW_READONLY)) {
1759 Dmsg3(400, "Skipping readonly extensible attributes %s%s on file \"%s\"\n",
1760 current_xattr_namespace, dp->d_name, jcr->last_fname);
1766 * We are only interested in read-write extensible attributes
1767 * when they contain non-transient values.
1769 if (bstrcmp(dp->d_name, VIEW_READWRITE)) {
1771 * Determine if there are non-transient system attributes at the toplevel.
1772 * We need to provide a fd to the open file.
1774 if (!solaris_has_non_transient_extensible_attributes(filefd)) {
1775 Dmsg3(400, "Skipping transient extensible attributes %s%s on file \"%s\"\n",
1776 current_xattr_namespace, dp->d_name, jcr->last_fname);
1783 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1784 false, STREAM_XATTR_SOLARIS_SYS);
1787 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1792 solaris_save_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1793 false, STREAM_XATTR_SOLARIS);
1797 retval = bxattr_exit_ok;
1800 if (attrdirfd != -1)
1808 static bxattr_exit_code solaris_restore_xattr_acl(JCR *jcr, int fd, const char *attrname, char *acl_text)
1810 #ifdef HAVE_EXTENDED_ACL
1815 if ((error = acl_fromtext(acl_text, &aclp)) != 0) {
1816 Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"),
1818 return bxattr_exit_error;
1821 if ((fd != -1 && facl_set(fd, aclp) != 0) ||
1822 acl_set(attrname, aclp) != 0) {
1823 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1824 attrname, jcr->last_fname, be.bstrerror());
1825 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1826 attrname, jcr->last_fname, be.bstrerror());
1827 return bxattr_exit_error;
1833 return bxattr_exit_ok;
1835 #else /* HAVE_EXTENDED_ACL */
1837 aclent_t *acls = NULL;
1840 acls = aclfromtext(acl_text, &n);
1842 if ((fd != -1 && facl(fd, SETACL, n, acls) != 0) ||
1843 acl(attrname, SETACL, n, acls) != 0) {
1844 Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1845 attrname, jcr->last_fname, be.bstrerror());
1846 Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1847 attrname, jcr->last_fname, be.bstrerror());
1848 return bxattr_exit_error;
1855 return bxattr_exit_ok;
1857 #endif /* HAVE_EXTENDED_ACL */
1860 #endif /* HAVE_ACL */
1862 static bxattr_exit_code solaris_restore_xattrs(JCR *jcr, bool is_extensible)
1864 int fd, filefd = -1, attrdirfd = -1, attrfd = -1;
1865 int used_bytes, total_bytes, cnt;
1866 char *bp, *target_attrname, *attribs;
1867 char *linked_target = NULL;
1868 char *acl_text = NULL;
1872 struct timeval times[2];
1873 bxattr_exit_code retval = bxattr_exit_error;
1877 * Parse the xattr stream. First the part that is the same for all xattrs.
1880 total_bytes = jcr->xattr_data->content_length;
1883 * The name of the target xattr has a leading / we are not interested
1884 * in that so skip it when decoding the string. We always start a the /
1885 * of the xattr space anyway.
1887 target_attrname = jcr->xattr_data->content + 1;
1888 if ((bp = strchr(target_attrname, '\0')) == (char *)NULL ||
1889 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1895 * Open the file on which to restore the xattrs read-only.
1897 if ((filefd = open(jcr->last_fname, O_RDONLY | O_NONBLOCK)) < 0) {
1898 Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
1899 jcr->last_fname, be.bstrerror());
1900 Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1901 jcr->last_fname, be.bstrerror());
1906 * Open the xattr naming space and make it the current working dir.
1908 if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1909 Mmsg2(jcr->errmsg, _("Unable to open xattr space on file \"%s\": ERR=%s\n"),
1910 jcr->last_fname, be.bstrerror());
1911 Dmsg2(100, "Unable to open xattr space on file \"%s\": ERR=%s\n",
1912 jcr->last_fname, be.bstrerror());
1916 if (fchdir(attrdirfd) < 0) {
1917 Mmsg2(jcr->errmsg, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1918 jcr->last_fname, be.bstrerror());
1919 Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1920 jcr->last_fname, attrdirfd, be.bstrerror());
1925 * Try to open the correct xattr subdir based on the target_attrname given.
1926 * e.g. check if its a subdir attrname. Each / in the string makes us go
1929 while ((bp = strchr(target_attrname, '/')) != (char *)NULL) {
1932 if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) {
1933 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
1934 target_attrname, jcr->last_fname, be.bstrerror());
1935 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
1936 target_attrname, jcr->last_fname, be.bstrerror());
1944 * Open the xattr naming space.
1946 if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1947 Mmsg3(jcr->errmsg, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1948 target_attrname, jcr->last_fname, be.bstrerror());
1949 Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1950 target_attrname, jcr->last_fname, be.bstrerror());
1958 * Make the xattr space our current workingdir.
1960 if (fchdir(attrdirfd) < 0) {
1961 Mmsg3(jcr->errmsg, _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"),
1962 target_attrname, jcr->last_fname, be.bstrerror());
1963 Dmsg4(100, "Unable to fchdir to xattr space %s on file \"%s\" using fd %d: ERR=%s\n",
1964 target_attrname, jcr->last_fname, attrdirfd, be.bstrerror());
1968 target_attrname = ++bp;
1972 * Decode the attributes from the stream.
1974 decode_stat(attribs, &st, &inum);
1977 * Decode the next field (acl_text).
1979 if ((bp = strchr(attribs, '\0')) == (char *)NULL ||
1980 (used_bytes = (bp - jcr->xattr_data->content)) >= (total_bytes - 1)) {
1986 * Based on the filetype perform the correct action. We support most filetypes here, more
1987 * then the actual implementation on Solaris supports so some code may never get executed
1988 * due to limitations in the implementation.
1990 switch (st.st_mode & S_IFMT) {
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 (mkfifo(target_attrname, st.st_mode) < 0) {
1997 Mmsg3(jcr->errmsg, _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"),
1998 target_attrname, jcr->last_fname, be.bstrerror());
1999 Dmsg3(100, "Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n",
2000 target_attrname, jcr->last_fname, be.bstrerror());
2007 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2009 unlinkat(attrdirfd, target_attrname, 0);
2010 if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) {
2011 Mmsg3(jcr->errmsg, _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"),
2012 target_attrname, jcr->last_fname, be.bstrerror());
2013 Dmsg3(100, "Unable to mknod xattr %s on file \"%s\": ERR=%s\n",
2014 target_attrname, jcr->last_fname, be.bstrerror());
2020 * If its not the hidden_dir create the entry.
2021 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2023 if (!bstrcmp(target_attrname, ".")) {
2024 unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR);
2025 if (mkdir(target_attrname, st.st_mode) < 0) {
2026 Jmsg3(jcr, M_WARNING, 0, _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"),
2027 target_attrname, jcr->last_fname, be.bstrerror());
2028 Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n",
2029 target_attrname, jcr->last_fname, be.bstrerror());
2036 * See if this is a hard linked file. e.g. inum != 0
2041 unlinkat(attrdirfd, target_attrname, 0);
2042 if (link(linked_target, target_attrname) < 0) {
2043 Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"),
2044 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2045 Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n",
2046 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2051 * Successfully restored xattr.
2053 retval = bxattr_exit_ok;
2056 if ((bp = strchr(acl_text, '\0')) == (char *)NULL ||
2057 (used_bytes = (bp - jcr->xattr_data->content)) >= total_bytes) {
2061 if (used_bytes < (total_bytes - 1))
2065 * Restore the actual xattr.
2067 if (!is_extensible) {
2068 unlinkat(attrdirfd, target_attrname, 0);
2071 if ((attrfd = openat(attrdirfd, target_attrname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) {
2072 Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
2073 target_attrname, jcr->last_fname, be.bstrerror());
2074 Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
2075 target_attrname, jcr->last_fname, be.bstrerror());
2081 * Restore the actual data.
2083 if (st.st_size > 0) {
2084 used_bytes = (data - jcr->xattr_data->content);
2085 cnt = total_bytes - used_bytes;
2088 * Do a sanity check, the st.st_size should be the same as the number of bytes
2089 * we have available as data of the stream.
2091 if (cnt != st.st_size) {
2092 Mmsg2(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n"),
2093 target_attrname, jcr->last_fname);
2094 Dmsg2(100, "Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n",
2095 target_attrname, jcr->last_fname);
2100 cnt = write(attrfd, data, cnt);
2102 Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"),
2103 target_attrname, jcr->last_fname, be.bstrerror());
2104 Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n",
2105 target_attrname, jcr->last_fname, be.bstrerror());
2111 cnt = total_bytes - used_bytes;
2117 * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
2121 if (symlink(linked_target, target_attrname) < 0) {
2122 Mmsg4(jcr->errmsg, _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"),
2123 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2124 Dmsg4(100, "Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n",
2125 target_attrname, linked_target, jcr->last_fname, be.bstrerror());
2130 * Successfully restored xattr.
2132 retval = bxattr_exit_ok;
2139 * Restore owner and acl for non extensible attributes.
2141 if (!is_extensible) {
2142 if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0) {
2146 * Gentile way of the system saying this type of xattr layering is not supported.
2147 * But as this is not an error we return a positive return value.
2149 retval = bxattr_exit_ok;
2152 retval = bxattr_exit_ok;
2155 Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"),
2156 target_attrname, jcr->last_fname, be.bstrerror());
2157 Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n",
2158 target_attrname, jcr->last_fname, be.bstrerror());
2165 if (acl_text && *acl_text)
2166 if (solaris_restore_xattr_acl(jcr, attrfd, target_attrname, acl_text) != bxattr_exit_ok)
2168 #endif /* HAVE_ACL */
2171 * For a non extensible attribute restore access and modification time on the xattr.
2173 if (!is_extensible) {
2174 times[0].tv_sec = st.st_atime;
2175 times[0].tv_usec = 0;
2176 times[1].tv_sec = st.st_mtime;
2177 times[1].tv_usec = 0;
2179 if (futimesat(attrdirfd, target_attrname, times) < 0) {
2180 Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"),
2181 target_attrname, jcr->last_fname, be.bstrerror());
2182 Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n",
2183 target_attrname, jcr->last_fname, be.bstrerror());
2189 * Successfully restored xattr.
2191 retval = bxattr_exit_ok;
2195 Mmsg1(jcr->errmsg, _("Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n"),
2197 Dmsg1(100, "Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n",
2204 if (attrdirfd != -1) {
2213 static bxattr_exit_code solaris_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2216 bxattr_exit_code retval = bxattr_exit_ok;
2219 * First see if extended attributes or extensible attributes are present.
2220 * If not just pretend things went ok.
2222 if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0) {
2223 jcr->xattr_data->nr_saved = 0;
2224 jcr->xattr_data->link_cache = New(alist(10, not_owned_by_alist));
2227 * As we change the cwd in the save function save the current cwd
2228 * for restore after return from the solaris_save_xattrs function.
2230 getcwd(cwd, sizeof(cwd));
2231 retval = solaris_save_xattrs(jcr, NULL, NULL);
2233 delete jcr->xattr_data->link_cache;
2234 jcr->xattr_data->link_cache = NULL;
2239 static bxattr_exit_code solaris_parse_xattr_streams(JCR *jcr, int stream)
2242 bool is_extensible = false;
2243 bxattr_exit_code retval;
2246 * First make sure we can restore xattr on the filesystem.
2249 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
2250 case STREAM_XATTR_SOLARIS_SYS:
2251 if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0) {
2252 Qmsg1(jcr, M_WARNING, 0,
2253 _("Failed to restore extensible attributes on file \"%s\"\n"),
2255 Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n",
2257 return bxattr_exit_error;
2260 is_extensible = true;
2263 case STREAM_XATTR_SOLARIS:
2264 if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0) {
2265 Qmsg1(jcr, M_WARNING, 0,
2266 _("Failed to restore extended attributes on file \"%s\"\n"),
2268 Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n",
2270 return bxattr_exit_error;
2274 return bxattr_exit_error;
2278 * As we change the cwd in the restore function save the current cwd
2279 * for restore after return from the solaris_restore_xattrs function.
2281 getcwd(cwd, sizeof(cwd));
2282 retval = solaris_restore_xattrs(jcr, is_extensible);
2289 * Function pointers to the build and parse function to use for these xattrs.
2291 static bxattr_exit_code (*os_build_xattr_streams)(JCR *jcr, FF_PKT *ff_pkt) = solaris_build_xattr_streams;
2292 static bxattr_exit_code (*os_parse_xattr_streams)(JCR *jcr, int stream) = solaris_parse_xattr_streams;
2294 #endif /* defined(HAVE_SUN_OS) */
2297 * Entry points when compiled with support for XATTRs on a supported platform.
2299 bxattr_exit_code build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
2301 if (os_build_xattr_streams) {
2302 return (*os_build_xattr_streams)(jcr, ff_pkt);
2304 return bxattr_exit_error;
2307 bxattr_exit_code parse_xattr_streams(JCR *jcr, int stream)
2311 if (os_parse_xattr_streams) {
2313 * See if we can parse this stream, and ifso give it a try.
2315 for (cnt = 0; cnt < sizeof(os_default_xattr_streams) / sizeof(int); cnt++) {
2316 if (os_default_xattr_streams[cnt] == stream) {
2317 return (*os_parse_xattr_streams)(jcr, stream);
2322 * Issue a warning and discard the message. But pretend the restore was ok.
2324 Jmsg2(jcr, M_WARNING, 0,
2325 _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"),
2326 jcr->last_fname, stream);
2327 return bxattr_exit_error;