2 Bacula® - The Network Backup Solution
4 Copyright (C) 2008-2008 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:
40 * Written by Marco van Wieringen, November MMVIII
42 * Version $Id: xattr.c 7879 2008-10-23 10:12:36Z kerns $
50 * List of supported OSs.
52 #if !defined(HAVE_XATTR) /* Extended Attributes support is required, of course */ \
53 || !( defined(HAVE_DARWIN_OS) \
54 || defined(HAVE_FREEBSD_OS) \
55 || defined(HAVE_LINUX_OS) \
56 || defined(HAVE_NETBSD_OS) \
59 bool build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
61 Jmsg(jcr, M_FATAL, 0, _("XATTR support not configured for your machine.\n"));
65 bool parse_xattr_stream(JCR *jcr, int stream)
67 Jmsg(jcr, M_FATAL, 0, _("XATTR support not configured for your machine.\n"));
73 #ifdef HAVE_SYS_XATTR_H
74 #include <sys/xattr.h>
77 #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
78 #define lgetxattr getxattr
80 #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
81 #define lsetxattr setxattr
83 #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
84 #define llistxattr listxattr
88 * Send a XATTR stream to the SD.
90 static bool send_xattr_stream(JCR *jcr, int stream, int len)
92 BSOCK *sd = jcr->store_bsock;
94 #ifdef FD_NO_SEND_TEST
101 if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
102 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
109 * Send the buffer to the storage deamon
111 Dmsg1(400, "Backing up XATTR <%s>\n", jcr->xattr_data);
113 sd->msg = jcr->xattr_data;
118 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
124 jcr->JobBytes += sd->msglen;
126 if (!sd->signal(BNET_EOD)) {
127 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
133 Dmsg1(200, "XATTR of file: %s successfully backed up!\n", jcr->last_fname);
138 static void xattr_drop_internal_table(xattr_t *xattr_value_list)
140 xattr_t *current_xattr;
143 * Walk the list of xattrs and free allocated memory on traversing.
145 for (current_xattr = xattr_value_list;
146 current_xattr != (xattr_t *)NULL;
149 * See if we can shortcut.
151 if (current_xattr->magic != XATTR_MAGIC)
154 free(current_xattr->name);
156 if (current_xattr->value_length > 0)
157 free(current_xattr->value);
161 * Free the array of control structs.
163 free(xattr_value_list);
167 static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len, xattr_t *xattr_value_list)
169 xattr_t *current_xattr;
173 * Make sure the serialized stream fits in the poolmem buffer.
174 * We allocate some more to be sure the stream is gonna fit.
176 jcr->xattr_data = check_pool_memory_size(jcr->xattr_data, expected_serialize_len + 10);
177 ser_begin(jcr->xattr_data, expected_serialize_len + 10);
180 * Walk the list of xattrs and serialize the data.
182 for (current_xattr = xattr_value_list; current_xattr != (xattr_t *)NULL; current_xattr++) {
184 * See if we can shortcut.
186 if (current_xattr->magic != XATTR_MAGIC)
189 ser_uint32(current_xattr->magic);
190 ser_uint32(current_xattr->name_length);
191 ser_bytes(current_xattr->name, current_xattr->name_length);
193 ser_uint32(current_xattr->value_length);
194 ser_bytes(current_xattr->value, current_xattr->value_length);
197 ser_end(jcr->xattr_data, expected_serialize_len + 10);
199 return ser_length(jcr->xattr_data);
202 static bool generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt, int stream)
205 ssize_t xattr_list_len,
207 expected_serialize_len = 0,
209 char *xattr_list, *bp;
210 xattr_t *xattr_value_list, *current_xattr;
213 * First get the length of the available list with extended attributes.
215 xattr_list_len = llistxattr(jcr->last_fname, NULL, 0);
216 if (xattr_list_len < 0) {
218 Jmsg2(jcr, M_ERROR, 0, _("llistxattr error on file \"%s\": ERR=%s\n"),
219 jcr->last_fname, be.bstrerror());
220 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
221 jcr->last_fname, be.bstrerror());
223 } else if (xattr_list_len == 0) {
228 * Allocate room for the extented attribute list.
230 if ((xattr_list = (char *)malloc(xattr_list_len + 1)) == (char *)NULL) {
231 Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), xattr_list_len + 1);
235 memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
238 * Get the actual list of extended attributes names for a file.
240 xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len);
241 if (xattr_list_len < 0) {
243 Jmsg2(jcr, M_ERROR, 0, _("llistxattr error on file \"%s\": ERR=%s\n"),
244 jcr->last_fname, be.bstrerror());
245 Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
246 jcr->last_fname, be.bstrerror());
251 xattr_list[xattr_list_len] = '\0';
254 * Count the number of extended attributes on a file.
257 while ((bp - xattr_list) + 1 < xattr_list_len) {
260 bp = strchr(bp, '\0') + 1;
264 * Allocate enough room to hold all extended attributes.
265 * After allocating the storage make sure its empty by zeroing it.
267 if ((xattr_value_list = (xattr_t *)malloc(count * sizeof(xattr_t))) == (xattr_t *)NULL) {
268 Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), count * sizeof(xattr_t));
272 memset((caddr_t)xattr_value_list, 0, count * sizeof(xattr_t));
275 * Walk the list of extended attributes names and retrieve the data.
276 * We already count the bytes needed for serializing the stream later on.
278 current_xattr = xattr_value_list;
280 while ((bp - xattr_list) + 1 < xattr_list_len) {
281 #if defined(HAVE_LINUX_OS)
283 * On Linux you also get the acls in the extented attribute list.
284 * So we check if we are already backing up acls and if we do we
285 * don't store the extended attribute with the same info.
287 if (ff_pkt->flags & FO_ACL && !strcmp(bp, "system.posix_acl_access")) {
288 bp = strchr(bp, '\0') + 1;
295 * Each xattr valuepair starts with a magic so we can parse it easier.
297 current_xattr->magic = XATTR_MAGIC;
298 expected_serialize_len += sizeof(current_xattr->magic);
301 * Allocate space for storing the name.
303 current_xattr->name_length = strlen(bp);
304 if ((current_xattr->name = (char *)malloc(current_xattr->name_length)) == (char *)NULL) {
305 Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), current_xattr->name_length);
309 memcpy((caddr_t)current_xattr->name, (caddr_t)bp, current_xattr->name_length);
311 expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
314 * First see how long the value is for the extended attribute.
316 xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0);
317 if (xattr_value_len < 0) {
319 Jmsg2(jcr, M_ERROR, 0, _("lgetxattr error on file \"%s\": ERR=%s\n"),
320 jcr->last_fname, be.bstrerror());
321 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
322 jcr->last_fname, be.bstrerror());
328 * Allocate space for storing the value.
330 if ((current_xattr->value = (char *)malloc(xattr_value_len)) == (char *)NULL) {
331 Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), xattr_value_len);
335 memset((caddr_t)current_xattr->value, 0, xattr_value_len);
337 xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
338 if (xattr_value_len < 0) {
340 Jmsg2(jcr, M_ERROR, 0, _("lgetxattr error on file \"%s\": ERR=%s\n"),
341 jcr->last_fname, be.bstrerror());
342 Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
343 jcr->last_fname, be.bstrerror());
349 * Store the actual length of the value.
351 current_xattr->value_length = xattr_value_len;
353 expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
359 bp = strchr(bp, '\0') + 1;
363 * Serialize the datastream.
365 if ((serialize_len = serialize_xattr_stream(jcr, expected_serialize_len,
366 xattr_value_list)) < expected_serialize_len) {
367 Jmsg1(jcr, M_ERROR, 0, _("failed to serialize extended attributes on file \"%s\"\n"),
373 xattr_drop_internal_table(xattr_value_list);
377 * Send the datastream to the SD.
379 return send_xattr_stream(jcr, stream, serialize_len);
382 xattr_drop_internal_table(xattr_value_list);
388 static bool generic_xattr_parse_streams(JCR *jcr)
391 xattr_t current_xattr;
395 * Parse the stream and perform the setxattr calls on the file.
397 * Start unserializing the data. We keep on looping while we have not
398 * unserialized all bytes in the stream.
400 unser_begin(jcr->xattr_data, jcr->xattr_data_len);
401 while (unser_length(jcr->xattr_data) < jcr->xattr_data_len) {
403 * First make sure the magic is present. This way we can easily catch corruption.
404 * Any missing MAGIC is fatal we do NOT try to continue.
406 unser_uint32(current_xattr.magic);
407 if (current_xattr.magic != XATTR_MAGIC) {
408 Jmsg1(jcr, M_ERROR, 0, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"),
415 * Decode the valuepair. First decode the length of the name.
417 unser_uint32(current_xattr.name_length);
420 * Allocate room for the name and decode its content.
422 if ((current_xattr.name = (char *)malloc(current_xattr.name_length + 1)) == (char *)NULL) {
423 Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), current_xattr.name_length + 1);
427 unser_bytes(current_xattr.name, current_xattr.name_length);
430 * The xattr_name needs to be null terminated for lsetxattr.
432 current_xattr.name[current_xattr.name_length] = '\0';
435 * Decode the value length.
437 unser_uint32(current_xattr.value_length);
440 * Allocate room for the value and decode its content.
442 if ((current_xattr.value = (char *)malloc(current_xattr.value_length)) == (char *)NULL) {
443 Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), current_xattr.value_length);
447 unser_bytes(current_xattr.value, current_xattr.value_length);
450 * Try to set the extended attribute on the file.
451 * If we fail to set this attribute we flag the error but its not fatal,
452 * we try to restore the other extended attributes too.
454 if (lsetxattr(jcr->last_fname, current_xattr.name, current_xattr.value,
455 current_xattr.value_length, 0) != 0) {
457 Jmsg2(jcr, M_ERROR, 0, _("lsetxattr error on file \"%s\": ERR=%s\n"),
458 jcr->last_fname, be.bstrerror());
459 Dmsg2(100, "lsetxattr error file=%s ERR=%s\n",
460 jcr->last_fname, be.bstrerror());
463 * Reset the return flag to false to indicate one or more extended attributes
464 * could not be restored.
470 * Free the temporary buffers.
472 free(current_xattr.name);
473 free(current_xattr.value);
476 unser_end(jcr->xattr_data, jcr->xattr_data_len);
480 #if defined(HAVE_DARWIN_OS)
481 static bool darwin_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
483 return generic_xattr_build_streams(jcr, ff_pkt, STREAM_XATTR_DARWIN);
486 static bool darwin_parse_xattr_stream(JCR *jcr, int stream)
489 case STREAM_XATTR_DARWIN:
490 return generic_xattr_parse_streams(jcr);
493 #elif defined(HAVE_FREEBSD_OS)
494 static bool freebsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
496 return generic_xattr_build_streams(jcr, ff_pkt, STREAM_XATTR_FREEBSD);
499 static bool freebsd_parse_xattr_stream(JCR *jcr, int stream)
502 case STREAM_XATTR_FREEBSD:
503 return generic_xattr_parse_streams(jcr);
506 #elif defined(HAVE_LINUX_OS)
507 static bool linux_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
509 return generic_xattr_build_streams(jcr, ff_pkt, STREAM_XATTR_LINUX);
512 static bool linux_parse_xattr_stream(JCR *jcr, int stream)
515 case STREAM_XATTR_LINUX:
516 return generic_xattr_parse_streams(jcr);
520 #elif defined(HAVE_NETBSD_OS)
521 static bool netbsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
523 return generic_xattr_build_streams(jcr, ff_pkt, STREAM_XATTR_NETBSD);
526 static bool netbsd_parse_xattr_stream(JCR *jcr, int stream)
529 case STREAM_XATTR_NETBSD:
530 return generic_xattr_parse_streams(jcr);
536 bool build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
538 #if defined(HAVE_DARWIN_OS)
539 return darwin_build_xattr_streams(jcr, ff_pkt);
540 #elif defined(HAVE_FREEBSD_OS)
541 return freebsd_build_xattr_streams(jcr, ff_pkt);
542 #elif defined(HAVE_LINUX_OS)
543 return linux_build_xattr_streams(jcr, ff_pkt);
544 #elif defined(HAVE_NETBSD_OS)
545 return netbsd_build_xattr_streams(jcr, ff_pkt);
549 bool parse_xattr_stream(JCR *jcr, int stream)
552 * Based on the stream being passed in dispatch to the right function
553 * for parsing and restoring a specific xattr. The platform determines
554 * which streams are recognized and parsed and which are handled by
555 * the default case and ignored. As only one of the platform defines
556 * is true per compile we never end up with duplicate switch values.
559 #if defined(HAVE_DARWIN_OS)
560 case STREAM_XATTR_DARWIN:
561 return darwin_parse_xattr_stream(jcr, stream);
562 #elif defined(HAVE_FREEBSD_OS)
563 case STREAM_XATTR_FREEBSD:
564 return freebsd_parse_xattr_stream(jcr, stream);
565 #elif defined(HAVE_LINUX_OS)
566 case STREAM_XATTR_LINUX:
567 return linux_parse_xattr_stream(jcr, stream);
568 #elif defined(HAVE_NETBSD_OS)
569 case STREAM_XATTR_NETBSD:
570 return netbsd_parse_xattr_stream(jcr, stream);
574 * Issue a warning and discard the message. But pretend the restore was ok.
576 Qmsg2(jcr, M_WARNING, 0,
577 _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"),
578 jcr->last_fname, stream);
580 } /* end switch (stream) */