1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18  */
19 /**
20  * Major refactoring of XATTR code written by:
21  *
22  *  Radosław Korzeniewski, MMXVI
23  *  radoslaw@korzeniewski.net, radekk@inteos.pl
24  *  Inteos Sp. z o.o. http://www.inteos.pl/
25  *
26  */
27 
28 #include "bacula.h"
29 #include "filed.h"
30 #include "bxattr_solaris.h"
31 
32 #if defined(HAVE_SUN_OS)
33 
34 /* check if XATTR support is enabled */
35 #if defined(HAVE_XATTR)
36 
37 #ifdef HAVE_SYS_NVPAIR_H
38 #include <sys/nvpair.h>
39 #endif
40 
41 #if !defined(HAVE_OPENAT) || \
42     !defined(HAVE_ATTROPEN) || \
43     !defined(HAVE_UNLINKAT) || \
44     !defined(HAVE_FCHOWNAT) || \
45     !defined(HAVE_FUTIMESAT)
46 #error "Unable to compile code because of missing openat, attropen, unlinkat, fchownat or futimesat functions"
47 #endif
48 
49 /*
50  * Define the supported XATTR streams for this OS
51  */
52 static const int os_xattr_streams[] = {
53    STREAM_XACL_SOLARIS_XATTR,
54 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
55    STREAM_XACL_SOLARIS_SYS_XATTR,
56 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
57    0
58 };
59 
60 static const char *os_xattr_skiplist[] = {
61    "..",
62 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
63    VIEW_READONLY,
64 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
65    NULL
66 };
67 
68 static const char *os_xattr_acl_skiplist[] = {
69    NULL
70 };
71 
72 /* Provide a suplement for Solaris 10 missing linkat function */
73 #ifndef HAVE_LINKAT
linkat(int fd1,const char * path1,int fd2,const char * path2,int flag)74 int linkat(int fd1, const char *path1, int fd2, const char *path2, int flag)
75 {
76    int rc;
77 
78    rc = fchdir(fd1);
79    if (rc != 0){
80       return rc;
81    }
82 
83    rc = link(path1, path2);
84    return rc;
85 };
86 #endif
87 /*
88  * OS Specific constructor
89  */
BXATTR_Solaris()90 BXATTR_Solaris::BXATTR_Solaris(){
91 
92    set_xattr_streams(os_xattr_streams);
93    set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist);
94    cache = NULL;
95 };
96 
97 /*
98  * OS Specific destructor
99  */
~BXATTR_Solaris()100 BXATTR_Solaris::~BXATTR_Solaris(){
101 
102    delete_xattr_cache();
103 };
104 
105 /*
106  * Perform OS specific extended attribute backup
107  *
108  * in/out - check API at bxattr.h
109  *
110  * The Solaris implementation of XATTR is very, very different then all other "generic" unix implementations,
111  * so the original author of the Bacula XATTR support for Solaris OS decided to totally change the XATTR Stream
112  * content, and we need to follow this design to support previous behavior. The stream consist of a number of
113  * "files" with STREAM_XACL_SOLARIS_XATTR or STREAM_XACL_SOLARIS_SYS_XATTR stream id. Every singe stream represents
114  * a single attribute. The content is a NULL-terminated array with a following data:
115  *    <xattr name>\0<encoded stat>\0<acl rendered text>\0<xattr data>
116  * when an attribute file has a hardlinked other attributes then a content stream changes a bit into:
117  *    <xattr name>\0<encoded stat>\0<target xattr name>\0
118  * where:
119  *    <xattr name> is an attribute name - a file name in Solaris
120  *    <encoded stat> is a standard file stat struct encoded by Bacula (the same encoding goes with a regular file)
121  *    <acl rendered text> is a Solaris dependent acltotext data
122  *    <xattr data> is the attribute file raw content
123  *    <target xattr name> is a name of the first hardlinked attribute file which a current attribute has to linked to
124  *
125  * The raw content of the attribute is copied into memory before send to the SD and for a very large attribute
126  * data can allocate a large amount of additional memory. In most cases it should not be a problem because most
127  * xattrs should has a few /hundred/ bytes in size. This is the same behavior as in previous implementation.
128  */
os_backup_xattr(JCR * jcr,FF_PKT * ff_pkt)129 bRC_BXATTR BXATTR_Solaris::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
130 
131    bRC_BXATTR rc;
132    POOLMEM *xlist = NULL;
133    uint32_t xlen;
134    char *name;
135    char *lnkname;
136    uint32_t name_len;
137    POOLMEM *value = NULL;
138    uint32_t value_len;
139    char * bxattracl;
140    POOLMEM *data = NULL;
141    bool skip;
142    struct stat st;
143    char attribs[MAXSTRING];
144    int stream;
145    int attrfd;
146    int len;
147    mode_t stmode;
148 
149    /* sanity check of input variables */
150    if (jcr == NULL || ff_pkt == NULL){
151       return bRC_BXATTR_inval;
152    }
153 
154    /* check if extended/extensible attributes are present */
155    if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0){
156       /* xlist is allocated as POOLMEM by os_get_xattr_names */
157       rc = os_get_xattr_names(jcr, &xlist, &xlen);
158       switch (rc){
159          case bRC_BXATTR_ok:
160             /* it's ok, so go further */
161             break;
162          case bRC_BXATTR_skip:
163          case bRC_BXATTR_cont:
164             /* no xattr available, so skip rest of it */
165             return bRC_BXATTR_ok;
166          default:
167             return rc;
168       }
169 
170       data = get_pool_memory(PM_BSOCK);
171       /* follow the list of xattr names and get the values */
172       for (name = xlist; (unsigned int)(name - xlist) + 1 < xlen; name = strchr(name, '\0') + 1){
173          name_len = strlen(name);
174          /* skip read-only or other unused attribute names */
175          skip =  check_xattr_skiplists(jcr, ff_pkt, name);
176          if (skip || name_len == 0){
177             Dmsg1(200, "Skipping xattr named \"%s\"\n", name);
178             continue;
179          }
180          Dmsg1(200, "Processing xattr: %s\n", name);
181          /* set a correct stream */
182          stream = STREAM_XACL_SOLARIS_XATTR;
183 
184 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
185          /* check for system attributes name */
186          if (bstrcmp(name, VIEW_READWRITE)){
187             stream = STREAM_XACL_SOLARIS_SYS_XATTR;
188          }
189 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
190 
191          /* open an attribute descriptor, it will be used for backup */
192          attrfd = attropen(jcr->last_fname, name, O_RDONLY);
193 
194          /* get the stat of the attribute */
195          if (fstat(attrfd, &st) < 0){
196             berrno be;
197 
198             switch (errno){
199                case ENOENT:
200                   rc = bRC_BXATTR_ok;
201                   goto bailout;
202                default:
203                   Mmsg3(jcr->errmsg, _("Unable to get status on xattr \"%s\" on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
204                   Dmsg3(100, "fstat of xattr %s on \"%s\" failed: ERR=%s\n", name, jcr->last_fname, be.bstrerror());
205                   rc = bRC_BXATTR_error;
206                   goto bailout;
207             }
208          }
209 
210          /* get xattr acl data, but only when it is not trivial acls */
211          rc = os_get_xattr_acl(jcr, attrfd, &bxattracl, name);
212          if (rc != bRC_BXATTR_ok){
213             goto bailout;
214          }
215 
216          /*
217           * Solaris support only S_IFREG and S_IFDIR as an attribute file type, no other types are supported
218           * the previous Solaris xattr implementation in Bacula had an unverified and untested code for other
219           * types of attribute files which was a nonsense and unnecessarily complicate the code. We decided
220           * to remove unsupported code. To check if the current Solaris version support for xattr was extended
221           * simply verify a man fsattr(5) for it.
222           */
223          stmode = st.st_mode & S_IFMT;
224          Dmsg1(800, "st.st_mode: 0x%X\n", stmode);
225          switch (stmode){
226             case S_IFDIR:
227                /* we have a struct stat of the attribute so encode it to the buffer
228                 * we distinguish standard file by setting ino to zero in attribs */
229                encode_stat(attribs, &st, sizeof(st), 0, stream);
230                /* prepare and save xattr info */
231                len = bsnprintf(data, sizeof_pool_memory(data), "%s%c%s%c%s%c", name, 0, attribs, 0, (bxattracl) ? bxattracl : "", 0);
232                set_content(data, len);
233                break;
234             case S_IFREG:
235                /* check for hardlinked attributes which Solaris support */
236                if (st.st_nlink > 1){
237                   /* search for already saved file of the same inode number */
238                   lnkname = find_xattr_cache(jcr, st.st_ino, name);
239                   if (lnkname != NULL){
240                      /* found a previous saved file, link to it and render xattr data for hardlinked attribute */
241                      Dmsg0(400, "saving linked attr\n");
242                      /* we have a struct stat of the attribute so encode it to the buffer
243                       * we distinguish hardlinked file by setting a real ino in attribs */
244                      encode_stat(attribs, &st, sizeof(st), st.st_ino, stream);
245                      len = bsnprintf(data, sizeof_pool_memory(data), "%s%c%s%c%s%c", name, 0, attribs, 0, lnkname, 0);
246                      set_content(data, len);
247                      /* content is ready */
248                      break;
249                   }
250                }
251                /* value is allocated as POOLMEM by os_get_xattr_value */
252                rc = os_get_xattr_value(jcr, name, &value, &value_len);
253                switch (rc){
254                   case bRC_BXATTR_ok:
255                      /* it's ok, so go further */
256                      break;
257                   case bRC_BXATTR_skip:
258                      /* no xattr available, so skip rest of it */
259                      rc = bRC_BXATTR_ok;
260                      goto bailout;
261                   default:
262                      /* error / fatal */
263                      goto bailout;
264                }
265                /* we have a struct stat of the attribute so encode it to the buffer
266                 * we distinguish standard file by setting ino to zero in attribs */
267                encode_stat(attribs, &st, sizeof(st), 0, stream);
268                /* prepare and save xattr info */
269                len = bsnprintf(data, sizeof_pool_memory(data), "%s%c%s%c%s%c", name, 0, attribs, 0, (bxattracl) ? bxattracl : "", 0);
270                /* append value data to the end of the xattr info */
271                if (value_len > 0){
272                   check_pool_memory_size(data, len + value_len);
273                   memcpy(data + len, value, value_len);
274                   set_content(data, len + value_len);
275                   free_pool_memory(value);
276                   value = NULL;
277                } else {
278                   set_content(data, len);
279                }
280                break;
281             default:
282                Dmsg3(100, "Unsupported extended attribute type: 0x%X for \"%s\" on file \"%s\"\n", stmode, name, jcr->last_fname);
283                Mmsg3(jcr->errmsg, _("Unsupported extended attribute type: 0x%X for \"%s\" on file \"%s\"\n"), stmode, name, jcr->last_fname);
284                rc = bRC_BXATTR_error;
285                goto bailout;
286          }
287          if (bxattracl){
288             actuallyfree(bxattracl);
289             bxattracl = NULL;
290          }
291          /* send stream to the sd */
292          rc = send_xattr_stream(jcr, stream);
293          if (rc != bRC_BXATTR_ok){
294             Mmsg2(jcr->errmsg, _("Failed to send extended attribute \"%s\" on file \"%s\"\n"), name, jcr->last_fname);
295             Dmsg2(100, "Failed to send extended attribute \"%s\" on file \"%s\"\n", name, jcr->last_fname);
296             goto bailout;
297          }
298       }
299 
300 bailout:
301       /* free allocated data: xlist, value (if not freed), data, etc. */
302       free_pool_memory(data);
303       if (value != NULL){
304          free_pool_memory(value);
305       }
306       if (xlist != NULL){
307          // Dmsg1(400, "free xlist: %p\n", xlist);
308          free_pool_memory(xlist);
309       }
310       /* this is a cache for a particular file, so no needed after backup of this file */
311       delete_xattr_cache();
312 
313       return rc;
314    } else {
315       Dmsg1(500, "xattr does not exist on: %s\n", jcr->last_fname);
316    }
317    return bRC_BXATTR_ok;
318 };
319 
320 /*
321  * BXATTR_Solaris cache is a simple linked list cache of inode number and names used to handle
322  * xattr hard linked data. The function is searching for cached entry. When not found it append
323  * entry to the cache.
324  * in:
325  *    jcr - Job Control Record (well, it is not used here)
326  *    ino - inode number to compare/search for
327  *    name - the name of the current attribute
328  * out:
329  *    NULL - when entry not found in cache and new entry was added
330  *    <str> - a name of the linked entry
331  */
find_xattr_cache(JCR * jcr,ino_t ino,char * name)332 inline char * BXATTR_Solaris::find_xattr_cache(JCR *jcr, ino_t ino, char * name){
333 
334    BXATTR_Solaris_Cache *entry;
335 
336    if (cache != NULL){
337       foreach_alist(entry, cache){
338          if (entry && entry->inode == ino){
339             /* found in cache, return name */
340             return entry->name;
341          }
342       }
343    } else {
344       cache = New (alist(10, not_owned_by_alist));
345    }
346    /* not found, so add this one to the cache */
347    entry = (BXATTR_Solaris_Cache*) malloc (sizeof(BXATTR_Solaris_Cache));
348    entry->inode = ino;
349    entry->name = name;
350    cache->append(entry);
351    return NULL;
352 }
353 
354 /*
355  * The function deletes a cache
356  * in/out - void
357  */
delete_xattr_cache()358 inline void BXATTR_Solaris::delete_xattr_cache(){
359 
360    BXATTR_Solaris_Cache *entry;
361 
362    if (cache != NULL){
363       foreach_alist(entry, cache){
364          free(entry);
365       }
366       delete cache;
367       cache = NULL;
368    }
369 }
370 
371 /*
372  * Perform OS specific XATTR restore. Runtime is called only when stream is supported by OS.
373  *
374  * The way Solaris xattr support is designed in Bacula we will have a single attribute restore
375  * with every call to this function. So multiple attributes are restored with multiple calls.
376  *
377  * in/out - check API at bxattr.h
378  */
os_restore_xattr(JCR * jcr,int stream,char * content,uint32_t length)379 bRC_BXATTR BXATTR_Solaris::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){
380 
381    bRC_BXATTR rc = bRC_BXATTR_error;
382    bool extended = false;
383 
384    /* check input data */
385    if (jcr == NULL || content == NULL){
386       return bRC_BXATTR_inval;
387    }
388 
389    Dmsg2(400, "restore xattr stream %i on file: %s\n", stream, jcr->last_fname);
390    /* First make sure we can restore xattr on the filesystem */
391    switch (stream){
392 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
393       case STREAM_XACL_SOLARIS_SYS_XATTR:
394          if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0){
395             Mmsg(jcr->errmsg, _("Failed to restore extensible attributes on file \"%s\"\n"), jcr->last_fname);
396             Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n", jcr->last_fname);
397             goto bail_out;
398          }
399          extended = true;
400          break;
401 #endif
402       case STREAM_XACL_SOLARIS_XATTR:
403          if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0){
404             Mmsg(jcr->errmsg, _("Failed to restore extended attributes on file \"%s\"\n"), jcr->last_fname);
405             Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n", jcr->last_fname);
406             goto bail_out;
407          }
408          break;
409       default:
410          goto bail_out;
411    }
412 
413    rc = os_set_xattr(jcr, extended, content, length);
414 
415 bail_out:
416    return rc;
417 };
418 
419 #ifdef HAVE_ACL
420 /*
421  * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
422  * There is no need to store those acls as we already store the stat bits too.
423  */
acl_is_trivial(int count,aclent_t * entries)424 bool acl_is_trivial(int count, aclent_t *entries)
425 {
426    int n;
427    aclent_t *ace;
428 
429    for (n = 0; n < count; n++) {
430       ace = &entries[n];
431 
432       if (!(ace->a_type == USER_OBJ ||
433             ace->a_type == GROUP_OBJ ||
434             ace->a_type == OTHER_OBJ ||
435             ace->a_type == CLASS_OBJ))
436         return false;
437    }
438    return true;
439 }
440 #endif
441 
442 /*
443  * Low level OS specific runtime to get ACL on XATTR. The ACL data is set in supplied buffer
444  *
445  * in:
446  *    jcr - Job Control Record
447  *    fd - an opened file descriptor of the saved attribute
448  *    buffer - a memory buffer pointer which we will return when render an acl text
449  * out:
450  *    buffer - NULL when no ACL or any error else the memory with acl representation.
451  *    The memory for the external text string is obtained using malloc(3C).
452  *    The caller is responsible for freeing the memory upon completion.
453  *    bRC_BXATTR_ok - backup acl for extended attribute finish without problems
454  *    bRC_BXATTR_error - backup acl unsuccessful
455  *    bRC_BXATTR_inval - input variables are invalid (null)
456  *
457  */
os_get_xattr_acl(JCR * jcr,int fd,char ** buffer,char * attrname)458 bRC_BXATTR BXATTR_Solaris::os_get_xattr_acl(JCR *jcr, int fd, char **buffer, char *attrname)
459 {
460 // a function is valid only when Bacula have a support for ACL
461 #ifdef HAVE_ACL
462    bRC_BXATTR rc = bRC_BXATTR_error;
463 
464    /* sanity check of input variables */
465    if (jcr == NULL || buffer == NULL || fd < 0){
466       return bRC_BXATTR_inval;
467    }
468 
469 #ifdef HAVE_EXTENDED_ACL
470 
471    int flags;
472    acl_t *aclp = NULL;
473 
474    /* check if an attribute has acl on it which we can save */
475    if (fpathconf(fd, _PC_ACL_ENABLED) > 0){
476       /* check for non trivial acl on the file */
477       if (facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0){
478          berrno be;
479 
480          switch (errno){
481          case ENOENT:
482             rc = bRC_BXATTR_ok;
483             goto bail_out;
484          default:
485             Mmsg2(jcr->errmsg, _("Unable to get xattr acl on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
486             Dmsg2(100, "facl_get/acl_get of xattr on \"%s\" failed: ERR=%s\n", jcr->last_fname, be.bstrerror());
487             goto bail_out;
488          }
489       }
490 
491       if (aclp != NULL){
492 #if defined(ACL_SID_FMT)
493          /* New format flag added in newer Solaris versions. */
494          flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
495 #else
496          flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
497 #endif /* ACL_SID_FMT */
498 
499          *buffer = acl_totext(aclp, flags);
500          Dmsg1(500, "xattr extended acl found: %s\n", *buffer);
501          acl_free(aclp);
502       } else {
503          *buffer = NULL;
504       }
505    } else {
506       *buffer = NULL;
507    }
508    rc = bRC_BXATTR_ok;
509 bail_out:
510 
511 #else /* !HAVE_EXTENDED_ACL */
512 
513    int n;
514    aclent_t *acls = NULL;
515 
516    /* See if this attribute has an ACL */
517    n = facl(fd, GETACLCNT, 0, NULL);
518 
519    if (n >= MIN_ACL_ENTRIES){
520       acls = (aclent_t *)malloc(n * sizeof(aclent_t));
521       if (facl(fd, GETACL, n, acls) != n){
522          berrno be;
523 
524          switch (errno){
525          case ENOENT:
526             rc = bRC_BXATTR_ok;
527             goto bail_out;
528          default:
529             Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"), attrname, jcr->last_fname, be.bstrerror());
530             Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n", attrname, jcr->last_fname, be.bstrerror());
531             rc = bRC_BXATTR_error;
532             goto bail_out;
533          }
534       }
535 
536       /* See if there is a non trivial acl on the file. */
537       if (!acl_is_trivial(n, acls)){
538          if ((*buffer = acltotext(acls, n)) == NULL){
539             berrno be;
540 
541             Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"), attrname, jcr->last_fname, be.bstrerror());
542             Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n", attrname, jcr->last_fname, be.bstrerror());
543             rc = bRC_BXATTR_error;
544          }
545          Dmsg1(500, "xattr acl found: %s\n", *buffer);
546       } else {
547          *buffer = NULL;
548       }
549 bail_out:
550      free(acls);
551    } else {
552       *buffer = NULL;
553    }
554    rc = bRC_BXATTR_ok;
555 #endif /* HAVE_EXTENDED_ACL */
556    return rc;
557 #else /* HAVE_ACL */
558    return bRC_BXATTR_ok;
559 #endif /* HAVE_ACL */
560 }
561 
562 /*
563  * Low level OS specific runtime to set ACL on XATTR. The ACL data is set from supplied text
564  *
565  * in:
566  *    jcr - Job Control Record
567  *    fd - an opened file descriptor of the restored attribute
568  *    buffer - a pointer to the memory buffer where we will render an acl text
569  * out:
570  *    bRC_BXATTR_ok - backup acl for extended attribute finish without problems
571  *    bRC_BXATTR_inval - input variables are invalid (null)
572  *
573  */
os_set_xattr_acl(JCR * jcr,int fd,char * name,char * acltext)574 bRC_BXATTR BXATTR_Solaris::os_set_xattr_acl(JCR *jcr, int fd, char *name, char *acltext)
575 {
576 // a function is valid only when Bacula have a support for ACL
577 #ifdef HAVE_ACL
578 
579    bRC_BXATTR rc = bRC_BXATTR_ok;
580 
581    /* sanity check of input variables */
582    if (jcr == NULL || name == NULL || acltext == NULL || fd < 0){
583       return bRC_BXATTR_inval;
584    }
585 
586 #ifdef HAVE_EXTENDED_ACL
587 
588    int error;
589    acl_t *aclp = NULL;
590 
591    if ((error = acl_fromtext(acltext, &aclp)) != 0){
592       Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"), jcr->last_fname);
593       return bRC_BXATTR_error;
594    }
595 
596    if (facl_set(fd, aclp) != 0){
597       berrno be;
598 
599       Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
600       Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
601       rc = bRC_BXATTR_error;
602    }
603 
604    if (aclp){
605       acl_free(aclp);
606    }
607 
608 #else /* !HAVE_EXTENDED_ACL */
609 
610    int n;
611    aclent_t *acls = NULL;
612 
613    acls = aclfromtext(acltext, &n);
614    if (acls){
615       if (facl(fd, SETACL, n, acls) != 0){
616          berrno be;
617 
618          Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
619          Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
620          rc = bRC_BXATTR_error;
621       }
622       actuallyfree(acls);
623    }
624 
625 #endif /* HAVE_EXTENDED_ACL */
626 
627    return rc;
628 #else /* HAVE_ACL */
629    return bRC_BXATTR_ok;
630 #endif /* HAVE_ACL */
631 };
632 
633 /*
634  * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer.
635  * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
636  * when not needed.
637  *
638  * in/out - check API at bxattr.h
639  */
os_get_xattr_names(JCR * jcr,POOLMEM ** pxlist,uint32_t * xlen)640 bRC_BXATTR BXATTR_Solaris::os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){
641 
642    int xattrdfd;
643    DIR *dirp;
644    struct dirent *dp;
645 
646    int len;
647    int slen;
648    POOLMEM * list;
649 
650    /* check input data */
651    if (jcr == NULL || xlen == NULL || pxlist == NULL){
652       return bRC_BXATTR_inval;
653    }
654 
655    /* Open the xattr stream on file */
656    Dmsg1(500, "os_get_xattr_names on file: %s\n", jcr->last_fname);
657    if ((xattrdfd = attropen(jcr->last_fname, ".", O_RDONLY)) < 0){
658       berrno be;
659 
660       switch (errno){
661          case ENOENT:
662             /* no file available, skip it */
663             return bRC_BXATTR_skip;
664          case EINVAL:
665             /* no xattr supported on file skip it */
666             return bRC_BXATTR_skip;
667          default:
668             Mmsg2(jcr->errmsg, _("Unable to open xattr on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
669             Dmsg2(100, "Unable to open xattr on file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
670             return bRC_BXATTR_error;
671       }
672    }
673 
674    /* open an extended file directory to read all xattr names */
675    if ((dirp = fdopendir(xattrdfd)) == (DIR *)NULL){
676       berrno be;
677 
678       Mmsg2(jcr->errmsg, _("Unable to list the xattr on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
679       Dmsg3(100, "Unable to fdopendir xattr on file \"%s\" using fd %d: ERR=%s\n", jcr->last_fname, xattrdfd, be.bstrerror());
680       close(xattrdfd);
681       return bRC_BXATTR_error;
682    }
683 
684    /*
685     * allocate memory for the extented attribute list
686     * default size is a 4k for PM_BSOCK, which should be sufficient in most cases
687     */
688    list = get_pool_memory(PM_BSOCK);
689    // Dmsg1(400, "allocated xlist: %p\n", list);
690    memset(list, 0, sizeof_pool_memory(list));
691    bstrncpy(list, ".", sizeof_pool_memory(list));
692    len = strlen(list) + 1;
693 
694    /* read all directory entries as a xattr names */
695    while ((dp = readdir(dirp)) != NULL){
696 
697       /* skip '.' as we added it above */
698       if (bstrcmp(dp->d_name, ".")){
699          continue;
700       }
701 
702       Dmsg1(500, "Found attribute: %s\n", dp->d_name);
703       /* compute a buffer length = string length and nul char */
704       slen = strlen (dp->d_name) + 1;
705       list = check_pool_memory_size(list, len + slen);
706       // Dmsg3(400, "xlist: %p len: %i slen: %i\n", list, len, slen);
707       /* copy the name into a list */
708       bstrncpy(list + len, dp->d_name, sizeof_pool_memory(list) - len);
709       len += slen;
710    }
711    if (closedir(dirp) < 0){
712       berrno be;
713 
714       Mmsg2(jcr->errmsg, _("Unable to close xattr list on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
715       Dmsg2(100, "Unable to close xattr list on file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
716       return bRC_BXATTR_error;
717    }
718 
719    *pxlist = list;
720    *xlen = len;
721 
722    return bRC_BXATTR_ok;
723 };
724 
725 /*
726  * Return a value of the requested attribute name and a length of the allocated buffer.
727  * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
728  * when not needed.
729  *
730  * in/out - check API at bxattr.h
731  */
os_get_xattr_value(JCR * jcr,char * name,char ** pvalue,uint32_t * plen)732 bRC_BXATTR BXATTR_Solaris::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){
733 
734    int xattrfd;
735    int len;
736    POOLMEM * value;
737    struct stat st;
738 
739    /* check input data */
740    if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){
741       return bRC_BXATTR_inval;
742    }
743 
744    /* Open the xattr on file */
745    if ((xattrfd = attropen(jcr->last_fname, name, O_RDONLY)) < 0){
746       berrno be;
747 
748       switch (errno){
749          case ENOENT:
750             /* no file available, skip it */
751             return bRC_BXATTR_skip;
752          case EINVAL:
753             /* no xattr supported on file skip it */
754             return bRC_BXATTR_skip;
755          default:
756             Mmsg2(jcr->errmsg, _("Unable to open xattr on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
757             Dmsg2(100, "Unable to open xattr on file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
758             return bRC_BXATTR_error;
759       }
760    }
761 
762    /* get some info about extended attribute */
763    if (fstat(xattrfd, &st) < 0){
764       berrno be;
765 
766       switch (errno){
767          case ENOENT:
768             /* no file available, skip it */
769             return bRC_BXATTR_skip;
770          default:
771             Mmsg3(jcr->errmsg, _("Unable to stat xattr \"%s\" on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
772             Dmsg3(100, "Unable to stat xattr \"%s\" on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
773             return bRC_BXATTR_error;
774       }
775    }
776 
777    /* default empty value */
778    value = NULL;
779    len = 0;
780 
781    /* only a file has a data/value we should care about */
782    if ((st.st_mode & S_IFMT) != S_IFDIR){
783       /* get size of the attribute data/value */
784       len = lseek(xattrfd, 0, SEEK_END);
785       lseek(xattrfd, 0, SEEK_SET);
786    }
787    if (len > 0){
788       /*
789        * allocate memory for the extented attribute value
790        * default size is a 256B for PM_MESSAGE, so we need to check required size
791        */
792       value = get_pool_memory(PM_MESSAGE);
793       value = check_pool_memory_size(value, len);
794       memset(value, 0, len);
795       /* read teh data */
796       read (xattrfd, value, len);
797       close(xattrfd);
798    }
799 
800    /* setup return data */
801    *pvalue = value;
802    *plen = len;
803    return bRC_BXATTR_ok;
804 };
805 
806 /*
807  * Low level OS specific runtime to set extended attribute on file
808  *
809  * in/out - check API at bxattr.h
810  */
os_set_xattr(JCR * jcr,bool extended,char * content,uint32_t length)811 bRC_BXATTR BXATTR_Solaris::os_set_xattr (JCR *jcr, bool extended, char *content, uint32_t length){
812 
813    char *bp;
814    char *name;
815    char *attribs;
816    char *acltext;
817    char *lntarget;
818    int attrfd = 0;
819    int attrdirfd = 0;
820    int cnt;
821    int len;
822    int inum;
823    struct stat st;
824    struct timeval times[2];
825    bRC_BXATTR rc = bRC_BXATTR_ok;
826    mode_t stmode;
827 
828    /* check input data */
829    if (jcr == NULL || content == NULL){
830       return bRC_BXATTR_inval;
831    }
832    /*
833     * Parse content stream and extract valuable data.
834     * STD/EXT: <xattr name>\0<encoded stat>\0<acl rendered text>\0<xattr data>
835     *    where <encoded stat>.inum = 0
836     * LNK:     <xattr name>\0<encoded stat>\0<target xattr name>\0
837     *    where <encoded stat>.inum != 0
838     */
839    bp = content;
840    if (bp[0] == '/'){
841       bp = content + 1;    /* original code saves attribute name with '/' */
842    }
843    /* attribute name in name variable */
844    name = bp;
845    len = strlen (bp);
846    bp += len + 1;
847 
848    /* attribute encoded stat in attribs variable */
849    attribs = bp;
850    len = strlen (bp);
851    bp += len + 1;
852    /* decode attributes into st and inum which distinguish between STX/EXT and LNK xattrs */
853    decode_stat(attribs, &st, sizeof(st), &inum);
854 
855    /* acltext and link target name goes here */
856    acltext = lntarget = bp;
857    len = strlen (bp);
858    /* now 'bp' should have the xattr data */
859    bp += len + 1;
860 
861    /*
862     * Open the xattr on which to restore the xattrs read-only.
863     */
864    if ((attrdirfd = attropen(jcr->last_fname, ".", O_RDONLY)) < 0){
865       berrno be;
866 
867       Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
868       Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
869       rc = bRC_BXATTR_error;
870       goto bail_out;
871    }
872 
873    stmode = st.st_mode & S_IFMT;
874    Dmsg1(800, "st.st_mode: 0x%X\n", stmode);
875    switch (stmode) {
876       case S_IFDIR:
877          /* if it is a current dir on file then we can restore acl data only */
878          Dmsg1(400, "Processing dir xattr: %s\n", name);
879          if (bstrcmp(name, ".")){
880             break;
881          }
882          break;
883       case S_IFREG:
884          if (inum != 0){
885             /* it is a linked attribute, perform a link operation */
886             Dmsg2(400, "Processing linked xattr: %s => %s\n", lntarget, name);
887             unlinkat(attrdirfd, name, 0);
888             if (linkat(attrdirfd, lntarget, attrdirfd, name, 0) < 0){
889                berrno be;
890 
891                Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"), name, lntarget, jcr->last_fname, be.bstrerror());
892                Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n", name, lntarget, jcr->last_fname, be.bstrerror());
893                rc = bRC_BXATTR_error;
894                goto bail_out;
895             }
896             goto bail_out;
897          } else {
898             Dmsg1(400, "Processing xattr: %s\n", name);
899             if (!extended){
900                unlinkat(attrdirfd, name, 0);
901             }
902             if ((attrfd = openat(attrdirfd, name, O_CREAT | O_RDWR | O_TRUNC, st.st_mode)) < 0){
903                berrno be;
904 
905                Mmsg3(jcr->errmsg, _("Unable to open attribute \"%s\" at file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
906                Dmsg3(100, "Unable to open attribute \"%s\" at file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
907                rc = bRC_BXATTR_error;
908                goto bail_out;
909             }
910             /* restore any data if are available */
911             if (st.st_size > 0){
912                cnt = write (attrfd, bp, length - (bp - content) );
913                if (cnt < 0){
914                   berrno be;
915 
916                   Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
917                   Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
918                   rc = bRC_BXATTR_error;
919                   goto bail_out;
920                }
921             }
922          }
923          break;
924       default:
925          Dmsg3(100, "Unsupported extended attribute type: 0x%X for \"%s\" on file \"%s\"\n", stmode, name, jcr->last_fname);
926          Mmsg3(jcr->errmsg, _("Unsupported extended attribute type: 0x%X for \"%s\" on file \"%s\"\n"), stmode, name, jcr->last_fname);
927          goto bail_out;
928    }
929 
930    /* file data restored, so setup permissions and acl data */
931    if (!extended){
932       if (fchownat(attrdirfd, name, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0){
933          berrno be;
934 
935          switch (errno){
936             case EINVAL:
937             case ENOENT:
938                break;
939             default:
940                Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
941                Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
942                rc = bRC_BXATTR_error;
943          }
944          goto bail_out;
945       }
946    }
947 
948 #ifdef HAVE_ACL
949    if (strlen(acltext)){
950       rc = os_set_xattr_acl(jcr, attrfd, name, acltext);
951       if (rc != bRC_BXATTR_ok){
952          goto bail_out;
953       }
954    }
955 #endif /* HAVE_ACL */
956 
957    /* now restore a access and modification time - only for standard attribute */
958    if (!extended){
959       times[0].tv_sec = st.st_atime;
960       times[0].tv_usec = 0;
961       times[1].tv_sec = st.st_mtime;
962       times[1].tv_usec = 0;
963 
964       if (futimesat(attrdirfd, name, times) < 0){
965          berrno be;
966 
967          Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
968          Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
969          rc = bRC_BXATTR_error;
970          goto bail_out;
971       }
972    }
973 
974 bail_out:
975    if (attrfd != 0){
976       close(attrfd);
977    }
978    if (attrdirfd != 0){
979       close(attrdirfd);
980    }
981    return rc;
982 };
983 
984 #endif /* HAVE_XATTR */
985 
986 #endif /* HAVE_SUN_OS */
987