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