1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2008-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero 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    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero 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 /*
24  * Functions to handle Extended Attributes for bareos.
25  *
26  * Extended Attributes are so OS specific we only restore Extended Attributes if
27  * they were saved using a filed on the same platform.
28  *
29  * Currently we support the following OSes:
30  *   Written by Marco van Wieringen, November 2008
31  *   Major overhaul January 2012 + June 2012
32  *   Moved into findlib so it can be used from other programs, May 2012
33  */
34 /**
35  * @file
36  * Functions to handle Extended Attributes for bareos.
37  *
38  * Extended Attributes are so OS specific we only restore Extended Attributes if
39  * they were saved using a filed on the same platform.
40  *
41  * Currently we support the following OSes:
42  *   - AIX (Extended Attributes)
43  *   - Darwin (Extended Attributes)
44  *   - FreeBSD (Extended Attributes)
45  *   - GNU HURD (Extended Attributes)
46  *   - IRIX (Extended Attributes)
47  *   - Linux (Extended Attributes)
48  *   - NetBSD (Extended Attributes)
49  *   - OpenBSD (Extended Attributes)
50  *     (As it seems either they never implemented xattr or they are removed
51  *      the support as it stated it was in version 3.1 but the current syscall
52  *      tabled shows the extattr_ functions are not implemented. So as such we
53  *      might eventually support xattr on OpenBSD when they implemented them
54  * using the same interface as FreeBSD and NetBSD.
55  *   - Solaris (Extended Attributes and Extensible Attributes)
56  *   - Tru64 (Extended Attributes)
57  */
58 
59 #include "include/bareos.h"
60 #include "find.h"
61 #include "lib/berrno.h"
62 #include "lib/bsock.h"
63 #include "include/jcr.h"
64 
65 static std::string error_message_disabling_xattributes{
66     _("Disabling restore of XATTRs on this filesystem, "
67       "not supported. Current file: \"%s\"\n")};
68 
69 #if !defined(HAVE_XATTR)
70 /**
71  * Entry points when compiled without support for XATTRs or on an unsupported
72  * platform.
73  */
BuildXattrStreams(JobControlRecord * jcr,XattrData * xattr_data,FindFilesPacket * ff_pkt)74 BxattrExitCode BuildXattrStreams(JobControlRecord* jcr,
75                                  XattrData* xattr_data,
76                                  FindFilesPacket* ff_pkt)
77 {
78   return BxattrExitCode::kErrorFatal;
79 }
80 
ParseXattrStreams(JobControlRecord * jcr,XattrData * xattr_data,int stream,char * content,uint32_t content_length)81 BxattrExitCode ParseXattrStreams(JobControlRecord* jcr,
82                                  XattrData* xattr_data,
83                                  int stream,
84                                  char* content,
85                                  uint32_t content_length)
86 {
87   return BxattrExitCode::kErrorFatal;
88 }
89 #else
90 /**
91  * Send a XATTR stream to the SD.
92  */
SendXattrStream(JobControlRecord * jcr,XattrData * xattr_data,int stream)93 BxattrExitCode SendXattrStream(JobControlRecord* jcr,
94                                XattrData* xattr_data,
95                                int stream)
96 {
97   BareosSocket* sd = jcr->store_bsock;
98   POOLMEM* msgsave;
99 #  ifdef FD_NO_SEND_TEST
100   return BxattrExitCode::kSuccess;
101 #  endif
102 
103   /*
104    * Sanity check
105    */
106   if (xattr_data->u.build->content_length <= 0) {
107     return BxattrExitCode::kSuccess;
108   }
109 
110   /*
111    * Send header
112    */
113   if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
114     Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
115           sd->bstrerror());
116     return BxattrExitCode::kErrorFatal;
117   }
118 
119   /*
120    * Send the buffer to the storage deamon
121    */
122   Dmsg1(400, "Backing up XATTR <%s>\n", xattr_data->u.build->content);
123   msgsave = sd->msg;
124   sd->msg = xattr_data->u.build->content;
125   sd->message_length = xattr_data->u.build->content_length;
126   if (!sd->send()) {
127     sd->msg = msgsave;
128     sd->message_length = 0;
129     Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
130           sd->bstrerror());
131     return BxattrExitCode::kErrorFatal;
132   }
133 
134   jcr->JobBytes += sd->message_length;
135   sd->msg = msgsave;
136   if (!sd->signal(BNET_EOD)) {
137     Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
138           sd->bstrerror());
139     return BxattrExitCode::kErrorFatal;
140   }
141   Dmsg1(200, "XATTR of file: %s successfully backed up!\n",
142         xattr_data->last_fname);
143   return BxattrExitCode::kSuccess;
144 }
145 
146 /**
147  * First some generic functions for OSes that use the same xattr encoding
148  * scheme. Currently for all OSes except for Solaris.
149  */
XattrDropInternalTable(alist * xattr_value_list)150 void XattrDropInternalTable(alist* xattr_value_list)
151 {
152   xattr_t* current_xattr = nullptr;
153 
154   /*
155    * Walk the list of xattrs and free allocated memory on traversing.
156    */
157   foreach_alist (current_xattr, xattr_value_list) {
158     /*
159      * See if we can shortcut.
160      */
161     if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC) break;
162 
163     free(current_xattr->name);
164 
165     if (current_xattr->value_length > 0) free(current_xattr->value);
166 
167     free(current_xattr);
168   }
169 
170   delete xattr_value_list;
171 }
172 
173 /**
174  * The xattr stream for OSX, FreeBSD, Linux and NetBSD is a serialized stream of
175  * bytes which encodes one or more xattr_t structures.
176  *
177  * The Serialized stream consists of the following elements:
178  *    magic - A magic string which makes it easy to detect any binary
179  * incompatabilites name_length - The length of the following xattr name name -
180  * The name of the extended attribute value_length - The length of the following
181  * xattr data value - The actual content of the extended attribute
182  *
183  * This is repeated 1 or more times.
184  *
185  */
SerializeXattrStream(JobControlRecord * jcr,XattrData * xattr_data,uint32_t expected_serialize_len,alist * xattr_value_list)186 uint32_t SerializeXattrStream(JobControlRecord* jcr,
187                               XattrData* xattr_data,
188                               uint32_t expected_serialize_len,
189                               alist* xattr_value_list)
190 {
191   xattr_t* current_xattr = nullptr;
192   ser_declare;
193 
194   /*
195    * Make sure the serialized stream fits in the poolmem buffer.
196    * We allocate some more to be sure the stream is gonna fit.
197    */
198   xattr_data->u.build->content = CheckPoolMemorySize(
199       xattr_data->u.build->content, expected_serialize_len + 10);
200   SerBegin(xattr_data->u.build->content, expected_serialize_len + 10);
201 
202   /*
203    * Walk the list of xattrs and Serialize the data.
204    */
205   foreach_alist (current_xattr, xattr_value_list) {
206     /*
207      * See if we can shortcut.
208      */
209     if (current_xattr == NULL || current_xattr->magic != XATTR_MAGIC) break;
210 
211     ser_uint32(current_xattr->magic);
212     ser_uint32(current_xattr->name_length);
213     SerBytes(current_xattr->name, current_xattr->name_length);
214 
215     ser_uint32(current_xattr->value_length);
216     if (current_xattr->value_length > 0 && current_xattr->value) {
217       SerBytes(current_xattr->value, current_xattr->value_length);
218 
219       Dmsg3(100, "Backup xattr named %s, value %*s\n", current_xattr->name,
220             current_xattr->value_length, current_xattr->value);
221     } else {
222       Dmsg1(100, "Backup empty xattr named %s\n", current_xattr->name);
223     }
224   }
225 
226   SerEnd(xattr_data->u.build->content, expected_serialize_len + 10);
227   xattr_data->u.build->content_length = SerLength(xattr_data->u.build->content);
228 
229   return xattr_data->u.build->content_length;
230 }
231 
UnSerializeXattrStream(JobControlRecord * jcr,XattrData * xattr_data,char * content,uint32_t content_length,alist * xattr_value_list)232 BxattrExitCode UnSerializeXattrStream(JobControlRecord* jcr,
233                                       XattrData* xattr_data,
234                                       char* content,
235                                       uint32_t content_length,
236                                       alist* xattr_value_list)
237 {
238   unser_declare;
239   xattr_t* current_xattr;
240 
241   /**
242    * Parse the stream and call restore_xattr_on_file for each extended
243    * attribute.
244    *
245    * Start unserializing the data. We keep on looping while we have not
246    * unserialized all bytes in the stream.
247    */
248   UnserBegin(content, content_length);
249   while (UnserLength(content) < content_length) {
250     /*
251      * First make sure the magic is present. This way we can easily catch
252      * corruption. Any missing MAGIC is fatal we do NOT try to continue.
253      */
254     current_xattr = (xattr_t*)malloc(sizeof(xattr_t));
255     unser_uint32(current_xattr->magic);
256     if (current_xattr->magic != XATTR_MAGIC) {
257       Mmsg1(jcr->errmsg,
258             _("Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n"),
259             xattr_data->last_fname);
260       Dmsg1(100, "Illegal xattr stream, no XATTR_MAGIC on file \"%s\"\n",
261             xattr_data->last_fname);
262       free(current_xattr);
263       return BxattrExitCode::kError;
264     }
265 
266     /*
267      * Decode the valuepair. First decode the length of the name.
268      */
269     unser_uint32(current_xattr->name_length);
270     if (current_xattr->name_length == 0) {
271       Mmsg1(jcr->errmsg,
272             _("Illegal xattr stream, xattr name length <= 0 on file \"%s\"\n"),
273             xattr_data->last_fname);
274       Dmsg1(100,
275             "Illegal xattr stream, xattr name length <= 0 on file \"%s\"\n",
276             xattr_data->last_fname);
277       free(current_xattr);
278       return BxattrExitCode::kError;
279     }
280 
281     /*
282      * Allocate room for the name and decode its content.
283      */
284     current_xattr->name = (char*)malloc(current_xattr->name_length + 1);
285     UnserBytes(current_xattr->name, current_xattr->name_length);
286 
287     /*
288      * The xattr_name needs to be null terminated.
289      */
290     current_xattr->name[current_xattr->name_length] = '\0';
291 
292     /*
293      * Decode the value length.
294      */
295     unser_uint32(current_xattr->value_length);
296 
297     if (current_xattr->value_length > 0) {
298       /*
299        * Allocate room for the value and decode its content.
300        */
301       current_xattr->value = (char*)malloc(current_xattr->value_length);
302       UnserBytes(current_xattr->value, current_xattr->value_length);
303 
304       Dmsg3(100, "Restoring xattr named %s, value %*s\n", current_xattr->name,
305             current_xattr->value_length, current_xattr->value);
306     } else {
307       current_xattr->value = NULL;
308       Dmsg1(100, "Restoring empty xattr named %s\n", current_xattr->name);
309     }
310 
311     xattr_value_list->append(current_xattr);
312   }
313 
314   UnserEnd(content, content_length);
315   return BxattrExitCode::kSuccess;
316 }
317 
318 /**
319  * This is a supported OS, See what kind of interface we should use.
320  */
321 #  if defined(HAVE_AIX_OS)
322 
323 #    if (!defined(HAVE_LISTEA) && !defined(HAVE_LLISTEA))  \
324         || (!defined(HAVE_GETEA) && !defined(HAVE_LGETEA)) \
325         || (!defined(HAVE_SETEA) && !defined(HAVE_LSETEA))
326 #      error "Missing full support for the Extended Attributes (EA) functions."
327 #    endif
328 
329 #    ifdef HAVE_SYS_EA_H
330 #      include <sys/ea.h>
331 #    else
332 #      error "Missing sys/ea.h header file"
333 #    endif
334 
335 /**
336  * Define the supported XATTR streams for this OS
337  */
338 static int os_default_xattr_streams[1] = {STREAM_XATTR_AIX};
339 
340 /**
341  * Fallback to the non l-functions when those are not available.
342  */
343 #    if defined(HAVE_GETEA) && !defined(HAVE_LGETEA)
344 #      define lgetea getea
345 #    endif
346 #    if defined(HAVE_SETEA) && !defined(HAVE_LSETEA)
347 #      define lsetea setea
348 #    endif
349 #    if defined(HAVE_LISTEA) && !defined(HAVE_LLISTEA)
350 #      define llistea listea
351 #    endif
352 
aix_build_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,FindFilesPacket * ff_pkt)353 static BxattrExitCode aix_build_xattr_streams(JobControlRecord* jcr,
354                                               XattrData* xattr_data,
355                                               FindFilesPacket* ff_pkt)
356 {
357   char* bp;
358   bool skip_xattr;
359   char* xattr_list = NULL;
360   int cnt, xattr_count = 0;
361   uint32_t name_length;
362   int32_t xattr_list_len, xattr_value_len;
363   uint32_t expected_serialize_len = 0;
364   xattr_t* current_xattr;
365   alist* xattr_value_list = NULL;
366   BxattrExitCode retval = BxattrExitCode::kError;
367 
368   /*
369    * First get the length of the available list with extended attributes.
370    */
371   xattr_list_len = llistea(xattr_data->last_fname, NULL, 0);
372   switch (xattr_list_len) {
373     case -1: {
374       BErrNo be;
375 
376       switch (errno) {
377         case ENOENT:
378         case EFORMAT:
379           retval = BxattrExitCode::kSuccess;
380           goto bail_out;
381         case ENOTSUP:
382           /*
383            * If the filesystem reports it doesn't support XATTRs we clear the
384            * BXATTR_FLAG_SAVE_NATIVE flag so we skip XATTR saves on all other
385            * files on the same filesystem. The BXATTR_FLAG_SAVE_NATIVE flags
386            * gets sets again when we change from one filesystem to another.
387            */
388           xattr_data->flags &= ~BXATTR_FLAG_SAVE_NATIVE;
389           retval = BxattrExitCode::kWarning;
390           Mmsg(jcr->errmsg, error_message_disabling_xattributes.c_str(),
391                xattr_data->last_fname);
392           Dmsg1(100, error_message_disabling_xattributes.c_str(),
393                 xattr_data->last_fname);
394           goto bail_out;
395         default:
396           Mmsg2(jcr->errmsg, _("llistea error on file \"%s\": ERR=%s\n"),
397                 xattr_data->last_fname, be.bstrerror());
398           Dmsg2(100, "llistea error file=%s ERR=%s\n", xattr_data->last_fname,
399                 be.bstrerror());
400           goto bail_out;
401       }
402       break;
403     }
404     case 0:
405       retval = BxattrExitCode::kSuccess;
406       goto bail_out;
407     default:
408       break;
409   }
410 
411   /*
412    * Allocate room for the extented attribute list.
413    */
414   xattr_list = (char*)malloc(xattr_list_len + 1);
415   memset(xattr_list, 0, xattr_list_len + 1);
416 
417   /*
418    * Get the actual list of extended attributes names for a file.
419    */
420   xattr_list_len = llistea(xattr_data->last_fname, xattr_list, xattr_list_len);
421   switch (xattr_list_len) {
422     case -1: {
423       BErrNo be;
424 
425       switch (errno) {
426         case ENOENT:
427         case EFORMAT:
428           retval = BxattrExitCode::kSuccess;
429           goto bail_out;
430         default:
431           Mmsg2(jcr->errmsg, _("llistea error on file \"%s\": ERR=%s\n"),
432                 xattr_data->last_fname, be.bstrerror());
433           Dmsg2(100, "llistea error file=%s ERR=%s\n", xattr_data->last_fname,
434                 be.bstrerror());
435           goto bail_out;
436       }
437       break;
438     }
439     default:
440       break;
441   }
442   xattr_list[xattr_list_len] = '\0';
443 
444   /*
445    * Walk the list of extended attributes names and retrieve the data.
446    * We already count the bytes needed for serializing the stream later on.
447    */
448   for (bp = xattr_list; (bp - xattr_list) + 1 < xattr_list_len;
449        bp = strchr(bp, '\0') + 1) {
450     skip_xattr = false;
451 
452     /*
453      * We want to skip certain xattrs which start with a 0xF8 character on AIX.
454      */
455     if (*bp == 0xF8) { skip_xattr = true; }
456 
457     name_length = strlen(bp);
458     if (skip_xattr || name_length == 0) {
459       Dmsg1(100, "Skipping xattr named %s\n", bp);
460       continue;
461     }
462 
463     /*
464      * First see how long the value is for the extended attribute.
465      */
466     xattr_value_len = lgetea(xattr_data->last_fname, bp, NULL, 0);
467     switch (xattr_value_len) {
468       case -1: {
469         BErrNo be;
470 
471         switch (errno) {
472           case ENOENT:
473           case EFORMAT:
474             retval = BxattrExitCode::kSuccess;
475             goto bail_out;
476           default:
477             Mmsg2(jcr->errmsg, _("lgetea error on file \"%s\": ERR=%s\n"),
478                   xattr_data->last_fname, be.bstrerror());
479             Dmsg2(100, "lgetea error file=%s ERR=%s\n", xattr_data->last_fname,
480                   be.bstrerror());
481             goto bail_out;
482         }
483         break;
484       }
485       default:
486         break;
487     }
488 
489     /*
490      * Each xattr valuepair starts with a magic so we can parse it easier.
491      */
492     current_xattr = (xattr_t*)malloc(sizeof(xattr_t));
493     current_xattr->magic = XATTR_MAGIC;
494     expected_serialize_len += sizeof(current_xattr->magic);
495 
496     /*
497      * Allocate space for storing the name.
498      */
499     current_xattr->name_length = name_length;
500     current_xattr->name = (char*)malloc(current_xattr->name_length);
501     memcpy(current_xattr->name, bp, current_xattr->name_length);
502 
503     expected_serialize_len
504         += sizeof(current_xattr->name_length) + current_xattr->name_length;
505 
506     switch (xattr_value_len) {
507       case 0:
508         current_xattr->value = NULL;
509         current_xattr->value_length = 0;
510         expected_serialize_len += sizeof(current_xattr->value_length);
511         break;
512       default:
513         /*
514          * Allocate space for storing the value.
515          */
516         current_xattr->value = (char*)malloc(xattr_value_len);
517         memset(current_xattr->value, 0, xattr_value_len);
518 
519         xattr_value_len = lgetea(xattr_data->last_fname, bp,
520                                  current_xattr->value, xattr_value_len);
521         if (xattr_value_len < 0) {
522           BErrNo be;
523 
524           switch (errno) {
525             case ENOENT:
526             case EFORMAT:
527               retval = BxattrExitCode::kSuccess;
528               break;
529             default:
530               Mmsg2(jcr->errmsg, _("lgetea error on file \"%s\": ERR=%s\n"),
531                     xattr_data->last_fname, be.bstrerror());
532               Dmsg2(100, "lgetea error file=%s ERR=%s\n",
533                     xattr_data->last_fname, be.bstrerror());
534               break;
535           }
536 
537           /*
538            * Default failure path out when retrieval of attr fails.
539            */
540           free(current_xattr->value);
541           free(current_xattr->name);
542           free(current_xattr);
543           goto bail_out;
544         }
545 
546         /*
547          * Store the actual length of the value.
548          */
549         current_xattr->value_length = xattr_value_len;
550         expected_serialize_len += sizeof(current_xattr->value_length)
551                                   + current_xattr->value_length;
552         break;
553     }
554 
555     if (xattr_value_list == NULL) {
556       xattr_value_list = new alist(10, not_owned_by_alist);
557     }
558 
559     xattr_value_list->append(current_xattr);
560     xattr_count++;
561 
562     /*
563      * Protect ourself against things getting out of hand.
564      */
565     if (expected_serialize_len >= MAX_XATTR_STREAM) {
566       Mmsg2(jcr->errmsg,
567             _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
568             xattr_data->last_fname, MAX_XATTR_STREAM);
569       goto bail_out;
570     }
571   }
572 
573   free(xattr_list);
574   xattr_list = (char*)NULL;
575 
576   /*
577    * If we found any xattr send them to the SD.
578    */
579   if (xattr_count > 0) {
580     /*
581      * Serialize the datastream.
582      */
583     if (SerializeXattrStream(jcr, xattr_data, expected_serialize_len,
584                              xattr_value_list)
585         < expected_serialize_len) {
586       Mmsg1(jcr->errmsg,
587             _("Failed to Serialize extended attributes on file \"%s\"\n"),
588             xattr_data->last_fname);
589       Dmsg1(100, "Failed to Serialize extended attributes on file \"%s\"\n",
590             xattr_data->last_fname);
591       goto bail_out;
592     }
593 
594     /*
595      * Send the datastream to the SD.
596      */
597     retval = SendXattrStream(jcr, xattr_data, os_default_xattr_streams[0]);
598   } else {
599     retval = BxattrExitCode::kSuccess;
600   }
601 
602 bail_out:
603   if (xattr_list != NULL) { free(xattr_list); }
604   if (xattr_value_list != NULL) { XattrDropInternalTable(xattr_value_list); }
605 
606   return retval;
607 }
608 
aix_parse_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,int stream,char * content,uint32_t content_length)609 static BxattrExitCode aix_parse_xattr_streams(JobControlRecord* jcr,
610                                               XattrData* xattr_data,
611                                               int stream,
612                                               char* content,
613                                               uint32_t content_length)
614 {
615   xattr_t* current_xattr;
616   alist* xattr_value_list;
617   BxattrExitCode retval = BxattrExitCode::kError;
618 
619   xattr_value_list = new alist(10, not_owned_by_alist);
620 
621   if (UnSerializeXattrStream(jcr, xattr_data, content, content_length,
622                              xattr_value_list)
623       != BxattrExitCode::kSuccess) {
624     goto bail_out;
625   }
626 
627   foreach_alist (current_xattr, xattr_value_list) {
628     if (lsetea(xattr_data->last_fname, current_xattr->name,
629                current_xattr->value, current_xattr->value_length, 0)
630         != 0) {
631       BErrNo be;
632 
633       switch (errno) {
634         case ENOENT:
635         case EFORMAT:
636           goto bail_out;
637         case ENOTSUP:
638           /*
639            * If the filesystem reports it doesn't support XATTRs we clear
640            * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
641            * on all other files on the same filesystem. The
642            * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
643            * change from one filesystem to another.
644            */
645           xattr_data->flags &= ~BXATTR_FLAG_RESTORE_NATIVE;
646           retval = BxattrExitCode::kWarning;
647           Mmsg(jcr->errmsg, error_message_disabling_xattributes.c_str(),
648                xattr_data->last_fname);
649           Dmsg1(100, error_message_disabling_xattributes.c_str(),
650                 xattr_data->last_fname);
651           goto bail_out;
652         default:
653           Mmsg2(jcr->errmsg, _("lsetea error on file \"%s\": ERR=%s\n"),
654                 xattr_data->last_fname, be.bstrerror());
655           Dmsg2(100, "lsetea error file=%s ERR=%s\n", xattr_data->last_fname,
656                 be.bstrerror());
657           goto bail_out;
658       }
659     }
660   }
661 
662   retval = BxattrExitCode::kSuccess;
663 
664 bail_out:
665   XattrDropInternalTable(xattr_value_list);
666 
667   return retval;
668 }
669 
670 /**
671  * Function pointers to the build and parse function to use for these xattrs.
672  */
673 static BxattrExitCode (*os_build_xattr_streams)(JobControlRecord* jcr,
674                                                 XattrData* xattr_data,
675                                                 FindFilesPacket* ff_pkt)
676     = aix_build_xattr_streams;
677 static BxattrExitCode (*os_parse_xattr_streams)(JobControlRecord* jcr,
678                                                 XattrData* xattr_data,
679                                                 int stream,
680                                                 char* content,
681                                                 uint32_t content_length)
682     = aix_parse_xattr_streams;
683 
684 #  elif defined(HAVE_IRIX_OS)
685 
686 #    include <sys/attributes.h>
687 
688 /**
689  * Define the supported XATTR streams for this OS
690  */
691 static int os_default_xattr_streams[1] = {STREAM_XATTR_IRIX};
692 static const char* xattr_acl_skiplist[1] = {NULL};
693 static const char* xattr_skiplist[1] = {NULL};
694 
695 struct xattr_naming_space {
696   const char* name;
697   int flags;
698 };
699 
700 static xattr_naming_space xattr_naming_spaces[]
701     = {{"user.", ATTR_DONTFOLLOW},
702        {"root.", ATTR_ROOT | ATTR_DONTFOLLOW},
703        {NULL, 0}};
704 
irix_build_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,FindFilesPacket * ff_pkt)705 static BxattrExitCode irix_build_xattr_streams(JobControlRecord* jcr,
706                                                XattrData* xattr_data,
707                                                FindFilesPacket* ff_pkt)
708 {
709   char dummy[32];
710   int cnt, length, xattr_count = 0;
711   attrlist_cursor_t cursor;
712   attrlist_t* attrlist;
713   attrlist_ent_t* attrlist_ent;
714   xattr_t* current_xattr;
715   alist* xattr_value_list = NULL;
716   uint32_t expected_serialize_len = 0;
717   BxattrExitCode retval = BxattrExitCode::kError;
718   POOLMEM* xattrbuf = GetMemory(ATTR_MAX_VALUELEN);
719 
720   for (cnt = 0; xattr_naming_spaces[cnt].name != NULL; cnt++) {
721     memset(&cursor, 0, sizeof(attrlist_cursor_t));
722     while (1) {
723       if (attr_list(xattr_data->last_fname, xattrbuf, ATTR_MAX_VALUELEN,
724                     xattr_naming_spaces[cnt].flags, &cursor)
725           != 0) {
726         BErrNo be;
727 
728         switch (errno) {
729           case ENOENT:
730             retval = BxattrExitCode::kSuccess;
731             goto bail_out;
732           default:
733             Mmsg2(jcr->errmsg, _("attr_list error on file \"%s\": ERR=%s\n"),
734                   xattr_data->last_fname, be.bstrerror());
735             Dmsg2(100, "attr_list error file=%s ERR=%s\n",
736                   xattr_data->last_fname, be.bstrerror());
737             goto bail_out;
738         }
739       }
740 
741       attrlist = (attrlist_t*)xattrbuf;
742 
743       /*
744        * Walk the available attributes.
745        */
746       for (cnt = 0; cnt < attrlist->al_count; cnt++) {
747         attrlist_ent = ATTR_ENTRY(xattrbuf, cnt);
748 
749         /*
750          * First determine if we can retrieve the xattr and how big it really
751          * is.
752          */
753         length = sizeof(dummy);
754         if (attr_get(xattr_data->last_fname, attrlist_ent->a_name, dummy,
755                      &length, xattr_naming_spaces[cnt].flags)
756             != 0) {
757           BErrNo be;
758 
759           switch (errno) {
760             case ENOENT:
761             case ENOATTR:
762               retval = BxattrExitCode::kSuccess;
763               goto bail_out;
764             case E2BIG:
765               /*
766                * Size of the xattr is bigger then the 32 bytes dummy which is
767                * likely. As length now contains its actual length we can
768                * allocate a properly size buffer for the real retrieval.
769                */
770               break;
771             default:
772               Mmsg2(jcr->errmsg, _("attr_list error on file \"%s\": ERR=%s\n"),
773                     xattr_data->last_fname, be.bstrerror());
774               Dmsg2(100, "attr_list error file=%s ERR=%s\n",
775                     xattr_data->last_fname, be.bstrerror());
776               goto bail_out;
777           }
778         }
779 
780         /*
781          * Each xattr valuepair starts with a magic so we can parse it easier.
782          */
783         current_xattr = (xattr_t*)malloc(sizeof(xattr_t));
784         current_xattr->magic = XATTR_MAGIC;
785         expected_serialize_len += sizeof(current_xattr->magic);
786 
787         /*
788          * Allocate space for storing the name.
789          * We store the name as <naming_space_name><xattr_name>
790          */
791         current_xattr->name_length = strlen(xattr_naming_spaces[cnt].name)
792                                      + strlen(attrlist_ent->a_name) + 1;
793         current_xattr->name = (char*)malloc(current_xattr->name_length);
794         Bsnprintf(current_xattr->name, current_xattr->name_length, "%s%s",
795                   xattr_naming_spaces[cnt].name, attrlist_ent->a_name);
796 
797         expected_serialize_len
798             += sizeof(current_xattr->name_length) + current_xattr->name_length;
799 
800         current_xattr->value_length = length;
801         current_xattr->value = (char*)malloc(current_xattr->value_length);
802 
803         /*
804          * Retrieve the actual value of the xattr.
805          */
806         if (attr_get(xattr_data->last_fname, attrlist_ent->a_name,
807                      current_xattr->value, &length,
808                      xattr_naming_spaces[cnt].flags)
809             != 0) {
810           BErrNo be;
811 
812           switch (errno) {
813             case ENOENT:
814             case ENOATTR:
815               retval = BxattrExitCode::kSuccess;
816               break;
817             case E2BIG:
818               /*
819                * The buffer for the xattr isn't big enough. the value of
820                * current_xattr->value_length is updated with the actual size
821                * of the xattr. So we free the old buffer and create a new one
822                * and try again. Normally this cannot happen as we size the
823                * buffer using a call to attr_get before but in case of an
824                * race condition it might happen.
825                */
826               free(current_xattr->value);
827               current_xattr->value = (char*)malloc(length);
828               if (attr_get(xattr_data->last_fname, attrlist_ent->a_name,
829                            current_xattr->value, &length,
830                            xattr_naming_spaces[cnt].flags)
831                   != 0) {
832                 switch (errno) {
833                   case ENOENT:
834                   case ENOATTR:
835                     retval = BxattrExitCode::kSuccess;
836                     break;
837                   default:
838                     Mmsg2(jcr->errmsg,
839                           _("attr_list error on file \"%s\": ERR=%s\n"),
840                           xattr_data->last_fname, be.bstrerror(errno));
841                     Dmsg2(100, "attr_list error file=%s ERR=%s\n",
842                           xattr_data->last_fname, be.bstrerror());
843                     break;
844                 }
845               } else {
846                 goto ok_continue;
847               }
848               break;
849             default:
850               Mmsg2(jcr->errmsg, _("attr_list error on file \"%s\": ERR=%s\n"),
851                     xattr_data->last_fname, be.bstrerror());
852               Dmsg2(100, "attr_list error file=%s ERR=%s\n",
853                     xattr_data->last_fname, be.bstrerror());
854               break;
855           }
856 
857           /*
858            * Default failure path out when retrieval of attr fails.
859            */
860           free(current_xattr->value);
861           free(current_xattr->name);
862           free(current_xattr);
863           goto bail_out;
864         }
865 
866       ok_continue:
867         current_xattr->value_length = length;
868         expected_serialize_len += sizeof(current_xattr->value_length)
869                                   + current_xattr->value_length;
870 
871         if (xattr_value_list == NULL) {
872           xattr_value_list = new alist(10, not_owned_by_alist);
873         }
874 
875         xattr_value_list->append(current_xattr);
876         xattr_count++;
877 
878         /*
879          * Protect ourself against things getting out of hand.
880          */
881         if (expected_serialize_len >= MAX_XATTR_STREAM) {
882           Mmsg2(jcr->errmsg,
883                 _("Xattr stream on file \"%s\" exceeds maximum size of %d "
884                   "bytes\n"),
885                 xattr_data->last_fname, MAX_XATTR_STREAM);
886           goto bail_out;
887         }
888       }
889 
890       /*
891        * See if there are more attributes available for a next run of attr_list.
892        */
893       if (attrlist->al_more == 0) { break; }
894     }
895   }
896 
897   /*
898    * If we found any xattr send them to the SD.
899    */
900   if (xattr_count > 0) {
901     /*
902      * Serialize the datastream.
903      */
904     if (SerializeXattrStream(jcr, xattr_data, expected_serialize_len,
905                              xattr_value_list)
906         < expected_serialize_len) {
907       Mmsg1(jcr->errmsg,
908             _("Failed to Serialize extended attributes on file \"%s\"\n"),
909             xattr_data->last_fname);
910       Dmsg1(100, "Failed to Serialize extended attributes on file \"%s\"\n",
911             xattr_data->last_fname);
912       goto bail_out;
913     }
914 
915     /*
916      * Send the datastream to the SD.
917      */
918     retval = SendXattrStream(jcr, xattr_data, os_default_xattr_streams[0]);
919   } else {
920     retval = BxattrExitCode::kSuccess;
921   }
922 
923 bail_out:
924   FreePoolMemory(xattrbuf);
925 
926   if (xattr_value_list != NULL) { XattrDropInternalTable(xattr_value_list); }
927 
928   return retval;
929 }
930 
irix_parse_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,int stream,char * content,uint32_t content_length)931 static BxattrExitCode irix_parse_xattr_streams(JobControlRecord* jcr,
932                                                XattrData* xattr_data,
933                                                int stream,
934                                                char* content,
935                                                uint32_t content_length)
936 {
937   char* bp;
938   int cnt, cmp_size, name_space_index, flags;
939   xattr_t* current_xattr;
940   alist* xattr_value_list;
941   BxattrExitCode retval = BxattrExitCode::kError;
942 
943   xattr_value_list = new alist(10, not_owned_by_alist);
944 
945   if (UnSerializeXattrStream(jcr, xattr_data, content, content_length,
946                              xattr_value_list)
947       != BxattrExitCode::kSuccess) {
948     goto bail_out;
949   }
950 
951   foreach_alist (current_xattr, xattr_value_list) {
952     /*
953      * See to what namingspace this xattr belongs to.
954      */
955     name_space_index = 0;
956     for (cnt = 0; xattr_naming_spaces[cnt].name != NULL; cnt++) {
957       cmp_size = strlen(xattr_naming_spaces[cnt].name);
958       if (bstrncasecmp(current_xattr->name, xattr_naming_spaces[cnt].name,
959                        cmp_size)) {
960         name_space_index = cnt;
961         break;
962       }
963     }
964 
965     /*
966      * If we got a xattr that doesn't belong to an valid namespace complain.
967      */
968     if (name_space_index == 0) {
969       Mmsg2(jcr->errmsg, _("Received illegal xattr named %s on file \"%s\"\n"),
970             current_xattr->name, xattr_data->last_fname);
971       Dmsg2(100, "Received illegal xattr named %s on file \"%s\"\n",
972             current_xattr->name, xattr_data->last_fname);
973       goto bail_out;
974     }
975 
976     /*
977      * Restore the xattr first try to create the attribute from scratch.
978      */
979     flags = xattr_naming_spaces[name_space_index].flags | ATTR_CREATE;
980     bp = strchr(current_xattr->name, '.');
981     if (attr_set(xattr_data->last_fname, ++bp, current_xattr->value,
982                  current_xattr->value_length, flags)
983         != 0) {
984       BErrNo be;
985 
986       switch (errno) {
987         case ENOENT:
988           retval = BxattrExitCode::kSuccess;
989           goto bail_out;
990         case EEXIST:
991           /*
992            * The xattr already exists we need to replace it.
993            */
994           flags = xattr_naming_spaces[name_space_index].flags | ATTR_REPLACE;
995           if (attr_set(xattr_data->last_fname, bp, current_xattr->value,
996                        current_xattr->value_length, flags)
997               != 0) {
998             switch (errno) {
999               case ENOENT:
1000                 retval = BxattrExitCode::kSuccess;
1001                 goto bail_out;
1002               default:
1003                 Mmsg2(jcr->errmsg, _("attr_set error on file \"%s\": ERR=%s\n"),
1004                       xattr_data->last_fname, be.bstrerror(errno));
1005                 Dmsg2(100, "attr_set error file=%s ERR=%s\n",
1006                       xattr_data->last_fname, be.bstrerror());
1007                 goto bail_out;
1008             }
1009           }
1010           break;
1011         default:
1012           Mmsg2(jcr->errmsg, _("attr_set error on file \"%s\": ERR=%s\n"),
1013                 xattr_data->last_fname, be.bstrerror());
1014           Dmsg2(100, "attr_set error file=%s ERR=%s\n", xattr_data->last_fname,
1015                 be.bstrerror());
1016           goto bail_out;
1017       }
1018     }
1019   }
1020 
1021   retval = BxattrExitCode::kSuccess;
1022 
1023 bail_out:
1024   XattrDropInternalTable(xattr_value_list);
1025 
1026   return retval;
1027 }
1028 
1029 /**
1030  * Function pointers to the build and parse function to use for these xattrs.
1031  */
1032 static BxattrExitCode (*os_build_xattr_streams)(JobControlRecord* jcr,
1033                                                 XattrData* xattr_data,
1034                                                 FindFilesPacket* ff_pkt)
1035     = irix_build_xattr_streams;
1036 static BxattrExitCode (*os_parse_xattr_streams)(JobControlRecord* jcr,
1037                                                 XattrData* xattr_data,
1038                                                 int stream,
1039                                                 char* content,
1040                                                 uint32_t content_length)
1041     = irix_parse_xattr_streams;
1042 
1043 #  elif defined(HAVE_DARWIN_OS) || defined(HAVE_LINUX_OS) \
1044       || defined(HAVE_HURD_OS)
1045 
1046 #    if (!defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR))  \
1047         || (!defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)) \
1048         || (!defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR))
1049 #      error "Missing full support for the XATTR functions."
1050 #    endif
1051 
1052 #    ifdef HAVE_SYS_XATTR_H
1053 #      include <sys/xattr.h>
1054 #    else
1055 #      error "Missing sys/xattr.h header file"
1056 #    endif
1057 
1058 /**
1059  * Define the supported XATTR streams for this OS
1060  */
1061 #    if defined(HAVE_DARWIN_OS)
1062 static int os_default_xattr_streams[1] = {STREAM_XATTR_DARWIN};
1063 static const char* xattr_acl_skiplist[2] = {"com.apple.system.Security", NULL};
1064 static const char* xattr_skiplist[3]
1065     = {"com.apple.system.extendedsecurity", "com.apple.ResourceFork", NULL};
1066 #    elif defined(HAVE_LINUX_OS)
1067 static int os_default_xattr_streams[1] = {STREAM_XATTR_LINUX};
1068 static const char* xattr_acl_skiplist[3]
1069     = {"system.posix_acl_access", "system.posix_acl_default", NULL};
1070 static const char* xattr_skiplist[]
1071     = {"ceph.dir.entries",  "ceph.dir.files",    "ceph.dir.rbytes",
1072        "ceph.dir.rctime",   "ceph.dir.rentries", "ceph.dir.rfiles",
1073        "ceph.dir.rsubdirs", "ceph.dir.subdirs",  NULL};
1074 #    elif defined(HAVE_HURD_OS)
1075 static int os_default_xattr_streams[1] = {STREAM_XATTR_HURD};
1076 static const char* xattr_acl_skiplist[1] = {NULL};
1077 static const char* xattr_skiplist[1] = {NULL};
1078 #    endif
1079 
1080 /**
1081  * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
1082  * listxattr, getxattr and setxattr with an extra options argument
1083  * which mimics the l variants of the functions when we specify
1084  * XATTR_NOFOLLOW as the options value.
1085  */
1086 #    if defined(HAVE_DARWIN_OS)
1087 #      define llistxattr(path, list, size) \
1088         listxattr((path), (list), (size), XATTR_NOFOLLOW)
1089 #      define lgetxattr(path, name, value, size) \
1090         getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
1091 #      define lsetxattr(path, name, value, size, flags) \
1092         setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
1093 #    else
1094 /*
1095  * Fallback to the non l-functions when those are not available.
1096  */
1097 #      if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
1098 #        define lgetxattr getxattr
1099 #      endif
1100 #      if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
1101 #        define lsetxattr setxattr
1102 #      endif
1103 #      if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
1104 #        define llistxattr listxattr
1105 #      endif
1106 #    endif
1107 
generic_build_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,FindFilesPacket * ff_pkt)1108 static BxattrExitCode generic_build_xattr_streams(JobControlRecord* jcr,
1109                                                   XattrData* xattr_data,
1110                                                   FindFilesPacket* ff_pkt)
1111 {
1112   char* bp;
1113   bool skip_xattr;
1114   char* xattr_list = NULL;
1115   int cnt, xattr_count = 0;
1116   uint32_t name_length;
1117   int32_t xattr_list_len, xattr_value_len;
1118   uint32_t expected_serialize_len = 0;
1119   xattr_t* current_xattr;
1120   alist* xattr_value_list = NULL;
1121   BxattrExitCode retval = BxattrExitCode::kError;
1122 
1123   /*
1124    * First get the length of the available list with extended attributes.
1125    */
1126   xattr_list_len = llistxattr(xattr_data->last_fname, NULL, 0);
1127   switch (xattr_list_len) {
1128     case -1: {
1129       BErrNo be;
1130 
1131       switch (errno) {
1132         case ENOENT:
1133           retval = BxattrExitCode::kSuccess;
1134           goto bail_out;
1135         case BXATTR_ENOTSUP:
1136           /*
1137            * If the filesystem reports it doesn't support XATTRs we clear
1138            * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
1139            * on all other files on the same filesystem. The
1140            * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
1141            * change from one filesystem to another.
1142            */
1143           xattr_data->flags &= ~BXATTR_FLAG_SAVE_NATIVE;
1144           retval = BxattrExitCode::kWarning;
1145           Mmsg(jcr->errmsg, error_message_disabling_xattributes.c_str(),
1146                xattr_data->last_fname);
1147           Dmsg1(100, error_message_disabling_xattributes.c_str(),
1148                 xattr_data->last_fname);
1149           goto bail_out;
1150         default:
1151           Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
1152                 xattr_data->last_fname, be.bstrerror());
1153           Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
1154                 xattr_data->last_fname, be.bstrerror());
1155           goto bail_out;
1156       }
1157       break;
1158     }
1159     case 0:
1160       retval = BxattrExitCode::kSuccess;
1161       goto bail_out;
1162     default:
1163       break;
1164   }
1165 
1166   /*
1167    * Allocate room for the extented attribute list.
1168    */
1169   xattr_list = (char*)malloc(xattr_list_len + 1);
1170   memset(xattr_list, 0, xattr_list_len + 1);
1171 
1172   /*
1173    * Get the actual list of extended attributes names for a file.
1174    */
1175   xattr_list_len
1176       = llistxattr(xattr_data->last_fname, xattr_list, xattr_list_len);
1177   switch (xattr_list_len) {
1178     case -1: {
1179       BErrNo be;
1180 
1181       switch (errno) {
1182         case ENOENT:
1183           retval = BxattrExitCode::kSuccess;
1184           goto bail_out;
1185         default:
1186           Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"),
1187                 xattr_data->last_fname, be.bstrerror());
1188           Dmsg2(100, "llistxattr error file=%s ERR=%s\n",
1189                 xattr_data->last_fname, be.bstrerror());
1190           goto bail_out;
1191       }
1192       break;
1193     }
1194     default:
1195       break;
1196   }
1197   xattr_list[xattr_list_len] = '\0';
1198 
1199   /*
1200    * Walk the list of extended attributes names and retrieve the data.
1201    * We already count the bytes needed for serializing the stream later on.
1202    */
1203   for (bp = xattr_list; (bp - xattr_list) + 1 < xattr_list_len;
1204        bp = strchr(bp, '\0') + 1) {
1205     skip_xattr = false;
1206 
1207     /*
1208      * On some OSes you also get the acls in the extented attribute list.
1209      * So we check if we are already backing up acls and if we do we
1210      * don't store the extended attribute with the same info.
1211      */
1212     if (BitIsSet(FO_ACL, ff_pkt->flags)) {
1213       for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
1214         if (bstrcmp(bp, xattr_acl_skiplist[cnt])) {
1215           skip_xattr = true;
1216           break;
1217         }
1218       }
1219     }
1220 
1221     /*
1222      * On some OSes we want to skip certain xattrs which are in the
1223      * xattr_skiplist array.
1224      */
1225     if (!skip_xattr) {
1226       for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
1227         if (bstrcmp(bp, xattr_skiplist[cnt])) {
1228           skip_xattr = true;
1229           break;
1230         }
1231       }
1232     }
1233 
1234     name_length = strlen(bp);
1235     if (skip_xattr || name_length == 0) {
1236       Dmsg1(100, "Skipping xattr named %s\n", bp);
1237       continue;
1238     }
1239 
1240     /*
1241      * First see how long the value is for the extended attribute.
1242      */
1243     xattr_value_len = lgetxattr(xattr_data->last_fname, bp, NULL, 0);
1244     switch (xattr_value_len) {
1245       case -1: {
1246         BErrNo be;
1247 
1248         switch (errno) {
1249           case ENOENT:
1250             retval = BxattrExitCode::kSuccess;
1251             goto bail_out;
1252           default:
1253             Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
1254                   xattr_data->last_fname, be.bstrerror());
1255             Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
1256                   xattr_data->last_fname, be.bstrerror());
1257             goto bail_out;
1258         }
1259         break;
1260       }
1261       default:
1262         break;
1263     }
1264 
1265     /*
1266      * Each xattr valuepair starts with a magic so we can parse it easier.
1267      */
1268     current_xattr = (xattr_t*)malloc(sizeof(xattr_t));
1269     current_xattr->magic = XATTR_MAGIC;
1270     current_xattr->value = NULL;
1271     expected_serialize_len += sizeof(current_xattr->magic);
1272 
1273     /*
1274      * Allocate space for storing the name.
1275      */
1276     current_xattr->name_length = name_length;
1277     current_xattr->name = (char*)malloc(current_xattr->name_length);
1278     memcpy(current_xattr->name, bp, current_xattr->name_length);
1279 
1280     expected_serialize_len
1281         += sizeof(current_xattr->name_length) + current_xattr->name_length;
1282 
1283     switch (xattr_value_len) {
1284       case 0:
1285         current_xattr->value = NULL;
1286         current_xattr->value_length = 0;
1287         expected_serialize_len += sizeof(current_xattr->value_length);
1288         break;
1289       default:
1290         /*
1291          * Allocate space for storing the value.
1292          */
1293         current_xattr->value = (char*)malloc(xattr_value_len);
1294         memset(current_xattr->value, 0, xattr_value_len);
1295 
1296         xattr_value_len = lgetxattr(xattr_data->last_fname, bp,
1297                                     current_xattr->value, xattr_value_len);
1298         if (xattr_value_len < 0) {
1299           BErrNo be;
1300 
1301           switch (errno) {
1302             case ENOENT:
1303               retval = BxattrExitCode::kSuccess;
1304               break;
1305             default:
1306               Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"),
1307                     xattr_data->last_fname, be.bstrerror());
1308               Dmsg2(100, "lgetxattr error file=%s ERR=%s\n",
1309                     xattr_data->last_fname, be.bstrerror());
1310               break;
1311           }
1312 
1313           /*
1314            * Default failure path out when retrieval of attr fails.
1315            */
1316           free(current_xattr->value);
1317           free(current_xattr->name);
1318           free(current_xattr);
1319           goto bail_out;
1320         }
1321         /*
1322          * Store the actual length of the value.
1323          */
1324         current_xattr->value_length = xattr_value_len;
1325         expected_serialize_len += sizeof(current_xattr->value_length)
1326                                   + current_xattr->value_length;
1327         break;
1328     }
1329 
1330     if (xattr_value_list == NULL) {
1331       xattr_value_list = new alist(10, not_owned_by_alist);
1332     }
1333 
1334     xattr_value_list->append(current_xattr);
1335     xattr_count++;
1336 
1337     /*
1338      * Protect ourself against things getting out of hand.
1339      */
1340     if (expected_serialize_len >= MAX_XATTR_STREAM) {
1341       Mmsg2(jcr->errmsg,
1342             _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
1343             xattr_data->last_fname, MAX_XATTR_STREAM);
1344       goto bail_out;
1345     }
1346   }
1347 
1348   free(xattr_list);
1349   xattr_list = (char*)NULL;
1350 
1351   /*
1352    * If we found any xattr send them to the SD.
1353    */
1354   if (xattr_count > 0) {
1355     /*
1356      * Serialize the datastream.
1357      */
1358     if (SerializeXattrStream(jcr, xattr_data, expected_serialize_len,
1359                              xattr_value_list)
1360         < expected_serialize_len) {
1361       Mmsg1(jcr->errmsg,
1362             _("Failed to Serialize extended attributes on file \"%s\"\n"),
1363             xattr_data->last_fname);
1364       Dmsg1(100, "Failed to Serialize extended attributes on file \"%s\"\n",
1365             xattr_data->last_fname);
1366       goto bail_out;
1367     }
1368 
1369     /*
1370      * Send the datastream to the SD.
1371      */
1372     retval = SendXattrStream(jcr, xattr_data, os_default_xattr_streams[0]);
1373   } else {
1374     retval = BxattrExitCode::kSuccess;
1375   }
1376 
1377 bail_out:
1378   if (xattr_list != NULL) { free(xattr_list); }
1379   if (xattr_value_list != NULL) { XattrDropInternalTable(xattr_value_list); }
1380 
1381   return retval;
1382 }
1383 
generic_parse_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,int stream,char * content,uint32_t content_length)1384 static BxattrExitCode generic_parse_xattr_streams(JobControlRecord* jcr,
1385                                                   XattrData* xattr_data,
1386                                                   int stream,
1387                                                   char* content,
1388                                                   uint32_t content_length)
1389 {
1390   xattr_t* current_xattr = nullptr;
1391   alist* xattr_value_list;
1392   BxattrExitCode retval = BxattrExitCode::kError;
1393 
1394   xattr_value_list = new alist(10, not_owned_by_alist);
1395 
1396   if (UnSerializeXattrStream(jcr, xattr_data, content, content_length,
1397                              xattr_value_list)
1398       != BxattrExitCode::kSuccess) {
1399     goto bail_out;
1400   }
1401 
1402   foreach_alist (current_xattr, xattr_value_list) {
1403     if (lsetxattr(xattr_data->last_fname, current_xattr->name,
1404                   current_xattr->value, current_xattr->value_length, 0)
1405         != 0) {
1406       BErrNo be;
1407 
1408       switch (errno) {
1409         case ENOENT:
1410           goto bail_out;
1411         case BXATTR_ENOTSUP:
1412           /*
1413            * If the filesystem reports it doesn't support XATTRs we clear
1414            * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
1415            * on all other files on the same filesystem. The
1416            * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
1417            * change from one filesystem to another.
1418            */
1419           xattr_data->flags &= ~BXATTR_FLAG_RESTORE_NATIVE;
1420           retval = BxattrExitCode::kWarning;
1421           Mmsg(jcr->errmsg, error_message_disabling_xattributes.c_str(),
1422                xattr_data->last_fname);
1423           Dmsg1(100, error_message_disabling_xattributes.c_str(),
1424                 xattr_data->last_fname);
1425           goto bail_out;
1426         default:
1427           Mmsg2(jcr->errmsg, _("lsetxattr error on file \"%s\": ERR=%s\n"),
1428                 xattr_data->last_fname, be.bstrerror());
1429           Dmsg2(100, "lsetxattr error file=%s ERR=%s\n", xattr_data->last_fname,
1430                 be.bstrerror());
1431           goto bail_out;
1432       }
1433     }
1434   }
1435 
1436   retval = BxattrExitCode::kSuccess;
1437 
1438 bail_out:
1439   XattrDropInternalTable(xattr_value_list);
1440 
1441   return retval;
1442 }
1443 
1444 /**
1445  * Function pointers to the build and parse function to use for these xattrs.
1446  */
1447 static BxattrExitCode (*os_build_xattr_streams)(JobControlRecord* jcr,
1448                                                 XattrData* xattr_data,
1449                                                 FindFilesPacket* ff_pkt)
1450     = generic_build_xattr_streams;
1451 static BxattrExitCode (*os_parse_xattr_streams)(JobControlRecord* jcr,
1452                                                 XattrData* xattr_data,
1453                                                 int stream,
1454                                                 char* content,
1455                                                 uint32_t content_length)
1456     = generic_parse_xattr_streams;
1457 
1458 #  elif defined(HAVE_FREEBSD_OS) || defined(HAVE_NETBSD_OS) \
1459       || defined(HAVE_OPENBSD_OS)
1460 
1461 #    if (!defined(HAVE_EXTATTR_GET_LINK) && !defined(HAVE_EXTATTR_GET_FILE)) \
1462         || (!defined(HAVE_EXTATTR_SET_LINK)                                  \
1463             && !defined(HAVE_EXTATTR_SET_FILE))                              \
1464         || (!defined(HAVE_EXTATTR_LIST_LINK)                                 \
1465             && !defined(HAVE_EXTATTR_LIST_FILE))                             \
1466         || !defined(HAVE_EXTATTR_NAMESPACE_TO_STRING)                        \
1467         || !defined(HAVE_EXTATTR_STRING_TO_NAMESPACE)
1468 #      error "Missing full support for the extattr functions."
1469 #    endif
1470 
1471 #    ifdef HAVE_SYS_EXTATTR_H
1472 #      include <sys/extattr.h>
1473 #    else
1474 #      error "Missing sys/extattr.h header file"
1475 #    endif
1476 
1477 #    ifdef HAVE_LIBUTIL_H
1478 #      include <libutil.h>
1479 #    endif
1480 
1481 #    if !defined(HAVE_EXTATTR_GET_LINK) && defined(HAVE_EXTATTR_GET_FILE)
1482 #      define extattr_get_link extattr_get_file
1483 #    endif
1484 #    if !defined(HAVE_EXTATTR_SET_LINK) && defined(HAVE_EXTATTR_SET_FILE)
1485 #      define extattr_set_link extattr_set_file
1486 #    endif
1487 #    if !defined(HAVE_EXTATTR_LIST_LINK) && defined(HAVE_EXTATTR_LIST_FILE)
1488 #      define extattr_list_link extattr_list_file
1489 #    endif
1490 
1491 #    if defined(HAVE_FREEBSD_OS)
1492 static int os_default_xattr_streams[1] = {STREAM_XATTR_FREEBSD};
1493 static int os_default_xattr_namespaces[2]
1494     = {EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM};
1495 static const char* xattr_acl_skiplist[4]
1496     = {"system.posix1e.acl_access", "system.posix1e.acl_default",
1497        "system.nfs4.acl", NULL};
1498 static const char* xattr_skiplist[1] = {NULL};
1499 #    elif defined(HAVE_NETBSD_OS)
1500 static int os_default_xattr_streams[1] = {STREAM_XATTR_NETBSD};
1501 static int os_default_xattr_namespaces[2]
1502     = {EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM};
1503 static const char* xattr_acl_skiplist[1] = {NULL};
1504 static const char* xattr_skiplist[1] = {NULL};
1505 #    elif defined(HAVE_OPENBSD_OS)
1506 static int os_default_xattr_streams[1] = {STREAM_XATTR_OPENBSD};
1507 static int os_default_xattr_namespaces[2]
1508     = {EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM};
1509 static const char* xattr_acl_skiplist[1] = {NULL};
1510 static const char* xattr_skiplist[1] = {NULL};
1511 #    endif
1512 
bsd_build_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,FindFilesPacket * ff_pkt)1513 static BxattrExitCode bsd_build_xattr_streams(JobControlRecord* jcr,
1514                                               XattrData* xattr_data,
1515                                               FindFilesPacket* ff_pkt)
1516 {
1517   bool skip_xattr;
1518   char* xattr_list = NULL;
1519   int cnt, index, xattr_count = 0;
1520   int32_t xattr_list_len, xattr_value_len;
1521   uint32_t expected_serialize_len = 0;
1522   unsigned int namespace_index;
1523   int attrnamespace;
1524   char* current_attrnamespace = NULL;
1525   char current_attrname[XATTR_BUFSIZ], current_attrtuple[XATTR_BUFSIZ];
1526   xattr_t* current_xattr;
1527   alist* xattr_value_list = NULL;
1528   BxattrExitCode retval = BxattrExitCode::kError;
1529 
1530   /*
1531    * Loop over all available xattr namespaces.
1532    */
1533   for (namespace_index = 0;
1534        namespace_index < sizeof(os_default_xattr_namespaces) / sizeof(int);
1535        namespace_index++) {
1536     attrnamespace = os_default_xattr_namespaces[namespace_index];
1537 
1538     /*
1539      * First get the length of the available list with extended attributes.
1540      * If we get EPERM on system namespace, don't return error.
1541      * This is expected for normal users trying to archive the system
1542      * namespace on FreeBSD 6.2 and later. On NetBSD 3.1 and later,
1543      * they've decided to return EOPNOTSUPP instead.
1544      */
1545     xattr_list_len
1546         = extattr_list_link(xattr_data->last_fname, attrnamespace, NULL, 0);
1547     switch (xattr_list_len) {
1548       case -1: {
1549         BErrNo be;
1550 
1551         switch (errno) {
1552           case ENOENT:
1553             retval = BxattrExitCode::kSuccess;
1554             goto bail_out;
1555 #    if defined(EOPNOTSUPP)
1556           case EOPNOTSUPP:
1557 #    endif
1558           case EPERM:
1559             if (attrnamespace == EXTATTR_NAMESPACE_SYSTEM) { continue; }
1560             /*
1561              * FALLTHROUGH
1562              */
1563           default:
1564             Mmsg2(jcr->errmsg,
1565                   _("extattr_list_link error on file \"%s\": ERR=%s\n"),
1566                   xattr_data->last_fname, be.bstrerror());
1567             Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
1568                   xattr_data->last_fname, be.bstrerror());
1569             goto bail_out;
1570         }
1571         break;
1572       }
1573       case 0:
1574         continue;
1575       default:
1576         break;
1577     }
1578 
1579     /*
1580      * Allocate room for the extented attribute list.
1581      */
1582     xattr_list = (char*)malloc(xattr_list_len + 1);
1583     memset(xattr_list, 0, xattr_list_len + 1);
1584 
1585     /*
1586      * Get the actual list of extended attributes names for a file.
1587      */
1588     xattr_list_len = extattr_list_link(xattr_data->last_fname, attrnamespace,
1589                                        xattr_list, xattr_list_len);
1590     switch (xattr_list_len) {
1591       case -1: {
1592         BErrNo be;
1593 
1594         switch (errno) {
1595           case ENOENT:
1596             retval = BxattrExitCode::kSuccess;
1597             goto bail_out;
1598           default:
1599             Mmsg2(jcr->errmsg,
1600                   _("extattr_list_link error on file \"%s\": ERR=%s\n"),
1601                   xattr_data->last_fname, be.bstrerror());
1602             Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n",
1603                   xattr_data->last_fname, be.bstrerror());
1604             goto bail_out;
1605         }
1606         break;
1607       }
1608       default:
1609         break;
1610     }
1611     xattr_list[xattr_list_len] = '\0';
1612 
1613     /*
1614      * Convert the numeric attrnamespace into a string representation and make
1615      * a private copy of that string. The extattr_namespace_to_string functions
1616      * returns a strdupped string which we need to free.
1617      */
1618     if (extattr_namespace_to_string(attrnamespace, &current_attrnamespace)
1619         != 0) {
1620       Mmsg2(jcr->errmsg,
1621             _("Failed to convert %d into namespace on file \"%s\"\n"),
1622             attrnamespace, xattr_data->last_fname);
1623       Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n",
1624             attrnamespace, xattr_data->last_fname);
1625       goto bail_out;
1626     }
1627 
1628     /*
1629      * Walk the list of extended attributes names and retrieve the data.
1630      * We already count the bytes needed for serializing the stream later on.
1631      */
1632     for (index = 0; index < xattr_list_len; index += xattr_list[index] + 1) {
1633       skip_xattr = false;
1634 
1635       /*
1636        * Print the current name into the buffer as its not null terminated
1637        * we need to use the length encoded in the string for copying only
1638        * the needed bytes.
1639        */
1640       cnt = xattr_list[index];
1641       if (cnt > ((int)sizeof(current_attrname) - 1)) {
1642         cnt = ((int)sizeof(current_attrname) - 1);
1643       }
1644       strncpy(current_attrname, xattr_list + (index + 1), cnt);
1645       current_attrname[cnt] = '\0';
1646 
1647       /*
1648        * First make a xattr tuple of the current namespace and the name of
1649        * the xattr. e.g. something like user.<attrname> or system.<attrname>
1650        */
1651       Bsnprintf(current_attrtuple, sizeof(current_attrtuple), "%s.%s",
1652                 current_attrnamespace, current_attrname);
1653 
1654       /*
1655        * On some OSes you also get the acls in the extented attribute list.
1656        * So we check if we are already backing up acls and if we do we
1657        * don't store the extended attribute with the same info.
1658        */
1659       if (BitIsSet(FO_ACL, ff_pkt->flags)) {
1660         for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
1661           if (bstrcmp(current_attrtuple, xattr_acl_skiplist[cnt])) {
1662             skip_xattr = true;
1663             break;
1664           }
1665         }
1666       }
1667 
1668       /*
1669        * On some OSes we want to skip certain xattrs which are in the
1670        * xattr_skiplist array.
1671        */
1672       if (!skip_xattr) {
1673         for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
1674           if (bstrcmp(current_attrtuple, xattr_skiplist[cnt])) {
1675             skip_xattr = true;
1676             break;
1677           }
1678         }
1679       }
1680 
1681       if (skip_xattr) {
1682         Dmsg1(100, "Skipping xattr named %s\n", current_attrname);
1683         continue;
1684       }
1685 
1686       /*
1687        * First see how long the value is for the extended attribute.
1688        */
1689       xattr_value_len = extattr_get_link(xattr_data->last_fname, attrnamespace,
1690                                          current_attrname, NULL, 0);
1691       switch (xattr_value_len) {
1692         case -1: {
1693           BErrNo be;
1694 
1695           switch (errno) {
1696             case ENOENT:
1697               retval = BxattrExitCode::kSuccess;
1698               goto bail_out;
1699             default:
1700               Mmsg2(jcr->errmsg,
1701                     _("extattr_get_link error on file \"%s\": ERR=%s\n"),
1702                     xattr_data->last_fname, be.bstrerror());
1703               Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
1704                     xattr_data->last_fname, be.bstrerror());
1705               goto bail_out;
1706           }
1707           break;
1708         }
1709         default:
1710           break;
1711       }
1712 
1713       /*
1714        * Each xattr valuepair starts with a magic so we can parse it easier.
1715        */
1716       current_xattr = (xattr_t*)malloc(sizeof(xattr_t));
1717       current_xattr->magic = XATTR_MAGIC;
1718       current_xattr->value = NULL;
1719       expected_serialize_len += sizeof(current_xattr->magic);
1720 
1721       /*
1722        * Allocate space for storing the name.
1723        */
1724       current_xattr->name_length = strlen(current_attrtuple);
1725       current_xattr->name = (char*)malloc(current_xattr->name_length);
1726       memcpy(current_xattr->name, current_attrtuple,
1727              current_xattr->name_length);
1728 
1729       expected_serialize_len
1730           += sizeof(current_xattr->name_length) + current_xattr->name_length;
1731 
1732       switch (xattr_value_len) {
1733         case 0:
1734           current_xattr->value = NULL;
1735           current_xattr->value_length = 0;
1736           expected_serialize_len += sizeof(current_xattr->value_length);
1737           break;
1738         default:
1739           /*
1740            * Allocate space for storing the value.
1741            */
1742           current_xattr->value = (char*)malloc(xattr_value_len);
1743           memset(current_xattr->value, 0, xattr_value_len);
1744 
1745           xattr_value_len = extattr_get_link(
1746               xattr_data->last_fname, attrnamespace, current_attrname,
1747               current_xattr->value, xattr_value_len);
1748           if (xattr_value_len < 0) {
1749             BErrNo be;
1750 
1751             switch (errno) {
1752               case ENOENT:
1753                 retval = BxattrExitCode::kSuccess;
1754                 break;
1755               default:
1756                 Mmsg2(jcr->errmsg,
1757                       _("extattr_get_link error on file \"%s\": ERR=%s\n"),
1758                       xattr_data->last_fname, be.bstrerror());
1759                 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n",
1760                       xattr_data->last_fname, be.bstrerror());
1761                 break;
1762             }
1763 
1764             /*
1765              * Default failure path out when retrieval of attr fails.
1766              */
1767             free(current_xattr->value);
1768             free(current_xattr->name);
1769             free(current_xattr);
1770             goto bail_out;
1771           }
1772 
1773           /*
1774            * Store the actual length of the value.
1775            */
1776           current_xattr->value_length = xattr_value_len;
1777           expected_serialize_len += sizeof(current_xattr->value_length)
1778                                     + current_xattr->value_length;
1779           break;
1780       }
1781 
1782       if (xattr_value_list == NULL) {
1783         xattr_value_list = new alist(10, not_owned_by_alist);
1784       }
1785 
1786       xattr_value_list->append(current_xattr);
1787       xattr_count++;
1788 
1789       /*
1790        * Protect ourself against things getting out of hand.
1791        */
1792       if (expected_serialize_len >= MAX_XATTR_STREAM) {
1793         Mmsg2(
1794             jcr->errmsg,
1795             _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
1796             xattr_data->last_fname, MAX_XATTR_STREAM);
1797         goto bail_out;
1798       }
1799     }
1800 
1801     /*
1802      * Drop the local copy of the current_attrnamespace.
1803      */
1804     free(current_attrnamespace);
1805     current_attrnamespace = NULL;
1806 
1807     /*
1808      * We are done with this xattr list.
1809      */
1810     free(xattr_list);
1811     xattr_list = (char*)NULL;
1812   }
1813 
1814   /*
1815    * If we found any xattr send them to the SD.
1816    */
1817   if (xattr_count > 0) {
1818     /*
1819      * Serialize the datastream.
1820      */
1821     if (SerializeXattrStream(jcr, xattr_data, expected_serialize_len,
1822                              xattr_value_list)
1823         < expected_serialize_len) {
1824       Mmsg1(jcr->errmsg,
1825             _("Failed to Serialize extended attributes on file \"%s\"\n"),
1826             xattr_data->last_fname);
1827       Dmsg1(100, "Failed to Serialize extended attributes on file \"%s\"\n",
1828             xattr_data->last_fname);
1829       goto bail_out;
1830     }
1831 
1832     /*
1833      * Send the datastream to the SD.
1834      */
1835     retval = SendXattrStream(jcr, xattr_data, os_default_xattr_streams[0]);
1836   } else {
1837     retval = BxattrExitCode::kSuccess;
1838   }
1839 
1840 bail_out:
1841   if (current_attrnamespace != NULL) { free(current_attrnamespace); }
1842   if (xattr_list != NULL) { free(xattr_list); }
1843   if (xattr_value_list != NULL) { XattrDropInternalTable(xattr_value_list); }
1844 
1845   return retval;
1846 }
1847 
bsd_parse_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,int stream,char * content,uint32_t content_length)1848 static BxattrExitCode bsd_parse_xattr_streams(JobControlRecord* jcr,
1849                                               XattrData* xattr_data,
1850                                               int stream,
1851                                               char* content,
1852                                               uint32_t content_length)
1853 {
1854   xattr_t* current_xattr = nullptr;
1855   alist* xattr_value_list;
1856   int current_attrnamespace, cnt;
1857   char *attrnamespace, *attrname;
1858   BxattrExitCode retval = BxattrExitCode::kError;
1859 
1860   xattr_value_list = new alist(10, not_owned_by_alist);
1861 
1862   if (UnSerializeXattrStream(jcr, xattr_data, content, content_length,
1863                              xattr_value_list)
1864       != BxattrExitCode::kSuccess) {
1865     goto bail_out;
1866   }
1867 
1868   foreach_alist (current_xattr, xattr_value_list) {
1869     /*
1870      * Try splitting the xattr_name into a namespace and name part.
1871      * The splitting character is a .
1872      */
1873     attrnamespace = current_xattr->name;
1874     if ((attrname = strchr(attrnamespace, '.')) == (char*)NULL) {
1875       Mmsg2(
1876           jcr->errmsg,
1877           _("Failed to split %s into namespace and name part on file \"%s\"\n"),
1878           current_xattr->name, xattr_data->last_fname);
1879       Dmsg2(100,
1880             "Failed to split %s into namespace and name part on file \"%s\"\n",
1881             current_xattr->name, xattr_data->last_fname);
1882       goto bail_out;
1883     }
1884     *attrname++ = '\0';
1885 
1886     /*
1887      * Make sure the attrnamespace makes sense.
1888      */
1889     if (extattr_string_to_namespace(attrnamespace, &current_attrnamespace)
1890         != 0) {
1891       Mmsg2(jcr->errmsg,
1892             _("Failed to convert %s into namespace on file \"%s\"\n"),
1893             attrnamespace, xattr_data->last_fname);
1894       Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n",
1895             attrnamespace, xattr_data->last_fname);
1896       goto bail_out;
1897     }
1898 
1899     /*
1900      * Try restoring the extended attribute.
1901      */
1902     cnt = extattr_set_link(xattr_data->last_fname, current_attrnamespace,
1903                            attrname, current_xattr->value,
1904                            current_xattr->value_length);
1905     if (cnt < 0 || cnt != (int)current_xattr->value_length) {
1906       BErrNo be;
1907 
1908       switch (errno) {
1909         case ENOENT:
1910           goto bail_out;
1911           break;
1912         default:
1913           Mmsg2(jcr->errmsg,
1914                 _("extattr_set_link error on file \"%s\": ERR=%s\n"),
1915                 xattr_data->last_fname, be.bstrerror());
1916           Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n",
1917                 xattr_data->last_fname, be.bstrerror());
1918           goto bail_out;
1919           break;
1920       }
1921     }
1922   }
1923 
1924   retval = BxattrExitCode::kSuccess;
1925 
1926 bail_out:
1927   XattrDropInternalTable(xattr_value_list);
1928 
1929   return retval;
1930 }
1931 
1932 /**
1933  * Function pointers to the build and parse function to use for these xattrs.
1934  */
1935 static BxattrExitCode (*os_build_xattr_streams)(JobControlRecord* jcr,
1936                                                 XattrData* xattr_data,
1937                                                 FindFilesPacket* ff_pkt)
1938     = bsd_build_xattr_streams;
1939 static BxattrExitCode (*os_parse_xattr_streams)(JobControlRecord* jcr,
1940                                                 XattrData* xattr_data,
1941                                                 int stream,
1942                                                 char* content,
1943                                                 uint32_t content_length)
1944     = bsd_parse_xattr_streams;
1945 
1946 #  elif defined(HAVE_OSF1_OS)
1947 
1948 #    if !defined(HAVE_GETPROPLIST) || !defined(HAVE_GET_PROPLIST_ENTRY) \
1949         || !defined(HAVE_SIZEOF_PROPLIST_ENTRY)                         \
1950         || !defined(HAVE_ADD_PROPLIST_ENTRY) || !defined(HAVE_SETPROPLIST)
1951 #      error "Missing full support for the Extended Attributes functions."
1952 #    endif
1953 
1954 #    ifdef HAVE_SYS_PROPLIST_H
1955 #      include <sys/proplist.h>
1956 #    else
1957 #      error "Missing sys/proplist.h header file"
1958 #    endif
1959 
1960 /**
1961  * Define the supported XATTR streams for this OS
1962  */
1963 static int os_default_xattr_streams[1] = {STREAM_XATTR_TRU64};
1964 static const char* xattr_acl_skiplist[1] = {NULL};
1965 static const char* xattr_skiplist[1] = {NULL};
1966 
tru64_build_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,FindFilesPacket * ff_pkt)1967 static BxattrExitCode tru64_build_xattr_streams(JobControlRecord* jcr,
1968                                                 XattrData* xattr_data,
1969                                                 FindFilesPacket* ff_pkt)
1970 {
1971   int cnt;
1972   char *bp, *xattr_name, *xattr_value;
1973   bool skip_xattr;
1974   int xattr_count = 0;
1975   int32_t *flags, *xattr_value_len;
1976   int32_t xattr_list_len, xattrbuf_size, xattrbuf_min_size;
1977   uint32_t expected_serialize_len = 0;
1978   xattr_t* current_xattr;
1979   alist* xattr_value_list = NULL;
1980   struct proplistname_args prop_args;
1981   BxattrExitCode retval = BxattrExitCode::kError;
1982   POOLMEM* xattrbuf = GetPoolMemory(PM_MESSAGE);
1983 
1984   xattrbuf_size = SizeofPoolMemory(xattrbuf);
1985   xattrbuf_min_size = 0;
1986   xattr_list_len = getproplist(xattr_data->last_fname, 1, &prop_args,
1987                                xattrbuf_size, xattrbuf, &xattrbuf_min_size);
1988 
1989   /*
1990    * See what xattr are available.
1991    */
1992   switch (xattr_list_len) {
1993     case -1: {
1994       BErrNo be;
1995 
1996       switch (errno) {
1997         case EOPNOTSUPP:
1998           /*
1999            * If the filesystem reports it doesn't support XATTRs we clear
2000            * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
2001            * on all other files on the same filesystem. The
2002            * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
2003            * change from one filesystem to another.
2004            */
2005           xattr_data->flags &= ~BXATTR_FLAG_SAVE_NATIVE;
2006           retval = BxattrExitCode::kWarning;
2007           Mmsg(jcr->errmsg, error_message_disabling_xattributes.c_str(),
2008                xattr_data->last_fname);
2009           Dmsg2(100, error_message_disabling_xattributes.c_str(),
2010                 xattr_data->last_fname);
2011           goto bail_out;
2012         default:
2013           Mmsg2(jcr->errmsg, _("getproplist error on file \"%s\": ERR=%s\n"),
2014                 xattr_data->last_fname, be.bstrerror());
2015           Dmsg2(100, "getproplist error file=%s ERR=%s\n",
2016                 xattr_data->last_fname, be.bstrerror());
2017           goto bail_out;
2018       }
2019       break;
2020     }
2021     case 0:
2022       if (xattrbuf_min_size) {
2023         /*
2024          * The buffer isn't big enough to hold the xattr data, we now have
2025          * a minimum buffersize so we resize the buffer and try again.
2026          */
2027         xattrbuf = CheckPoolMemorySize(xattrbuf, xattrbuf_min_size + 1);
2028         xattrbuf_size = xattrbuf_min_size + 1;
2029         xattr_list_len
2030             = getproplist(xattr_data->last_fname, 1, &prop_args, xattrbuf_size,
2031                           xattrbuf, &xattrbuf_min_size);
2032         switch (xattr_list_len) {
2033           case -1: {
2034             BErrNo be;
2035 
2036             switch (errno) {
2037               default:
2038                 Mmsg2(jcr->errmsg,
2039                       _("getproplist error on file \"%s\": ERR=%s\n"),
2040                       xattr_data->last_fname, be.bstrerror());
2041                 Dmsg2(100, "getproplist error file=%s ERR=%s\n",
2042                       xattr_data->last_fname, be.bstrerror());
2043                 goto bail_out;
2044             }
2045             break;
2046           }
2047           case 0:
2048             /*
2049              * This should never happen as we sized the buffer according to the
2050              * minimumsize returned by a previous getproplist call. If it does
2051              * happen things are fishy and we are better of forgetting this
2052              * xattr as it seems its list is changing at this exact moment so we
2053              * can never make a good backup copy of it.
2054              */
2055             retval = BxattrExitCode::kSuccess;
2056             goto bail_out;
2057           default:
2058             break;
2059         }
2060       } else {
2061         /*
2062          * No xattr on file.
2063          */
2064         retval = BxattrExitCode::kSuccess;
2065         goto bail_out;
2066       }
2067       break;
2068     default:
2069       break;
2070   }
2071 
2072   /*
2073    * Walk the list of extended attributes names and retrieve the data.
2074    * We already count the bytes needed for serializing the stream later on.
2075    */
2076   bp = xattrbuf;
2077   while (xattrbuf_size > 0) {
2078     /*
2079      * Call getproplist_entry to initialize name and value
2080      * pointers to entries position within buffer.
2081      */
2082     xattrbuf_size -= get_proplist_entry(&xattr_name, &flags, &xattr_value_len,
2083                                         &xattr_value, &bp);
2084 
2085     /*
2086      * On some OSes you also get the acls in the extented attribute list.
2087      * So we check if we are already backing up acls and if we do we
2088      * don't store the extended attribute with the same info.
2089      */
2090     if (BitIsSet(FO_ACL, ff_pkt->flags)) {
2091       for (cnt = 0; xattr_acl_skiplist[cnt] != NULL; cnt++) {
2092         if (bstrcmp(xattr_name, xattr_acl_skiplist[cnt])) {
2093           skip_xattr = true;
2094           break;
2095         }
2096       }
2097     }
2098 
2099     /*
2100      * On some OSes we want to skip certain xattrs which are in the
2101      * xattr_skiplist array.
2102      */
2103     if (!skip_xattr) {
2104       for (cnt = 0; xattr_skiplist[cnt] != NULL; cnt++) {
2105         if (bstrcmp(xattr_name, xattr_skiplist[cnt])) {
2106           skip_xattr = true;
2107           break;
2108         }
2109       }
2110     }
2111 
2112     if (skip_xattr) {
2113       Dmsg1(100, "Skipping xattr named %s\n", xattr_name);
2114       continue;
2115     }
2116 
2117     /*
2118      * Each xattr valuepair starts with a magic so we can parse it easier.
2119      */
2120     current_xattr = (xattr_t*)malloc(sizeof(xattr_t));
2121     current_xattr->magic = XATTR_MAGIC;
2122     expected_serialize_len += sizeof(current_xattr->magic);
2123 
2124     current_xattr->name_length = strlen(xattr_name);
2125     current_xattr->name = strdup(xattr_name);
2126 
2127     expected_serialize_len
2128         += sizeof(current_xattr->name_length) + current_xattr->name_length;
2129 
2130     current_xattr->value_length = *xattr_value_len;
2131     current_xattr->value = (char*)malloc(current_xattr->value_length);
2132     memcpy(current_xattr->value, xattr_value, current_xattr->value_length);
2133 
2134     expected_serialize_len
2135         += sizeof(current_xattr->value_length) + current_xattr->value_length;
2136 
2137     if (xattr_value_list == NULL) {
2138       xattr_value_list = new alist(10, not_owned_by_alist);
2139     }
2140 
2141     xattr_value_list->append(current_xattr);
2142     xattr_count++;
2143 
2144     /*
2145      * Protect ourself against things getting out of hand.
2146      */
2147     if (expected_serialize_len >= MAX_XATTR_STREAM) {
2148       Mmsg2(jcr->errmsg,
2149             _("Xattr stream on file \"%s\" exceeds maximum size of %d bytes\n"),
2150             xattr_data->last_fname, MAX_XATTR_STREAM);
2151       goto bail_out;
2152     }
2153   }
2154 
2155   /*
2156    * If we found any xattr send them to the SD.
2157    */
2158   if (xattr_count > 0) {
2159     /*
2160      * Serialize the datastream.
2161      */
2162     if (SerializeXattrStream(jcr, xattr_data, expected_serialize_len,
2163                              xattr_value_list)
2164         < expected_serialize_len) {
2165       Mmsg1(jcr->errmsg,
2166             _("Failed to Serialize extended attributes on file \"%s\"\n"),
2167             xattr_data->last_fname);
2168       Dmsg1(100, "Failed to Serialize extended attributes on file \"%s\"\n",
2169             xattr_data->last_fname);
2170       goto bail_out;
2171     }
2172 
2173     /*
2174      * Send the datastream to the SD.
2175      */
2176     retval = SendXattrStream(jcr, xattr_data, os_default_xattr_streams[0]);
2177   } else {
2178     retval = BxattrExitCode::kSuccess;
2179   }
2180 
2181 bail_out:
2182   if (xattr_value_list != NULL) { XattrDropInternalTable(xattr_value_list); }
2183   FreePoolMemory(xattrbuf);
2184 
2185   return retval;
2186 }
2187 
tru64_parse_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,int stream,char * content,uint32_t content_length)2188 static BxattrExitCode tru64_parse_xattr_streams(JobControlRecord* jcr,
2189                                                 XattrData* xattr_data,
2190                                                 int stream,
2191                                                 char* content,
2192                                                 uint32_t content_length)
2193 {
2194   char *bp, *xattrbuf = NULL;
2195   int32_t xattrbuf_size, cnt;
2196   xattr_t* current_xattr;
2197   alist* xattr_value_list;
2198   BxattrExitCode retval = BxattrExitCode::kError;
2199 
2200   xattr_value_list = new alist(10, not_owned_by_alist);
2201 
2202   if (UnSerializeXattrStream(jcr, xattr_data, content, content_length,
2203                              xattr_value_list)
2204       != BxattrExitCode::kSuccess) {
2205     goto bail_out;
2206   }
2207 
2208   /*
2209    * See how big the propertylist must be.
2210    */
2211   xattrbuf_size = 0;
2212   foreach_alist (current_xattr, xattr_value_list) {
2213     xattrbuf_size += sizeof_proplist_entry(current_xattr->name,
2214                                            current_xattr->value_length);
2215   }
2216 
2217   xattrbuf = (char*)malloc(xattrbuf_size);
2218 
2219   /*
2220    * Add all value pairs to the proplist.
2221    */
2222   cnt = 0;
2223   bp = xattrbuf;
2224   foreach_alist (current_xattr, xattr_value_list) {
2225     cnt = add_proplist_entry(current_xattr->name, 0,
2226                              current_xattr->value_length, current_xattr->value,
2227                              &bp);
2228   }
2229 
2230   /*
2231    * Sanity check.
2232    */
2233   if (cnt != xattrbuf_size) {
2234     Mmsg1(jcr->errmsg,
2235           _("Unable create proper proplist to restore xattrs on file \"%s\"\n"),
2236           xattr_data->last_fname);
2237     Dmsg1(100,
2238           "Unable create proper proplist to restore xattrs on file \"%s\"\n",
2239           xattr_data->last_fname);
2240     goto bail_out;
2241   }
2242 
2243   /*
2244    * Restore the list of extended attributes on the file.
2245    */
2246   cnt = setproplist(xattr_data->last_fname, 1, xattrbuf_size, xattrbuf);
2247   switch (cnt) {
2248     case -1: {
2249       BErrNo be;
2250 
2251       switch (errno) {
2252         case EOPNOTSUPP:
2253           /*
2254            * If the filesystem reports it doesn't support XATTRs we clear
2255            * the BXATTR_FLAG_RESTORE_NATIVE flag so we skip XATTR restores
2256            * on all other files on the same filesystem. The
2257            * BXATTR_FLAG_RESTORE_NATIVE flags gets sets again when we
2258            * change from one filesystem to another.
2259            */
2260           xattr_data->flags &= ~BXATTR_FLAG_RESTORE_NATIVE;
2261           retval = BxattrExitCode::kWarning;
2262           Mmsg(jcr->errmsg, error_message_disabling_xattributes.c_str(),
2263                xattr_data->last_fname);
2264           Dmsg2(100, error_message_disabling_xattributes.c_str(),
2265                 xattr_data->last_fname);
2266           goto bail_out;
2267         default:
2268           Mmsg2(jcr->errmsg, _("setproplist error on file \"%s\": ERR=%s\n"),
2269                 xattr_data->last_fname, be.bstrerror());
2270           Dmsg2(100, "setproplist error file=%s ERR=%s\n",
2271                 xattr_data->last_fname, be.bstrerror());
2272           goto bail_out;
2273       }
2274       break;
2275     }
2276     default:
2277       break;
2278   }
2279 
2280   retval = BxattrExitCode::kSuccess;
2281 
2282 bail_out:
2283   if (xattrbuf) { free(xattrbuf); }
2284   XattrDropInternalTable(xattr_value_list);
2285 
2286   return retval;
2287 }
2288 
2289 /**
2290  * Function pointers to the build and parse function to use for these xattrs.
2291  */
2292 static BxattrExitCode (*os_build_xattr_streams)(JobControlRecord* jcr,
2293                                                 XattrData* xattr_data,
2294                                                 FindFilesPacket* ff_pkt)
2295     = tru64_build_xattr_streams;
2296 static BxattrExitCode (*os_parse_xattr_streams)(JobControlRecord* jcr,
2297                                                 XattrData* xattr_data,
2298                                                 int stream,
2299                                                 char* content,
2300                                                 uint32_t content_length)
2301     = tru64_parse_xattr_streams;
2302 
2303 #  elif defined(HAVE_SUN_OS)
2304 /**
2305  * Solaris extended attributes were introduced in Solaris 9
2306  * by PSARC 1999/209
2307  *
2308  * Solaris extensible attributes were introduced in OpenSolaris
2309  * by PSARC 2007/315 Solaris extensible attributes are also
2310  * sometimes called extended system attributes.
2311  *
2312  * man fsattr(5) on Solaris gives a wealth of info. The most
2313  * important bits are:
2314  *
2315  * Attributes are logically supported as files within the  file
2316  * system.   The  file  system  is  therefore augmented with an
2317  * orthogonal name space of file attributes. Any file  (includ-
2318  * ing  attribute files) can have an arbitrarily deep attribute
2319  * tree associated with it. Attribute values  are  accessed  by
2320  * file descriptors obtained through a special attribute inter-
2321  * face.  This logical view of "attributes as files" allows the
2322  * leveraging  of  existing file system interface functionality
2323  * to support the construction, deletion, and  manipulation  of
2324  * attributes.
2325  *
2326  * The special files  "."  and  ".."  retain  their  accustomed
2327  * semantics within the attribute hierarchy.  The "." attribute
2328  * file refers to the current directory and the ".."  attribute
2329  * file  refers to the parent directory.  The unnamed directory
2330  * at the head of each attribute tree is considered the "child"
2331  * of  the  file it is associated with and the ".." file refers
2332  * to the associated file.  For  any  non-directory  file  with
2333  * attributes,  the  ".." entry in the unnamed directory refers
2334  * to a file that is not a directory.
2335  *
2336  * Conceptually, the attribute model is fully general. Extended
2337  * attributes  can  be  any  type of file (doors, links, direc-
2338  * tories, and so forth) and can even have their own attributes
2339  * (fully  recursive).   As a result, the attributes associated
2340  * with a file could be an arbitrarily deep directory hierarchy
2341  * where each attribute could have an equally complex attribute
2342  * tree associated with it.  Not all implementations  are  able
2343  * to,  or  want to, support the full model. Implementation are
2344  * therefore permitted to reject operations that are  not  sup-
2345  * ported.   For  example,  the implementation for the UFS file
2346  * system allows only regular files as attributes (for example,
2347  * no sub-directories) and rejects attempts to place attributes
2348  * on attributes.
2349  *
2350  * The following list details the operations that are  rejected
2351  * in the current implementation:
2352  *
2353  * link                     Any attempt to create links between
2354  *                          attribute  and  non-attribute space
2355  *                          is rejected  to  prevent  security-
2356  *                          related   or   otherwise  sensitive
2357  *                          attributes from being exposed,  and
2358  *                          therefore  manipulable,  as regular
2359  *                          files.
2360  *
2361  * rename                   Any  attempt  to   rename   between
2362  *                          attribute  and  non-attribute space
2363  *                          is rejected to prevent  an  already
2364  *                          linked  file from being renamed and
2365  *                          thereby circumventing the link res-
2366  *                          triction above.
2367  *
2368  * mkdir, symlink, mknod    Any  attempt  to  create  a   "non-
2369  *                          regular" file in attribute space is
2370  *                          rejected to reduce the  functional-
2371  *                          ity,  and  therefore  exposure  and
2372  *                          risk, of  the  initial  implementa-
2373  *                          tion.
2374  *
2375  * The entire available name space has been allocated to  "gen-
2376  * eral use" to bring the implementation in line with the NFSv4
2377  * draft standard [NFSv4]. That standard defines "named  attri-
2378  * butes"  (equivalent  to Solaris Extended Attributes) with no
2379  * naming restrictions.  All Sun  applications  making  use  of
2380  * opaque extended attributes will use the prefix "SUNW".
2381  *
2382  */
2383 #    ifdef HAVE_SYS_ATTR_H
2384 #      include <sys/attr.h>
2385 #    endif
2386 
2387 #    ifdef HAVE_ATTR_H
2388 #      include <attr.h>
2389 #    endif
2390 
2391 #    ifdef HAVE_SYS_NVPAIR_H
2392 #      include <sys/nvpair.h>
2393 #    endif
2394 
2395 #    ifdef HAVE_SYS_ACL_H
2396 #      include <sys/acl.h>
2397 #    endif
2398 
2399 #    if !defined(HAVE_OPENAT) || !defined(HAVE_UNLINKAT) \
2400         || !defined(HAVE_FCHOWNAT) || !defined(HAVE_FUTIMESAT)
2401 #      error \
2402           "Unable to compile code because of missing openat, unlinkat, fchownat or futimesat function"
2403 #    endif
2404 
2405 /**
2406  * Define the supported XATTR streams for this OS
2407  */
2408 #    if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
2409 static int os_default_xattr_streams[2]
2410     = {STREAM_XATTR_SOLARIS, STREAM_XATTR_SOLARIS_SYS};
2411 #    else
2412 static int os_default_xattr_streams[1] = {STREAM_XATTR_SOLARIS};
2413 #    endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
2414 
2415 /**
2416  * This code creates a temporary cache with entries for each xattr which has
2417  * a link count > 1 (which indicates it has one or more hard linked
2418  * counterpart(s))
2419  */
find_xattr_link_cache_entry(XattrData * xattr_data,ino_t inum)2420 static inline xattr_link_cache_entry_t* find_xattr_link_cache_entry(
2421     XattrData* xattr_data,
2422     ino_t inum)
2423 {
2424   xattr_link_cache_entry_t* ptr;
2425 
2426   foreach_alist (ptr, xattr_data->u.build->link_cache) {
2427     if (ptr && ptr->inum == inum) { return ptr; }
2428   }
2429   return NULL;
2430 }
2431 
add_xattr_link_cache_entry(XattrData * xattr_data,ino_t inum,char * target)2432 static inline void add_xattr_link_cache_entry(XattrData* xattr_data,
2433                                               ino_t inum,
2434                                               char* target)
2435 {
2436   xattr_link_cache_entry_t* ptr;
2437 
2438   ptr = (xattr_link_cache_entry_t*)malloc(sizeof(xattr_link_cache_entry_t));
2439   memset(ptr, 0, sizeof(xattr_link_cache_entry_t));
2440   ptr->inum = inum;
2441   ptr->target = strdup(target);
2442 
2443   if (!xattr_data->u.build->link_cache) {
2444     xattr_data->u.build->link_cache = new alist(10, not_owned_by_alist);
2445   }
2446   xattr_data->u.build->link_cache->append(ptr);
2447 }
2448 
DropXattrLinkCache(XattrData * xattr_data)2449 static inline void DropXattrLinkCache(XattrData* xattr_data)
2450 {
2451   xattr_link_cache_entry_t* ptr;
2452 
2453   /*
2454    * Walk the list of xattr link cache entries and free allocated memory on
2455    * traversing.
2456    */
2457   foreach_alist (ptr, xattr_data->u.build->link_cache) {
2458     free(ptr->target);
2459     free(ptr);
2460   }
2461 
2462   delete xattr_data->u.build->link_cache;
2463   xattr_data->u.build->link_cache = NULL;
2464 }
2465 
2466 #    if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
2467 /**
2468  * This function returns true if a non default extended system attribute
2469  * list is associated with fd and returns false when an error has occured
2470  * or when only extended system attributes other than archive,
2471  * av_modified or crtime are set.
2472  *
2473  * The function returns true for the following cases:
2474  *
2475  * - any extended system attribute other than the default attributes
2476  *   ('archive', 'av_modified' and 'crtime') is set
2477  * - nvlist has NULL name string
2478  * - nvpair has data type of 'nvlist'
2479  * - default data type.
2480  */
SolarisHasNonTransientExtensibleAttributes(int fd)2481 static bool SolarisHasNonTransientExtensibleAttributes(int fd)
2482 {
2483   boolean_t value;
2484   data_type_t type;
2485   nvlist_t* response;
2486   nvpair_t* pair;
2487   f_attr_t fattr;
2488   char* name;
2489   bool retval = false;
2490 
2491   if (fgetattr(fd, XATTR_VIEW_READWRITE, &response) != 0) { return false; }
2492 
2493   pair = NULL;
2494   while ((pair = nvlist_next_nvpair(response, pair)) != NULL) {
2495     name = nvpair_name(pair);
2496 
2497     if (name != NULL) {
2498       fattr = name_to_attr(name);
2499     } else {
2500       retval = true;
2501       goto bail_out;
2502     }
2503 
2504     type = nvpair_type(pair);
2505     switch (type) {
2506       case DATA_TYPE_BOOLEAN_VALUE:
2507         if (nvpair_value_boolean_value(pair, &value) != 0) { continue; }
2508         if (value && fattr != F_ARCHIVE && fattr != F_AV_MODIFIED) {
2509           retval = true;
2510           goto bail_out;
2511         }
2512         break;
2513       case DATA_TYPE_UINT64_ARRAY:
2514         if (fattr != F_CRTIME) {
2515           retval = true;
2516           goto bail_out;
2517         }
2518         break;
2519       case DATA_TYPE_NVLIST:
2520       default:
2521         retval = true;
2522         goto bail_out;
2523     }
2524   }
2525 
2526 bail_out:
2527   if (response != NULL) { nvlist_free(response); }
2528   return retval;
2529 }
2530 #    endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
2531 
2532 #    if defined(HAVE_ACL) && !defined(HAVE_EXTENDED_ACL)
2533 /**
2534  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
2535  * There is no need to store those acls as we already store the stat bits too.
2536  */
AclIsTrivial(int count,aclent_t * entries)2537 static bool AclIsTrivial(int count, aclent_t* entries)
2538 {
2539   int n;
2540   aclent_t* ace;
2541 
2542   for (n = 0; n < count; n++) {
2543     ace = &entries[n];
2544     if (!(ace->a_type == USER_OBJ || ace->a_type == GROUP_OBJ
2545           || ace->a_type == OTHER_OBJ || ace->a_type == CLASS_OBJ))
2546       return false;
2547   }
2548   return true;
2549 }
2550 #    endif /* HAVE_ACL && !HAVE_EXTENDED_ACL */
2551 
solaris_save_xattr_acl(JobControlRecord * jcr,XattrData * xattr_data,int fd,const char * attrname,char ** acl_text)2552 static BxattrExitCode solaris_save_xattr_acl(JobControlRecord* jcr,
2553                                              XattrData* xattr_data,
2554                                              int fd,
2555                                              const char* attrname,
2556                                              char** acl_text)
2557 {
2558   BxattrExitCode retval = BxattrExitCode::kError;
2559 #    ifdef HAVE_ACL
2560 #      ifdef HAVE_EXTENDED_ACL
2561   int flags;
2562   acl_t* aclp = NULL;
2563 
2564   /*
2565    * See if this attribute has an ACL
2566    */
2567   if ((fd != -1 && fpathconf(fd, _PC_ACL_ENABLED) > 0)
2568       || pathconf(attrname, _PC_ACL_ENABLED) > 0) {
2569     /*
2570      * See if there is a non trivial acl on the file.
2571      */
2572     if ((fd != -1 && facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0)
2573         || acl_get(attrname, ACL_NO_TRIVIAL, &aclp) != 0) {
2574       BErrNo be;
2575 
2576       switch (errno) {
2577         case ENOENT:
2578           retval = BxattrExitCode::kSuccess;
2579           goto bail_out;
2580         default:
2581           Mmsg3(jcr->errmsg,
2582                 _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
2583                 attrname, xattr_data->last_fname, be.bstrerror());
2584           Dmsg3(100, "facl_get/acl_get of xattr %s on \"%s\" failed: ERR=%s\n",
2585                 attrname, xattr_data->last_fname, be.bstrerror());
2586           goto bail_out;
2587       }
2588     }
2589 
2590     if (aclp != NULL) {
2591 #        if defined(ACL_SID_FMT)
2592       /*
2593        * New format flag added in newer Solaris versions.
2594        */
2595       flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
2596 #        else
2597       flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
2598 #        endif /* ACL_SID_FMT */
2599 
2600       *acl_text = acl_totext(aclp, flags);
2601       acl_free(aclp);
2602     } else {
2603       *acl_text = NULL;
2604     }
2605   } else {
2606     *acl_text = NULL;
2607   }
2608   retval = BxattrExitCode::kSuccess;
2609 #      else    /* HAVE_EXTENDED_ACL */
2610   int n;
2611   aclent_t* acls = NULL;
2612 
2613   /*
2614    * See if this attribute has an ACL
2615    */
2616   if (fd != -1) {
2617     n = facl(fd, GETACLCNT, 0, NULL);
2618   } else {
2619     n = acl(attrname, GETACLCNT, 0, NULL);
2620   }
2621 
2622   if (n >= MIN_ACL_ENTRIES) {
2623     acls = (aclent_t*)malloc(n * sizeof(aclent_t));
2624     if ((fd != -1 && facl(fd, GETACL, n, acls) != n)
2625         || acl(attrname, GETACL, n, acls) != n) {
2626       BErrNo be;
2627 
2628       switch (errno) {
2629         case ENOENT:
2630           free(acls);
2631           retval = BxattrExitCode::kSuccess;
2632           goto bail_out;
2633         default:
2634           Mmsg3(jcr->errmsg,
2635                 _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"),
2636                 attrname, xattr_data->last_fname, be.bstrerror());
2637           Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n",
2638                 attrname, xattr_data->last_fname, be.bstrerror());
2639           free(acls);
2640           goto bail_out;
2641       }
2642     }
2643 
2644     /*
2645      * See if there is a non trivial acl on the file.
2646      */
2647     if (!AclIsTrivial(n, acls)) {
2648       if ((*acl_text = acltotext(acls, n)) == NULL) {
2649         BErrNo be;
2650 
2651         Mmsg3(jcr->errmsg,
2652               _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"),
2653               attrname, xattr_data->last_fname, be.bstrerror());
2654         Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n", attrname,
2655               xattr_data->last_fname, be.bstrerror());
2656         free(acls);
2657         goto bail_out;
2658       }
2659     } else {
2660       *acl_text = NULL;
2661     }
2662 
2663     free(acls);
2664   } else {
2665     *acl_text = NULL;
2666   }
2667   retval = BxattrExitCode::kSuccess;
2668 #      endif   /* HAVE_EXTENDED_ACL */
2669 
2670 #    else  /* HAVE_ACL */
2671   retval = BxattrExitCode::kSuccess;
2672 #    endif /* HAVE_ACL */
2673 
2674 bail_out:
2675   return retval;
2676 }
2677 
2678 /**
2679  * Forward declaration for recursive function call.
2680  */
2681 static BxattrExitCode solaris_save_xattrs(JobControlRecord* jcr,
2682                                           XattrData* xattr_data,
2683                                           const char* xattr_namespace,
2684                                           const char* attr_parent);
2685 
2686 /**
2687  * Save an extended or extensible attribute.
2688  * This is stored as an opaque stream of bytes with the following encoding:
2689  *
2690  * <xattr_name>\0<stat_buffer>\0<acl_string>\0<actual_xattr_data>
2691  *
2692  * or for a hardlinked or symlinked attribute
2693  *
2694  * <xattr_name>\0<stat_buffer>\0<xattr_link_source>\0
2695  *
2696  * xattr_name can be a subpath relative to the file the xattr is on.
2697  * stat_buffer is the string representation of the stat struct.
2698  * acl_string is an acl text when a non trivial acl is set on the xattr.
2699  * actual_xattr_data is the content of the xattr file.
2700  */
solaris_save_xattr(JobControlRecord * jcr,XattrData * xattr_data,int fd,const char * xattr_namespace,const char * attrname,bool toplevel_hidden_dir,int stream)2701 static BxattrExitCode solaris_save_xattr(JobControlRecord* jcr,
2702                                          XattrData* xattr_data,
2703                                          int fd,
2704                                          const char* xattr_namespace,
2705                                          const char* attrname,
2706                                          bool toplevel_hidden_dir,
2707                                          int stream)
2708 {
2709   int cnt;
2710   int attrfd = -1;
2711   struct stat st;
2712   xattr_link_cache_entry_t* xlce;
2713   char target_attrname[PATH_MAX];
2714   char link_source[PATH_MAX];
2715   char* acl_text = NULL;
2716   char attribs[XATTR_BUFSIZ];
2717   char buffer[XATTR_BUFSIZ];
2718   BxattrExitCode retval = BxattrExitCode::kError;
2719 
2720   Bsnprintf(target_attrname, sizeof(target_attrname), "%s%s", xattr_namespace,
2721             attrname);
2722 
2723   /*
2724    * Get the stats of the extended or extensible attribute.
2725    */
2726   if (fstatat(fd, attrname, &st, AT_SYMLINK_NOFOLLOW) < 0) {
2727     BErrNo be;
2728 
2729     switch (errno) {
2730       case ENOENT:
2731         retval = BxattrExitCode::kSuccess;
2732         goto bail_out;
2733       default:
2734         Mmsg3(jcr->errmsg,
2735               _("Unable to get status on xattr %s on file \"%s\": ERR=%s\n"),
2736               target_attrname, xattr_data->last_fname, be.bstrerror());
2737         Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n",
2738               target_attrname, xattr_data->last_fname, be.bstrerror());
2739         goto bail_out;
2740     }
2741   }
2742 
2743   /*
2744    * Based on the filetype perform the correct action. We support most filetypes
2745    * here, more then the actual implementation on Solaris supports so some code
2746    * may never get executed due to limitations in the implementation.
2747    */
2748   switch (st.st_mode & S_IFMT) {
2749     case S_IFIFO:
2750     case S_IFCHR:
2751     case S_IFBLK:
2752       /*
2753        * Get any acl on the xattr.
2754        */
2755       if (solaris_save_xattr_acl(jcr, xattr_data, attrfd, attrname, &acl_text)
2756           != BxattrExitCode::kSuccess)
2757         goto bail_out;
2758 
2759       /*
2760        * The current implementation of xattr on Solaris doesn't support this,
2761        * but if it ever does we are prepared.
2762        * Encode the stat struct into an ASCII representation.
2763        */
2764       EncodeStat(attribs, &st, sizeof(st), 0, stream);
2765       cnt = Bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c", target_attrname,
2766                       0, attribs, 0, (acl_text) ? acl_text : "", 0);
2767       break;
2768     case S_IFDIR:
2769       /*
2770        * Get any acl on the xattr.
2771        */
2772       if (solaris_save_xattr_acl(jcr, xattr_data, attrfd, attrname, &acl_text)
2773           != BxattrExitCode::kSuccess)
2774         goto bail_out;
2775 
2776       /*
2777        * See if this is the toplevel_hidden_dir being saved.
2778        */
2779       if (toplevel_hidden_dir) {
2780         /*
2781          * Save the data for later storage when we encounter a real xattr.
2782          * We store the data in the xattr_data->u.build->content buffer
2783          * and flush that just before sending out the first real xattr.
2784          * Encode the stat struct into an ASCII representation and jump
2785          * out of the function.
2786          */
2787         EncodeStat(attribs, &st, sizeof(st), 0, stream);
2788         cnt = Bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c", target_attrname,
2789                         0, attribs, 0, (acl_text) ? acl_text : "", 0);
2790         PmMemcpy(xattr_data->u.build->content, buffer, cnt);
2791         xattr_data->u.build->content_length = cnt;
2792         goto bail_out;
2793       } else {
2794         /*
2795          * The current implementation of xattr on Solaris doesn't support this,
2796          * but if it ever does we are prepared.
2797          * Encode the stat struct into an ASCII representation.
2798          */
2799         EncodeStat(attribs, &st, sizeof(st), 0, stream);
2800         cnt = Bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c", target_attrname,
2801                         0, attribs, 0, (acl_text) ? acl_text : "", 0);
2802       }
2803       break;
2804     case S_IFREG:
2805       /*
2806        * If this is a hardlinked file check the inode cache for a hit.
2807        */
2808       if (st.st_nlink > 1) {
2809         /*
2810          * See if the cache already knows this inode number.
2811          */
2812         if ((xlce = find_xattr_link_cache_entry(xattr_data, st.st_ino))
2813             != NULL) {
2814           /*
2815            * Generate a xattr encoding with the reference to the target in
2816            * there.
2817            */
2818           EncodeStat(attribs, &st, sizeof(st), st.st_ino, stream);
2819           cnt = Bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c",
2820                           target_attrname, 0, attribs, 0, xlce->target, 0);
2821           PmMemcpy(xattr_data->u.build->content, buffer, cnt);
2822           xattr_data->u.build->content_length = cnt;
2823           retval = SendXattrStream(jcr, xattr_data, stream);
2824 
2825           /*
2826            * For a hard linked file we are ready now, no need to recursively
2827            * save the attributes.
2828            */
2829           goto bail_out;
2830         }
2831 
2832         /*
2833          * Store this hard linked file in the cache.
2834          * Store the name relative to the top level xattr space.
2835          */
2836         add_xattr_link_cache_entry(xattr_data, st.st_ino, target_attrname + 1);
2837       }
2838 
2839       /*
2840        * Get any acl on the xattr.
2841        */
2842       if (solaris_save_xattr_acl(jcr, xattr_data, attrfd, attrname, &acl_text)
2843           != BxattrExitCode::kSuccess) {
2844         goto bail_out;
2845       }
2846 
2847       /*
2848        * Encode the stat struct into an ASCII representation.
2849        */
2850       EncodeStat(attribs, &st, sizeof(st), 0, stream);
2851       cnt = Bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c", target_attrname,
2852                       0, attribs, 0, (acl_text) ? acl_text : "", 0);
2853 
2854       /*
2855        * Open the extended or extensible attribute file.
2856        */
2857       if ((attrfd = openat(fd, attrname, O_RDONLY)) < 0) {
2858         BErrNo be;
2859 
2860         switch (errno) {
2861           case ENOENT:
2862             retval = BxattrExitCode::kSuccess;
2863             goto bail_out;
2864           default:
2865             Mmsg3(jcr->errmsg, _("Unable to open xattr %s on \"%s\": ERR=%s\n"),
2866                   target_attrname, xattr_data->last_fname, be.bstrerror());
2867             Dmsg3(100, "openat of xattr %s on \"%s\" failed: ERR=%s\n",
2868                   target_attrname, xattr_data->last_fname, be.bstrerror());
2869             goto bail_out;
2870         }
2871       }
2872       break;
2873     case S_IFLNK:
2874       /*
2875        * The current implementation of xattr on Solaris doesn't support this,
2876        * but if it ever does we are prepared. Encode the stat struct into an
2877        * ASCII representation.
2878        */
2879       if (readlink(attrname, link_source, sizeof(link_source)) < 0) {
2880         BErrNo be;
2881 
2882         switch (errno) {
2883           case ENOENT:
2884             retval = BxattrExitCode::kSuccess;
2885             goto bail_out;
2886           default:
2887             Mmsg3(jcr->errmsg,
2888                   _("Unable to read symlin %s on \"%s\": ERR=%s\n"),
2889                   target_attrname, xattr_data->last_fname, be.bstrerror());
2890             Dmsg3(100, "readlink of xattr %s on \"%s\" failed: ERR=%s\n",
2891                   target_attrname, xattr_data->last_fname, be.bstrerror());
2892             goto bail_out;
2893         }
2894       }
2895 
2896       /*
2897        * Generate a xattr encoding with the reference to the target in there.
2898        */
2899       EncodeStat(attribs, &st, sizeof(st), st.st_ino, stream);
2900       cnt = Bsnprintf(buffer, sizeof(buffer), "%s%c%s%c%s%c", target_attrname,
2901                       0, attribs, 0, link_source, 0);
2902       PmMemcpy(xattr_data->u.build->content, buffer, cnt);
2903       xattr_data->u.build->content_length = cnt;
2904       retval = SendXattrStream(jcr, xattr_data, stream);
2905 
2906       if (retval == BxattrExitCode::kSuccess) {
2907         xattr_data->u.build->nr_saved++;
2908       }
2909 
2910       /*
2911        * For a soft linked file we are ready now, no need to recursively save
2912        * the attributes.
2913        */
2914       goto bail_out;
2915     default:
2916       goto bail_out;
2917   }
2918 
2919   /*
2920    * See if this is the first real xattr being saved.
2921    * If it is save the toplevel_hidden_dir attributes first.
2922    * This is easy as its stored already in the
2923    * xattr_data->u.build->content buffer.
2924    */
2925   if (xattr_data->u.build->nr_saved == 0) {
2926     retval = SendXattrStream(jcr, xattr_data, STREAM_XATTR_SOLARIS);
2927     if (retval != BxattrExitCode::kSuccess) { goto bail_out; }
2928     xattr_data->u.build->nr_saved++;
2929   }
2930 
2931   PmMemcpy(xattr_data->u.build->content, buffer, cnt);
2932   xattr_data->u.build->content_length = cnt;
2933 
2934   /*
2935    * Only dump the content of regular files.
2936    */
2937   switch (st.st_mode & S_IFMT) {
2938     case S_IFREG:
2939       if (st.st_size > 0) {
2940         /*
2941          * Protect ourself against things getting out of hand.
2942          */
2943         if (st.st_size >= MAX_XATTR_STREAM) {
2944           Mmsg2(jcr->errmsg,
2945                 _("Xattr stream on file \"%s\" exceeds maximum size of %d "
2946                   "bytes\n"),
2947                 xattr_data->last_fname, MAX_XATTR_STREAM);
2948           goto bail_out;
2949         }
2950 
2951         while ((cnt = read(attrfd, buffer, sizeof(buffer))) > 0) {
2952           xattr_data->u.build->content
2953               = CheckPoolMemorySize(xattr_data->u.build->content,
2954                                     xattr_data->u.build->content_length + cnt);
2955           memcpy(xattr_data->u.build->content
2956                      + xattr_data->u.build->content_length,
2957                  buffer, cnt);
2958           xattr_data->u.build->content_length += cnt;
2959         }
2960 
2961         if (cnt < 0) {
2962           Mmsg2(jcr->errmsg,
2963                 _("Unable to read content of xattr %s on file \"%s\"\n"),
2964                 target_attrname, xattr_data->last_fname);
2965           Dmsg2(100, "read of data from xattr %s on \"%s\" failed\n",
2966                 target_attrname, xattr_data->last_fname);
2967           goto bail_out;
2968         }
2969       }
2970       break;
2971 
2972     default:
2973       break;
2974   }
2975 
2976   /*
2977    * We build a new xattr stream send it to the SD.
2978    */
2979   retval = SendXattrStream(jcr, xattr_data, stream);
2980   if (retval != BxattrExitCode::kSuccess) { goto bail_out; }
2981   xattr_data->u.build->nr_saved++;
2982 
2983   /*
2984    * Recursivly call solaris_save_extended_attributes for archiving the
2985    * attributes available on this extended attribute.
2986    */
2987   retval = solaris_save_xattrs(jcr, xattr_data, xattr_namespace, attrname);
2988 
2989   /*
2990    * The recursive call could change our working dir so change back to the
2991    * wanted workdir.
2992    */
2993   if (fchdir(fd) < 0) {
2994     BErrNo be;
2995 
2996     switch (errno) {
2997       case ENOENT:
2998         retval = BxattrExitCode::kSuccess;
2999         goto bail_out;
3000       default:
3001         Mmsg2(jcr->errmsg,
3002               _("Unable to chdir to xattr space of file \"%s\": ERR=%s\n"),
3003               xattr_data->last_fname, be.bstrerror());
3004         Dmsg3(100,
3005               "Unable to fchdir to xattr space of file \"%s\" using fd %d: "
3006               "ERR=%s\n",
3007               xattr_data->last_fname, fd, be.bstrerror());
3008         goto bail_out;
3009     }
3010   }
3011 
3012 bail_out:
3013   if (acl_text != NULL) { free(acl_text); }
3014   if (attrfd != -1) { close(attrfd); }
3015   return retval;
3016 }
3017 
solaris_save_xattrs(JobControlRecord * jcr,XattrData * xattr_data,const char * xattr_namespace,const char * attr_parent)3018 static BxattrExitCode solaris_save_xattrs(JobControlRecord* jcr,
3019                                           XattrData* xattr_data,
3020                                           const char* xattr_namespace,
3021                                           const char* attr_parent)
3022 {
3023   const char* name;
3024   int fd, filefd = -1, attrdirfd = -1;
3025   DIR* dirp;
3026   struct dirent* dp;
3027   char current_xattr_namespace[PATH_MAX];
3028   BxattrExitCode retval = BxattrExitCode::kError;
3029 
3030   /*
3031    * Determine what argument to use. Use attr_parent when set
3032    * (recursive call) or xattr_data->last_fname for first call. Also save
3033    * the current depth of the xattr_space we are in.
3034    */
3035   if (attr_parent) {
3036     name = attr_parent;
3037     if (xattr_namespace) {
3038       Bsnprintf(current_xattr_namespace, sizeof(current_xattr_namespace),
3039                 "%s%s/", xattr_namespace, attr_parent);
3040     } else {
3041       bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
3042     }
3043   } else {
3044     name = xattr_data->last_fname;
3045     bstrncpy(current_xattr_namespace, "/", sizeof(current_xattr_namespace));
3046   }
3047 
3048   /*
3049    * Open the file on which to save the xattrs read-only.
3050    */
3051   if ((filefd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
3052     BErrNo be;
3053 
3054     switch (errno) {
3055       case ENOENT:
3056         retval = BxattrExitCode::kSuccess;
3057         goto bail_out;
3058       default:
3059         Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
3060               xattr_data->last_fname, be.bstrerror());
3061         Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n",
3062               xattr_data->last_fname, be.bstrerror());
3063         goto bail_out;
3064     }
3065   }
3066 
3067   /*
3068    * Open the xattr naming space.
3069    */
3070   if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
3071     BErrNo be;
3072 
3073     switch (errno) {
3074       case EINVAL:
3075         /*
3076          * Gentile way of the system saying this type of xattr layering is not
3077          * supported. Which is not problem we just forget about this this xattr.
3078          * But as this is not an error we return a positive return value.
3079          */
3080         retval = BxattrExitCode::kSuccess;
3081         goto bail_out;
3082       case ENOENT:
3083         retval = BxattrExitCode::kSuccess;
3084         goto bail_out;
3085       default:
3086         Mmsg3(jcr->errmsg,
3087               _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"), name,
3088               xattr_data->last_fname, be.bstrerror());
3089         Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
3090               name, xattr_data->last_fname, be.bstrerror());
3091         goto bail_out;
3092     }
3093   }
3094 
3095   /*
3096    * We need to change into the attribute directory to determine if each of the
3097    * attributes should be saved.
3098    */
3099   if (fchdir(attrdirfd) < 0) {
3100     BErrNo be;
3101 
3102     Mmsg2(jcr->errmsg,
3103           _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
3104           xattr_data->last_fname, be.bstrerror());
3105     Dmsg3(
3106         100,
3107         "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
3108         xattr_data->last_fname, attrdirfd, be.bstrerror());
3109     goto bail_out;
3110   }
3111 
3112   /*
3113    * Save the data of the toplevel xattr hidden_dir. We save this one before
3114    * anything else because the readdir returns "." entry after the extensible
3115    * attr entry. And as we want this entry before anything else we better just
3116    * save its data.
3117    */
3118   if (!attr_parent)
3119     solaris_save_xattr(jcr, xattr_data, attrdirfd, current_xattr_namespace, ".",
3120                        true, STREAM_XATTR_SOLARIS);
3121 
3122   if ((fd = dup(attrdirfd)) == -1 || (dirp = fdopendir(fd)) == (DIR*)NULL) {
3123     BErrNo be;
3124 
3125     Mmsg2(jcr->errmsg,
3126           _("Unable to list the xattr space on file \"%s\": ERR=%s\n"),
3127           xattr_data->last_fname, be.bstrerror());
3128     Dmsg3(
3129         100,
3130         "Unable to fdopendir xattr space on file \"%s\" using fd %d: ERR=%s\n",
3131         xattr_data->last_fname, fd, be.bstrerror());
3132 
3133     goto bail_out;
3134   }
3135 
3136   /*
3137    * Walk the namespace.
3138    */
3139   while ((dp = readdir(dirp)) != NULL) {
3140     /*
3141      * Skip only the toplevel . dir.
3142      */
3143     if (!attr_parent && bstrcmp(dp->d_name, ".")) continue;
3144 
3145     /*
3146      * Skip all .. directories
3147      */
3148     if (bstrcmp(dp->d_name, "..")) continue;
3149 
3150     Dmsg3(400, "processing extended attribute %s%s on file \"%s\"\n",
3151           current_xattr_namespace, dp->d_name, xattr_data->last_fname);
3152 
3153 #    if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
3154     /*
3155      * We are not interested in read-only extensible attributes.
3156      */
3157     if (bstrcmp(dp->d_name, VIEW_READONLY)) {
3158       Dmsg3(400,
3159             "Skipping readonly extensible attributes %s%s on file \"%s\"\n",
3160             current_xattr_namespace, dp->d_name, xattr_data->last_fname);
3161 
3162       continue;
3163     }
3164 
3165     /*
3166      * We are only interested in read-write extensible attributes
3167      * when they contain non-transient values.
3168      */
3169     if (bstrcmp(dp->d_name, VIEW_READWRITE)) {
3170       /*
3171        * Determine if there are non-transient system attributes at the toplevel.
3172        * We need to provide a fd to the open file.
3173        */
3174       if (!SolarisHasNonTransientExtensibleAttributes(filefd)) {
3175         Dmsg3(400,
3176               "Skipping transient extensible attributes %s%s on file \"%s\"\n",
3177               current_xattr_namespace, dp->d_name, xattr_data->last_fname);
3178         continue;
3179       }
3180 
3181       /*
3182        * Save the xattr.
3183        */
3184       solaris_save_xattr(jcr, xattr_data, attrdirfd, current_xattr_namespace,
3185                          dp->d_name, false, STREAM_XATTR_SOLARIS_SYS);
3186       continue;
3187     }
3188 #    endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
3189 
3190     /*
3191      * Save the xattr.
3192      */
3193     solaris_save_xattr(jcr, xattr_data, attrdirfd, current_xattr_namespace,
3194                        dp->d_name, false, STREAM_XATTR_SOLARIS);
3195   }
3196 
3197   closedir(dirp);
3198   retval = BxattrExitCode::kSuccess;
3199 
3200 bail_out:
3201   if (attrdirfd != -1) close(attrdirfd);
3202   if (filefd != -1) close(filefd);
3203   return retval;
3204 }
3205 
3206 #    ifdef HAVE_ACL
solaris_restore_xattr_acl(JobControlRecord * jcr,XattrData * xattr_data,int fd,const char * attrname,char * acl_text)3207 static BxattrExitCode solaris_restore_xattr_acl(JobControlRecord* jcr,
3208                                                 XattrData* xattr_data,
3209                                                 int fd,
3210                                                 const char* attrname,
3211                                                 char* acl_text)
3212 {
3213 #      ifdef HAVE_EXTENDED_ACL
3214   int error;
3215   acl_t* aclp = NULL;
3216 
3217   if ((error = acl_fromtext(acl_text, &aclp)) != 0) {
3218     Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"),
3219           xattr_data->last_fname);
3220     return BxattrExitCode::kError;
3221   }
3222 
3223   if ((fd != -1 && facl_set(fd, aclp) != 0) || acl_set(attrname, aclp) != 0) {
3224     BErrNo be;
3225 
3226     Mmsg3(jcr->errmsg,
3227           _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
3228           attrname, xattr_data->last_fname, be.bstrerror());
3229     Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
3230           attrname, xattr_data->last_fname, be.bstrerror());
3231     return BxattrExitCode::kError;
3232   }
3233 
3234   if (aclp) { acl_free(aclp); }
3235   return BxattrExitCode::kSuccess;
3236 
3237 #      else /* HAVE_EXTENDED_ACL */
3238   int n;
3239   aclent_t* acls = NULL;
3240 
3241   acls = aclfromtext(acl_text, &n);
3242   if (!acls) {
3243     if ((fd != -1 && facl(fd, SETACL, n, acls) != 0)
3244         || acl(attrname, SETACL, n, acls) != 0) {
3245       BErrNo be;
3246 
3247       Mmsg3(jcr->errmsg,
3248             _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"),
3249             attrname, xattr_data->last_fname, be.bstrerror());
3250       Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n",
3251             attrname, xattr_data->last_fname, be.bstrerror());
3252       return BxattrExitCode::kError;
3253     }
3254   }
3255 
3256   if (acls) { free(acls); }
3257   return BxattrExitCode::kSuccess;
3258 
3259 #      endif /* HAVE_EXTENDED_ACL */
3260 }
3261 #    endif   /* HAVE_ACL */
3262 
solaris_restore_xattrs(JobControlRecord * jcr,XattrData * xattr_data,bool is_extensible,char * content,uint32_t content_length)3263 static BxattrExitCode solaris_restore_xattrs(JobControlRecord* jcr,
3264                                              XattrData* xattr_data,
3265                                              bool is_extensible,
3266                                              char* content,
3267                                              uint32_t content_length)
3268 
3269 {
3270   int fd, filefd = -1, attrdirfd = -1, attrfd = -1;
3271   int used_bytes, cnt;
3272   char *bp, *target_attrname, *attribs;
3273   char* linked_target = NULL;
3274   char* acl_text = NULL;
3275   char* data = NULL;
3276   int32_t inum;
3277   struct stat st;
3278   struct timeval times[2];
3279   BxattrExitCode retval = BxattrExitCode::kError;
3280 
3281   /*
3282    * Parse the xattr stream. First the part that is the same for all xattrs.
3283    */
3284   used_bytes = 0;
3285 
3286   /*
3287    * The name of the target xattr has a leading / we are not interested
3288    * in that so skip it when decoding the string. We always start a the /
3289    * of the xattr space anyway.
3290    */
3291   target_attrname = content + 1;
3292   if ((bp = strchr(target_attrname, '\0')) == (char*)NULL
3293       || (used_bytes = (bp - content)) >= (int32_t)(content_length - 1)) {
3294     goto parse_error;
3295   }
3296   attribs = ++bp;
3297 
3298   /*
3299    * Open the file on which to restore the xattrs read-only.
3300    */
3301   if ((filefd = open(xattr_data->last_fname, O_RDONLY | O_NONBLOCK)) < 0) {
3302     BErrNo be;
3303 
3304     Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"),
3305           xattr_data->last_fname, be.bstrerror());
3306     Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n", xattr_data->last_fname,
3307           be.bstrerror());
3308     goto bail_out;
3309   }
3310 
3311   /*
3312    * Open the xattr naming space and make it the current working dir.
3313    */
3314   if ((attrdirfd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
3315     BErrNo be;
3316 
3317     Mmsg2(jcr->errmsg, _("Unable to open xattr space on file \"%s\": ERR=%s\n"),
3318           xattr_data->last_fname, be.bstrerror());
3319     Dmsg2(100, "Unable to open xattr space on file \"%s\": ERR=%s\n",
3320           xattr_data->last_fname, be.bstrerror());
3321     goto bail_out;
3322   }
3323 
3324   if (fchdir(attrdirfd) < 0) {
3325     BErrNo be;
3326 
3327     Mmsg2(jcr->errmsg,
3328           _("Unable to chdir to xattr space on file \"%s\": ERR=%s\n"),
3329           xattr_data->last_fname, be.bstrerror());
3330     Dmsg3(
3331         100,
3332         "Unable to fchdir to xattr space on file \"%s\" using fd %d: ERR=%s\n",
3333         xattr_data->last_fname, attrdirfd, be.bstrerror());
3334     goto bail_out;
3335   }
3336 
3337   /*
3338    * Try to open the correct xattr subdir based on the target_attrname given.
3339    * e.g. check if its a subdir attrname. Each / in the string makes us go
3340    * one level deeper.
3341    */
3342   while ((bp = strchr(target_attrname, '/')) != (char*)NULL) {
3343     *bp = '\0';
3344 
3345     if ((fd = open(target_attrname, O_RDONLY | O_NONBLOCK)) < 0) {
3346       BErrNo be;
3347 
3348       Mmsg3(jcr->errmsg, _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
3349             target_attrname, xattr_data->last_fname, be.bstrerror());
3350       Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
3351             target_attrname, xattr_data->last_fname, be.bstrerror());
3352       goto bail_out;
3353     }
3354 
3355     close(filefd);
3356     filefd = fd;
3357 
3358     /*
3359      * Open the xattr naming space.
3360      */
3361     if ((fd = openat(filefd, ".", O_RDONLY | O_XATTR)) < 0) {
3362       BErrNo be;
3363 
3364       Mmsg3(jcr->errmsg,
3365             _("Unable to open xattr space %s on file \"%s\": ERR=%s\n"),
3366             target_attrname, xattr_data->last_fname, be.bstrerror());
3367       Dmsg3(100, "Unable to open xattr space %s on file \"%s\": ERR=%s\n",
3368             target_attrname, xattr_data->last_fname, be.bstrerror());
3369       goto bail_out;
3370     }
3371 
3372     close(attrdirfd);
3373     attrdirfd = fd;
3374 
3375     /*
3376      * Make the xattr space our current workingdir.
3377      */
3378     if (fchdir(attrdirfd) < 0) {
3379       BErrNo be;
3380 
3381       Mmsg3(jcr->errmsg,
3382             _("Unable to chdir to xattr space %s on file \"%s\": ERR=%s\n"),
3383             target_attrname, xattr_data->last_fname, be.bstrerror());
3384       Dmsg4(100,
3385             "Unable to fchdir to xattr space %s on file \"%s\" using fd %d: "
3386             "ERR=%s\n",
3387             target_attrname, xattr_data->last_fname, attrdirfd, be.bstrerror());
3388       goto bail_out;
3389     }
3390 
3391     target_attrname = ++bp;
3392   }
3393 
3394   /*
3395    * Decode the attributes from the stream.
3396    */
3397   DecodeStat(attribs, &st, sizeof(st), &inum);
3398 
3399   /*
3400    * Decode the next field (acl_text).
3401    */
3402   if ((bp = strchr(attribs, '\0')) == (char*)NULL
3403       || (used_bytes = (bp - content)) >= (int32_t)(content_length - 1)) {
3404     goto parse_error;
3405   }
3406   acl_text = ++bp;
3407 
3408   /*
3409    * Based on the filetype perform the correct action. We support most filetypes
3410    * here, more then the actual implementation on Solaris supports so some code
3411    * may never get executed due to limitations in the implementation.
3412    */
3413   switch (st.st_mode & S_IFMT) {
3414     case S_IFIFO:
3415       /*
3416        * The current implementation of xattr on Solaris doesn't support this,
3417        * but if it ever does we are prepared.
3418        */
3419       unlinkat(attrdirfd, target_attrname, 0);
3420       if (mkfifo(target_attrname, st.st_mode) < 0) {
3421         BErrNo be;
3422 
3423         Mmsg3(jcr->errmsg,
3424               _("Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n"),
3425               target_attrname, xattr_data->last_fname, be.bstrerror());
3426         Dmsg3(100, "Unable to mkfifo xattr %s on file \"%s\": ERR=%s\n",
3427               target_attrname, xattr_data->last_fname, be.bstrerror());
3428         goto bail_out;
3429       }
3430       break;
3431     case S_IFCHR:
3432     case S_IFBLK:
3433       /*
3434        * The current implementation of xattr on Solaris doesn't support this,
3435        * but if it ever does we are prepared.
3436        */
3437       unlinkat(attrdirfd, target_attrname, 0);
3438       if (mknod(target_attrname, st.st_mode, st.st_rdev) < 0) {
3439         BErrNo be;
3440 
3441         Mmsg3(jcr->errmsg,
3442               _("Unable to mknod xattr %s on file \"%s\": ERR=%s\n"),
3443               target_attrname, xattr_data->last_fname, be.bstrerror());
3444         Dmsg3(100, "Unable to mknod xattr %s on file \"%s\": ERR=%s\n",
3445               target_attrname, xattr_data->last_fname, be.bstrerror());
3446         goto bail_out;
3447       }
3448       break;
3449     case S_IFDIR:
3450       /*
3451        * If its not the hidden_dir create the entry.
3452        * The current implementation of xattr on Solaris doesn't support this,
3453        * but if it ever does we are prepared.
3454        */
3455       if (!bstrcmp(target_attrname, ".")) {
3456         unlinkat(attrdirfd, target_attrname, AT_REMOVEDIR);
3457         if (mkdir(target_attrname, st.st_mode) < 0) {
3458           BErrNo be;
3459 
3460           Jmsg3(jcr, M_WARNING, 0,
3461                 _("Unable to mkdir xattr %s on file \"%s\": ERR=%s\n"),
3462                 target_attrname, xattr_data->last_fname, be.bstrerror());
3463           Dmsg3(100, "Unable to mkdir xattr %s on file \"%s\": ERR=%s\n",
3464                 target_attrname, xattr_data->last_fname, be.bstrerror());
3465           goto bail_out;
3466         }
3467       }
3468       break;
3469     case S_IFREG:
3470       /*
3471        * See if this is a hard linked file. e.g. inum != 0
3472        */
3473       if (inum != 0) {
3474         linked_target = bp;
3475 
3476         unlinkat(attrdirfd, target_attrname, 0);
3477         if (link(linked_target, target_attrname) < 0) {
3478           BErrNo be;
3479 
3480           Mmsg4(jcr->errmsg,
3481                 _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"),
3482                 target_attrname, linked_target, xattr_data->last_fname,
3483                 be.bstrerror());
3484           Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n",
3485                 target_attrname, linked_target, xattr_data->last_fname,
3486                 be.bstrerror());
3487           goto bail_out;
3488         }
3489 
3490         /*
3491          * Successfully restored xattr.
3492          */
3493         retval = BxattrExitCode::kSuccess;
3494         goto bail_out;
3495       } else {
3496         if ((bp = strchr(acl_text, '\0')) == (char*)NULL
3497             || (used_bytes = (bp - content)) >= (int32_t)content_length) {
3498           goto parse_error;
3499         }
3500 
3501         if (used_bytes < (int32_t)(content_length - 1)) data = ++bp;
3502 
3503         /*
3504          * Restore the actual xattr.
3505          */
3506         if (!is_extensible) { unlinkat(attrdirfd, target_attrname, 0); }
3507 
3508         if ((attrfd = openat(attrdirfd, target_attrname,
3509                              O_RDWR | O_CREAT | O_TRUNC, st.st_mode))
3510             < 0) {
3511           BErrNo be;
3512 
3513           Mmsg3(jcr->errmsg,
3514                 _("Unable to open xattr %s on file \"%s\": ERR=%s\n"),
3515                 target_attrname, xattr_data->last_fname, be.bstrerror());
3516           Dmsg3(100, "Unable to open xattr %s on file \"%s\": ERR=%s\n",
3517                 target_attrname, xattr_data->last_fname, be.bstrerror());
3518           goto bail_out;
3519         }
3520       }
3521 
3522       /*
3523        * Restore the actual data.
3524        */
3525       if (st.st_size > 0) {
3526         used_bytes = (data - content);
3527         cnt = content_length - used_bytes;
3528 
3529         /*
3530          * Do a sanity check, the st.st_size should be the same as the number of
3531          * bytes we have available as data of the stream.
3532          */
3533         if (cnt != st.st_size) {
3534           Mmsg2(jcr->errmsg,
3535                 _("Unable to restore data of xattr %s on file \"%s\": Not all "
3536                   "data available in xattr stream\n"),
3537                 target_attrname, xattr_data->last_fname);
3538           Dmsg2(100,
3539                 "Unable to restore data of xattr %s on file \"%s\": Not all "
3540                 "data available in xattr stream\n",
3541                 target_attrname, xattr_data->last_fname);
3542           goto bail_out;
3543         }
3544 
3545         while (cnt > 0) {
3546           cnt = write(attrfd, data, cnt);
3547           if (cnt < 0) {
3548             BErrNo be;
3549 
3550             Mmsg3(jcr->errmsg,
3551                   _("Unable to restore data of xattr %s on file \"%s\": "
3552                     "ERR=%s\n"),
3553                   target_attrname, xattr_data->last_fname, be.bstrerror());
3554             Dmsg3(100,
3555                   "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n",
3556                   target_attrname, xattr_data->last_fname, be.bstrerror());
3557             goto bail_out;
3558           }
3559 
3560           used_bytes += cnt;
3561           data += cnt;
3562           cnt = content_length - used_bytes;
3563         }
3564       }
3565       break;
3566     case S_IFLNK:
3567       /*
3568        * The current implementation of xattr on Solaris doesn't support this,
3569        * but if it ever does we are prepared.
3570        */
3571       linked_target = bp;
3572 
3573       if (symlink(linked_target, target_attrname) < 0) {
3574         BErrNo be;
3575 
3576         Mmsg4(jcr->errmsg,
3577               _("Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n"),
3578               target_attrname, linked_target, xattr_data->last_fname,
3579               be.bstrerror());
3580         Dmsg4(100, "Unable to symlink xattr %s to %s on file \"%s\": ERR=%s\n",
3581               target_attrname, linked_target, xattr_data->last_fname,
3582               be.bstrerror());
3583         goto bail_out;
3584       }
3585 
3586       /*
3587        * Successfully restored xattr.
3588        */
3589       retval = BxattrExitCode::kSuccess;
3590       goto bail_out;
3591     default:
3592       goto bail_out;
3593   }
3594 
3595   /*
3596    * Restore owner and acl for non extensible attributes.
3597    */
3598   if (!is_extensible) {
3599     if (fchownat(attrdirfd, target_attrname, st.st_uid, st.st_gid,
3600                  AT_SYMLINK_NOFOLLOW)
3601         < 0) {
3602       BErrNo be;
3603 
3604       switch (errno) {
3605         case EINVAL:
3606           /*
3607            * Gentile way of the system saying this type of xattr layering is not
3608            * supported. But as this is not an error we return a positive return
3609            * value.
3610            */
3611           retval = BxattrExitCode::kSuccess;
3612           break;
3613         case ENOENT:
3614           retval = BxattrExitCode::kSuccess;
3615           break;
3616         default:
3617           Mmsg3(
3618               jcr->errmsg,
3619               _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"),
3620               target_attrname, xattr_data->last_fname, be.bstrerror());
3621           Dmsg3(100,
3622                 "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n",
3623                 target_attrname, xattr_data->last_fname, be.bstrerror());
3624       }
3625       goto bail_out;
3626     }
3627   }
3628 
3629 #    ifdef HAVE_ACL
3630   if (acl_text && *acl_text)
3631     if (solaris_restore_xattr_acl(jcr, xattr_data, attrfd, target_attrname,
3632                                   acl_text)
3633         != BxattrExitCode::kSuccess)
3634       goto bail_out;
3635 #    endif /* HAVE_ACL */
3636 
3637   /*
3638    * For a non extensible attribute restore access and modification time on the
3639    * xattr.
3640    */
3641   if (!is_extensible) {
3642     times[0].tv_sec = st.st_atime;
3643     times[0].tv_usec = 0;
3644     times[1].tv_sec = st.st_mtime;
3645     times[1].tv_usec = 0;
3646 
3647     if (futimesat(attrdirfd, target_attrname, times) < 0) {
3648       BErrNo be;
3649 
3650       Mmsg3(
3651           jcr->errmsg,
3652           _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"),
3653           target_attrname, xattr_data->last_fname, be.bstrerror());
3654       Dmsg3(100,
3655             "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n",
3656             target_attrname, xattr_data->last_fname, be.bstrerror());
3657       goto bail_out;
3658     }
3659   }
3660 
3661   /*
3662    * Successfully restored xattr.
3663    */
3664   retval = BxattrExitCode::kSuccess;
3665   goto bail_out;
3666 
3667 parse_error:
3668   Mmsg1(
3669       jcr->errmsg,
3670       _("Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n"),
3671       xattr_data->last_fname);
3672   Dmsg1(100,
3673         "Illegal xattr stream, failed to parse xattr stream on file \"%s\"\n",
3674         xattr_data->last_fname);
3675 
3676 bail_out:
3677   if (attrfd != -1) { close(attrfd); }
3678   if (attrdirfd != -1) { close(attrdirfd); }
3679   if (filefd != -1) { close(filefd); }
3680   return retval;
3681 }
3682 
solaris_build_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,FindFilesPacket * ff_pkt)3683 static BxattrExitCode solaris_build_xattr_streams(JobControlRecord* jcr,
3684                                                   XattrData* xattr_data,
3685                                                   FindFilesPacket* ff_pkt)
3686 {
3687   char cwd[PATH_MAX];
3688   BxattrExitCode retval = BxattrExitCode::kSuccess;
3689 
3690   /*
3691    * First see if extended attributes or extensible attributes are present.
3692    * If not just pretend things went ok.
3693    */
3694   if (pathconf(xattr_data->last_fname, _PC_XATTR_EXISTS) > 0) {
3695     xattr_data->u.build->nr_saved = 0;
3696 
3697     /*
3698      * As we change the cwd in the save function save the current cwd
3699      * for restore after return from the solaris_save_xattrs function.
3700      */
3701     getcwd(cwd, sizeof(cwd));
3702     retval = solaris_save_xattrs(jcr, xattr_data, NULL, NULL);
3703     chdir(cwd);
3704     if (xattr_data->u.build->link_cache) { DropXattrLinkCache(xattr_data); }
3705   }
3706   return retval;
3707 }
3708 
solaris_parse_xattr_streams(JobControlRecord * jcr,XattrData * xattr_data,int stream,char * content,uint32_t content_length)3709 static BxattrExitCode solaris_parse_xattr_streams(JobControlRecord* jcr,
3710                                                   XattrData* xattr_data,
3711                                                   int stream,
3712                                                   char* content,
3713                                                   uint32_t content_length)
3714 {
3715   char cwd[PATH_MAX];
3716   bool is_extensible = false;
3717   BxattrExitCode retval = BxattrExitCode::kError;
3718 
3719   /*
3720    * First make sure we can restore xattr on the filesystem.
3721    */
3722   switch (stream) {
3723 #    if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
3724     case STREAM_XATTR_SOLARIS_SYS:
3725       if (pathconf(xattr_data->last_fname, _PC_SATTR_ENABLED) <= 0) {
3726         Mmsg1(jcr->errmsg,
3727               _("Failed to restore extensible attributes on file \"%s\"\n"),
3728               xattr_data->last_fname);
3729         Dmsg1(100,
3730               "Unable to restore extensible attributes on file \"%s\", "
3731               "filesystem doesn't support this\n",
3732               xattr_data->last_fname);
3733         goto bail_out;
3734       }
3735 
3736       is_extensible = true;
3737       break;
3738 #    endif
3739     case STREAM_XATTR_SOLARIS:
3740       if (pathconf(xattr_data->last_fname, _PC_XATTR_ENABLED) <= 0) {
3741         Mmsg1(jcr->errmsg,
3742               _("Failed to restore extended attributes on file \"%s\"\n"),
3743               xattr_data->last_fname);
3744         Dmsg1(100,
3745               "Unable to restore extended attributes on file \"%s\", "
3746               "filesystem doesn't support this\n",
3747               xattr_data->last_fname);
3748         goto bail_out;
3749       }
3750       break;
3751     default:
3752       goto bail_out;
3753   }
3754 
3755   /*
3756    * As we change the cwd in the restore function save the current cwd
3757    * for restore after return from the solaris_restore_xattrs function.
3758    */
3759   getcwd(cwd, sizeof(cwd));
3760   retval = solaris_restore_xattrs(jcr, xattr_data, is_extensible, content,
3761                                   content_length);
3762   chdir(cwd);
3763 
3764 bail_out:
3765   return retval;
3766 }
3767 
3768 
3769 /**
3770  * Function pointers to the build and parse function to use for these xattrs.
3771  */
3772 static BxattrExitCode (*os_build_xattr_streams)(JobControlRecord* jcr,
3773                                                 XattrData* xattr_data,
3774                                                 FindFilesPacket* ff_pkt)
3775     = solaris_build_xattr_streams;
3776 static BxattrExitCode (*os_parse_xattr_streams)(JobControlRecord* jcr,
3777                                                 XattrData* xattr_data,
3778                                                 int stream,
3779                                                 char* content,
3780                                                 uint32_t content_length)
3781     = solaris_parse_xattr_streams;
3782 
3783 #  endif /* defined(HAVE_SUN_OS) */
3784 
3785 /**
3786  * Entry points when compiled with support for XATTRs on a supported platform.
3787  */
BuildXattrStreams(JobControlRecord * jcr,XattrData * xattr_data,FindFilesPacket * ff_pkt)3788 BxattrExitCode BuildXattrStreams(JobControlRecord* jcr,
3789                                  XattrData* xattr_data,
3790                                  FindFilesPacket* ff_pkt)
3791 {
3792   /*
3793    * See if we are changing from one device to another.
3794    * We save the current device we are scanning and compare
3795    * it with the current st_dev in the last stat performed on
3796    * the file we are currently storing.
3797    */
3798   Dmsg0(1000, "BuildXattrStreams(): Enter\n");
3799   if (xattr_data->first_dev
3800       || xattr_data->current_dev != ff_pkt->statp.st_dev) {
3801     xattr_data->flags = BXATTR_FLAG_SAVE_NATIVE;
3802     xattr_data->first_dev = false;
3803     xattr_data->current_dev = ff_pkt->statp.st_dev;
3804   }
3805 
3806 #ifdef __DragonFly__
3807   {
3808 #else
3809   if ((xattr_data->flags & BXATTR_FLAG_SAVE_NATIVE) && os_build_xattr_streams) {
3810     return os_build_xattr_streams(jcr, xattr_data, ff_pkt);
3811   } else {
3812 #endif
3813     return BxattrExitCode::kSuccess;
3814   }
3815 }
3816 
3817 BxattrExitCode ParseXattrStreams(JobControlRecord* jcr,
3818                                  XattrData* xattr_data,
3819                                  int stream,
3820                                  char* content,
3821                                  uint32_t content_length)
3822 {
3823   int ret;
3824   struct stat st;
3825   unsigned int cnt;
3826   BxattrExitCode retval = BxattrExitCode::kError;
3827 
3828   Dmsg0(1000, "ParseXattrStreams(): Enter\n");
3829   /*
3830    * See if we are changing from one device to another.
3831    * We save the current device we are restoring to and compare
3832    * it with the current st_dev in the last stat performed on
3833    * the file we are currently restoring.
3834    */
3835   ret = lstat(xattr_data->last_fname, &st);
3836   switch (ret) {
3837     case -1: {
3838       BErrNo be;
3839 
3840       switch (errno) {
3841         case ENOENT:
3842           retval = BxattrExitCode::kSuccess;
3843           goto bail_out;
3844         default:
3845           Mmsg2(jcr->errmsg, _("Unable to stat file \"%s\": ERR=%s\n"),
3846                 xattr_data->last_fname, be.bstrerror());
3847           Dmsg2(100, "Unable to stat file \"%s\": ERR=%s\n",
3848                 xattr_data->last_fname, be.bstrerror());
3849           goto bail_out;
3850       }
3851       break;
3852     }
3853     case 0:
3854       break;
3855   }
3856   if (xattr_data->first_dev || xattr_data->current_dev != st.st_dev) {
3857     xattr_data->flags = BXATTR_FLAG_RESTORE_NATIVE;
3858     xattr_data->first_dev = false;
3859     xattr_data->current_dev = st.st_dev;
3860   }
3861 
3862   /*
3863    * See if we are still restoring native xattr to this filesystem.
3864    */
3865 #ifdef __DragonFly__
3866   {
3867 #else
3868   if ((xattr_data->flags & BXATTR_FLAG_RESTORE_NATIVE)
3869       && os_parse_xattr_streams) {
3870     /*
3871      * See if we can parse this stream, and ifso give it a try.
3872      */
3873     for (cnt = 0; cnt < sizeof(os_default_xattr_streams) / sizeof(int); cnt++) {
3874       if (os_default_xattr_streams[cnt] == stream) {
3875         retval = os_parse_xattr_streams(jcr, xattr_data, stream, content,
3876                                         content_length);
3877         goto bail_out;
3878       }
3879     }
3880   } else {
3881 #endif
3882     /*
3883      * Increment error count but don't log an error again for the same
3884      * filesystem.
3885      */
3886     xattr_data->u.parse->nr_errors++;
3887     retval = BxattrExitCode::kSuccess;
3888     goto bail_out;
3889   }
3890 
3891   /*
3892    * Issue a warning and discard the message. But pretend the restore was ok.
3893    */
3894   Jmsg2(jcr, M_WARNING, 0,
3895         _("Can't restore Extended Attributes of %s - incompatible xattr stream "
3896           "encountered - %d\n"),
3897         xattr_data->last_fname, stream);
3898 
3899 bail_out:
3900   return retval;
3901 }
3902 #endif
3903