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