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