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