]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/xacl_osx.c
baculum: Improve slow reloading config resource list
[bacula/bacula] / bacula / src / filed / xacl_osx.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_osx.h"
31
32 #if defined(HAVE_DARWIN_OS)
33 /*
34  * Define the supported ACL streams for this OS
35  */
36 static const int os_acl_streams[] = {
37    STREAM_XACL_DARWIN_ACCESS,
38    0
39 };
40
41 static const int os_default_acl_streams[] = {
42    0
43 };
44
45 /*
46  * Define the supported XATTR streams for this OS
47  */
48 static const int os_xattr_streams[] = {
49    STREAM_XACL_DARWIN_XATTR,
50    0
51 };
52
53 static const char *os_xattr_acl_skiplist[] = {
54    "com.apple.system.Security",
55    NULL
56 };
57
58 static const char *os_xattr_skiplist[] = {
59    "com.apple.system.extendedsecurity",
60    "com.apple.ResourceFork",
61    NULL
62 };
63
64 /*
65  * OS Specyfic constructor
66  */
67 XACL_OSX::XACL_OSX(){
68
69    set_acl_streams(os_acl_streams, os_default_acl_streams);
70    set_xattr_streams(os_xattr_streams);
71    set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist);
72 };
73
74 /*
75  * Translates Bacula internal acl representation into
76  * acl type
77  *
78  * in:
79  *    xacltype - internal Bacula acl type (XACL_type)
80  * out:
81  *    acl_type_t - os dependent acl type
82  *    when failed - ACL_TYPE_NONE is returned
83  */
84 acl_type_t XACL_OSX::get_acltype(XACL_type xacltype){
85
86    acl_type_t acltype;
87
88    switch (xacltype){
89       case XACL_TYPE_ACCESS:
90          acltype = ACL_TYPE_ACCESS;
91          break;
92    #ifdef HAVE_ACL_TYPE_EXTENDED
93       case XACL_TYPE_EXTENDED:
94          acltype = ACL_TYPE_EXTENDED;
95          break;
96    #endif
97       default:
98          /*
99           * sanity check for acl's not supported by OS
100           */
101          acltype = (acl_type_t)ACL_TYPE_NONE;
102          break;
103    }
104    return acltype;
105 };
106
107 /*
108  * Counts a number of acl entries
109  *
110  * in:
111  *    acl - acl object
112  * out:
113  *    int - number of entries in acl object
114  *    when no acl entry available or any error then return zero '0'
115  */
116 int XACL_OSX::acl_nrentries(acl_t acl){
117
118    int nr = 0;
119    acl_entry_t entry;
120    int rc;
121
122    rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
123    while (rc == 0){
124       nr++;
125       rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
126    }
127
128    return nr;
129 };
130
131 /*
132  * Perform OS specyfic ACL backup
133  *
134  * in/out - check API at xacl.h
135  */
136 bRC_XACL XACL_OSX::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
137
138    /* check input data */
139    if (jcr == NULL || ff_pkt == NULL){
140       return bRC_XACL_inval;
141    }
142
143 #if defined(HAVE_ACL_TYPE_EXTENDED)
144    /*
145     * Use XACL_TYPE_EXTENDED only when available
146     */
147    Dmsg0(400, "MacOSX Extended ACL computed\n");
148    if (os_get_acl(jcr, XACL_TYPE_EXTENDED) == bRC_XACL_fatal){
149       return bRC_XACL_fatal;
150    }
151 #else
152    Dmsg0(400, "MacOSX standard ACL computed\n");
153    if (os_get_acl(jcr, XACL_TYPE_ACCESS) == bRC_XACL_fatal){
154       return bRC_XACL_fatal;
155    }
156 #endif
157
158    return send_acl_stream(jcr, STREAM_XACL_DARWIN_ACCESS);
159 };
160
161 /*
162  * Perform OS specyfic ACL restore
163  *
164  * in/out - check API at xacl.h
165  */
166 bRC_XACL XACL_OSX::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
167
168 #if defined(HAVE_ACL_TYPE_EXTENDED)
169       return os_set_acl(jcr, XACL_TYPE_EXTENDED, content, length);
170 #else
171       return os_set_acl(jcr, XACL_TYPE_ACCESS, content, length);
172 #endif
173 };
174
175 /*
176  * Perform OS specyfic extended attribute backup
177  *
178  * in/out - check API at xacl.h
179  */
180 bRC_XACL XACL_OSX::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
181    return generic_backup_xattr(jcr, ff_pkt);
182 };
183
184 /*
185  * Perform OS specyfic XATTR restore. Runtime is called only when stream is supported by OS.
186  *
187  * in/out - check API at xacl.h
188  */
189 bRC_XACL XACL_OSX::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){
190    return generic_restore_xattr(jcr, stream);
191 };
192
193 /*
194  * Low level OS specyfic runtime to get ACL data from file. The ACL data is set in internal content buffer
195  *
196  * in/out - check API at xacl.h
197  */
198 bRC_XACL XACL_OSX::os_get_acl(JCR *jcr, XACL_type xacltype){
199
200    acl_t acl;
201    acl_type_t acltype;
202    char *acltext;
203    bRC_XACL rc = bRC_XACL_ok;
204
205    /* check input data */
206    if (jcr == NULL){
207       return bRC_XACL_inval;
208    }
209
210    acltype = get_acltype(xacltype);
211    acl = acl_get_file(jcr->last_fname, acltype);
212
213    if (acl){
214       Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname);
215       if (acl_nrentries(acl) == 0){
216          goto bail_out;
217       }
218
219       if ((acltext = acl_to_text(acl, NULL)) != NULL){
220          set_content(acltext);
221          acl_free(acl);
222          acl_free(acltext);
223          return bRC_XACL_ok;
224       }
225
226       berrno be;
227
228       Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
229       Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
230
231       rc = bRC_XACL_error;
232    } else {
233       berrno be;
234
235       switch (errno){
236       case EOPNOTSUPP:
237          /* fs does not support acl, skip it */
238          Dmsg0(400, "Wow, ACL is not supported on this filesystem\n");
239          clear_flag(XACL_FLAG_NATIVE);
240          break;
241       case ENOENT:
242          break;
243       default:
244          /* Some real error */
245          Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
246          Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
247          rc = bRC_XACL_error;
248          break;
249       }
250    }
251
252 bail_out:
253    if (acl){
254       acl_free(acl);
255    }
256    /*
257     * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
258     * but it is working, hehe :)
259     * you may ask why it is working? it is simple, a pm_strcpy function is handling
260     * a null pointer with a substitiution of empty string.
261     */
262    set_content(NULL);
263    return rc;
264 };
265
266 /*
267  * Low level OS specyfic runtime to set ACL data on file
268  *
269  * in/out - check API at xacl.h
270  */
271 bRC_XACL XACL_OSX::os_set_acl(JCR *jcr, XACL_type xacltype, char *content, uint32_t length){
272
273    acl_t acl;
274    acl_type_t acltype;
275
276    /* check input data */
277    if (jcr == NULL || content == NULL){
278       return bRC_XACL_inval;
279    }
280
281    acl = acl_from_text(content);
282    if (acl == NULL){
283       berrno be;
284
285       Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
286       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
287       return bRC_XACL_error;
288    }
289
290    acltype = get_acltype(xacltype);
291
292    /*
293     * Restore the ACLs, but don't complain about links which really should
294     * not have attributes, and the file it is linked to may not yet be restored.
295     * This is only true for the old acl streams as in the new implementation we
296     * don't save acls of symlinks (which cannot have acls anyhow)
297     */
298    if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){
299       berrno be;
300       switch (errno){
301       case ENOENT:
302          acl_free(acl);
303          return bRC_XACL_ok;
304       case EOPNOTSUPP:
305          /*
306           * If the filesystem reports it doesn't support ACLs we clear the
307           * XACL_FLAG_NATIVE flag so we skip ACL restores on all other files
308           * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
309           * when we change from one filesystem to an other.
310           */
311          clear_flag(XACL_FLAG_NATIVE);
312          Mmsg1(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
313          Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname);
314          acl_free(acl);
315          return bRC_XACL_error;
316       default:
317          Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
318          Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
319          acl_free(acl);
320          return bRC_XACL_error;
321       }
322    }
323    acl_free(acl);
324    return bRC_XACL_ok;
325 };
326
327 /*
328  * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer.
329  * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
330  * when not needed.
331  *
332  * in/out - check API at xacl.h
333  */
334 bRC_XACL XACL_OSX::os_get_xattr_names (JCR *jcr, POOLMEM ** pxlist, uint32_t * xlen){
335
336    int len;
337    POOLMEM * list;
338
339    /* check input data */
340    if (jcr == NULL || xlen == NULL || pxlist == NULL){
341       return bRC_XACL_inval;
342    }
343    /* get the length of the extended attributes */
344    len = listxattr(jcr->last_fname, NULL, 0, XATTR_NOFOLLOW);
345    switch (len){
346       case -1: {
347          berrno be;
348
349          switch (errno){
350             case ENOENT:
351                /* no file available, skip it */
352                return bRC_XACL_skip;
353             case ENOTSUP:
354                /* no xattr supported on filesystem, clear a flag and skip it */
355                clear_flag(XACL_FLAG_NATIVE);
356                set_content(NULL);
357                return bRC_XACL_skip;
358             default:
359                Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
360                Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
361                return bRC_XACL_error;
362          }
363          break;
364       }
365       case 0:
366          /* xattr available but empty, skip it */
367          return bRC_XACL_skip;
368       default:
369          break;
370    }
371
372    /*
373     * allocate memory for the extented attribute list
374     * default size is a 4k for PM_BSOCK, which should be sufficient on almost all
375     * Linux system where xattrs a limited in size to single filesystem block ~4kB
376     * so we need to check required size
377     */
378    list = get_pool_memory(PM_BSOCK);
379    list = check_pool_memory_size(list, len + 1);
380    memset(list, 0, len + 1);
381
382    /* get the list of extended attributes names for a file */
383    len = listxattr(jcr->last_fname, list, len, XATTR_NOFOLLOW);
384    switch (len){
385    case -1: {
386       berrno be;
387
388       switch (errno){
389       case ENOENT:
390          /* no file available, skip it, first release allocated memory */
391          free_pool_memory(list);
392          return bRC_XACL_skip;
393       default:
394          Mmsg2(jcr->errmsg, _("llistxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
395          Dmsg2(100, "llistxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
396          free_pool_memory(list);
397          return bRC_XACL_error;
398       }
399       break;
400    }
401    default:
402       break;
403    }
404    /* ensure a list is nul terminated */
405    list[len] = '\0';
406    /* setup return data */
407    *pxlist = list;
408    *xlen = len;
409    return bRC_XACL_ok;
410 };
411
412 /*
413  * Return a value of the requested attribute name and a length of the allocated buffer.
414  * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
415  * when not needed.
416  *
417  * in/out - check API at xacl.h
418  */
419 bRC_XACL XACL_OSX::os_get_xattr_value (JCR *jcr, char * name, char ** pvalue, uint32_t * plen){
420
421    int len;
422    POOLMEM * value;
423
424    /* check input data */
425    if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){
426       return bRC_XACL_inval;
427    }
428
429    /* get the length of the value for extended attribute */
430    len = getxattr(jcr->last_fname, name, NULL, 0, 0, XATTR_NOFOLLOW);
431    switch (len){
432       case -1: {
433          berrno be;
434
435          switch (errno){
436             case ENOENT:
437                /* no file available, skip it */
438                return bRC_XACL_skip;
439             default:
440                /* XXX: what about ENOATTR error value? */
441                Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
442                Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
443                return bRC_XACL_error;
444          }
445          break;
446       }
447       default:
448          break;
449    }
450
451    if (len > 0){
452       /*
453        * allocate memory for the extented attribute value
454        * default size is a 256B for PM_MESSAGE, so we need to check required size
455        */
456       value = get_pool_memory(PM_MESSAGE);
457       value = check_pool_memory_size(value, len + 1);
458       memset(value, 0, len + 1);
459       /* value is not empty, get a data */
460       len = getxattr(jcr->last_fname, name, value, len, 0, XATTR_NOFOLLOW);
461       switch (len){
462       case -1: {
463          berrno be;
464
465          switch (errno){
466          case ENOENT:
467             /* no file available, skip it, first release allocated memory */
468             free_pool_memory(value);
469             return bRC_XACL_skip;
470          default:
471             Mmsg2(jcr->errmsg, _("lgetxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
472             Dmsg2(100, "lgetxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
473             free_pool_memory(value);
474             return bRC_XACL_error;
475          }
476          break;
477       }
478       default:
479          break;
480       }
481       /* ensure a value is nul terminated */
482       value[len] = '\0';
483    } else {
484       /* empty value */
485       value = NULL;
486       len = 0;
487    }
488    /* setup return data */
489    *pvalue = value;
490    *plen = len;
491    return bRC_XACL_ok;
492 };
493
494 /*
495  * Low level OS specyfic runtime to set extended attribute on file
496  *
497  * in/out - check API at xacl.h
498  */
499 bRC_XACL XACL_OSX::os_set_xattr (JCR *jcr, XACL_xattr *xattr){
500
501    /* check input data */
502    if (jcr == NULL || xattr == NULL){
503       return bRC_XACL_inval;
504    }
505
506    /* set extattr on file */
507    if (setxattr(jcr->last_fname, xattr->name, xattr->value, xattr->value_len, 0, XATTR_NOFOLLOW) != 0){
508       berrno be;
509
510       switch (errno){
511       case ENOENT:
512          break;
513       case ENOTSUP:
514          /*
515           * If the filesystem reports it doesn't support XATTR we clear the
516           * XACL_FLAG_NATIVE flag so we skip XATTR restores on all other files
517           * on the same filesystem. The XACL_FLAG_NATIVE flag gets set again
518           * when we change from one filesystem to an other.
519           */
520          clear_flag(XACL_FLAG_NATIVE);
521          Mmsg1(jcr->errmsg, _("setxattr error on file \"%s\": filesystem doesn't support XATTR\n"), jcr->last_fname);
522          Dmsg3(100, "setxattr error name=%s value=%s file=%s filesystem doesn't support XATTR\n", xattr->name, xattr->value, jcr->last_fname);
523          break;
524       default:
525          Mmsg2(jcr->errmsg, _("setxattr error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
526          Dmsg2(100, "setxattr error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
527          return bRC_XACL_error;
528       }
529    }
530    return bRC_XACL_ok;
531 };
532
533 #endif /* HAVE_DARWIN_OS */