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