2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Major refactoring of ACL code written by:
22 * Radosław Korzeniewski, MMXVI
23 * radoslaw@korzeniewski.net, radekk@inteos.pl
24 * Inteos Sp. z o.o. http://www.inteos.pl/
30 #include "bacl_freebsd.h"
32 #if defined(HAVE_FREEBSD_OS)
34 /* check if ACL support is enabled */
38 * Define the supported ACL streams for this OS
40 static const int os_acl_streams[] = {
41 STREAM_XACL_FREEBSD_ACCESS,
42 STREAM_XACL_FREEBSD_NFS4,
46 static const int os_default_acl_streams[] = {
47 STREAM_XACL_FREEBSD_DEFAULT,
52 * OS specific constructor
54 BACL_FreeBSD::BACL_FreeBSD(){
56 set_acl_streams(os_acl_streams, os_default_acl_streams);
60 * Translates Bacula internal acl representation into acl type
63 * bacltype - internal Bacula acl type (BACL_type)
65 * acl_type_t - os dependent acl type
66 * when failed - ACL_TYPE_NONE is returned
68 acl_type_t BACL_FreeBSD::get_acltype(BACL_type bacltype){
73 #ifdef HAVE_ACL_TYPE_NFS4
75 acltype = ACL_TYPE_NFS4;
78 case BACL_TYPE_ACCESS:
79 acltype = ACL_TYPE_ACCESS;
81 case BACL_TYPE_DEFAULT:
82 acltype = ACL_TYPE_DEFAULT;
86 * sanity check for acl's not supported by OS
88 acltype = (acl_type_t)ACL_TYPE_NONE;
95 * Counts a number of acl entries
100 * int - number of entries in acl object
101 * when no acl entry available or any error then return zero '0'
103 int BACL_FreeBSD::acl_nrentries(acl_t acl){
106 acl_entry_t aclentry;
109 rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
112 rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
119 * Checks if acl is simple.
121 * acl is simple if it has only the following entries:
129 * true - when acl object is simple
130 * false - when acl object is not simple
132 bool BACL_FreeBSD::acl_issimple(acl_t acl){
134 acl_entry_t aclentry;
138 rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
140 if (acl_get_tag_type(aclentry, &acltag) < 0){
144 * Check for ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER to find out.
146 if (acltag != ACL_USER_OBJ &&
147 acltag != ACL_GROUP_OBJ &&
148 acltag != ACL_OTHER){
151 rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
157 * Checks if ACL's are available for a specified file
160 * jcr - Job Control Record
161 * name - specifies the system variable to be queried
163 * bRC_BACL_ok - check successful, lets setup bacltype variable
164 * bRC_BACL_error - in case of error
165 * bRC_BACL_skip - you should skip all other routine
166 * bRC_BACL_cont - you need to continue your routine
168 bRC_BACL BACL_FreeBSD::check_bacltype (JCR *jcr, int name){
172 aclrc = pathconf(jcr->last_fname, name);
175 /* some error check why */
177 if (errno == ENOENT){
178 /* file does not exist skip it */
179 return bRC_BACL_skip;
181 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
182 Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
183 return bRC_BACL_error;
187 /* continue the routine */
188 return bRC_BACL_cont;
196 * Perform OS specific ACL backup
198 * in/out - check API at bacl.h
200 bRC_BACL BACL_FreeBSD::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
203 BACL_type bacltype = BACL_TYPE_NONE;
205 #if defined(_PC_ACL_NFS4)
207 * Check if filesystem supports NFS4 acls.
209 rc = check_bacltype(jcr, _PC_ACL_NFS4);
212 bacltype = BACL_TYPE_NFS4;
223 if (bacltype == BACL_TYPE_NONE){
225 * Check if filesystem supports POSIX acls.
227 rc = check_bacltype(jcr, _PC_ACL_EXTENDED);
230 bacltype = BACL_TYPE_ACCESS;
242 /* no ACL's available for file, so skip this filesystem */
243 if (bacltype == BACL_TYPE_NONE){
244 clear_flag(BACL_FLAG_NATIVE);
246 * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
247 * but it is working, hehe :)
248 * you may ask why it is working? it is simple, a pm_strcpy function is handling
249 * a null pointer with a substitiution of empty string.
260 if (os_get_acl(jcr, BACL_TYPE_NFS4) == bRC_BACL_fatal)
261 return bRC_BACL_fatal;
263 if (get_content_len() > 0){
264 if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_NFS4) == bRC_BACL_fatal)
265 return bRC_BACL_fatal;
268 case BACL_TYPE_ACCESS:
272 if (os_get_acl(jcr, BACL_TYPE_ACCESS) == bRC_BACL_fatal)
273 return bRC_BACL_fatal;
275 if (get_content_len() > 0){
276 if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_ACCESS) == bRC_BACL_fatal)
277 return bRC_BACL_fatal;
281 * Directories can have default ACLs too
283 if (ff_pkt->type == FT_DIREND){
284 if (os_get_acl(jcr, BACL_TYPE_DEFAULT) == bRC_BACL_fatal)
285 return bRC_BACL_fatal;
286 if (get_content_len() > 0){
287 if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_DEFAULT) == bRC_BACL_fatal)
288 return bRC_BACL_fatal;
300 * Perform OS specific ACL restore
302 * in/out - check API at bacl.h
304 bRC_BACL BACL_FreeBSD::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
307 const char *acl_type_name;
310 case STREAM_UNIX_ACCESS_ACL:
311 case STREAM_XACL_FREEBSD_ACCESS:
312 case STREAM_UNIX_DEFAULT_ACL:
313 case STREAM_XACL_FREEBSD_DEFAULT:
314 aclrc = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
315 acl_type_name = "POSIX";
317 case STREAM_XACL_FREEBSD_NFS4:
318 #if defined(_PC_ACL_NFS4)
319 aclrc = pathconf(jcr->last_fname, _PC_ACL_NFS4);
321 acl_type_name = "NFS4";
324 acl_type_name = "unknown";
336 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
337 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
338 return bRC_BACL_error;
342 clear_flag(BACL_FLAG_NATIVE);
343 Mmsg2(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"), jcr->last_fname, acl_type_name);
344 return bRC_BACL_error;
350 case STREAM_UNIX_ACCESS_ACL:
351 case STREAM_XACL_FREEBSD_ACCESS:
352 return os_set_acl(jcr, BACL_TYPE_ACCESS, content, length);
353 case STREAM_UNIX_DEFAULT_ACL:
354 case STREAM_XACL_FREEBSD_DEFAULT:
355 return os_set_acl(jcr, BACL_TYPE_DEFAULT, content, length);
356 case STREAM_XACL_FREEBSD_NFS4:
357 return os_set_acl(jcr, BACL_TYPE_NFS4, content, length);
361 return bRC_BACL_error;
365 * Low level OS specific runtime to get ACL data from file.
366 * The ACL data is set in internal content buffer
368 * in/out - check API at bacl.h
370 bRC_BACL BACL_FreeBSD::os_get_acl(JCR *jcr, BACL_type bacltype){
375 bRC_BACL rc = bRC_BACL_ok;
377 acltype = get_acltype(bacltype);
378 acl = acl_get_file(jcr->last_fname, acltype);
381 Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname);
382 if (acl_nrentries(acl) == 0){
386 /* check for simple ACL which correspond to standard permissions only */
387 if (bacltype == BACL_TYPE_ACCESS && acl_issimple(acl)){
391 #if defined(_PC_ACL_NFS4)
392 if (bacltype == BACL_TYPE_NFS4){
394 if (acl_is_trivial_np(acl, &trivial) == 0){
402 if ((acltext = acl_to_text(acl, NULL)) != NULL){
403 set_content(acltext);
411 Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
412 Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
421 /* fs does not support acl, skip it */
422 Dmsg0(400, "Wow, ACL is not supported on this filesystem\n");
423 clear_flag(BACL_FLAG_NATIVE);
428 /* Some real error */
429 Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
430 Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
441 * it is a bit of hardcore to clear a pool memory with a NULL pointer,
442 * but it is working, hehe :)
443 * you may ask why it is working? it is simple, a pm_strcpy function is handling
444 * a null pointer with a substitution of empty string.
451 * Low level OS specific runtime to set ACL data on file
453 * in/out - check API at bacl.h
455 bRC_BACL BACL_FreeBSD::os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length){
460 acltype = get_acltype(bacltype);
461 if (acltype == ACL_TYPE_DEFAULT && length == 0){
462 /* delete ACl from file when no acl data available for default acl's */
463 if (acl_delete_def_file(jcr->last_fname) == 0){
473 * If the filesystem reports it doesn't support acl's we clear the
474 * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files
475 * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again
476 * when we change from one filesystem to an other.
478 clear_flag(BACL_FLAG_NATIVE);
479 Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
480 return bRC_BACL_error;
482 Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
483 return bRC_BACL_error;
487 acl = acl_from_text(content);
491 Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
492 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
493 return bRC_BACL_error;
497 * Restore the ACLs, but don't complain about links which really should
498 * not have attributes, and the file it is linked to may not yet be restored.
499 * This is only true for the old acl streams as in the new implementation we
500 * don't save acls of symlinks (which cannot have acls anyhow)
502 if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){
510 * If the filesystem reports it doesn't support ACLs we clear the
511 * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files
512 * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again
513 * when we change from one filesystem to an other.
515 clear_flag(BACL_FLAG_NATIVE);
516 Mmsg(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
517 Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname);
519 return bRC_BACL_error;
521 Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
522 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
524 return bRC_BACL_error;
531 #endif /* HAVE_ACL */
533 #endif /* HAVE_FREEBSD_OS */