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