]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/xacl_freebsd.c
baculum: Improve slow reloading config resource list
[bacula/bacula] / bacula / src / filed / xacl_freebsd.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_freebsd.h"
31
32 #if defined(HAVE_FREEBSD_OS)
33 /*
34  * Define the supported ACL streams for this OS
35  */
36 static const int os_acl_streams[] = {
37    STREAM_XACL_FREEBSD_ACCESS,
38    STREAM_XACL_FREEBSD_NFS4,
39    0
40 };
41
42 static const int os_default_acl_streams[] = {
43    STREAM_XACL_FREEBSD_DEFAULT,
44    0
45 };
46
47 /*
48  * Define the supported XATTR streams for this OS
49  */
50 static const int os_xattr_streams[] = {
51    STREAM_XACL_FREEBSD_XATTR,
52    0
53 };
54
55 static const int os_xattr_namespaces[] = {
56    EXTATTR_NAMESPACE_USER,
57    EXTATTR_NAMESPACE_SYSTEM,
58    -1
59 };
60
61 static const char *os_xattr_acl_skiplist[] = {
62    "system.posix1e.acl_access",
63    "system.posix1e.acl_default",
64    "system.nfs4.acl",
65    NULL
66 };
67
68 static const char *os_xattr_skiplist[] = {
69    NULL
70 };
71
72 /*
73  * OS Specyfic constructor
74  */
75 XACL_FreeBSD::XACL_FreeBSD(){
76
77    set_acl_streams(os_acl_streams, os_default_acl_streams);
78    set_xattr_streams(os_xattr_streams);
79    set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist);
80 };
81
82 /*
83  * Translates Bacula internal acl representation into
84  * acl type
85  *
86  * in:
87  *    xacltype - internal Bacula acl type (XACL_type)
88  * out:
89  *    acl_type_t - os dependent acl type
90  *    when failed - ACL_TYPE_NONE is returned
91  */
92 acl_type_t XACL_FreeBSD::get_acltype(XACL_type xacltype){
93
94    acl_type_t acltype;
95
96    switch (xacltype){
97 #ifdef HAVE_ACL_TYPE_NFS4
98       case XACL_TYPE_NFS4:
99          acltype = ACL_TYPE_NFS4;
100          break;
101 #endif
102       case XACL_TYPE_ACCESS:
103          acltype = ACL_TYPE_ACCESS;
104          break;
105       case XACL_TYPE_DEFAULT:
106          acltype = ACL_TYPE_DEFAULT;
107          break;
108       default:
109          /*
110           * sanity check for acl's not supported by OS
111           */
112          acltype = (acl_type_t)ACL_TYPE_NONE;
113          break;
114    }
115    return acltype;
116 };
117
118 /*
119  * Counts a number of acl entries
120  *
121  * in:
122  *    acl - acl object
123  * out:
124  *    int - number of entries in acl object
125  *    when no acl entry available or any error then return zero '0'
126  */
127 int XACL_FreeBSD::acl_nrentries(acl_t acl){
128
129    int nr = 0;
130    acl_entry_t aclentry;
131    int rc;
132
133    rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
134    while (rc == 1){
135       nr++;
136       rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
137    }
138
139    return nr;
140 };
141
142 /*
143  * Checks if acl is simple.
144  *
145  * acl is simple if it has only the following entries:
146  * "user::",
147  * "group::",
148  * "other::"
149  *
150  * in:
151  *    acl - acl object
152  * out:
153  *    true - when acl object is simple
154  *    false - when acl object is not simple
155  */
156 bool XACL_FreeBSD::acl_issimple(acl_t acl){
157
158    acl_entry_t aclentry;
159    acl_tag_t acltag;
160    int rc;
161
162    rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
163    while (rc == 1){
164       if (acl_get_tag_type(aclentry, &acltag) < 0){
165          return true;
166       }
167       /*
168        * Check for ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER to find out.
169        */
170       if (acltag != ACL_USER_OBJ &&
171           acltag != ACL_GROUP_OBJ &&
172           acltag != ACL_OTHER){
173          return false;
174       }
175       rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
176    }
177    return true;
178 };
179
180 /*
181  * Checks if ACL's are available for a specified file
182  *
183  * in:
184  *    jcr - Job Control Record
185  *    name - specifies the system variable to be queried
186  * out:
187  *    bRC_XACL_ok - check successful, lets setup xacltype variable
188  *    bRC_XACL_error -  in case of error
189  *    bRC_XACL_skip - you should skip all other routine
190  *    bRC_XACL_cont - you need to continue your routine
191  */
192 bRC_XACL XACL_FreeBSD::check_xacltype (JCR *jcr, int name){
193
194    int aclrc = 0;
195
196    aclrc = pathconf(jcr->last_fname, name);
197    switch (aclrc){
198       case -1: {
199          /* some error check why */
200          berrno be;
201          if (errno == ENOENT){
202             /* file does not exist skip it */
203             return bRC_XACL_skip;
204          } else {
205             Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
206             Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
207             return bRC_XACL_error;
208          }
209       }
210       case 0:
211          /* continue the routine */
212          return bRC_XACL_cont;
213       default:
214          break;
215    }
216    return bRC_XACL_ok;
217 };
218
219 /*
220  * Perform OS specyfic ACL backup
221  *
222  * in/out - check API at xacl.h
223  */
224 bRC_XACL XACL_FreeBSD::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
225
226    bRC_XACL rc;
227    XACL_type xacltype = XACL_TYPE_NONE;
228
229 #if defined(_PC_ACL_NFS4)
230    /*
231     * Check if filesystem supports NFS4 acls.
232     */
233    rc = check_xacltype(jcr, _PC_ACL_NFS4);
234    switch (rc){
235       case bRC_XACL_ok:
236          xacltype = XACL_TYPE_NFS4;
237          break;
238       case bRC_XACL_skip:
239          return bRC_XACL_ok;
240       case bRC_XACL_cont:
241          break;
242       default:
243          /* errors */
244          return rc;
245    }
246 #endif
247    if (xacltype == XACL_TYPE_NONE){
248       /*
249        * Check if filesystem supports POSIX acls.
250        */
251       rc = check_xacltype(jcr, _PC_ACL_EXTENDED);
252       switch (rc){
253          case bRC_XACL_ok:
254             xacltype = XACL_TYPE_ACCESS;
255             break;
256          case bRC_XACL_skip:
257             return bRC_XACL_ok;
258          case bRC_XACL_cont:
259             break;
260          default:
261             /* errors */
262             return rc;
263       }
264    }
265
266    /* no ACL's available for file, so skip this filesystem */
267    if (xacltype == XACL_TYPE_NONE){
268       clear_flag(XACL_FLAG_NATIVE);
269       /*
270        * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
271        * but it is working, hehe :)
272        * you may ask why it is working? it is simple, a pm_strcpy function is handling
273        * a null pointer with a substitiution of empty string.
274        */
275       set_content(NULL);
276       return bRC_XACL_ok;
277    }
278
279    switch (xacltype){
280       case XACL_TYPE_NFS4:
281          /*
282           * Read NFS4 ACLs
283           */
284          if (os_get_acl(jcr, XACL_TYPE_NFS4) == bRC_XACL_fatal)
285             return bRC_XACL_fatal;
286
287          if (get_content_len() > 0){
288             if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_NFS4) == bRC_XACL_fatal)
289                return bRC_XACL_fatal;
290          }
291          break;
292       case XACL_TYPE_ACCESS:
293          /*
294           * Read access ACLs
295           */
296          if (os_get_acl(jcr, XACL_TYPE_ACCESS) == bRC_XACL_fatal)
297             return bRC_XACL_fatal;
298
299          if (get_content_len() > 0){
300             if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_ACCESS) == bRC_XACL_fatal)
301                return bRC_XACL_fatal;
302          }
303
304          /*
305           * Directories can have default ACLs too
306           */
307          if (ff_pkt->type == FT_DIREND){
308             if (os_get_acl(jcr, XACL_TYPE_DEFAULT) == bRC_XACL_fatal)
309                return bRC_XACL_fatal;
310             if (get_content_len() > 0){
311                if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_DEFAULT) == bRC_XACL_fatal)
312                   return bRC_XACL_fatal;
313             }
314          }
315          break;
316       default:
317          break;
318    }
319
320    return bRC_XACL_ok;
321 };
322
323 /*
324  * Perform OS specyfic ACL restore
325  *
326  * in/out - check API at xacl.h
327  */
328 bRC_XACL XACL_FreeBSD::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
329
330    int aclrc = 0;
331    const char *acl_type_name;
332
333    switch (stream){
334       case STREAM_UNIX_ACCESS_ACL:
335       case STREAM_XACL_FREEBSD_ACCESS:
336       case STREAM_UNIX_DEFAULT_ACL:
337       case STREAM_XACL_FREEBSD_DEFAULT:
338          aclrc = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
339          acl_type_name = "POSIX";
340          break;
341       case STREAM_XACL_FREEBSD_NFS4:
342 #if defined(_PC_ACL_NFS4)
343          aclrc = pathconf(jcr->last_fname, _PC_ACL_NFS4);
344 #endif
345          acl_type_name = "NFS4";
346          break;
347       default:
348          acl_type_name = "unknown";
349          break;
350    }
351
352    switch (aclrc){
353       case -1: {
354          berrno be;
355
356          switch (errno){
357             case ENOENT:
358                return bRC_XACL_ok;
359             default:
360                Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
361                Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
362                return bRC_XACL_error;
363          }
364       }
365       case 0:
366          clear_flag(XACL_FLAG_NATIVE);
367          Mmsg2(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"), jcr->last_fname, acl_type_name);
368          return bRC_XACL_error;
369       default:
370          break;
371    }
372
373    switch (stream){
374       case STREAM_UNIX_ACCESS_ACL:
375       case STREAM_XACL_FREEBSD_ACCESS:
376          return os_set_acl(jcr, XACL_TYPE_ACCESS, content, length);
377       case STREAM_UNIX_DEFAULT_ACL:
378       case STREAM_XACL_FREEBSD_DEFAULT:
379          return os_set_acl(jcr, XACL_TYPE_DEFAULT, content, length);
380       case STREAM_XACL_FREEBSD_NFS4:
381          return os_set_acl(jcr, XACL_TYPE_NFS4, content, length);
382       default:
383          break;
384    }
385    return bRC_XACL_error;
386 };
387
388 /*
389  * Perform OS specyfic extended attribute backup
390  *
391  * in/out - check API at xacl.h
392  */
393 bRC_XACL XACL_FreeBSD::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
394
395    bRC_XACL rc;
396    POOLMEM *xlist;
397    uint32_t xlen;
398    char *name;
399    uint32_t name_len;
400    POOLMEM *value;
401    uint32_t value_len;
402    POOLMEM *name_gen;
403    uint32_t name_gen_len;
404    char * namespace_str;
405    int namespace_len;
406    bool skip;
407    alist *xattr_list = NULL;
408    int xattr_count = 0;
409    uint32_t len = 0;
410    XACL_xattr *xattr;
411    int a;
412
413    for (a = 0; os_xattr_namespaces[a] != -1; a++){ // loop through all available namespaces
414       /* xlist is allocated as POOLMEM by os_get_xattr_names */
415       rc = os_get_xattr_names(jcr, os_xattr_namespaces[a], &xlist, &xlen);
416       switch (rc){
417          case bRC_XACL_ok:
418             /* it's ok, so go further */
419             break;
420          case bRC_XACL_skip:
421          case bRC_XACL_cont:
422             /* no xattr available, so skip rest of it */
423             return bRC_XACL_ok;
424          default:
425             return rc;
426       }
427
428       /* get a string representation of the namespace */
429       if (extattr_namespace_to_string(os_xattr_namespaces[a], &namespace_str) != 0){
430          Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"), os_xattr_namespaces[a], jcr->last_fname);
431          Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n", os_xattr_namespaces[a], jcr->last_fname);
432          goto bail_out;
433       }
434       namespace_len = strlen(namespace_str);
435
436       /* follow the list of xattr names and get the values */
437       for (name = xlist; (name - xlist) + 1 < xlen; name = strchr(name, '\0') + 1){
438          name_len = strlen(name);
439          name_gen = get_pool_memory(PM_FNAME);
440          name_gen = check_pool_memory_size(name_gen, name_len + namespace_len + 2);
441          bsnprintf(name_gen, name_len + namespace_len + 2, "%s.%s", namespace_str, name);
442          name_gen_len = strlen(name_gen);
443
444          skip =  check_xattr_skiplists(jcr, ff_pkt, name_gen);
445          if (skip || name_len == 0){
446             Dmsg1(100, "Skipping xattr named %s\n", name_gen);
447             continue;
448          }
449
450          /* value is allocated as POOLMEM by os_get_xattr_value */
451          rc = os_get_xattr_value(jcr, os_xattr_namespaces[a], name, &value, &value_len);
452          switch (rc){
453             case bRC_XACL_ok:
454                /* it's ok, so go further */
455                break;
456             case bRC_XACL_skip:
457                /* no xattr available, so skip rest of it */
458                rc = bRC_XACL_ok;
459                goto bail_out;
460             default:
461                /* error / fatal */
462                goto bail_out;
463          }
464
465          /*
466           * we have a name of the extended attribute in the name variable
467           * and value of the extended attribute in the value variable
468           * so we need to build a list
469           */
470          xattr = (XACL_xattr*)malloc(sizeof(XACL_xattr));
471          xattr->name_len = name_gen_len;
472          xattr->name = name_gen;
473          xattr->value_len = value_len;
474          xattr->value = value;
475          /*       magic              name_len          name        value_len       value */
476          len += sizeof(uint32_t) + sizeof(uint32_t) + name_gen_len + sizeof(uint32_t) + value_len;
477
478          if (xattr_list == NULL){
479             xattr_list = New(alist(10, not_owned_by_alist));
480          }
481          xattr_list->append(xattr);
482          xattr_count++;
483       }
484       if (xattr_count > 0){
485          /* serialize the stream */
486          rc = serialize_xattr_stream(jcr, len, xattr_list);
487          if (rc != bRC_XACL_ok){
488             Mmsg(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), jcr->last_fname);
489             Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n", jcr->last_fname);
490             goto bail_out;
491          } else {
492             /* send data to SD */
493             rc = send_xattr_stream(jcr, STREAM_XACL_FREEBSD_XATTR);
494          }
495       } else {
496          rc = bRC_XACL_ok;
497       }
498    }
499 bail_out:
500    /* free allocated data */
501    if (xattr_list != NULL){
502       foreach_alist(xattr, xattr_list){
503          if (xattr == NULL){
504             break;
505          }
506          if (xattr->name){
507             free_pool_memory(name_gen);
508          }
509          if (xattr->value){
510             free(xattr->value);
511          }
512          free(xattr);
513       }
514       delete xattr_list;
515    }
516    if (xlist != NULL){
517       free(xlist);
518    }
519
520    return rc;
521 };
522
523 /*
524  * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS.
525  *
526  * in/out - check API at xacl.h
527  */
528 bRC_XACL XACL_FreeBSD::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){
529    return generic_restore_xattr(jcr, stream);
530 };
531
532 /*
533  * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer
534  *
535  * in/out - check API at xacl.h
536  */
537 bRC_XACL XACL_FreeBSD::os_get_acl(JCR *jcr, XACL_type xacltype){
538
539    acl_t acl;
540    acl_type_t acltype;
541    char *acltext;
542    bRC_XACL rc = bRC_XACL_ok;
543
544    acltype = get_acltype(xacltype);
545    acl = acl_get_file(jcr->last_fname, acltype);
546
547    if (acl){
548       Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname);
549       if (acl_nrentries(acl) == 0){
550          goto bail_out;
551       }
552
553       /* check for fimple ACL which correspond to standard permissions only */
554       if (xacltype == XACL_TYPE_ACCESS && acl_issimple(acl)){
555          goto bail_out;
556       }
557
558 #if defined(_PC_ACL_NFS4)
559       if (xacltype == XACL_TYPE_NFS4){
560          int trivial;
561          if (acl_is_trivial_np(acl, &trivial) == 0){
562             if (trivial == 1){
563                goto bail_out;
564             }
565          }
566       }
567 #endif
568
569       if ((acltext = acl_to_text(acl, NULL)) != NULL){
570          set_content(acltext);
571          acl_free(acl);
572          acl_free(acltext);
573          return bRC_XACL_ok;
574       }
575
576       berrno be;
577
578       Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
579       Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
580
581       rc = bRC_XACL_error;
582
583    } else {
584       berrno be;
585
586       switch (errno){
587       case EOPNOTSUPP:
588          /* fs does not support acl, skip it */
589          Dmsg0(400, "Wow, ACL is not supported on this filesystem\n");
590          clear_flag(XACL_FLAG_NATIVE);
591          break;
592       case ENOENT:
593          break;
594       default:
595          /* Some real error */
596          Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
597          Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
598          rc = bRC_XACL_error;
599          break;
600       }
601    }
602
603 bail_out:
604    if (acl){
605       acl_free(acl);
606    }
607    /*
608     * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
609     * but it is working, hehe :)
610     * you may ask why it is working? it is simple, a pm_strcpy function is handling
611     * a null pointer with a substitiution of empty string.
612     */
613    set_content(NULL);
614    return rc;
615 };
616
617 /*
618  * Low level OS specyfic runtime to set ACL data on file
619  *
620  * in/out - check API at xacl.h
621  */
622 bRC_XACL XACL_FreeBSD::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length){
623
624    acl_t acl;
625    acl_type_t acltype;
626
627    acltype = get_acltype(xacltype);
628    if (acltype == ACL_TYPE_DEFAULT && length == 0){
629       /* delete ACl from file when no acl data available for default acl's */
630       if (acl_delete_def_file(jcr->last_fname) == 0){
631          return bRC_XACL_ok;
632       }
633
634       berrno be;
635       switch (errno){
636          case ENOENT:
637             return bRC_XACL_ok;
638          case ENOTSUP:
639             /*
640              * If the filesystem reports it doesn't support acl's we clear the
641              * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files
642              * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
643              * when we change from one filesystem to an other.
644              */
645             clear_flag(XACL_FLAG_NATIVE);
646             Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
647             return bRC_XACL_error;
648          default:
649             Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
650             return bRC_XACL_error;
651       }
652    }
653
654    acl = acl_from_text(content);
655    if (acl == NULL){
656       berrno be;
657
658       Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
659       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
660       return bRC_XACL_error;
661    }
662
663    /*
664     * Restore the ACLs, but don't complain about links which really should
665     * not have attributes, and the file it is linked to may not yet be restored.
666     * This is only true for the old acl streams as in the new implementation we
667     * don't save acls of symlinks (which cannot have acls anyhow)
668     */
669    if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){
670       berrno be;
671       switch (errno){
672       case ENOENT:
673          acl_free(acl);
674          return bRC_XACL_ok;
675       case ENOTSUP:
676          /*
677           * If the filesystem reports it doesn't support ACLs we clear the
678           * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files
679           * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
680           * when we change from one filesystem to an other.
681           */
682          clear_flag(XACL_FLAG_NATIVE);
683          Mmsg(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
684          Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname);
685          acl_free(acl);
686          return bRC_XACL_error;
687       default:
688          Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
689          Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
690          acl_free(acl);
691          return bRC_XACL_error;
692       }
693    }
694    acl_free(acl);
695    return bRC_XACL_ok;
696 };
697
698 /*
699  * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer.
700  * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
701  * when not needed.
702  *
703  * in/out - check API at xacl.h
704  *
705  * As a FreeBSD uses a different attributes name schema/format then this method is a very different
706  * from a standard generic method because it uses a namespace (ns) value for os dependent optimization.
707  */
708 bRC_XACL XACL_FreeBSD::os_get_xattr_names (JCR *jcr, int ns, POOLMEM ** pxlist, uint32_t * xlen){
709
710    int len;
711    POOLMEM * list;
712    int a;
713    int stra;
714    POOLMEM * genlist;
715
716    /* check input data */
717    if (jcr == NULL || xlen == NULL || pxlist == NULL){
718       return bRC_XACL_inval;
719    }
720    /* get the length of the extended attributes */
721    len = extattr_list_link(jcr->last_fname, ns, NULL, 0);
722    switch (len){
723       case -1: {
724          berrno be;
725
726          switch (errno){
727             case ENOENT:
728                /* no file available, skip it */
729                return bRC_XACL_skip;
730             case EOPNOTSUPP:
731                /* no xattr supported on filesystem, clear a flag and skip it */
732                clear_flag(XACL_FLAG_NATIVE);
733                set_content(NULL);
734                return bRC_XACL_skip;
735             case EPERM:
736                if (ns == EXTATTR_NAMESPACE_SYSTEM){
737                   return bRC_XACL_cont;
738                } /* else show error */
739             default:
740                Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
741                Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
742                return bRC_XACL_error;
743          }
744          break;
745       }
746       case 0:
747          /* xattr available but empty, skip it */
748          return bRC_XACL_skip;
749       default:
750          break;
751    }
752
753    /*
754     * allocate memory for the extented attribute list
755     * default size is a 4k for PM_BSOCK, which should be sufficient on almost all
756     * Linux system where xattrs a limited in size to single filesystem block ~4kB
757     * so we need to check required size
758     */
759    list = get_pool_memory(PM_BSOCK);
760    list = check_pool_memory_size(list, len + 1);
761    memset(list, 0, len + 1);
762
763    /* get the list of extended attributes names for a file */
764    len = extattr_list_link(jcr->last_fname, ns, list, len);
765    switch (len){
766    case -1: {
767       berrno be;
768
769       switch (errno){
770       case ENOENT:
771          /* no file available, skip it, first release allocated memory */
772          free_pool_memory(list);
773          return bRC_XACL_skip;
774          case EPERM:
775             if (ns == EXTATTR_NAMESPACE_SYSTEM){
776                return bRC_XACL_cont;
777             } /* else show error */
778       default:
779          Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
780          Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
781          free_pool_memory(list);
782          return bRC_XACL_error;
783       }
784       break;
785    }
786    default:
787       break;
788    }
789    /* convert FreeBSD list type to the generic one */
790    genlist = get_pool_memory(PM_BSOCK);
791    genlist = check_pool_memory_size(genlist, len + 1);
792    memset(genlist, 0, len + 1);
793    for (a = 0; a < len; a += list[a] + 1){
794       stra = list[a];
795       memcpy(genlist + a, list + a + 1, stra);
796       genlist[a + stra] = '\0';
797    }
798    free_pool_memory(list);
799    /* setup return data */
800    *pxlist = genlist;
801    *xlen = len;
802    return bRC_XACL_ok;
803 };
804
805 /*
806  * Return a value of the requested attribute name and a length of the allocated buffer.
807  * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
808  * when not needed.
809  *
810  * in/out - check API at xacl.h
811  *
812  * As a FreeBSD uses a different attributes name schema/format then this method is a very different
813  * from a standard generic method because it uses a namespace (ns) value for os dependent optimization.
814  */
815 bRC_XACL XACL_FreeBSD::os_get_xattr_value (JCR *jcr, int ns, char * name, char ** pvalue, uint32_t * plen){
816
817    int len;
818    POOLMEM * value;
819
820    /* check input data */
821    if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){
822       return bRC_XACL_inval;
823    }
824    /* get the length of the value for extended attribute */
825    len = extattr_get_link(jcr->last_fname, ns, name, NULL, 0);
826    switch (len){
827       case -1: {
828          berrno be;
829
830          switch (errno){
831             case ENOENT:
832                /* no file available, skip it */
833                return bRC_XACL_skip;
834             default:
835                /* XXX: what about ENOATTR error value? */
836                Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
837                Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
838                return bRC_XACL_error;
839          }
840          break;
841       }
842       default:
843          break;
844    }
845
846    if (len > 0){
847       /*
848        * allocate memory for the extented attribute value
849        * default size is a 256B for PM_MESSAGE, so we need to check required size
850        */
851       value = get_pool_memory(PM_MESSAGE);
852       value = check_pool_memory_size(value, len + 1);
853       memset(value, 0, len + 1);
854       /* value is not empty, get a data */
855       len = extattr_get_link(jcr->last_fname, ns, name, value, len);
856       switch (len){
857       case -1: {
858          berrno be;
859
860          switch (errno){
861          case ENOENT:
862             /* no file available, skip it, first release allocated memory */
863             free_pool_memory(value);
864             return bRC_XACL_skip;
865          default:
866             Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
867             Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
868             free_pool_memory(value);
869             return bRC_XACL_error;
870          }
871          break;
872       }
873       default:
874          break;
875       }
876       /* ensure a value is nul terminated */
877       value[len] = '\0';
878    } else {
879       /* empty value */
880       value = NULL;
881       len = 0;
882    }
883    /* setup return data */
884    *pvalue = value;
885    *plen = len;
886    return bRC_XACL_ok;
887 };
888
889 /*
890  * Low level OS specyfic runtime to set extended attribute on file
891  *
892  * in/out - check API at xacl.h
893  *
894  * xattr->name should be in '<namespace>.<name>' format which
895  * function handle without problem, otherwise it returns an error
896  * TODO: it is possible to handle a different attributes name format
897  * for os portability where default namespace 'user' can be used
898  */
899 bRC_XACL XACL_FreeBSD::os_set_xattr (JCR *jcr, XACL_xattr *xattr){
900
901    char * name;
902    char * nspace;
903    int ns;
904    int rc;
905
906    /* check input data */
907    if (jcr == NULL || xattr == NULL){
908       return bRC_XACL_inval;
909    }
910
911    /* search for attribute namespace which is distinguished from attribute name by a dot '.' character */
912    if ((name = strchr(xattr->name, '.')) == (char *)NULL){
913       Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"), xattr->name, jcr->last_fname);
914       Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n", xattr->name, jcr->last_fname);
915       return bRC_XACL_error;
916    }
917
918    /* split namespace and name of the attribute */
919    nspace = xattr->name;
920    *name++ = '\0';
921
922    /* check if namespace is valid on this system */
923    if (extattr_string_to_namespace(nspace, &ns) != 0){
924       Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"), nspace, jcr->last_fname);
925       Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n", nspace, jcr->last_fname);
926       return bRC_XACL_error;
927    }
928
929    /* set extattr on file */
930    rc = extattr_set_link(jcr->last_fname, ns, name, xattr->value, xattr->value_len);
931    if (rc < 0 || rc != (int)xattr->value_len){
932       berrno be;
933
934       switch (errno){
935       case ENOENT:
936          break;
937       default:
938          Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
939          Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
940          return bRC_XACL_error;
941       }
942    }
943    return bRC_XACL_ok;
944 };
945
946 #endif /* HAVE_FREEBSD_OS */