]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/xattr.c
Change to Copyright (C) 2007-2009
[bacula/bacula] / bacula / src / filed / xattr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2008-2009 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  * Functions to handle Extended Attributes for bacula.
30  *
31  * Extended Attributes are so OS specific we only restore Extended Attributes if
32  * they were saved using a filed on the same platform.
33  *
34  * Currently we support the following OSes:
35  *   - FreeBSD (Extended Attributes)
36  *   - Darwin (Extended Attributes)
37  *   - Linux (Extended Attributes)
38  *   - NetBSD (Extended Attributes)
39  *   - Solaris (Extended Attributes and Extensible Attributes)
40  *
41  *   Written by Marco van Wieringen, November MMVIII
42  *
43  *   Version $Id$
44  */
45
46 #include "bacula.h"
47 #include "filed.h"
48 #include "xattr.h"
49
50 /*
51  * List of supported OSes. Everything outside that gets stub functions.
52  * Also when XATTR support is explicitly disabled.
53  */
54 #if !defined(HAVE_XATTR)          /* Extended Attributes support is required, of course */ \
55    || !( defined(HAVE_DARWIN_OS)  /* OSX has XATTR support  using getxattr etc. */ \
56       || defined(HAVE_FREEBSD_OS) /* FreeBSD has XATTR support using lgetxattr etc. */ \
57       || defined(HAVE_LINUX_OS)   /* Linux has XATTR support using the lgetxattr etc. */ \
58       || defined(HAVE_NETBSD_OS)  /* NetBSD has XATTR support using the lgetxattr etc. */ \
59       || defined(HAVE_SUN_OS)     /* Solaris has XATTR support using attropen etc. */ \
60         )
61
62 bool build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
63 {
64    Jmsg(jcr, M_FATAL, 0, _("XATTR support not configured for your machine.\n"));
65    return false;
66 }
67
68 bool parse_xattr_stream(JCR *jcr, int stream)
69 {
70    Jmsg(jcr, M_FATAL, 0, _("XATTR support not configured for your machine.\n"));
71    return false;
72 }
73
74 #else
75
76 /*
77  * Send a XATTR stream to the SD.
78  */
79 static bool send_xattr_stream(JCR *jcr, int stream)
80 {
81    BSOCK *sd = jcr->store_bsock;
82    POOLMEM *msgsave;
83 #ifdef FD_NO_SEND_TEST
84    return true;
85 #endif
86
87    /*
88     * Send header
89     */
90    if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
91       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
92             sd->bstrerror());
93
94       return false;
95    }
96
97    /*
98     * Send the buffer to the storage deamon
99     */
100    Dmsg1(400, "Backing up XATTR <%s>\n", jcr->xattr_data);
101    msgsave = sd->msg;
102    sd->msg = jcr->xattr_data;
103    sd->msglen = jcr->xattr_data_len;
104    if (!sd->send()) {
105       sd->msg = msgsave;
106       sd->msglen = 0;
107       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
108             sd->bstrerror());
109
110       return false;
111    }
112
113    jcr->JobBytes += sd->msglen;
114    sd->msg = msgsave;
115    if (!sd->signal(BNET_EOD)) {
116       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
117             sd->bstrerror());
118
119       return false;
120    }
121
122    Dmsg1(200, "XATTR of file: %s successfully backed up!\n", jcr->last_fname);
123
124    return true;
125 }
126
127 /*
128  * This is a supported OS, See what kind of interface we should use.
129  * Start with the generic interface used by most OS-es.
130  */
131 #if defined(HAVE_DARWIN_OS) \
132    || defined(HAVE_FREEBSD_OS) \
133    || defined(HAVE_LINUX_OS) \
134    || defined(HAVE_NETBSD_OS)
135        
136 #ifdef HAVE_SYS_XATTR_H
137 #include <sys/xattr.h>
138 #endif
139
140 /*
141  * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
142  * listxattr, getxattr and setxattr with an extra options argument
143  * which mimics the l variants of the functions when we specify
144  * XATTR_NOFOLLOW as the options value.
145  */
146 #if defined(HAVE_DARWIN_OS)
147    #define llistxattr(path, list, size) listxattr((path), (list), (size), XATTR_NOFOLLOW)
148    #define lgetxattr(path, name, value, size) getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
149    #define lsetxattr(path, name, value, size, flags) setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
150 #else
151    /*
152     * Fallback to the non l-functions when those are not available.
153     */
154    #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
155    #define lgetxattr getxattr
156    #endif
157    #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
158    #define lsetxattr setxattr
159    #endif
160    #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
161    #define llistxattr listxattr
162    #endif
163 #endif
164
165 static void xattr_drop_internal_table(xattr_t *xattr_value_list)
166 {
167    xattr_t *current_xattr;
168
169    /*
170     * Walk the list of xattrs and free allocated memory on traversing.
171     */
172    for (current_xattr = xattr_value_list;
173         current_xattr != (xattr_t *)NULL;
174         current_xattr++) {
175       /*
176        * See if we can shortcut.
177        */
178       if (current_xattr->magic != XATTR_MAGIC)
179          break;
180
181       free(current_xattr->name);
182
183       if (current_xattr->value_length > 0)
184          free(current_xattr->value);
185    }
186
187    /*
188     * Free the array of control structs.
189     */
190    free(xattr_value_list);
191 }
192
193 /*
194  * The xattr stream for OSX, FreeBSD, Linux and NetBSD is a serialized stream of bytes
195  * which encodes one or more xattr_t structures.
196  *
197  * The Serialized stream consists of the following elements:
198  *    magic - A magic string which makes it easy to detect any binary incompatabilites
199  *    name_length - The length of the following xattr name
200  *    name - The name of the extended attribute
201  *    value_length - The length of the following xattr data
202  *    value - The actual content of the extended attribute
203  *
204  * This is repeated 1 or more times.
205  * 
206  */
207 static uint32_t serialize_xattr_stream(JCR *jcr, uint32_t expected_serialize_len, xattr_t *xattr_value_list)
208 {
209    xattr_t *current_xattr;
210    ser_declare;
211
212    /*
213     * Make sure the serialized stream fits in the poolmem buffer.
214     * We allocate some more to be sure the stream is gonna fit.
215     */
216    jcr->xattr_data = check_pool_memory_size(jcr->xattr_data, expected_serialize_len + 10);
217    ser_begin(jcr->xattr_data, expected_serialize_len + 10);
218
219    /*
220     * Walk the list of xattrs and serialize the data.
221     */
222    for (current_xattr = xattr_value_list; current_xattr != (xattr_t *)NULL; current_xattr++) {
223       /*
224        * See if we can shortcut.
225        */
226       if (current_xattr->magic != XATTR_MAGIC)
227          break;
228
229       ser_uint32(current_xattr->magic);
230       ser_uint32(current_xattr->name_length);
231       ser_bytes(current_xattr->name, current_xattr->name_length);
232
233       ser_uint32(current_xattr->value_length);
234       ser_bytes(current_xattr->value, current_xattr->value_length);
235    }
236
237    ser_end(jcr->xattr_data, expected_serialize_len + 10);
238    jcr->xattr_data_len = ser_length(jcr->xattr_data);
239
240    return jcr->xattr_data_len;
241 }
242
243 static bool generic_xattr_build_streams(JCR *jcr, FF_PKT *ff_pkt, int stream)
244 {
245    int count = 0;
246    int32_t xattr_list_len,
247            xattr_value_len;
248    uint32_t expected_serialize_len = 0;
249    char *xattr_list, *bp;
250    xattr_t *xattr_value_list, *current_xattr;
251
252    /*
253     * First get the length of the available list with extended attributes.
254     */
255    xattr_list_len = llistxattr(jcr->last_fname, NULL, 0);
256    if (xattr_list_len < 0) {
257       berrno be;
258       Jmsg2(jcr, M_ERROR, 0, _("llistxattr error on file \"%s\": ERR=%s\n"),
259          jcr->last_fname, be.bstrerror());
260       Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
261          jcr->last_fname, be.bstrerror());
262       return false;
263    } else if (xattr_list_len == 0) {
264       return true;
265    }
266
267    /*
268     * Allocate room for the extented attribute list.
269     */
270    if ((xattr_list = (char *)malloc(xattr_list_len + 1)) == (char *)NULL) {
271       Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), xattr_list_len + 1);
272
273       return false;
274    }
275    memset((caddr_t)xattr_list, 0, xattr_list_len + 1);
276
277    /*
278     * Get the actual list of extended attributes names for a file.
279     */
280    xattr_list_len = llistxattr(jcr->last_fname, xattr_list, xattr_list_len);
281    if (xattr_list_len < 0) {
282       berrno be;
283       Jmsg2(jcr, M_ERROR, 0, _("llistxattr error on file \"%s\": ERR=%s\n"),
284          jcr->last_fname, be.bstrerror());
285       Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
286          jcr->last_fname, be.bstrerror());
287
288       free(xattr_list);
289       return false;
290    }
291    xattr_list[xattr_list_len] = '\0';
292
293    /*
294     * Count the number of extended attributes on a file.
295     */
296    bp = xattr_list;
297    while ((bp - xattr_list) + 1 < xattr_list_len) {
298       count++;
299
300       bp = strchr(bp, '\0') + 1;
301    }
302
303    /*
304     * Allocate enough room to hold all extended attributes.
305     * After allocating the storage make sure its empty by zeroing it.
306     */
307    if ((xattr_value_list = (xattr_t *)malloc(count * sizeof(xattr_t))) == (xattr_t *)NULL) {
308       Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), count * sizeof(xattr_t));
309
310       return false;
311    }
312    memset((caddr_t)xattr_value_list, 0, count * sizeof(xattr_t));
313
314    /*
315     * Walk the list of extended attributes names and retrieve the data.
316     * We already count the bytes needed for serializing the stream later on.
317     */
318    current_xattr = xattr_value_list;
319    bp = xattr_list;
320    while ((bp - xattr_list) + 1 < xattr_list_len) {
321 #if defined(HAVE_LINUX_OS)
322       /*
323        * On Linux you also get the acls in the extented attribute list.
324        * So we check if we are already backing up acls and if we do we
325        * don't store the extended attribute with the same info.
326        */
327       if (ff_pkt->flags & FO_ACL && !strcmp(bp, "system.posix_acl_access")) {
328          bp = strchr(bp, '\0') + 1;
329
330          continue;
331       }
332 #endif
333
334       /*
335        * Each xattr valuepair starts with a magic so we can parse it easier.
336        */
337       current_xattr->magic = XATTR_MAGIC;
338       expected_serialize_len += sizeof(current_xattr->magic);
339
340       /*
341        * Allocate space for storing the name.
342        */
343       current_xattr->name_length = strlen(bp);
344       if ((current_xattr->name = (char *)malloc(current_xattr->name_length)) == (char *)NULL) {
345          Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), current_xattr->name_length);
346
347          goto bail_out;
348       }
349       memcpy((caddr_t)current_xattr->name, (caddr_t)bp, current_xattr->name_length);
350
351       expected_serialize_len += sizeof(current_xattr->name_length) + current_xattr->name_length;
352
353       /*
354        * First see how long the value is for the extended attribute.
355        */
356       xattr_value_len = lgetxattr(jcr->last_fname, bp, NULL, 0);
357       if (xattr_value_len < 0) {
358          berrno be;
359          Jmsg2(jcr, M_ERROR, 0, _("lgetxattr error on file \"%s\": ERR=%s\n"),
360             jcr->last_fname, be.bstrerror());
361          Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
362             jcr->last_fname, be.bstrerror());
363
364          goto bail_out;
365       }
366
367       /*
368        * Allocate space for storing the value.
369        */
370       if ((current_xattr->value = (char *)malloc(xattr_value_len)) == (char *)NULL) {
371          Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), xattr_value_len);
372
373          goto bail_out;
374       }
375       memset((caddr_t)current_xattr->value, 0, xattr_value_len);
376
377       xattr_value_len = lgetxattr(jcr->last_fname, bp, current_xattr->value, xattr_value_len);
378       if (xattr_value_len < 0) {
379          berrno be;
380          Jmsg2(jcr, M_ERROR, 0, _("lgetxattr error on file \"%s\": ERR=%s\n"),
381             jcr->last_fname, be.bstrerror());
382          Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
383             jcr->last_fname, be.bstrerror());
384
385          goto bail_out;
386       }
387
388       /*
389        * Store the actual length of the value.
390        */
391       current_xattr->value_length = xattr_value_len;
392       expected_serialize_len += sizeof(current_xattr->value_length) + current_xattr->value_length;
393
394       /*
395        * Protect ourself against things getting out of hand.
396        */
397       if (expected_serialize_len >= MAX_XATTR_STREAM) {
398          Jmsg2(jcr, M_ERROR, 0, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
399             jcr->last_fname, MAX_XATTR_STREAM);
400
401          goto bail_out;
402       }
403       
404       /*
405        * Next attribute.
406        */
407       current_xattr++;
408       bp = strchr(bp, '\0') + 1;
409    }
410
411    /*
412     * Serialize the datastream.
413     */
414    if (serialize_xattr_stream(jcr, expected_serialize_len, xattr_value_list) < expected_serialize_len) {
415       Jmsg1(jcr, M_ERROR, 0, _("Failed to serialize extended attributes on file \"%s\"\n"),
416          jcr->last_fname);
417       Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n",
418          jcr->last_fname);
419
420       goto bail_out;
421    }
422
423    xattr_drop_internal_table(xattr_value_list);
424    free(xattr_list);
425
426    /*
427     * Send the datastream to the SD.
428     */
429    return send_xattr_stream(jcr, stream);
430
431 bail_out:
432    xattr_drop_internal_table(xattr_value_list);
433    free(xattr_list);
434
435    return false;
436 }
437
438 static bool generic_xattr_parse_streams(JCR *jcr)
439 {
440    unser_declare;
441    xattr_t current_xattr;
442    bool retval = true;
443
444    /*
445     * Parse the stream and perform the setxattr calls on the file.
446     *
447     * Start unserializing the data. We keep on looping while we have not
448     * unserialized all bytes in the stream.
449     */
450    unser_begin(jcr->xattr_data, jcr->xattr_data_len);
451    while (unser_length(jcr->xattr_data) < jcr->xattr_data_len) {
452       /*
453        * First make sure the magic is present. This way we can easily catch corruption.
454        * Any missing MAGIC is fatal we do NOT try to continue.
455        */
456       unser_uint32(current_xattr.magic);
457       if (current_xattr.magic != XATTR_MAGIC) {
458          Jmsg1(jcr, M_ERROR, 0, _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"),
459             jcr->last_fname);
460          Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n",
461             jcr->last_fname);
462
463          return false;
464       }
465
466       /*
467        * Decode the valuepair. First decode the length of the name.
468        */
469       unser_uint32(current_xattr.name_length);
470       
471       /*
472        * Allocate room for the name and decode its content.
473        */
474       if ((current_xattr.name = (char *)malloc(current_xattr.name_length + 1)) == (char *)NULL) {
475          Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), current_xattr.name_length + 1);
476
477          return false;
478       }
479       unser_bytes(current_xattr.name, current_xattr.name_length);
480
481       /*
482        * The xattr_name needs to be null terminated for lsetxattr.
483        */
484       current_xattr.name[current_xattr.name_length] = '\0';
485
486       /*
487        * Decode the value length.
488        */
489       unser_uint32(current_xattr.value_length);
490
491       /*
492        * Allocate room for the value and decode its content.
493        */
494       if ((current_xattr.value = (char *)malloc(current_xattr.value_length)) == (char *)NULL) {
495          Emsg1(M_ABORT, 0, _("Out of memory requesting %d bytes\n"), current_xattr.value_length);
496
497          return false;
498       }
499       unser_bytes(current_xattr.value, current_xattr.value_length);
500
501       /*
502        * Try to set the extended attribute on the file.
503        * If we fail to set this attribute we flag the error but its not fatal,
504        * we try to restore the other extended attributes too.
505        */
506       if (lsetxattr(jcr->last_fname, current_xattr.name, current_xattr.value,
507          current_xattr.value_length, 0) != 0) {
508          berrno be;
509          Jmsg2(jcr, M_ERROR, 0, _("lsetxattr error on file \"%s\": ERR=%s\n"),
510             jcr->last_fname, be.bstrerror());
511          Dmsg2(100, "lsetxattr error file=%s ERR=%s\n",
512             jcr->last_fname, be.bstrerror());
513
514          /*
515           * Reset the return flag to false to indicate one or more extended attributes
516           * could not be restored.
517           */
518          retval = false;
519       }
520
521       /*
522        * Free the temporary buffers.
523        */
524       free(current_xattr.name);
525       free(current_xattr.value);
526    }
527
528    unser_end(jcr->xattr_data, jcr->xattr_data_len);
529    return retval;
530 }
531
532 #if defined(HAVE_DARWIN_OS)
533 static bool darwin_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
534 {
535    return generic_xattr_build_streams(jcr, ff_pkt, STREAM_XATTR_DARWIN);
536 }
537
538 static bool darwin_parse_xattr_stream(JCR *jcr, int stream)
539 {
540    switch (stream) {
541    case STREAM_XATTR_DARWIN:
542       return generic_xattr_parse_streams(jcr);
543    default:
544       return false;
545    }
546 }
547 #elif defined(HAVE_FREEBSD_OS)
548 static bool freebsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
549 {
550    return generic_xattr_build_streams(jcr, ff_pkt, STREAM_XATTR_FREEBSD);
551 }
552
553 static bool freebsd_parse_xattr_stream(JCR *jcr, int stream)
554 {
555    switch (stream) {
556    case STREAM_XATTR_FREEBSD:
557       return generic_xattr_parse_streams(jcr);
558    default:
559       return false;
560    }
561 }
562 #elif defined(HAVE_LINUX_OS)
563 static bool linux_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
564 {
565    return generic_xattr_build_streams(jcr, ff_pkt, STREAM_XATTR_LINUX);
566 }
567
568 static bool linux_parse_xattr_stream(JCR *jcr, int stream)
569 {
570    switch (stream) {
571    case STREAM_XATTR_LINUX:
572       return generic_xattr_parse_streams(jcr);
573    default:
574       return false;
575    }
576 }
577 #elif defined(HAVE_NETBSD_OS)
578 static bool netbsd_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
579 {
580    return generic_xattr_build_streams(jcr, ff_pkt, STREAM_XATTR_NETBSD);
581 }
582
583 static bool netbsd_parse_xattr_stream(JCR *jcr, int stream)
584 {
585    switch (stream) {
586    case STREAM_XATTR_NETBSD:
587       return generic_xattr_parse_streams(jcr);
588    default:
589       return false;
590    }
591 }
592 #endif
593 #elif defined(HAVE_SUN_OS)
594 /*
595  * Solaris extended attributes were introduced in Solaris 9
596  * by PSARC 1999/209
597  *
598  * Solaris extensible attributes were introduced in OpenSolaris
599  * by PSARC 2007/315 Solaris extensible attributes are also
600  * sometimes called extended system attributes.
601  *
602  * man fsattr(5) on Solaris gives a wealth of info. The most
603  * important bits are:
604  *
605  * Attributes are logically supported as files within the  file
606  * system.   The  file  system  is  therefore augmented with an
607  * orthogonal name space of file attributes. Any file  (includ-
608  * ing  attribute files) can have an arbitrarily deep attribute
609  * tree associated with it. Attribute values  are  accessed  by
610  * file descriptors obtained through a special attribute inter-
611  * face.  This logical view of "attributes as files" allows the
612  * leveraging  of  existing file system interface functionality
613  * to support the construction, deletion, and  manipulation  of
614  * attributes.
615  *
616  * The special files  "."  and  ".."  retain  their  accustomed
617  * semantics within the attribute hierarchy.  The "." attribute
618  * file refers to the current directory and the ".."  attribute
619  * file  refers to the parent directory.  The unnamed directory
620  * at the head of each attribute tree is considered the "child"
621  * of  the  file it is associated with and the ".." file refers
622  * to the associated file.  For  any  non-directory  file  with
623  * attributes,  the  ".." entry in the unnamed directory refers
624  * to a file that is not a directory.
625  *
626  * Conceptually, the attribute model is fully general. Extended
627  * attributes  can  be  any  type of file (doors, links, direc-
628  * tories, and so forth) and can even have their own attributes
629  * (fully  recursive).   As a result, the attributes associated
630  * with a file could be an arbitrarily deep directory hierarchy
631  * where each attribute could have an equally complex attribute
632  * tree associated with it.  Not all implementations  are  able
633  * to,  or  want to, support the full model. Implementation are
634  * therefore permitted to reject operations that are  not  sup-
635  * ported.   For  example,  the implementation for the UFS file
636  * system allows only regular files as attributes (for example,
637  * no sub-directories) and rejects attempts to place attributes
638  * on attributes.
639  *
640  * The following list details the operations that are  rejected
641  * in the current implementation:
642  *
643  * link                     Any attempt to create links between
644  *                          attribute  and  non-attribute space
645  *                          is rejected  to  prevent  security-
646  *                          related   or   otherwise  sensitive
647  *                          attributes from being exposed,  and
648  *                          therefore  manipulable,  as regular
649  *                          files.
650  *
651  * rename                   Any  attempt  to   rename   between
652  *                          attribute  and  non-attribute space
653  *                          is rejected to prevent  an  already
654  *                          linked  file from being renamed and
655  *                          thereby circumventing the link res-
656  *                          triction above.
657  *
658  * mkdir, symlink, mknod    Any  attempt  to  create  a   "non-
659  *                          regular" file in attribute space is
660  *                          rejected to reduce the  functional-
661  *                          ity,  and  therefore  exposure  and
662  *                          risk, of  the  initial  implementa-
663  *                          tion.
664  *
665  * The entire available name space has been allocated to  "gen-
666  * eral use" to bring the implementation in line with the NFSv4
667  * draft standard [NFSv4]. That standard defines "named  attri-
668  * butes"  (equivalent  to Solaris Extended Attributes) with no
669  * naming restrictions.  All Sun  applications  making  use  of
670  * opaque extended attributes will use the prefix "SUNW".
671  *
672  */
673 #ifdef HAVE_SYS_ATTR_H
674 #include <sys/attr.h>
675 #endif
676
677 #ifdef HAVE_ATTR_H
678 #include <attr.h>
679 #endif
680
681 #ifdef HAVE_SYS_NVPAIR_H
682 #include <sys/nvpair.h>
683 #endif
684
685 #ifdef HAVE_SYS_ACL_H
686 #include <sys/acl.h>
687 #endif
688
689 /*
690  * This is the count of xattrs saved on a certain file, it gets reset
691  * on each new file processed and is used to see if we need to send
692  * the hidden xattr dir data. We only send that data when we encounter
693  * an other xattr on the file.
694  */
695 static int nr_xattr_saved = 0;
696 static char toplevel_hidden_dir_xattr_data[MAXSTRING];
697 static int toplevel_hidden_dir_xattr_data_len;
698
699 /*
700  * This code creates a temporary cache with entries for each xattr which has
701  * a link count > 1 (which indicates it has one or more hard linked counterpart(s))
702  */
703 static xattr_link_cache_entry_t *xattr_link_cache_head = NULL,
704                                 *xattr_link_cache_tail = NULL;
705
706 static struct xattr_link_cache_entry *find_xattr_link_cache_entry(ino_t inum)
707 {
708    xattr_link_cache_entry_t *ptr;
709
710    for (ptr = xattr_link_cache_head; ptr != NULL; ptr = ptr->next)
711       if (ptr->inum == inum)
712          return ptr;
713
714    return NULL;
715 }
716
717 static void add_xattr_link_cache_entry(ino_t inum, char *target)
718 {
719    xattr_link_cache_entry_t *ptr;
720
721    if ((ptr = (xattr_link_cache_entry_t *)malloc(sizeof(struct xattr_link_cache_entry))) != NULL) {
722       memset((caddr_t)ptr, 0, sizeof(struct xattr_link_cache_entry));
723       ptr->inum = inum;
724       strncpy(ptr->target, target, sizeof(ptr->target));
725       if (xattr_link_cache_head == NULL)
726          xattr_link_cache_head = ptr;
727       xattr_link_cache_tail = ptr;
728    }
729 }
730
731 static void drop_xattr_link_cache(void)
732 {
733    xattr_link_cache_entry_t *ptr, *next;
734
735    for (ptr = xattr_link_cache_tail; ptr != NULL; ptr = next) {
736       next = ptr->next;
737       free(ptr);
738    }
739
740    xattr_link_cache_head = NULL;
741    xattr_link_cache_tail = NULL;
742 }
743
744 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
745 /*
746  * This function returns true if a non default extended system attribute
747  * list is associated with fd and returns false when an error has occured
748  * or when only extended system attributes other than archive,
749  * av_modified or crtime are set.
750  *
751  * The function returns true for the following cases:
752  *
753  * - any extended system attribute other than the default attributes
754  *   ('archive', 'av_modified' and 'crtime') is set
755  * - nvlist has NULL name string
756  * - nvpair has data type of 'nvlist'
757  * - default data type.
758  */
759 static bool solaris_has_non_transient_extensible_attributes(int fd)
760 {
761    boolean_t value;
762    data_type_t type;
763    nvlist_t *response;
764    nvpair_t *pair;
765    f_attr_t fattr;
766    char *name;
767    bool retval = false;
768
769    if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) {
770       return false;
771    }
772
773    pair = NULL;
774    while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
775       name = nvpair_name(pair);
776
777       if (name != NULL) {
778          fattr = name_to_attr(name);
779       } else {
780          retval = true;
781          goto cleanup;
782       }
783
784       type = nvpair_type(pair);
785       switch (type) {
786       case DATA_TYPE_BOOLEAN_VALUE:
787          if (nvpair_value_boolean_value(pair, &value) != 0) {
788             continue;
789          }
790          if (value && fattr != F_ARCHIVE &&
791                       fattr != F_AV_MODIFIED) {
792             retval = true;
793             goto cleanup;
794          }
795          break;
796       case DATA_TYPE_UINT64_ARRAY:
797          if (fattr != F_CRTIME) {
798             retval = true;
799             goto cleanup;
800          }
801          break;
802       case DATA_TYPE_NVLIST:
803       default:
804          retval = true;
805          goto cleanup;
806       }
807    }
808
809 cleanup:
810    if (response != NULL)
811       nvlist_free(response);
812
813    return retval;
814 }
815 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
816
817 #if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL)
818 /*
819  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
820  * There is no need to store those acls as we already store the stat bits too.
821  */
822 static bool acl_is_trivial(int count, aclent_t *entries)
823 {
824    int n;
825    aclent_t *ace;
826
827    for (n = 0; n < count; n++) {
828       ace = &entries[n];
829
830       if (!(ace->a_type == USER_OBJ ||
831             ace->a_type == GROUP_OBJ ||
832             ace->a_type == OTHER_OBJ ||
833             ace->a_type == CLASS_OBJ))
834         return false;
835    }
836
837    return true;
838 }
839 #endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */
840
841 static bool solaris_archive_xattr_acl(JCR *jcr, int fd, const char *attrname, char **acl_text)
842 {
843 #ifdef HAVE_ACL
844 #ifdef HAVE_EXTENDED_ACL
845    int flags;
846    acl_t *aclp = NULL;
847
848    /*
849     * See if this attribute has an ACL
850     */
851    if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0) ||
852        pathconf(attrname, _PC_ACL_ENABLED) > 0) {
853       /*
854        * See if there is a non trivial acl on the file.
855        */
856       if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0) ||
857            acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) {
858          berrno be;
859          Jmsg3(jcr, M_ERROR, 0, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
860             attrname, jcr->last_fname, be.bstrerror());
861          Dmsg3(100, "facl_get/acl_get of xattr %s on \"%s\" failed: ERR=%s\n",
862             attrname, jcr->last_fname, be.bstrerror());
863
864          return false;
865       }
866
867       if (aclp != NULL) {
868 #if defined(ACL_SID_FMT)
869          /*
870           * New format flag added in newer Solaris versions.
871           */
872          flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
873 #else
874          flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
875 #endif /* ACL_SID_FMT */
876
877          *acl_text = acl_totext(aclp, flags);
878          acl_free(aclp);
879       } else {
880          *acl_text = NULL;
881       }
882    } else {
883       *acl_text = NULL;
884    }
885
886    return true;
887 #else /* HAVE_EXTENDED_ACL */
888    int n;
889    aclent_t *acls = NULL;
890
891    /*
892     * See if this attribute has an ACL
893     */
894    if (fd != -1)
895       n = facl(fd, GETACLCNT, 0, NULL);
896    else
897       n = acl(attrname, GETACLCNT, 0, NULL);
898
899    if (n >= MIN_ACL_ENTRIES) {
900       acls = (aclent_t *)malloc(n * sizeof(aclent_t));
901       if ((fd != -1 && facl(fd, GETACL, n, acls) != n) ||
902           acl(attrname, GETACL, n, acls) != n) {
903          berrno be;
904          Jmsg3(jcr, M_ERROR, 0, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
905             attrname, jcr->last_fname, be.bstrerror());
906          Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n",
907             attrname, jcr->last_fname, be.bstrerror());
908
909          free(acls);
910          return false;
911       }
912
913       /*
914        * See if there is a non trivial acl on the file.
915        */
916       if (!acl_is_trivial(n, acls)) {
917          if ((*acl_text = acltotext(acls, n)) == NULL) {
918             berrno be;
919             Jmsg3(jcr, M_ERROR, 0, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"),
920                attrname, jcr->last_fname, be.bstrerror());
921             Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n",
922                attrname, jcr->last_fname, be.bstrerror());
923
924             free(acls);
925             return false;
926          }
927       } else {
928          *acl_text = NULL;
929       }
930
931      free(acls);
932    } else {
933       *acl_text = NULL;
934    }
935
936    return true;
937 #endif /* HAVE_EXTENDED_ACL */
938 #else /* HAVE_ACL */
939    return NULL;
940 #endif /* HAVE_ACL */
941 }
942
943 /*
944  * Forward declaration for recursive function call.
945  */
946 static bool solaris_archive_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent);
947
948 /*
949  * Archive an extended or extensible attribute.
950  * This is stored as an opaque stream of bytes with the following encoding:
951  *
952  * <xattr_name>\0<stat_buffer>\0<acl_string>\0<actual_xattr_data>
953  * 
954  * or for a hardlinked or symlinked attribute
955  *
956  * <xattr_name>\0<stat_buffer>\0<xattr_link_source>\0
957  *
958  * xattr_name can be a subpath relative to the file the xattr is on.
959  * stat_buffer is the string representation of the stat struct.
960  * acl_string is an acl text when a non trivial acl is set on the xattr.
961  * actual_xattr_data is the content of the xattr file.
962  */
963 static bool solaris_archive_xattr(JCR *jcr, int fd, const char *xattr_namespace,
964                                   const char *attrname, bool toplevel_hidden_dir, int stream)
965 {
966    int cnt;
967    int attrfd = -1;
968    struct stat st;
969    struct xattr_link_cache_entry *xlce;
970    char target_attrname[PATH_MAX];
971    char link_source[PATH_MAX];
972    char *acl_text = NULL;
973    char attribs[MAXSTRING];
974    char buffer[BUFSIZ];
975    bool retval = false;
976
977    snprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace, attrname);
978
979    /*
980     * Get the stats of the extended or extensible attribute.
981     */
982    if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) {
983       berrno be;
984       Jmsg3(jcr, M_ERROR, 0, _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"),
985          target_attrname, jcr->last_fname, be.bstrerror());
986       Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n",
987          target_attrname, jcr->last_fname, be.bstrerror());
988
989       goto cleanup;
990    }
991
992    /*
993     * Based on the filetype perform the correct action. We support most filetypes here, more
994     * then the actual implementation on Solaris supports so some code may never get executed
995     * due to limitations in the implementation.
996     */
997    switch (st.st_mode & S_IFMT) {
998    case S_IFIFO:
999    case S_IFCHR:
1000    case S_IFBLK:
1001       /*
1002        * Get any acl on the xattr.
1003        */
1004       if (!solaris_archive_xattr_acl(jcr, attrfd, attrname, &acl_text))
1005          goto cleanup;
1006
1007       /*
1008        * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1009        * Encode the stat struct into an ASCII representation.
1010        */
1011       encode_stat(attribs, &st, 0, stream);
1012       cnt = snprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c",
1013                      target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1014       break;
1015    case S_IFDIR:
1016       /*
1017        * Get any acl on the xattr.
1018        */
1019       if (!solaris_archive_xattr_acl(jcr, attrfd, attrname, &acl_text))
1020          goto cleanup;
1021
1022       /*
1023        * See if this is the toplevel_hidden_dir being archived.
1024        */
1025       if (toplevel_hidden_dir) {
1026          /*
1027           * Save the data for later storage when we encounter a real xattr.
1028           * Encode the stat struct into an ASCII representation and jump out of the function.
1029           */
1030          encode_stat(attribs, &st, 0, stream);
1031          toplevel_hidden_dir_xattr_data_len = snprintf(toplevel_hidden_dir_xattr_data,
1032                                                        sizeof(toplevel_hidden_dir_xattr_data),
1033                                                        "%s%c%s%c%s%c",
1034                                                        target_attrname, 0, attribs, 0,
1035                                                        (acl_text) ? acl_text : "", 0);
1036
1037          goto cleanup;
1038       } else {
1039          /*
1040           * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1041           * Encode the stat struct into an ASCII representation.
1042           */
1043          encode_stat(attribs, &st, 0, stream);
1044          cnt = snprintf(buffer, sizeof(buffer),
1045                         "%s%c%s%c%s%c",
1046                         target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1047       }
1048       break;
1049    case S_IFREG:
1050       /*
1051        * If this is a hardlinked file check the inode cache for a hit.
1052        */
1053       if (st.st_nlink > 1) {
1054          /*
1055           * See if the cache already knows this inode number.
1056           */
1057          if ((xlce = find_xattr_link_cache_entry(st.st_ino)) != NULL) {
1058             /*
1059              * Generate a xattr encoding with the reference to the target in there.
1060              */
1061             encode_stat(attribs, &st, st.st_ino, stream);
1062             cnt = snprintf(buffer, sizeof(buffer),
1063                            "%s%c%s%c%s%c",
1064                            target_attrname, 0, attribs, 0, xlce->target, 0);
1065             pm_memcpy(jcr->xattr_data, buffer, cnt);
1066             jcr->xattr_data_len = cnt;
1067             retval = send_xattr_stream(jcr, stream);
1068
1069             /*
1070              * For a hard linked file we are ready now, no need to recursively archive the attributes.
1071              */
1072             goto cleanup;
1073          }
1074
1075          /*
1076           * Store this hard linked file in the cache.
1077           * Store the name relative to the top level xattr space.
1078           */
1079          add_xattr_link_cache_entry(st.st_ino, target_attrname + 1);
1080       }
1081
1082       /*
1083        * Get any acl on the xattr.
1084        */
1085       if (!solaris_archive_xattr_acl(jcr, attrfd, attrname, &acl_text))
1086          goto cleanup;
1087
1088       /*
1089        * Encode the stat struct into an ASCII representation.
1090        */
1091       encode_stat(attribs, &st, 0, stream);
1092       cnt = snprintf(buffer, sizeof(buffer),
1093                      "%s%c%s%c%s%c",
1094                      target_attrname, 0, attribs, 0, (acl_text) ? acl_text : "", 0);
1095
1096       /*
1097        * Open the extended or extensible attribute file.
1098        */
1099       if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) {
1100          berrno be;
1101          Jmsg3(jcr, M_ERROR, 0, _("Unable to open xattr %s on \"%s\": ERR=%s\n"),
1102             target_attrname, jcr->last_fname, be.bstrerror());
1103          Dmsg3(100, "openat of xattr %s on \"%s\" failed: ERR=%s\n",
1104             target_attrname, jcr->last_fname, be.bstrerror());
1105
1106          goto cleanup;
1107       }
1108       break;
1109    case S_IFLNK:
1110       /*
1111        * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1112        * Encode the stat struct into an ASCII representation.
1113        */
1114       if (readlink(attrname, link_source, sizeof(link_source)) < 0) {
1115          berrno be;
1116          Jmsg3(jcr, M_ERROR, 0, _("Unable to read symlin %s on \"%s\": ERR=%s\n"),
1117             target_attrname, jcr->last_fname, be.bstrerror());
1118          Dmsg3(100, "readlink of xattr %s on \"%s\" failed: ERR=%s\n",
1119             target_attrname, jcr->last_fname, be.bstrerror());
1120
1121          goto cleanup;
1122       }
1123
1124       /*
1125        * Generate a xattr encoding with the reference to the target in there.
1126        */
1127       encode_stat(attribs, &st, st.st_ino, stream);
1128       cnt = snprintf(buffer, sizeof(buffer),
1129                      "%s%c%s%c%s%c",
1130                      target_attrname, 0, attribs, 0, link_source, 0);
1131       pm_memcpy(jcr->xattr_data, buffer, cnt);
1132       jcr->xattr_data_len = cnt;
1133       retval = send_xattr_stream(jcr, stream);
1134
1135       /*
1136        * For a soft linked file we are ready now, no need to recursively archive the attributes.
1137        */
1138       goto cleanup;
1139    default:
1140       goto cleanup;
1141    }
1142
1143    /*
1144     * See if this is the first real xattr being saved. If it is save the toplevel_hidden_dir attributes first.
1145     */
1146    if (nr_xattr_saved == 0) {
1147       pm_memcpy(jcr->xattr_data, toplevel_hidden_dir_xattr_data, toplevel_hidden_dir_xattr_data_len);
1148       jcr->xattr_data_len = toplevel_hidden_dir_xattr_data_len;
1149       send_xattr_stream(jcr, STREAM_XATTR_SOLARIS);
1150    }
1151
1152    pm_memcpy(jcr->xattr_data, buffer, cnt);
1153    jcr->xattr_data_len = cnt;
1154
1155    /*
1156     * Only dump the content of regular files.
1157     */
1158    switch (st.st_mode & S_IFMT) {
1159    case S_IFREG:
1160       if (st.st_size > 0) {
1161          /*
1162           * Protect ourself against things getting out of hand.
1163           */
1164          if (st.st_size >= MAX_XATTR_STREAM) {
1165             Jmsg2(jcr, M_ERROR, 0, _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
1166                jcr->last_fname, MAX_XATTR_STREAM);
1167
1168             goto cleanup;
1169          }
1170
1171          while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) {
1172             jcr->xattr_data = check_pool_memory_size(jcr->xattr_data, jcr->xattr_data_len + cnt);
1173             memcpy(jcr->xattr_data + jcr->xattr_data_len, buffer, cnt);
1174             jcr->xattr_data_len += cnt;
1175          }
1176
1177          if (cnt < 0) {
1178             Jmsg2(jcr, M_ERROR, 0, _("Unable to read content of xattr %s on file \"%s\"\n"),
1179                target_attrname, jcr->last_fname);
1180             Dmsg2(100, "read of data from xattr %s on \"%s\" failed\n",
1181                target_attrname, jcr->last_fname);
1182
1183             goto cleanup;
1184          }
1185       }
1186       break;
1187    default:
1188       break;
1189    }
1190
1191    retval = send_xattr_stream(jcr, stream);
1192    nr_xattr_saved++;
1193
1194    /*
1195     * Recursivly call solaris_archive_extended_attributes for archiving the attributes
1196     * available on this extended attribute.
1197     */
1198    if (retval) {
1199       retval = solaris_archive_xattrs(jcr, xattr_namespace, attrname);
1200       
1201       /*
1202        * The recursive call could change our working dir so change back to the wanted workdir.
1203        */
1204       if (fchdir(fd) < 0) {
1205          berrno be;
1206          Jmsg2(jcr, M_ERROR, 0, _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"),
1207             jcr->last_fname, be.bstrerror());
1208          Dmsg3(100, "Unable to fchdir to xattr space of file \"%s\" using fd %d: ERR=%s\n",
1209             jcr->last_fname, fd, be.bstrerror());
1210
1211          goto cleanup;
1212       }
1213    }
1214
1215 cleanup:
1216    if (acl_text)
1217       free(acl_text);
1218    if (attrfd != -1)
1219       close(attrfd);
1220
1221    return retval;
1222 }
1223
1224 static bool solaris_archive_xattrs(JCR *jcr, const char *xattr_namespace, const char *attr_parent)
1225 {
1226    const char *name;
1227    int fd, filefd = -1, attrdirfd = -1;
1228    DIR *dirp;
1229    struct dirent *dp;
1230    char current_xattr_namespace[PATH_MAX];
1231    bool retval = false;
1232  
1233    /*
1234     * Determine what argument to use. Use attr_parent when set
1235     * (recursive call) or jcr->last_fname for first call. Also save
1236     * the current depth of the xattr_space we are in.
1237     */
1238    if (attr_parent) {
1239       name = attr_parent;
1240       if (xattr_namespace)
1241          snprintf(current_xattr_namespace, sizeof(current_xattr_namespace), "%s%s/",
1242                   xattr_namespace, attr_parent);
1243       else
1244          strcpy(current_xattr_namespace, "/");
1245    } else {
1246       name = jcr->last_fname;
1247       strcpy(current_xattr_namespace, "/");
1248    }
1249
1250    /*
1251     * Open the file on which to archive the xattrs read-only.
1252     */
1253    if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
1254       berrno be;
1255       Jmsg2(jcr, M_ERROR, 0, _("Unable to open file \"%s\": ERR=%s\n"),
1256          jcr->last_fname, be.bstrerror());
1257       Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1258          jcr->last_fname, be.bstrerror());
1259
1260       goto cleanup;
1261    }
1262
1263    /*
1264     * Open the xattr naming space.
1265     */
1266    if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1267       switch (errno) {
1268       case EINVAL:
1269          /*
1270           * Gentile way of the system saying this type of xattr layering is not supported.
1271           * Which is not problem we just forget about this this xattr.
1272           * But as this is not an error we return a positive return value.
1273           */
1274          retval = true;
1275          break;
1276       default:
1277          berrno be;
1278          Jmsg3(jcr, M_ERROR, 0, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1279             name, jcr->last_fname, be.bstrerror());
1280          Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1281             name, jcr->last_fname, be.bstrerror());
1282       }
1283
1284       goto cleanup;
1285    }
1286
1287   /*
1288    * We need to change into the attribute directory to determine if each of the
1289    * attributes should be archived.
1290    */
1291    if (fchdir(attrdirfd) < 0) {
1292       berrno be;
1293       Jmsg2(jcr, M_ERROR, 0, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1294          jcr->last_fname, be.bstrerror());
1295       Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1296          jcr->last_fname, attrdirfd, be.bstrerror());
1297
1298       goto cleanup;
1299    }
1300
1301    /*
1302     * Save the data of the toplevel xattr hidden_dir. We save this one before anything
1303     * else because the readdir returns "." entry after the extensible attr entry.
1304     * And as we want this entry before anything else we better just save its data.
1305     */
1306    if (!attr_parent)
1307       solaris_archive_xattr(jcr, attrdirfd, current_xattr_namespace, ".",
1308                             true, STREAM_XATTR_SOLARIS);
1309
1310    if ((fd = dup(attrdirfd)) == -1 ||
1311        (dirp = fdopendir(fd)) == (DIR *)NULL) {
1312       berrno be;
1313       Jmsg2(jcr, M_ERROR, 0, _("Unable to list the xattr space on file \"%s\": ERR=%s\n"),
1314          jcr->last_fname, be.bstrerror());
1315       Dmsg3(100, "Unable to fdopendir xattr space on file \"%s\" using fd %d: ERR=%s\n",
1316          jcr->last_fname, fd, be.bstrerror());
1317
1318       goto cleanup;
1319    }
1320
1321    /*
1322     * Walk the namespace.
1323     */
1324    while (dp = readdir(dirp)) {
1325       /*
1326        * Skip only the toplevel . dir.
1327        */
1328       if (!attr_parent && !strcmp(dp->d_name, "."))
1329          continue;
1330
1331       /*
1332        * Skip all .. directories
1333        */
1334       if (!strcmp(dp->d_name, ".."))
1335          continue;
1336
1337       Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n",
1338          current_xattr_namespace, dp->d_name, jcr->last_fname);
1339
1340 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1341       /*
1342        * We are not interested in read-only extensible attributes.
1343        */
1344       if (!strcmp(dp->d_name, VIEW_READONLY)) {
1345          Dmsg3(400, "Skipping readonly extensible attributes %s%s on file \"%s\"\n",
1346             current_xattr_namespace, dp->d_name, jcr->last_fname);
1347
1348          continue;
1349       }
1350
1351       /*
1352        * We are only interested in read-write extensible attributes
1353        * when they contain non-transient values.
1354        */
1355       if (!strcmp(dp->d_name, VIEW_READWRITE)) {
1356          /*
1357           * Determine if there are non-transient system attributes at the toplevel.
1358           * We need to provide a fd to the open file.
1359           */
1360          if (!solaris_has_non_transient_extensible_attributes(filefd)) {
1361             Dmsg3(400, "Skipping transient extensible attributes %s%s on file \"%s\"\n",
1362                current_xattr_namespace, dp->d_name, jcr->last_fname);
1363
1364             continue;
1365          }
1366
1367          /*
1368           * Archive the xattr.
1369           */
1370          solaris_archive_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1371                                false, STREAM_XATTR_SOLARIS_SYS);
1372          continue;
1373       }
1374 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
1375
1376       /*
1377        * Archive the xattr.
1378        */
1379       solaris_archive_xattr(jcr, attrdirfd, current_xattr_namespace, dp->d_name,
1380                             false, STREAM_XATTR_SOLARIS);
1381    }
1382
1383    closedir(dirp);
1384    retval = true;
1385
1386 cleanup:
1387    if (attrdirfd != -1)
1388       close(attrdirfd);
1389    if (filefd != -1)
1390       close(filefd);
1391
1392    return retval;
1393 }
1394
1395 #ifdef HAVE_ACL
1396 static bool solaris_restore_xattr_acl(JCR *jcr, int fd, const char *attrname, char *acl_text)
1397 {
1398 #ifdef HAVE_EXTENDED_ACL
1399    int error;
1400    acl_t *aclp = NULL;
1401
1402    if ((error = acl_fromtext(acl_text, &aclp)) != 0) {
1403       return false;
1404    }
1405
1406    if ((fd != -1 && facl_set(fd, aclp) != 0) ||
1407         acl_set(attrname, aclp) != 0) {
1408       berrno be;
1409       Jmsg3(jcr, M_ERROR, 0, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1410          attrname, jcr->last_fname, be.bstrerror());
1411       Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1412          attrname, jcr->last_fname, be.bstrerror());
1413
1414       return false;
1415    }
1416
1417    if (aclp)
1418       acl_free(aclp);
1419
1420    return true;
1421 #else /* HAVE_EXTENDED_ACL */
1422    int n;
1423    aclent_t *acls = NULL;
1424
1425    acls = aclfromtext(acl_text, &n);
1426    if (!acls) {
1427       if ((fd != -1 && facl(fd, SETACL, n, acls) != 0) ||
1428            acl(attrname, SETACL, n, acls) != 0) {
1429          berrno be;
1430          Jmsg3(jcr, M_ERROR, 0, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
1431             attrname, jcr->last_fname, be.bstrerror());
1432          Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
1433             attrname, jcr->last_fname, be.bstrerror());
1434
1435          return false;
1436       }
1437    }
1438
1439    if (acls)
1440       free(acls);
1441
1442    return true;
1443 #endif /* HAVE_EXTENDED_ACL */
1444 }
1445 #endif /* HAVE_ACL */
1446
1447 static bool solaris_restore_xattrs(JCR *jcr, bool is_extensible)
1448 {
1449    int fd, filefd = -1, attrdirfd = -1, attrfd = -1;
1450    int used_bytes, total_bytes, cnt;
1451    char *bp, *target_attrname, *attribs;
1452    char *linked_target = NULL;
1453    char *acl_text = NULL;
1454    char *data = NULL;
1455    int32_t inum;
1456    struct stat st;
1457    struct timeval times[2];
1458    bool retval = false;
1459
1460    /*
1461     * Parse the xattr stream. First the part that is the same for all xattrs.
1462     */
1463    used_bytes = 0;
1464    total_bytes = jcr->xattr_data_len;
1465
1466    /*
1467     * The name of the target xattr has a leading / we are not interested
1468     * in that so skip it when decoding the string. We always start a the /
1469     * of the xattr space anyway.
1470     */
1471    target_attrname = jcr->xattr_data + 1;
1472    if ((bp = strchr(target_attrname, '\0')) == (char *)NULL ||
1473        (used_bytes = (bp - jcr->xattr_data)) >= (total_bytes - 1)) {
1474       goto parse_error;
1475    }
1476    attribs = ++bp;
1477
1478    /*
1479     * Open the file on which to restore the xattrs read-only.
1480     */
1481    if ((filefd = open(jcr->last_fname, O_RDONLY | O_NONBLOCK)) < 0) {
1482       berrno be;
1483       Jmsg2(jcr, M_ERROR, 0, _("Unable to open file \"%s\": ERR=%s\n"),
1484          jcr->last_fname, be.bstrerror());
1485       Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
1486          jcr->last_fname, be.bstrerror());
1487
1488       goto cleanup;
1489    }
1490
1491    /*
1492     * Open the xattr naming space and make it the current working dir.
1493     */
1494    if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1495       berrno be;
1496       Jmsg2(jcr, M_ERROR, 0, _("Unable to open xattr space on file \"%s\": ERR=%s\n"),
1497          jcr->last_fname, be.bstrerror());
1498       Dmsg2(100, "Unable to open xattr space on file \"%s\": ERR=%s\n",
1499          jcr->last_fname, be.bstrerror());
1500
1501       goto cleanup;
1502    }
1503
1504    if (fchdir(attrdirfd) < 0) {
1505       berrno be;
1506       Jmsg2(jcr, M_ERROR, 0, _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
1507          jcr->last_fname, be.bstrerror());
1508       Dmsg3(100, "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
1509          jcr->last_fname, attrdirfd, be.bstrerror());
1510
1511       goto cleanup;
1512    }
1513
1514    /*
1515     * Try to open the correct xattr subdir based on the target_attrname given.
1516     * e.g. check if its a subdir attrname. Each / in the string makes us go
1517     * one level deeper.
1518     */
1519    while ((bp = strchr(target_attrname, '/')) != (char *)NULL) {
1520       *bp = '\0';
1521
1522       if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) {
1523          berrno be;
1524          Jmsg3(jcr, M_ERROR, 0, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
1525             target_attrname, jcr->last_fname, be.bstrerror());
1526          Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
1527             target_attrname, jcr->last_fname, be.bstrerror());
1528
1529          goto cleanup;
1530       }
1531
1532       close(filefd);
1533       filefd = fd;
1534
1535       /*
1536        * Open the xattr naming space.
1537        */
1538       if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
1539          berrno be;
1540          Jmsg3(jcr, M_ERROR, 0, _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
1541             target_attrname, jcr->last_fname, be.bstrerror());
1542          Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
1543             target_attrname, jcr->last_fname, be.bstrerror());
1544
1545          goto cleanup;
1546       }
1547
1548       close(attrdirfd);
1549       attrdirfd = fd;
1550
1551       /*
1552        * Make the xattr space our current workingdir.
1553        */
1554       if (fchdir(attrdirfd) < 0) {
1555          berrno be;
1556          Jmsg3(jcr, M_ERROR, 0, _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"),
1557             target_attrname, jcr->last_fname, be.bstrerror());
1558          Dmsg4(100, "Unable to fchdir to xattr space %s on file \"%s\" using fd %d: ERR=%s\n",
1559             target_attrname, jcr->last_fname, attrdirfd, be.bstrerror());
1560
1561          goto cleanup;
1562       }
1563
1564       target_attrname = ++bp;
1565    }
1566
1567    /*
1568     * Decode the attributes from the stream.
1569     */
1570    decode_stat(attribs, &st, &inum);
1571
1572    /*
1573     * Decode the next field (acl_text).
1574     */
1575    if ((bp = strchr(attribs, '\0')) == (char *)NULL ||
1576        (used_bytes = (bp - jcr->xattr_data)) >= (total_bytes - 1)) {
1577       goto parse_error;
1578    }
1579    acl_text = ++bp;
1580
1581    /*
1582     * Based on the filetype perform the correct action. We support most filetypes here, more
1583     * then the actual implementation on Solaris supports so some code may never get executed
1584     * due to limitations in the implementation.
1585     */
1586    switch (st.st_mode & S_IFMT) {
1587    case S_IFIFO:
1588       /*
1589        * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1590        */
1591       unlinkat(attrdirfd, target_attrname, 0);
1592       if (mkfifo(target_attrname, st.st_mode) < 0) {
1593          berrno be;
1594          Jmsg3(jcr, M_ERROR, 0, _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"),
1595             target_attrname, jcr->last_fname, be.bstrerror());
1596          Dmsg3(100, "Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n",
1597             target_attrname,  jcr->last_fname, be.bstrerror());
1598
1599             goto cleanup;
1600       }
1601       break;
1602    case S_IFCHR:
1603    case S_IFBLK:
1604       /*
1605        * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1606        */
1607       unlinkat(attrdirfd, target_attrname, 0);
1608       if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) {
1609          berrno be;
1610          Jmsg3(jcr, M_ERROR, 0, _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"),
1611             target_attrname, jcr->last_fname, be.bstrerror());
1612          Dmsg3(100, "Unable to mknod xattr %s on file \"%s\": ERR=%s\n",
1613             target_attrname,  jcr->last_fname, be.bstrerror());
1614
1615          goto cleanup;
1616       }
1617       break;
1618    case S_IFDIR:
1619       /*
1620        * If its not the hidden_dir create the entry.
1621        * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1622        */
1623       if (strcmp(target_attrname, ".")) {
1624          unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR);
1625          if (mkdir(target_attrname, st.st_mode) < 0) {
1626             berrno be;
1627             Jmsg3(jcr, M_WARNING, 0, _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"),
1628                target_attrname, jcr->last_fname, be.bstrerror());
1629             Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n",
1630                target_attrname,  jcr->last_fname, be.bstrerror());
1631
1632             goto cleanup;
1633          }
1634       }
1635       break;
1636    case S_IFREG:
1637       /*
1638        * See if this is a hard linked file. e.g. inum != 0
1639        */
1640       if (inum != 0) {
1641          linked_target = bp;
1642
1643          unlinkat(attrdirfd, target_attrname, 0);
1644          if (link(linked_target, target_attrname) < 0) {
1645             berrno be;
1646             Jmsg4(jcr, M_ERROR, 0, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"),
1647                target_attrname, linked_target, jcr->last_fname, be.bstrerror());
1648             Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n",
1649                target_attrname, linked_target, jcr->last_fname, be.bstrerror());
1650
1651             goto cleanup;
1652          }
1653
1654          /*
1655           * Successfully restored xattr.
1656           */
1657          retval = true;
1658          goto cleanup;
1659       } else {
1660          if ((bp = strchr(acl_text, '\0')) == (char *)NULL ||
1661              (used_bytes = (bp - jcr->xattr_data)) >= total_bytes) {
1662             goto parse_error;
1663          }
1664
1665          if (used_bytes < (total_bytes - 1))
1666             data = ++bp;
1667
1668          /*
1669           * Restore the actual xattr.
1670           */
1671          if (!is_extensible) {
1672             unlinkat(attrdirfd, target_attrname, 0);
1673          }
1674
1675          if ((attrfd = openat(attrdirfd, target_attrname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) {
1676             berrno be;
1677             Jmsg3(jcr, M_ERROR, 0, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
1678                target_attrname, jcr->last_fname, be.bstrerror());
1679             Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
1680                target_attrname, jcr->last_fname, be.bstrerror());
1681
1682             goto cleanup;
1683          }
1684       }
1685
1686       /*
1687        * Restore the actual data.
1688        */
1689       if (st.st_size > 0) {
1690          used_bytes = (data - jcr->xattr_data);
1691          cnt = total_bytes - used_bytes;
1692
1693          /*
1694           * Do a sanity check, the st.st_size should be the same as the number of bytes
1695           * we have available as data of the stream.
1696           */
1697          if (cnt != st.st_size) {
1698             Jmsg2(jcr, M_ERROR, 0, _("Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n"),
1699                target_attrname, jcr->last_fname);
1700             Dmsg2(100, "Unable to restore data of xattr %s on file \"%s\": Not all data available in xattr stream\n",
1701                target_attrname, jcr->last_fname);
1702
1703             goto cleanup;
1704          }
1705
1706          while (cnt > 0) {
1707             cnt = write(attrfd, data, cnt);
1708             if (cnt < 0) {
1709                berrno be;
1710                Jmsg3(jcr, M_ERROR, 0, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"),
1711                   target_attrname, jcr->last_fname, be.bstrerror());
1712                Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n",
1713                   target_attrname, jcr->last_fname, be.bstrerror());
1714
1715                goto cleanup;
1716             }
1717
1718             used_bytes += cnt;
1719             data += cnt;
1720             cnt = total_bytes - used_bytes;
1721          }
1722       }
1723       break;
1724    case S_IFLNK:
1725       /*
1726        * The current implementation of xattr on Solaris doesn't support this, but if it ever does we are prepared.
1727        */
1728       linked_target = bp;
1729
1730       if (symlink(linked_target, target_attrname) < 0) {
1731          berrno be;
1732          Jmsg4(jcr, M_ERROR, 0, _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"),
1733             target_attrname, linked_target, jcr->last_fname, be.bstrerror());
1734          Dmsg4(100, "Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n",
1735             target_attrname, linked_target, jcr->last_fname, be.bstrerror());
1736
1737          goto cleanup;
1738       }
1739
1740       /*
1741        * Successfully restored xattr.
1742        */
1743       retval = true;
1744       goto cleanup;
1745    default:
1746       goto cleanup;
1747    }
1748
1749    /*
1750     * Restore owner and acl for non extensible attributes.
1751     */
1752    if (!is_extensible) {
1753       if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0) {
1754          switch (errno) {
1755          case EINVAL:
1756             /*
1757              * Gentile way of the system saying this type of xattr layering is not supported.
1758              * But as this is not an error we return a positive return value.
1759              */
1760             retval = true;
1761             break;
1762          default:
1763             berrno be;
1764             Jmsg3(jcr, M_ERROR, 0, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"),
1765                target_attrname, jcr->last_fname, be.bstrerror());
1766             Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n",
1767                target_attrname, jcr->last_fname, be.bstrerror());
1768          }
1769
1770          goto cleanup;
1771       }
1772    }
1773
1774 #ifdef HAVE_ACL
1775    if (acl_text && *acl_text)
1776       if (!solaris_restore_xattr_acl(jcr, attrfd, target_attrname, acl_text))
1777          goto cleanup;
1778 #endif /* HAVE_ACL */
1779
1780    /*
1781     * For a non extensible attribute restore access and modification time on the xattr.
1782     */
1783    if (!is_extensible) {
1784       times[0].tv_sec = st.st_atime;
1785       times[0].tv_usec = 0;
1786       times[1].tv_sec = st.st_mtime;
1787       times[1].tv_usec = 0;
1788
1789       if (futimesat(attrdirfd, target_attrname, times) < 0) {
1790          berrno be;
1791          Jmsg3(jcr, M_ERROR, 0, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"),
1792             target_attrname, jcr->last_fname, be.bstrerror());
1793          Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n",
1794             target_attrname, jcr->last_fname, be.bstrerror());
1795
1796          goto cleanup;
1797       }
1798    }
1799
1800    /*
1801     * Successfully restored xattr.
1802     */
1803    retval = true;
1804    goto cleanup;
1805
1806 parse_error:
1807    Jmsg1(jcr, M_ERROR, 0, _("Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n"),
1808       jcr->last_fname);
1809    Dmsg1(100, "Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n",
1810       jcr->last_fname);
1811
1812 cleanup:
1813    if (attrfd != -1)
1814       close(attrfd);
1815    if (attrdirfd != -1)
1816       close(attrdirfd);
1817    if (filefd != -1)
1818       close(filefd);
1819
1820    return retval;
1821 }
1822
1823 static bool solaris_extract_xattr(JCR *jcr, int stream)
1824 {
1825    char cwd[PATH_MAX];
1826    bool is_extensible = false;
1827    bool retval;
1828
1829    /*
1830     * First make sure we can restore xattr on the filesystem.
1831     */
1832    switch (stream) {
1833 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1834    case STREAM_XATTR_SOLARIS_SYS:
1835       if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0) {
1836          Qmsg1(jcr, M_WARNING, 0,
1837                _("Failed to restore extensible attributes on file \"%s\"\n"),
1838                jcr->last_fname);
1839          Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n",
1840             jcr->last_fname);
1841
1842          return false;
1843       }
1844
1845       is_extensible = true;
1846       break;
1847 #endif
1848    case STREAM_XATTR_SOLARIS:
1849       if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0) {
1850          Qmsg1(jcr, M_WARNING, 0,
1851                _("Failed to restore extended attributes on file \"%s\"\n"),
1852                jcr->last_fname);
1853          Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n",
1854             jcr->last_fname);
1855
1856          return false;
1857       }
1858
1859       break;
1860    default:
1861       return false;
1862    }
1863
1864    /*
1865     * As we change the cwd in the restore function save the current cwd
1866     * for restore after return from the solaris_restore_xattrs function.
1867     */
1868    getcwd(cwd, sizeof(cwd));
1869    retval = solaris_restore_xattrs(jcr, is_extensible);
1870    chdir(cwd);
1871
1872    return retval;
1873 }
1874
1875 static int solaris_build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
1876 {
1877    char cwd[PATH_MAX];
1878    bool retval = true;
1879
1880    /*
1881     * First see if extended attributes or extensible attributes are present.
1882     * If not just pretend things went ok.
1883     */
1884    if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0) {
1885       nr_xattr_saved = 0;
1886
1887       /*
1888        * As we change the cwd in the archive function save the current cwd
1889        * for restore after return from the solaris_archive_xattrs function.
1890        */
1891       getcwd(cwd, sizeof(cwd));
1892       retval = solaris_archive_xattrs(jcr, NULL, NULL);
1893       chdir(cwd);
1894
1895       drop_xattr_link_cache();
1896    }
1897
1898    return retval;
1899 }
1900
1901 static bool solaris_parse_xattr_stream(JCR *jcr, int stream)
1902 {
1903    switch (stream) {
1904 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1905    case STREAM_XATTR_SOLARIS_SYS:
1906 #endif
1907    case STREAM_XATTR_SOLARIS:
1908       return solaris_extract_xattr(jcr, stream);
1909    default:
1910       return false;
1911    }
1912 }
1913 #endif
1914
1915 bool build_xattr_streams(JCR *jcr, FF_PKT *ff_pkt)
1916 {
1917 #if defined(HAVE_SUN_OS)
1918    return solaris_build_xattr_streams(jcr, ff_pkt);
1919 #elif defined(HAVE_DARWIN_OS)
1920    return darwin_build_xattr_streams(jcr, ff_pkt);
1921 #elif defined(HAVE_FREEBSD_OS)
1922    return freebsd_build_xattr_streams(jcr, ff_pkt);
1923 #elif defined(HAVE_LINUX_OS)
1924    return linux_build_xattr_streams(jcr, ff_pkt);
1925 #elif defined(HAVE_NETBSD_OS)
1926    return netbsd_build_xattr_streams(jcr, ff_pkt);
1927 #endif
1928 }
1929
1930 bool parse_xattr_stream(JCR *jcr, int stream)
1931 {
1932    /*
1933     * Based on the stream being passed in dispatch to the right function
1934     * for parsing and restoring a specific xattr. The platform determines
1935     * which streams are recognized and parsed and which are handled by
1936     * the default case and ignored. As only one of the platform defines
1937     * is true per compile we never end up with duplicate switch values.
1938     */
1939    switch (stream) {
1940 #if defined(HAVE_SUN_OS)
1941 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
1942    case STREAM_XATTR_SOLARIS_SYS:
1943 #endif
1944    case STREAM_XATTR_SOLARIS:
1945       return solaris_parse_xattr_stream(jcr, stream);
1946 #elif defined(HAVE_DARWIN_OS)
1947    case STREAM_XATTR_DARWIN:
1948       return darwin_parse_xattr_stream(jcr, stream);
1949 #elif defined(HAVE_FREEBSD_OS)
1950    case STREAM_XATTR_FREEBSD:
1951       return freebsd_parse_xattr_stream(jcr, stream);
1952 #elif defined(HAVE_LINUX_OS)
1953    case STREAM_XATTR_LINUX:
1954       return linux_parse_xattr_stream(jcr, stream);
1955 #elif defined(HAVE_NETBSD_OS)
1956    case STREAM_XATTR_NETBSD:
1957       return netbsd_parse_xattr_stream(jcr, stream);
1958 #endif
1959    default:
1960       /*
1961        * Issue a warning and discard the message. But pretend the restore was ok.
1962        */
1963       Qmsg2(jcr, M_WARNING, 0,
1964          _("Can't restore Extended Attributes of %s - incompatible xattr stream encountered - %d\n"),
1965          jcr->last_fname, stream);
1966       return true;
1967    } /* end switch (stream) */
1968 }
1969
1970 #endif