]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/xacl_solaris.c
baculum: Update AUTHORS file
[bacula/bacula] / bacula / src / filed / xacl_solaris.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 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 ACL and 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 "xacl_solaris.h"
31
32 #if defined(HAVE_SUN_OS)
33 /*
34  * Define the supported ACL streams for this OS
35  */
36 static const int os_acl_streams[] = {
37    STREAM_XACL_SOLARIS_POSIX,
38    STREAM_XACL_SOLARIS_NFS4,
39    0
40 };
41
42 static const int os_default_acl_streams[] = {
43    0
44 };
45
46 /*
47  * Define the supported XATTR streams for this OS
48  */
49 static const int os_xattr_streams[] = {
50    STREAM_XACL_SOLARIS_XATTR,
51 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
52    STREAM_XACL_SOLARIS_SYS_XATTR,
53 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
54    0
55 };
56
57
58 static const char *os_xattr_acl_skiplist[] = {
59    NULL
60 };
61
62 static const char *os_xattr_skiplist[] = {
63    "..",
64 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
65    VIEW_READONLY,
66 #endif /* defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED) */
67    NULL
68 };
69
70 /*
71  * OS Specyfic constructor
72  */
73 XACL_Solaris::XACL_Solaris(){
74
75    set_acl_streams(os_acl_streams, os_default_acl_streams);
76    set_xattr_streams(os_xattr_streams);
77    set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist);
78    cache = NULL;
79 };
80
81 /*
82  * OS Specyfic destructor
83  */
84 XACL_Solaris::~XACL_Solaris(){
85
86    delete_xattr_cache();
87 };
88
89 /*
90  * Checks if ACL's are available for a specified file
91  *
92  * in:
93  *    jcr - Job Control Record
94  *    name - specifies the system variable to be queried
95  * out:
96  *    bRC_XACL_ok - check successful, lets setup xacltype variable
97  *    bRC_XACL_error -  in case of error
98  *    bRC_XACL_skip - you should skip all other routine
99  */
100 bRC_XACL XACL_Solaris::check_xacltype (JCR *jcr, int name){
101
102    int rc = 0;
103
104    rc = pathconf(jcr->last_fname, name);
105    switch (rc){
106       case -1: {
107          /* some error check why */
108          berrno be;
109          if (errno == ENOENT){
110             /* file does not exist skip it */
111             return bRC_XACL_skip;
112          } else {
113             Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
114             Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
115             return bRC_XACL_error;
116          }
117       }
118       case 0:
119          /* No support for ACLs */
120          clear_flag(XACL_FLAG_NATIVE);
121          set_content(NULL);
122          return bRC_XACL_skip;
123       default:
124          break;
125    }
126    return bRC_XACL_ok;
127 };
128
129 /*
130  * Perform OS specyfic ACL backup
131  *
132  * in/out - check API at xacl.h
133  */
134 bRC_XACL XACL_Solaris::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
135
136    bRC_XACL rc;
137    int stream;
138
139    /*
140     * See if filesystem supports acls.
141     */
142    rc = check_xacltype(jcr, _PC_ACL_ENABLED);
143    switch (rc){
144       case bRC_XACL_ok:
145          break;
146       case bRC_XACL_skip:
147          return bRC_XACL_ok;
148       default:
149          /* errors */
150          return rc;
151    }
152
153    rc = os_get_acl(jcr, &stream);
154    switch (rc){
155       case bRC_XACL_ok:
156          if (get_content_len() > 0){
157             if (send_acl_stream(jcr, stream) == bRC_XACL_fatal){
158                return bRC_XACL_fatal;
159             }
160          }
161          break;
162       default:
163          return rc;
164    }
165
166    return bRC_XACL_ok;
167 };
168
169 /*
170  * Perform OS specyfic ACL restore
171  *
172  * in/out - check API at xacl.h
173  */
174 bRC_XACL XACL_Solaris::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
175
176    int aclrc = 0;
177
178    switch (stream){
179       case STREAM_UNIX_ACCESS_ACL:
180       case STREAM_XACL_SOLARIS_POSIX:
181       case STREAM_XACL_SOLARIS_NFS4:
182          aclrc = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
183          break;
184       default:
185          return bRC_XACL_error;
186    }
187
188    switch (aclrc){
189       case -1: {
190          berrno be;
191
192          switch (errno){
193             case ENOENT:
194                return bRC_XACL_ok;
195             default:
196                Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
197                Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
198                return bRC_XACL_error;
199          }
200       }
201       case 0:
202          clear_flag(XACL_FLAG_NATIVE);
203          Mmsg(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"), jcr->last_fname);
204          return bRC_XACL_error;
205       default:
206          break;
207    }
208
209    switch (stream){
210       case STREAM_XACL_SOLARIS_POSIX:
211          if ((aclrc & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0){
212             Mmsg(jcr->errmsg, _("Trying to restore POSIX acl on file \"%s\" on filesystem without aclent acl support\n"), jcr->last_fname);
213             return bRC_XACL_error;
214          }
215          break;
216       case STREAM_XACL_SOLARIS_NFS4:
217          if ((aclrc & _ACL_ACE_ENABLED) == 0){
218             Mmsg(jcr->errmsg, _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without ace acl support\n"), jcr->last_fname);
219             return bRC_XACL_error;
220          }
221          break;
222       default:
223          break;
224    }
225
226    return os_set_acl(jcr, stream, content, length);
227 };
228
229 /*
230  * Perform OS specyfic extended attribute backup
231  *
232  * in/out - check API at xacl.h
233  *
234  * The Solaris implementation of XATTR is very, very different then all other "generic" unix implementations,
235  * so the original author of the Bacula XATTR support for Solaris OS decided to totally change the Xattr Stream
236  * content, and we need to follow this design to support previous behavior. The stream consist of a number of
237  * "files" with STREAM_XACL_SOLARIS_XATTR or STREAM_XACL_SOLARIS_SYS_XATTR stream' id. Every singe stream represents
238  * a single attibute. The content is a NULL-terminated array with a following data:
239  *    <xattr name>\0<encoded stat>\0<acl rendered text>\0<xattr data>
240  * when an attribute file has a hardlinked other attributes then a content stream changes a bit into:
241  *    <xattr name>\0<encoded stat>\0<target xattr name>\0
242  * where:
243  *    <xattr name> is an attribute name - a file name in Solaris
244  *    <encoded stat> is a standard file stat struct encoded by Bacula (the same encoding goes with a regular file)
245  *    <acl rendered text> is a Solaris dependent acltotext data
246  *    <xattr data> is the attribute file raw content
247  *    <target xattr name> is a name of the first hardlinked attribute file which a current attribute has to linked to
248  *
249  * The raw content of the attribute is copied into memory before send to the SD and for a very large attribute
250  * data can allocate a large amount of additional memory. In most cases it should not be a problem because most
251  * xattrs should has a few /hundred/ bytes in size. This is the same behavior as in previous implementation.
252  */
253 bRC_XACL XACL_Solaris::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
254
255    bRC_XACL rc;
256    POOLMEM *xlist = NULL;
257    uint32_t xlen;
258    char *name;
259    char *lnkname;
260    uint32_t name_len;
261    POOLMEM *value = NULL;
262    uint32_t value_len;
263    char * xacltext;
264    uint32_t xacltext_len;
265    POOLMEM *data = NULL;
266    bool skip;
267    struct stat st;
268    char attribs[MAXSTRING];
269    int stream;
270    int attrfd;
271    int len;
272
273    /* sanity check of input variables */
274    if (jcr == NULL || ff_pkt == NULL){
275       return bRC_XACL_inval;
276    }
277
278    /* check if extended/extensible attributes are present */
279    if (pathconf(jcr->last_fname, _PC_XATTR_EXISTS) > 0){
280       /* xlist is allocated as POOLMEM by os_get_xattr_names */
281       rc = os_get_xattr_names(jcr, &xlist, &xlen);
282       switch (rc){
283          case bRC_XACL_ok:
284             /* it's ok, so go further */
285             break;
286          case bRC_XACL_skip:
287          case bRC_XACL_cont:
288             /* no xattr available, so skip rest of it */
289             return bRC_XACL_ok;
290          default:
291             return rc;
292       }
293
294       data = get_pool_memory(PM_BSOCK);
295       /* follow the list of xattr names and get the values */
296       for (name = xlist; (name - xlist) + 1 < xlen; name = strchr(name, '\0') + 1){
297          name_len = strlen(name);
298          /* skip read-only or other unused attribute names */
299          skip =  check_xattr_skiplists(jcr, ff_pkt, name);
300          if (skip || name_len == 0){
301             Dmsg1(100, "Skipping xattr named \"%s\"\n", name);
302             continue;
303          }
304          /* set a correct stream */
305          stream = STREAM_XACL_SOLARIS_XATTR;
306
307 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
308          /* check for system attributes name */
309          if (bstrcmp(name, VIEW_READWRITE)){
310             stream = STREAM_XACL_SOLARIS_SYS_XATTR;
311          }
312 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
313
314          /* open an attribute descriptor, it will be used for backup */
315          attrfd = attropen(jcr->last_fname, name, O_RDONLY);
316
317          /* get the stat of the attribute */
318          if (fstat(attrfd, &st) < 0){
319             berrno be;
320
321             switch (errno){
322                case ENOENT:
323                   rc = bRC_XACL_ok;
324                   goto bailout;
325                default:
326                   Mmsg3(jcr->errmsg, _("Unable to get status on xattr \"%s\" on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
327                   Dmsg3(100, "fstatat of xattr %s on \"%s\" failed: ERR=%s\n", name, jcr->last_fname, be.bstrerror());
328                   rc = bRC_XACL_error;
329                   goto bailout;
330             }
331          }
332
333          /* we have a struct stat of the attribute so encode it to the buffer */
334          encode_stat(attribs, &st, sizeof(st), st.st_ino, stream);
335
336          /* get xattr acl data, but only when it is not trivial acls */
337          rc = os_get_xattr_acl(jcr, attrfd, &xacltext);
338          if (rc != bRC_XACL_ok){
339             goto bailout;
340          }
341          xacltext_len = strlen(xacltext);
342
343          /*
344           * Solaris support only S_IFREG and S_IFDIR as an attribute file type, no other types are supported
345           * the previous Solaris xattr implementation in Bacula had an unverified and untested code for other
346           * types of attribute files which was a nonsense and unnecessarily complicate the code. We decided
347           * to remove unsupported code. To check if the current Solaris version support for xattr was extended
348           * simply verify a man fsattr(5) for it.
349           */
350          switch (st.st_mode & S_IFMT){
351             case S_IFREG:
352                /* check for hardlinked attributes which solaris support */
353                if (st.st_nlink > 1){
354                   /* search for already saved file of the same inode number */
355                   lnkname = find_xattr_cache(jcr, st.st_ino, name);
356                   if (lnkname != NULL){
357                      /* found a previous saved file, link to it and render xattr data for hardlinked attribute */
358                      len = bsnprintf(data, sizeof_pool_memory(data), "%s%c%s%c%s%c", name, 0, attribs, 0, lnkname, 0);
359                      set_content(data, len);
360                      /* content is ready */
361                      break;
362                   }
363                }
364                /* value is allocated as POOLMEM by os_get_xattr_value */
365                rc = os_get_xattr_value(jcr, name, &value, &value_len);
366                switch (rc){
367                   case bRC_XACL_ok:
368                      /* it's ok, so go further */
369                      break;
370                   case bRC_XACL_skip:
371                      /* no xattr available, so skip rest of it */
372                      rc = bRC_XACL_ok;
373                      goto bailout;
374                   default:
375                      /* error / fatal */
376                      goto bailout;
377                }
378                /* save xattr info */
379                len = bsnprintf(data, sizeof_pool_memory(data), "%s%c%s%c%s%c", name, 0, attribs, 0, (xacltext) ? xacltext : "", 0);
380                /* append value data to the end of the xattr info */
381                check_pool_memory_size(data, len + value_len);
382                memcpy(data + len, value, value_len);
383                set_content(data,len + value_len);
384                free_pool_memory(value);
385                value = NULL;
386                break;
387             case S_IFDIR:
388                /* save xattr info */
389                len = bsnprintf(data, sizeof_pool_memory(data), "%s%c%s%c%s%c", name, 0, attribs, 0, (xacltext) ? xacltext : "", 0);
390                set_content(data);
391             default:
392                Mmsg3(jcr->errmsg, _("Unsupported extended attribute type: %i for \"%s\" on file \"%s\"\n"), st.st_mode & S_IFMT, name, jcr->last_fname);
393                Dmsg3(100, "Unsupported extended attribute type: %i for \"%s\" on file \"%s\"\n", st.st_mode & S_IFMT, name, jcr->last_fname);
394                rc = bRC_XACL_error;
395                goto bailout;
396          }
397          /* send stream to the sd */
398          rc = send_xattr_stream(jcr, stream);
399          if (rc != bRC_XACL_ok){
400             Mmsg2(jcr->errmsg, _("Failed to send extended attribute \"%s\" on file \"%s\"\n"), name, jcr->last_fname);
401             Dmsg2(100, "Failed to send extended attribute \"%s\" on file \"%s\"\n", name, jcr->last_fname);
402             goto bailout;
403          }
404       }
405
406 bailout:
407       /* free allocated data: xlist, value (if not freed), data, etc. */
408       free_pool_memory(data);
409       if (value != NULL){
410          free_pool_memory(value);
411       }
412       if (xlist != NULL){
413          free_pool_memory(xlist);
414       }
415       /* this is a cache for a particular file, so no needed after backup of this file */
416       delete_xattr_cache();
417
418       return rc;
419    }
420    return bRC_XACL_ok;
421 };
422
423 /*
424  * XACL_Solaris cache is a simple linked list cache of inode number and names used to handle
425  * xattr hard linked data. The function is searching for cached entry. When not found it append
426  * entry to the cache.
427  * in:
428  *    jcr - Job Control Record (well, it is not used here)
429  *    ino - inode number to compare/search for
430  *    name - the name of the current attribute
431  * out:
432  *    NULL - when entry not found in cache and new entry was added
433  *    <str> - a name of the linked entry
434  */
435 inline char * XACL_Solaris::find_xattr_cache(JCR *jcr, ino_t ino, char * name){
436
437    XACL_Solaris_Cache *entry;
438
439    if (cache != NULL){
440       foreach_alist(entry, cache){
441          if (entry && entry->inode == ino){
442             /* found in cache, return name */
443             return entry->name;
444          }
445       }
446    } else {
447       cache = New (alist(10, not_owned_by_alist));
448    }
449    /* not found, so add this one to the cache */
450    entry = (XACL_Solaris_Cache*) malloc (sizeof(XACL_Solaris_Cache));
451    entry->inode = ino;
452    entry->name = name;
453    cache->append(entry);
454    return NULL;
455 }
456
457 /*
458  * The function deletes a cache
459  * in/out - void
460  */
461 inline void XACL_Solaris::delete_xattr_cache(){
462
463    XACL_Solaris_Cache *entry;
464
465    if (cache != NULL){
466       foreach_alist(entry, cache){
467          free(entry);
468       }
469       delete cache;
470       cache = NULL;
471    }
472 }
473
474 /*
475  * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS.
476  *
477  * The way Solaris xattr support is designed in Bacula we will have a single attribute restore
478  * with every call to this function. So multiple attributes are restored with multiple calls.
479  *
480  * in/out - check API at xacl.h
481  */
482 bRC_XACL XACL_Solaris::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){
483
484    bRC_XACL rc = bRC_XACL_error;
485    bool extended = false;
486
487    /* check input data */
488    if (jcr == NULL || content == NULL){
489       return bRC_XACL_inval;
490    }
491
492    /* First make sure we can restore xattr on the filesystem */
493    switch (stream){
494 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
495       case STREAM_XACL_SOLARIS_SYS_XATTR:
496          if (pathconf(jcr->last_fname, _PC_SATTR_ENABLED) <= 0){
497             Mmsg(jcr->errmsg, _("Failed to restore extensible attributes on file \"%s\"\n"), jcr->last_fname);
498             Dmsg1(100, "Unable to restore extensible attributes on file \"%s\", filesystem doesn't support this\n", jcr->last_fname);
499             goto bail_out;
500          }
501          extended = true;
502          break;
503 #endif
504       case STREAM_XACL_SOLARIS_XATTR:
505          if (pathconf(jcr->last_fname, _PC_XATTR_ENABLED) <= 0){
506             Mmsg(jcr->errmsg, _("Failed to restore extended attributes on file \"%s\"\n"), jcr->last_fname);
507             Dmsg1(100, "Unable to restore extended attributes on file \"%s\", filesystem doesn't support this\n", jcr->last_fname);
508             goto bail_out;
509          }
510          break;
511       default:
512          goto bail_out;
513    }
514
515    rc = os_set_xattr(jcr, extended, content, length);
516
517 bail_out:
518    return rc;
519 };
520
521 /*
522  * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer
523  *
524  * in/out - check API at xacl.h
525  */
526 bRC_XACL XACL_Solaris::os_get_acl(JCR *jcr, int *stream){
527
528    int flags;
529    acl_t *aclp;
530    char *acl_text;
531    bRC_XACL rc = bRC_XACL_fatal;
532
533    if (!stream){
534       return bRC_XACL_fatal;
535    }
536
537    if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0){
538       /* we've got some error */
539       berrno be;
540       switch (errno){
541       case ENOENT:
542          /* file does not exist */
543          return bRC_XACL_ok;
544       default:
545          Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"), jcr->last_fname, acl_strerror(errno));
546          Dmsg2(100, "acl_get error file=%s ERR=%s\n", jcr->last_fname, acl_strerror(errno));
547          return bRC_XACL_error;
548       }
549    }
550
551    if (!aclp){
552       /*
553        * The ACLs simply reflect the (already known) standard permissions
554        * So we don't send an ACL stream to the SD.
555        */
556       set_content(NULL);
557       return bRC_XACL_ok;
558    }
559
560 #if defined(ACL_SID_FMT)
561    /* new format flag added in newer Solaris versions */
562    flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
563 #else
564    flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
565 #endif /* ACL_SID_FMT */
566
567    if ((acl_text = acl_totext(aclp, flags)) != NULL){
568       set_content(acl_text);
569       actuallyfree(acl_text);
570
571       switch (acl_type(aclp)){
572          case ACLENT_T:
573             *stream = STREAM_XACL_SOLARIS_POSIX;
574             break;
575          case ACE_T:
576             *stream = STREAM_XACL_SOLARIS_NFS4;
577             break;
578          default:
579             rc = bRC_XACL_error;
580             break;
581       }
582
583       acl_free(aclp);
584    }
585    return rc;
586 };
587
588 /*
589  * Low level OS specyfic runtime to get ACL on XATTR. The ACL data is set in supplied buffer
590  *
591  * in:
592  *    jcr - Job Control Record
593  *    fd - an opened file descriptor of the saved attribute
594  *    buffer - a pointer to the memory buffer where we will render an acl text
595  * out:
596  *    bRC_XACL_ok - backup acl for extended attribute finish without problems
597  *    bRC_XACL_error - backup acl unsuccessful
598  *    bRC_XACL_inval - input variables are invalid (null)
599  *
600  */
601 bRC_XACL XACL_Solaris::os_get_xattr_acl(JCR *jcr, int fd, char **buffer){
602
603 // a function is valid only when Bacula have a support for ACL
604 #ifdef HAVE_ACL
605    bRC_XACL rc = bRC_XACL_error;
606
607    /* sanity check of input variables */
608    if (jcr == NULL || buffer == NULL || *buffer == NULL || fd < 0){
609       return bRC_XACL_inval;
610    }
611
612 #ifdef HAVE_EXTENDED_ACL
613
614    int flags;
615    acl_t *aclp = NULL;
616
617    /* check if an attribute has acl on it which we can save */
618    if (fpathconf(fd, _PC_ACL_ENABLED) > 0){
619       /* check for non trivial acl on the file */
620       if (facl_get(fd, ACL_NO_TRIVIAL, &aclp) != 0){
621          berrno be;
622
623          switch (errno){
624          case ENOENT:
625             rc = bRC_XACL_ok;
626             goto bail_out;
627          default:
628             Mmsg2(jcr->errmsg, _("Unable to get xattr acl on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
629             Dmsg2(100, "facl_get/acl_get of xattr on \"%s\" failed: ERR=%s\n", jcr->last_fname, be.bstrerror());
630             goto bail_out;
631          }
632       }
633
634       if (aclp != NULL){
635 #if defined(ACL_SID_FMT)
636          /* New format flag added in newer Solaris versions. */
637          flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
638 #else
639          flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
640 #endif /* ACL_SID_FMT */
641
642          *buffer = acl_totext(aclp, flags);
643          acl_free(aclp);
644       } else {
645          *buffer = NULL;
646       }
647    } else {
648       *buffer = NULL;
649    }
650    rc = bRC_XACL_ok;
651 bail_out:
652
653 #else /* !HAVE_EXTENDED_ACL */
654
655    int n;
656    aclent_t *acls = NULL;
657
658    /* See if this attribute has an ACL */
659    if (fd != -1){
660       n = facl(fd, GETACLCNT, 0, NULL);
661    } else {
662       n = acl(attrname, GETACLCNT, 0, NULL);
663    }
664
665    if (n >= MIN_ACL_ENTRIES){
666       acls = (aclent_t *)malloc(n * sizeof(aclent_t));
667       if ((fd != -1 && facl(fd, GETACL, n, acls) != n) ||
668           acl(attrname, GETACL, n, acls) != n){
669          berrno be;
670
671          switch (errno){
672          case ENOENT:
673             free(acls);
674             retval = bRC_XACL_ok;
675             goto bail_out;
676          default:
677             Mmsg3(jcr->errmsg, _("Unable to get acl on xattr %s on file \"%s\": ERR=%s\n"), attrname, jcr->last_fname, be.bstrerror());
678             Dmsg3(100, "facl/acl of xattr %s on \"%s\" failed: ERR=%s\n", attrname, jcr->last_fname, be.bstrerror());
679             free(acls);
680             goto bail_out;
681          }
682       }
683
684       /* See if there is a non trivial acl on the file. */
685       if (!acl_is_trivial(n, acls)){
686          if ((*acl_text = acltotext(acls, n)) == NULL){
687             berrno be;
688
689             Mmsg3(jcr->errmsg, _("Unable to get acl text on xattr %s on file \"%s\": ERR=%s\n"), attrname, jcr->last_fname, be.bstrerror());
690             Dmsg3(100, "acltotext of xattr %s on \"%s\" failed: ERR=%s\n", attrname, jcr->last_fname, be.bstrerror());
691             free(acls);
692             goto bail_out;
693          }
694       } else {
695          *buffer = NULL;
696       }
697
698      free(acls);
699    } else {
700       *buffer = NULL;
701    }
702    rc = bRC_XACL_ok;
703 #endif /* HAVE_EXTENDED_ACL */
704    return rc;
705 #else /* HAVE_ACL */
706    return bRC_XACL_ok;
707 #endif /* HAVE_ACL */
708 }
709
710 /*
711  * Low level OS specyfic runtime to set ACL on XATTR. The ACL data is set from supplied text
712  *
713  * in:
714  *    jcr - Job Control Record
715  *    fd - an opened file descriptor of the restored attribute
716  *    buffer - a pointer to the memory buffer where we will render an acl text
717  * out:
718  *    bRC_XACL_ok - backup acl for extended attribute finish without problems
719  *    bRC_XACL_inval - input variables are invalid (null)
720  *
721  */
722 bRC_XACL XACL_Solaris::os_set_xattr_acl(JCR *jcr, int fd, char *name, char *acltext){
723
724 // a function is valid only when Bacula have a support for ACL
725 #ifdef HAVE_ACL
726
727    bRC_XACL rc = bRC_XACL_error;
728
729    /* sanity check of input variables */
730    if (jcr == NULL || name == NULL || acltext == NULL || fd < 0){
731       return bRC_XACL_inval;
732    }
733
734 #ifdef HAVE_EXTENDED_ACL
735
736    int error;
737    acl_t *aclp = NULL;
738
739    if ((error = acl_fromtext(acltext, &aclp)) != 0){
740       Mmsg1(jcr->errmsg, _("Unable to convert acl from text on file \"%s\"\n"), jcr->last_fname);
741       return bRC_XACL_error;
742    }
743
744    if (fd != -1 && facl_set(fd, aclp) != 0){
745       berrno be;
746
747       Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
748       Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
749       rc = bRC_XACL_error;
750    }
751
752 bail_out:
753    if (aclp){
754       acl_free(aclp);
755    }
756
757 #else /* !HAVE_EXTENDED_ACL */
758
759    int n;
760    aclent_t *acls = NULL;
761
762    acls = aclfromtext(acltext, &n);
763    if (acls){
764       if (fd != -1 && facl(fd, SETACL, n, acls) != 0){
765          berrno be;
766
767          Mmsg3(jcr->errmsg, _("Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
768          Dmsg3(100, "Unable to restore acl of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
769          rc = bRC_XACL_error;
770       }
771       free(acls);
772    }
773
774 #endif /* HAVE_EXTENDED_ACL */
775
776    return rc;
777 #else /* HAVE_ACL */
778    return bRC_XACL_ok;
779 #endif /* HAVE_ACL */
780 };
781
782 /*
783  * Low level OS specyfic runtime to set ACL data on file
784  *
785  * in/out - check API at xacl.h
786  */
787 bRC_XACL XACL_Solaris::os_set_acl(JCR *jcr, int stream, char *content, uint32_t length){
788
789    int rc;
790    acl_t *aclp;
791
792    if ((rc = acl_fromtext(content, &aclp)) != 0){
793       Mmsg2(jcr->errmsg, _("acl_fromtext error on file \"%s\": ERR=%s\n"), jcr->last_fname, acl_strerror(rc));
794       Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, acl_strerror(rc));
795       return bRC_XACL_error;
796    }
797
798    switch (stream){
799       case STREAM_XACL_SOLARIS_POSIX:
800          if (acl_type(aclp) != ACLENT_T){
801             Mmsg(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"), jcr->last_fname);
802             return bRC_XACL_error;
803          }
804          break;
805       case STREAM_XACL_SOLARIS_NFS4:
806          if (acl_type(aclp) != ACE_T){
807             Mmsg(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"), jcr->last_fname);
808             return bRC_XACL_error;
809          }
810          break;
811       default:
812          break;
813    }
814
815    if ((rc = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK){
816       switch (errno){
817          case ENOENT:
818             acl_free(aclp);
819             return bRC_XACL_ok;
820          default:
821             Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"), jcr->last_fname, acl_strerror(rc));
822             Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, acl_strerror(rc));
823             acl_free(aclp);
824             return bRC_XACL_error;
825       }
826    }
827
828    acl_free(aclp);
829    return bRC_XACL_ok;
830 };
831
832 /*
833  * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer.
834  * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
835  * when not needed.
836  *
837  * in/out - check API at xacl.h
838  */
839 bRC_XACL XACL_Solaris::os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){
840
841    int xattrdfd;
842    DIR *dirp;
843    struct dirent *dp;
844
845    int len;
846    int slen;
847    POOLMEM * list;
848    char * p;
849
850    /* check input data */
851    if (jcr == NULL || xlen == NULL || pxlist == NULL){
852       return bRC_XACL_inval;
853    }
854
855    /* Open the xattr stream on file */
856    if ((xattrdfd = attropen(jcr->last_fname, ".", O_RDONLY)) < 0){
857       berrno be;
858
859       switch (errno){
860          case ENOENT:
861             /* no file available, skip it */
862             return bRC_XACL_skip;
863          case EINVAL:
864             /* no xattr supported on file skip it */
865             return bRC_XACL_skip;
866          default:
867             Mmsg2(jcr->errmsg, _("Unable to open xattr on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
868             Dmsg2(100, "Unable to open xattr on file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
869             return bRC_XACL_error;
870       }
871    }
872
873    /* open an extended file directory to read all xattr names */
874    if ((dirp = fdopendir(xattrdfd)) == (DIR *)NULL){
875       berrno be;
876
877       Mmsg2(jcr->errmsg, _("Unable to list the xattr on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
878       Dmsg3(100, "Unable to fdopendir xattr on file \"%s\" using fd %d: ERR=%s\n", jcr->last_fname, xattrdfd, be.bstrerror());
879       close(xattrdfd);
880       return bRC_XACL_error;
881    }
882
883    /*
884     * allocate memory for the extented attribute list
885     * default size is a 4k for PM_BSOCK, which should be sufficient in most cases
886     */
887    list = p = get_pool_memory(PM_BSOCK);
888    memset(list, 0, sizeof_pool_memory(list));
889    len = 0;
890
891    /* read all directory entries as a xattr names */
892    while ((dp = readdir(dirp)) != NULL){
893
894       /* skip '..' name as it a file we backup */
895       if (bstrcmp(dp->d_name, "..") == 0){
896          continue;
897       }
898
899 #if defined(HAVE_SYS_NVPAIR_H) && defined(_PC_SATTR_ENABLED)
900       /* skip a read only attributes which we cant restore later */
901       if (bstrcmp(dp->d_name, VIEW_READONLY)){
902          Dmsg2(400, "Skipping readonly extensible attributes %s on file \"%s\"\n", dp->d_name, jcr->last_fname);
903          continue;
904       }
905 #endif /* HAVE_SYS_NVPAIR_H && _PC_SATTR_ENABLED */
906
907       /* compute a buffer length = string length and nul char */
908       slen = strlen (dp->d_name);
909       len += slen + 1;
910       list = check_pool_memory_size(list, len);
911
912       /* copy the name into a list */
913       bstrncpy(p, dp->d_name, slen);
914       p[slen] = '\0';
915       p += slen + 1;
916    }
917    if (closedir(dirp) < 0){
918       berrno be;
919
920       Mmsg2(jcr->errmsg, _("Unable to close xattr list on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
921       Dmsg2(100, "Unable to close xattr list on file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
922       return bRC_XACL_error;
923    }
924
925    *pxlist = list;
926    *xlen = len;
927
928    return bRC_XACL_ok;
929 };
930
931 /*
932  * Return a value of the requested attribute name and a length of the allocated buffer.
933  * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
934  * when not needed.
935  *
936  * in/out - check API at xacl.h
937  */
938 bRC_XACL XACL_Solaris::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){
939
940    int xattrfd;
941    int len;
942    POOLMEM * value;
943    struct stat st;
944
945    /* check input data */
946    if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){
947       return bRC_XACL_inval;
948    }
949
950    /* Open the xattr on file */
951    if ((xattrfd = attropen(jcr->last_fname, name, O_RDONLY)) < 0){
952       berrno be;
953
954       switch (errno){
955          case ENOENT:
956             /* no file available, skip it */
957             return bRC_XACL_skip;
958          case EINVAL:
959             /* no xattr supported on file skip it */
960             return bRC_XACL_skip;
961          default:
962             Mmsg2(jcr->errmsg, _("Unable to open xattr on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
963             Dmsg2(100, "Unable to open xattr on file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
964             return bRC_XACL_error;
965       }
966    }
967
968    /* get some info about extended attribute */
969    if (fstat(xattrfd, &st) < 0){
970       berrno be;
971
972       switch (errno){
973          case ENOENT:
974             /* no file available, skip it */
975             return bRC_XACL_skip;
976          default:
977             Mmsg3(jcr->errmsg, _("Unable to stat xattr \"%s\" on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
978             Dmsg3(100, "Unable to stat xattr \"%s\" on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
979             return bRC_XACL_error;
980       }
981    }
982
983    /* default empty value */
984    value = NULL;
985    len = 0;
986
987    /* only a file has a data/value we should care about */
988    if ((st.st_mode & S_IFMT) != S_IFDIR){
989       /* get size of the attribute data/value */
990       len = lseek(xattrfd, 0, SEEK_END);
991       lseek(xattrfd, 0, SEEK_SET);
992    }
993    if (len > 0){
994       /*
995        * allocate memory for the extented attribute value
996        * default size is a 256B for PM_MESSAGE, so we need to check required size
997        */
998       value = get_pool_memory(PM_MESSAGE);
999       value = check_pool_memory_size(value, len);
1000       memset(value, 0, len);
1001       /* read teh data */
1002       read (xattrfd, value, len);
1003       close(xattrfd);
1004    }
1005
1006    /* setup return data */
1007    *pvalue = value;
1008    *plen = len;
1009    return bRC_XACL_ok;
1010 };
1011
1012 /*
1013  * Low level OS specyfic runtime to set extended attribute on file
1014  *
1015  * in/out - check API at xacl.h
1016  */
1017 bRC_XACL XACL_Solaris::os_set_xattr (JCR *jcr, bool extended, char *content, uint32_t length){
1018
1019    char *bp = content + 1;    /* original code saves attribute name with '/' */
1020    char *name;
1021    char *attribs;
1022    char *acltext;
1023    char *lntarget;
1024    int attrfd = 0;
1025    int attrdirfd = 0;
1026    int cnt;
1027    int len;
1028    int inum;
1029    struct stat st;
1030    struct timeval times[2];
1031    bRC_XACL rc = bRC_XACL_ok;
1032
1033    /* check input data */
1034    if (jcr == NULL || content == NULL){
1035       return bRC_XACL_inval;
1036    }
1037    /*
1038     * Parse content stream and extract valuable data.
1039     * STD/EXT: <xattr name>\0<encoded stat>\0<acl rendered text>\0<xattr data>
1040     * LNK:     <xattr name>\0<encoded stat>\0<target xattr name>\0
1041     */
1042    /* attribute name and length */
1043    name = bp;
1044    len = strlen (bp);
1045    bp += len + 1;
1046
1047    /* attribute encoded stat */
1048    attribs = bp;
1049    len = strlen (bp);
1050    bp += len + 1;
1051    /* decode it */
1052    decode_stat(attribs, &st, sizeof(st), &inum);
1053
1054    /* acltext and link target name goes here */
1055    acltext = lntarget = bp;
1056    len = strlen (bp);
1057    /* now 'bp' should have a xattr data */
1058    bp += len + 1;
1059
1060    /*
1061     * Open the xattr on which to restore the xattrs read-only.
1062     */
1063    if ((attrdirfd = attropen(jcr->last_fname, ".", O_RDONLY)) < 0){
1064       berrno be;
1065
1066       Mmsg2(jcr->errmsg, _("Unable to open file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
1067       Dmsg2(100, "Unable to open file \"%s\": ERR=%s\n", jcr->last_fname, be.bstrerror());
1068       rc = bRC_XACL_error;
1069       goto bail_out;
1070    }
1071
1072    switch (st.st_mode & S_IFMT){
1073       case S_IFREG:
1074          if (inum != 0){
1075             /* it is a linked attribute, perform a link operation */
1076             unlinkat(attrdirfd, name, 0);
1077             if (link(lntarget, name) < 0){
1078                berrno be;
1079
1080                Mmsg4(jcr->errmsg, _("Unable to link xattr %s to %s on file \"%s\": ERR=%s\n"), name, lntarget, jcr->last_fname, be.bstrerror());
1081                Dmsg4(100, "Unable to link xattr %s to %s on file \"%s\": ERR=%s\n", name, lntarget, jcr->last_fname, be.bstrerror());
1082                rc = bRC_XACL_error;
1083                goto bail_out;
1084             }
1085             goto bail_out;
1086          } else {
1087             if (!extended){
1088                unlinkat(attrdirfd, name, 0);
1089             }
1090             if ((attrfd = openat(attrdirfd, name, O_CREAT|O_RDWR)) < 0){
1091                berrno be;
1092
1093                Mmsg3(jcr->errmsg, _("Unable to open attribute \"%s\" at file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
1094                Dmsg3(100, "Unable to open attribute \"%s\" at file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
1095                rc = bRC_XACL_error;
1096                goto bail_out;
1097             }
1098             /* restore any data if are available */
1099             if (st.st_size > 0){
1100                cnt = write (attrfd, bp, length - (bp - content) );
1101                if (cnt < 0){
1102                   berrno be;
1103
1104                   Mmsg3(jcr->errmsg, _("Unable to restore data of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
1105                   Dmsg3(100, "Unable to restore data of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
1106                   rc = bRC_XACL_error;
1107                   goto bail_out;
1108                }
1109             }
1110          }
1111          break;
1112       case S_IFDIR:
1113          /* if it is a current dir ob file then we can restore acl data only */
1114          if (bstrcmp(name, ".")){
1115             break;
1116          }
1117       default:
1118          Mmsg2(jcr->errmsg, _("Unsupported xattr type %s on file \"%s\"\n"), name, jcr->last_fname);
1119          Dmsg2(100, "Unsupported xattr type %s on file \"%s\"\n", name, jcr->last_fname);
1120          goto bail_out;
1121    }
1122
1123    /* file data restored, so setup permissions and acl data */
1124    if (!extended){
1125       if (fchownat(attrdirfd, name, st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW) < 0){
1126          berrno be;
1127
1128          switch (errno){
1129             case EINVAL:
1130             case ENOENT:
1131                break;
1132             default:
1133                Mmsg3(jcr->errmsg, _("Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
1134                Dmsg3(100, "Unable to restore owner of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
1135                rc = bRC_XACL_error;
1136          }
1137          goto bail_out;
1138       }
1139    }
1140
1141 #ifdef HAVE_ACL
1142    if (strlen(acltext)){
1143       rc = os_set_xattr_acl(jcr, attrfd, name, acltext);
1144       if (rc != bRC_XACL_ok){
1145          goto bail_out;
1146       }
1147    }
1148 #endif /* HAVE_ACL */
1149
1150    /* now restore a access and modification time - only for standard attribute */
1151    if (!extended){
1152       times[0].tv_sec = st.st_atime;
1153       times[0].tv_usec = 0;
1154       times[1].tv_sec = st.st_mtime;
1155       times[1].tv_usec = 0;
1156
1157       if (futimesat(attrdirfd, name, times) < 0){
1158          berrno be;
1159
1160          Mmsg3(jcr->errmsg, _("Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n"), name, jcr->last_fname, be.bstrerror());
1161          Dmsg3(100, "Unable to restore filetimes of xattr %s on file \"%s\": ERR=%s\n", name, jcr->last_fname, be.bstrerror());
1162          rc = bRC_XACL_error;
1163          goto bail_out;
1164       }
1165    }
1166
1167 bail_out:
1168    if (attrfd != 0){
1169       close(attrfd);
1170    }
1171    if (attrdirfd != 0){
1172       close(attrdirfd);
1173    }
1174    return rc;
1175 };
1176
1177 #endif /* HAVE_SUN_OS */