1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2004-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2016 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Original written by Preben 'Peppe' Guldberg, December 2004
25 * Major rewrite by Marco van Wieringen, November 2008
26 * Major overhaul by Marco van Wieringen, January 2012
27 * Moved into findlib so it can be used from other programs, May 2012
28 */
29 /**
30 * @file
31 * Functions to handle ACLs for bareos.
32 *
33 * Currently we support the following OSes:
34 * - AIX (pre-5.3 and post 5.3 acls, acl_get and aclx_get interface)
35 * - Darwin
36 * - FreeBSD (POSIX and NFSv4/ZFS acls)
37 * - GNU Hurd
38 * - HPUX
39 * - IRIX
40 * - Linux
41 * - Solaris (POSIX and NFSv4/ZFS acls)
42 * - Tru64
43 *
44 * Next to OS specific acls we support AFS acls using the pioctl interface.
45 *
46 * We handle two different types of ACLs: access and default ACLS.
47 * On most systems that support default ACLs they only apply to directories.
48 *
49 * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs
50 * independently, while others (eg. Solaris) provide both in one call.
51 *
52 * The Filed saves ACLs in their native format and uses different streams
53 * for all different platforms. Currently we only allow ACLs to be restored
54 * which were saved in the native format of the platform they are extracted
55 * on. Later on we might add conversion functions for mapping from one
56 * platform to another or allow restores of systems that use the same
57 * native format.
58 *
59 * Its also interesting to see what the exact format of acl text is on
60 * certain platforms and if they use they same encoding we might allow
61 * different platform streams to be decoded on another similar platform.
62 */
63
64 #include "include/bareos.h"
65 #include "include/jcr.h"
66 #include "find.h"
67
68 #if !defined(HAVE_ACL) && !defined(HAVE_AFS_ACL)
69 /**
70 * Entry points when compiled without support for ACLs or on an unsupported platform.
71 */
BuildAclStreams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)72 bacl_exit_code BuildAclStreams(JobControlRecord *jcr,
73 acl_data_t *acl_data,
74 FindFilesPacket *ff_pkt)
75 {
76 return bacl_exit_fatal;
77 }
78
parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)79 bacl_exit_code parse_acl_streams(JobControlRecord *jcr,
80 acl_data_t *acl_data,
81 int stream,
82 char *content,
83 uint32_t content_length)
84 {
85 return bacl_exit_fatal;
86 }
87 #else
88 /**
89 * Send an ACL stream to the SD.
90 */
SendAclStream(JobControlRecord * jcr,acl_data_t * acl_data,int stream)91 bacl_exit_code SendAclStream(JobControlRecord *jcr, acl_data_t *acl_data, int stream)
92 {
93 BareosSocket *sd = jcr->store_bsock;
94 POOLMEM *msgsave;
95 #ifdef FD_NO_SEND_TEST
96 return bacl_exit_ok;
97 #endif
98
99 /*
100 * Sanity check
101 */
102 if (acl_data->u.build->content_length <= 0) {
103 return bacl_exit_ok;
104 }
105
106 /*
107 * Send header
108 */
109 if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) {
110 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
111 sd->bstrerror());
112 return bacl_exit_fatal;
113 }
114
115 /*
116 * Send the buffer to the storage deamon
117 */
118 Dmsg1(400, "Backing up ACL <%s>\n", acl_data->u.build->content);
119 msgsave = sd->msg;
120 sd->msg = acl_data->u.build->content;
121 sd->message_length = acl_data->u.build->content_length + 1;
122 if (!sd->send()) {
123 sd->msg = msgsave;
124 sd->message_length = 0;
125 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
126 sd->bstrerror());
127 return bacl_exit_fatal;
128 }
129
130 jcr->JobBytes += sd->message_length;
131 sd->msg = msgsave;
132 if (!sd->signal(BNET_EOD)) {
133 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
134 sd->bstrerror());
135 return bacl_exit_fatal;
136 }
137
138 Dmsg1(200, "ACL of file: %s successfully backed up!\n", acl_data->last_fname);
139 return bacl_exit_ok;
140 }
141
142 /**
143 * First the native ACLs.
144 */
145 #if defined(HAVE_ACL)
146 #if defined(HAVE_AIX_OS)
147
148 #if defined(HAVE_EXTENDED_ACL)
149
150 #include <sys/access.h>
151 #include <sys/acl.h>
152
AclIsTrivial(struct acl * acl)153 static bool AclIsTrivial(struct acl *acl)
154 {
155 return (acl_last(acl) != acl->acl_ext ? false : true);
156 }
157
acl_nfs4_is_trivial(nfs4_acl_int_t * acl)158 static bool acl_nfs4_is_trivial(nfs4_acl_int_t *acl)
159 {
160 #if 0
161 return (acl->aclEntryN > 0 ? false : true);
162 #else
163 int i;
164 int count = acl->aclEntryN;
165 nfs4_ace_int_t *ace;
166
167 for (i = 0; i < count; i++) {
168 ace = &acl->aclEntry[i];
169 if (!((ace->flags & ACE4_ID_SPECIAL) != 0 &&
170 (ace->aceWho.special_whoid == ACE4_WHO_OWNER ||
171 ace->aceWho.special_whoid == ACE4_WHO_GROUP ||
172 ace->aceWho.special_whoid == ACE4_WHO_EVERYONE) &&
173 ace->aceType == ACE4_ACCESS_ALLOWED_ACE_TYPE &&
174 ace->aceFlags == 0 &&
175 (ace->aceMask & ~(ACE4_READ_DATA |
176 ACE4_LIST_DIRECTORY |
177 ACE4_WRITE_DATA |
178 ACE4_ADD_FILE |
179 ACE4_EXECUTE)) == 0)) {
180 return false;
181 }
182 }
183 return true;
184 #endif
185 }
186
187 /**
188 * Define the supported ACL streams for this OS
189 */
190 static int os_access_acl_streams[3] = {
191 STREAM_ACL_AIX_TEXT,
192 STREAM_ACL_AIX_AIXC,
193 STREAM_ACL_AIX_NFS4
194 };
195 static int os_default_acl_streams[1] = {
196 -1
197 };
198
aix_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)199 static bacl_exit_code aix_build_acl_streams(JobControlRecord *jcr,
200 acl_data_t *acl_data,
201 FindFilesPacket *ff_pkt)
202 {
203 mode_t mode;
204 acl_type_t type;
205 size_t aclsize, acltxtsize;
206 bacl_exit_code retval = bacl_exit_error;
207 POOLMEM *aclbuf = GetPoolMemory(PM_MESSAGE);
208
209 /*
210 * First see how big the buffers should be.
211 */
212 memset(&type, 0, sizeof(acl_type_t));
213 type.u64 = ACL_ANY;
214 if (aclx_get(acl_data->last_fname,
215 #if defined(GET_ACL_FOR_LINK)
216 GET_ACLINFO_ONLY | GET_ACL_FOR_LINK,
217 #else
218 GET_ACLINFO_ONLY,
219 #endif
220 &type, NULL, &aclsize, &mode) < 0) {
221 BErrNo be;
222
223 switch (errno) {
224 case ENOENT:
225 retval = bacl_exit_ok;
226 goto bail_out;
227 case ENOSYS:
228 /*
229 * If the filesystem reports it doesn't support ACLs we clear the
230 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
231 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
232 * when we change from one filesystem to another.
233 */
234 acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
235 retval = bacl_exit_ok;
236 goto bail_out;
237 default:
238 Mmsg2(jcr->errmsg,
239 _("aclx_get error on file \"%s\": ERR=%s\n"),
240 acl_data->last_fname, be.bstrerror());
241 Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
242 acl_data->last_fname, be.bstrerror());
243 goto bail_out;
244 }
245 }
246
247 /*
248 * Make sure the buffers are big enough.
249 */
250 aclbuf = CheckPoolMemorySize(aclbuf, aclsize + 1);
251
252 /*
253 * Retrieve the ACL info.
254 */
255 if (aclx_get(acl_data->last_fname,
256 #if defined(GET_ACL_FOR_LINK)
257 GET_ACL_FOR_LINK,
258 #else
259 0,
260 #endif
261 &type, aclbuf, &aclsize, &mode) < 0) {
262 BErrNo be;
263
264 switch (errno) {
265 case ENOENT:
266 retval = bacl_exit_ok;
267 goto bail_out;
268 default:
269 Mmsg2(jcr->errmsg,
270 _("aclx_get error on file \"%s\": ERR=%s\n"),
271 acl_data->last_fname, be.bstrerror());
272 Dmsg2(100, "aclx_get error file=%s ERR=%s\n",
273 acl_data->last_fname, be.bstrerror());
274 goto bail_out;
275 }
276 }
277
278 /*
279 * See if the acl is non trivial.
280 */
281 switch (type.u64) {
282 case ACL_AIXC:
283 if (AclIsTrivial((struct acl *)aclbuf)) {
284 retval = bacl_exit_ok;
285 goto bail_out;
286 }
287 break;
288 case ACL_NFS4:
289 if (acl_nfs4_is_trivial((nfs4_acl_int_t *)aclbuf)) {
290 retval = bacl_exit_ok;
291 goto bail_out;
292 }
293 break;
294 default:
295 Mmsg2(jcr->errmsg,
296 _("Unknown acl type encountered on file \"%s\": %ld\n"),
297 acl_data->last_fname, type.u64);
298 Dmsg2(100, "Unknown acl type encountered on file \"%s\": %ld\n",
299 acl_data->last_fname, type.u64);
300 goto bail_out;
301 }
302
303 /*
304 * We have a non-trivial acl lets convert it into some ASCII form.
305 */
306 acltxtsize = SizeofPoolMemory(acl_data->u.build->content);
307 if (aclx_printStr(acl_data->u.build->content, &acltxtsize, aclbuf,
308 aclsize, type, acl_data->last_fname, 0) < 0) {
309 switch (errno) {
310 case ENOSPC:
311 /*
312 * Our buffer is not big enough, acltxtsize should be updated with the value
313 * the aclx_printStr really need. So we increase the buffer and try again.
314 */
315 acl_data->u.build->content =
316 CheckPoolMemorySize(acl_data->u.build->content, acltxtsize + 1);
317 if (aclx_printStr(acl_data->u.build->content, &acltxtsize, aclbuf,
318 aclsize, type, acl_data->last_fname, 0) < 0) {
319 Mmsg1(jcr->errmsg,
320 _("Failed to convert acl into text on file \"%s\"\n"),
321 acl_data->last_fname);
322 Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
323 acl_data->last_fname, type.u64);
324 goto bail_out;
325 }
326 break;
327 default:
328 Mmsg1(jcr->errmsg,
329 _("Failed to convert acl into text on file \"%s\"\n"),
330 acl_data->last_fname);
331 Dmsg2(100, "Failed to convert acl into text on file \"%s\": %ld\n",
332 acl_data->last_fname, type.u64);
333 goto bail_out;
334 }
335 }
336
337 acl_data->u.build->content_length = strlen(acl_data->u.build->content) + 1;
338 switch (type.u64) {
339 case ACL_AIXC:
340 retval = SendAclStream(jcr, acl_data, STREAM_ACL_AIX_AIXC);
341 break;
342 case ACL_NFS4:
343 retval = SendAclStream(jcr, acl_data, STREAM_ACL_AIX_NFS4);
344 break;
345 }
346
347 bail_out:
348 FreePoolMemory(aclbuf);
349
350 return retval;
351 }
352
353 /**
354 * See if a specific type of ACLs are supported on the filesystem
355 * the file is located on.
356 */
aix_query_acl_support(JobControlRecord * jcr,acl_data_t * acl_data,uint64_t aclType,acl_type_t * pacl_type_info)357 static inline bool aix_query_acl_support(JobControlRecord *jcr,
358 acl_data_t *acl_data,
359 uint64_t aclType,
360 acl_type_t *pacl_type_info)
361 {
362 unsigned int i;
363 acl_types_list_t acl_type_list;
364 size_t acl_type_list_len = sizeof(acl_types_list_t);
365
366 memset(&acl_type_list, 0, sizeof(acl_type_list));
367 if (aclx_gettypes(acl_data->last_fname, &acl_type_list, &acl_type_list_len)) {
368 return false;
369 }
370
371 for (i = 0; i < acl_type_list.num_entries; i++) {
372 if (acl_type_list.entries[i].u64 == aclType) {
373 memcpy(pacl_type_info, acl_type_list.entries + i, sizeof(acl_type_t));
374 return true;
375 }
376 }
377 return false;
378 }
379
aix_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)380 static bacl_exit_code aix_parse_acl_streams(JobControlRecord *jcr,
381 acl_data_t *acl_data,
382 int stream,
383 char *content,
384 uint32_t content_length)
385 {
386 int cnt;
387 acl_type_t type;
388 size_t aclsize;
389 bacl_exit_code retval = bacl_exit_error;
390 POOLMEM *aclbuf = GetPoolMemory(PM_MESSAGE);
391
392 switch (stream) {
393 case STREAM_ACL_AIX_TEXT:
394 /*
395 * Handle the old stream using the old system call for now.
396 */
397 if (acl_put(acl_data->last_fname, content, 0) != 0) {
398 retval = bacl_exit_error;
399 goto bail_out;
400 }
401 retval = bacl_exit_ok;
402 goto bail_out;
403 case STREAM_ACL_AIX_AIXC:
404 if (!aix_query_acl_support(jcr, acl_data, ACL_AIXC, &type)) {
405 Mmsg1(jcr->errmsg,
406 _("Trying to restore POSIX acl on file \"%s\" on filesystem without AIXC acl support\n"),
407 acl_data->last_fname);
408 goto bail_out;
409 }
410 break;
411 case STREAM_ACL_AIX_NFS4:
412 if (!aix_query_acl_support(jcr, acl_data, ACL_NFS4, &type)) {
413 Mmsg1(jcr->errmsg,
414 _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without NFS4 acl support\n"),
415 acl_data->last_fname);
416 goto bail_out;
417 }
418 break;
419 default:
420 goto bail_out;
421 } /* end switch (stream) */
422
423 /*
424 * Set the acl buffer to an initial size. For now we set it
425 * to the same size as the ASCII representation.
426 */
427 aclbuf = CheckPoolMemorySize(aclbuf, content_length);
428 aclsize = content_length;
429 if (aclx_scanStr(content, aclbuf, &aclsize, type) < 0) {
430 BErrNo be;
431
432 switch (errno) {
433 case ENOSPC:
434 /*
435 * The buffer isn't big enough. The man page doesn't say that aclsize
436 * is updated to the needed size as what is done with aclx_printStr.
437 * So for now we try to increase the buffer a maximum of 3 times
438 * and retry the conversion.
439 */
440 for (cnt = 0; cnt < 3; cnt++) {
441 aclsize = 2 * aclsize;
442 aclbuf = CheckPoolMemorySize(aclbuf, aclsize);
443
444 if (aclx_scanStr(content, aclbuf, &aclsize, type) == 0) {
445 break;
446 }
447
448 /*
449 * See why we failed this time, ENOSPC retry if max retries not met,
450 * otherwise abort.
451 */
452 switch (errno) {
453 case ENOSPC:
454 if (cnt < 3) {
455 continue;
456 }
457 /*
458 * FALLTHROUGH
459 */
460 default:
461 Mmsg2(jcr->errmsg,
462 _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
463 acl_data->last_fname, be.bstrerror(errno));
464 Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
465 acl_data->last_fname, be.bstrerror());
466 goto bail_out;
467 }
468 }
469 break;
470 default:
471 Mmsg2(jcr->errmsg,
472 _("aclx_scanStr error on file \"%s\": ERR=%s\n"),
473 acl_data->last_fname, be.bstrerror());
474 Dmsg2(100, "aclx_scanStr error file=%s ERR=%s\n",
475 acl_data->last_fname, be.bstrerror());
476 }
477 }
478
479 if (aclx_put(acl_data->last_fname, SET_ACL, type, aclbuf, aclsize, 0) < 0) {
480 BErrNo be;
481
482 switch (errno) {
483 case ENOENT:
484 retval = bacl_exit_ok;
485 goto bail_out;
486 case ENOSYS:
487 /*
488 * If the filesystem reports it doesn't support ACLs we clear the
489 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
490 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
491 * when we change from one filesystem to another.
492 */
493 acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
494 retval = bacl_exit_ok;
495 goto bail_out;
496 default:
497 Mmsg2(jcr->errmsg,
498 _("aclx_put error on file \"%s\": ERR=%s\n"),
499 acl_data->last_fname, be.bstrerror());
500 Dmsg2(100, "aclx_put error file=%s ERR=%s\n",
501 acl_data->last_fname, be.bstrerror());
502 goto bail_out;
503 }
504 }
505
506 retval = bacl_exit_ok;
507
508 bail_out:
509 FreePoolMemory(aclbuf);
510
511 return retval;
512 }
513
514 #else /* HAVE_EXTENDED_ACL */
515
516 #include <sys/access.h>
517
518 /**
519 * Define the supported ACL streams for this OS
520 */
521 static int os_access_acl_streams[1] = {
522 STREAM_ACL_AIX_TEXT
523 };
524 static int os_default_acl_streams[1] = {
525 -1
526 };
527
aix_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)528 static bacl_exit_code aix_build_acl_streams(JobControlRecord *jcr,
529 acl_data_t *acl_data,
530 FindFilesPacket *ff_pkt)
531 {
532 char *acl_text;
533
534 if ((acl_text = acl_get(acl_data->last_fname)) != NULL) {
535 acl_data->u.build->content_length =
536 PmStrcpy(acl_data->u.build->content, acl_text);
537 Actuallyfree(acl_text);
538 return SendAclStream(jcr, acl_data, STREAM_ACL_AIX_TEXT);
539 }
540 return bacl_exit_error;
541 }
542
aix_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)543 static bacl_exit_code aix_parse_acl_streams(JobControlRecord *jcr,
544 acl_data_t *acl_data,
545 int stream,
546 char *content,
547 uint32_t content_length)
548 {
549 if (acl_put(acl_data->last_fname, content, 0) != 0) {
550 return bacl_exit_error;
551 }
552 return bacl_exit_ok;
553 }
554 #endif /* HAVE_EXTENDED_ACL */
555
556 /**
557 * For this OS setup the build and parse function pointer to the OS specific functions.
558 */
559 static bacl_exit_code (*os_build_acl_streams)
560 (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
561 aix_build_acl_streams;
562 static bacl_exit_code (*os_parse_acl_streams)
563 (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
564 aix_parse_acl_streams;
565
566 #elif defined(HAVE_DARWIN_OS) || \
567 defined(HAVE_FREEBSD_OS) || \
568 defined(HAVE_IRIX_OS) || \
569 defined(HAVE_OSF1_OS) || \
570 defined(HAVE_LINUX_OS) || \
571 defined(HAVE_HURD_OS)
572
573 #include <sys/types.h>
574
575 #ifdef HAVE_SYS_ACL_H
576 #include <sys/acl.h>
577 #else
578 #error "configure failed to detect availability of sys/acl.h"
579 #endif
580
581 /**
582 * On IRIX we can get shortened ACLs
583 */
584 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
585 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
586 #endif
587
588 /**
589 * On Linux we can get numeric and/or shorted ACLs
590 */
591 #if defined(HAVE_LINUX_OS)
592 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
593 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
594 #elif defined(BACL_WANT_SHORT_ACLS)
595 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
596 #elif defined(BACL_WANT_NUMERIC_IDS)
597 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
598 #endif
599 #ifdef BACL_ALTERNATE_TEXT
600 #include <acl/libacl.h>
601 #define acl_to_text(acl,len) (acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
602 #endif
603 #endif
604
605 /**
606 * On FreeBSD we can get numeric ACLs
607 */
608 #if defined(HAVE_FREEBSD_OS)
609 #if defined(BACL_WANT_NUMERIC_IDS)
610 #define BACL_ALTERNATE_TEXT ACL_TEXT_NUMERIC_IDS
611 #endif
612 #ifdef BACL_ALTERNATE_TEXT
613 #define acl_to_text(acl,len) (acl_to_text_np((acl), (len), BACL_ALTERNATE_TEXT))
614 #endif
615 #endif
616
617 /**
618 * Some generic functions used by multiple OSes.
619 */
BacToOsAcltype(bacl_type acltype)620 static acl_type_t BacToOsAcltype(bacl_type acltype)
621 {
622 acl_type_t ostype;
623
624 switch (acltype) {
625 case BACL_TYPE_ACCESS:
626 ostype = ACL_TYPE_ACCESS;
627 break;
628 case BACL_TYPE_DEFAULT:
629 ostype = ACL_TYPE_DEFAULT;
630 break;
631 #ifdef HAVE_ACL_TYPE_NFS4
632 /*
633 * FreeBSD has an additional acl type named ACL_TYPE_NFS4.
634 */
635 case BACL_TYPE_NFS4:
636 ostype = ACL_TYPE_NFS4;
637 break;
638 #endif
639 #ifdef HAVE_ACL_TYPE_DEFAULT_DIR
640 case BACL_TYPE_DEFAULT_DIR:
641 /*
642 * TRU64 has an additional acl type named ACL_TYPE_DEFAULT_DIR.
643 */
644 ostype = ACL_TYPE_DEFAULT_DIR;
645 break;
646 #endif
647 #ifdef HAVE_ACL_TYPE_EXTENDED
648 case BACL_TYPE_EXTENDED:
649 /*
650 * MacOSX has an additional acl type named ACL_TYPE_EXTENDED.
651 */
652 ostype = ACL_TYPE_EXTENDED;
653 break;
654 #endif
655 default:
656 /*
657 * This should never happen, as the per OS version function only tries acl
658 * types supported on a certain platform.
659 */
660 ostype = (acl_type_t)ACL_TYPE_NONE;
661 break;
662 }
663 return ostype;
664 }
665
AclCountEntries(acl_t acl)666 static int AclCountEntries(acl_t acl)
667 {
668 int count = 0;
669 #if defined(HAVE_FREEBSD_OS) || \
670 defined(HAVE_LINUX_OS) || \
671 defined(HAVE_HURD_OS)
672 acl_entry_t ace;
673 int entry_available;
674
675 entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
676 while (entry_available == 1) {
677 count++;
678 entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
679 }
680 #elif defined(HAVE_IRIX_OS)
681 count = acl->acl_cnt;
682 #elif defined(HAVE_OSF1_OS)
683 count = acl->acl_num;
684 #elif defined(HAVE_DARWIN_OS)
685 acl_entry_t ace;
686 int entry_available;
687
688 entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
689 while (entry_available == 0) {
690 count++;
691 entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
692 }
693 #endif
694 return count;
695 }
696
697 #if !defined(HAVE_DARWIN_OS)
698 /**
699 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
700 * There is no need to store those acls as we already store the stat bits too.
701 */
AclIsTrivial(acl_t acl)702 static bool AclIsTrivial(acl_t acl)
703 {
704 /*
705 * acl is trivial if it has only the following entries:
706 * "user::",
707 * "group::",
708 * "other::"
709 */
710 acl_entry_t ace;
711 acl_tag_t tag;
712 #if defined(HAVE_FREEBSD_OS) || \
713 defined(HAVE_LINUX_OS) || \
714 defined(HAVE_HURD_OS)
715 int entry_available;
716
717 entry_available = acl_get_entry(acl, ACL_FIRST_ENTRY, &ace);
718 while (entry_available == 1) {
719 /*
720 * Get the tag type of this acl entry.
721 * If we fail to get the tagtype we call the acl non-trivial.
722 */
723 if (acl_get_tag_type(ace, &tag) < 0)
724 return true;
725 /*
726 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
727 */
728 if (tag != ACL_USER_OBJ &&
729 tag != ACL_GROUP_OBJ &&
730 tag != ACL_OTHER)
731 return false;
732 entry_available = acl_get_entry(acl, ACL_NEXT_ENTRY, &ace);
733 }
734 return true;
735 #elif defined(HAVE_IRIX_OS)
736 int n;
737
738 for (n = 0; n < acl->acl_cnt; n++) {
739 ace = &acl->acl_entry[n];
740 tag = ace->ae_tag;
741
742 /*
743 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
744 */
745 if (tag != ACL_USER_OBJ &&
746 tag != ACL_GROUP_OBJ &&
747 tag != ACL_OTHER_OBJ)
748 return false;
749 }
750 return true;
751 #elif defined(HAVE_OSF1_OS)
752 int count;
753
754 ace = acl->acl_first;
755 count = acl->acl_num;
756
757 while (count > 0) {
758 tag = ace->entry->acl_type;
759 /*
760 * Anything other the ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER breaks the spell.
761 */
762 if (tag != ACL_USER_OBJ &&
763 tag != ACL_GROUP_OBJ &&
764 tag != ACL_OTHER)
765 return false;
766 /*
767 * On Tru64, perm can also contain non-standard bits such as
768 * PERM_INSERT, PERM_DELETE, PERM_MODIFY, PERM_LOOKUP, ...
769 */
770 if ((ace->entry->acl_perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)))
771 return false;
772 ace = ace->next;
773 count--;
774 }
775 return true;
776 #endif
777 }
778 #endif
779
780 /**
781 * Generic wrapper around acl_get_file call.
782 */
generic_get_acl_from_os(JobControlRecord * jcr,acl_data_t * acl_data,bacl_type acltype)783 static bacl_exit_code generic_get_acl_from_os(JobControlRecord *jcr,
784 acl_data_t *acl_data,
785 bacl_type acltype)
786 {
787 acl_t acl;
788 acl_type_t ostype;
789 char *acl_text;
790 bacl_exit_code retval = bacl_exit_ok;
791
792 ostype = BacToOsAcltype(acltype);
793 acl = acl_get_file(acl_data->last_fname, ostype);
794 if (acl) {
795 /**
796 * From observation, IRIX's acl_get_file() seems to return a
797 * non-NULL acl with a count field of -1 when a file has no ACL
798 * defined, while IRIX's acl_to_text() returns NULL when presented
799 * with such an ACL.
800 *
801 * For all other implmentations we check if there are more then
802 * zero entries in the acl returned.
803 */
804 if (AclCountEntries(acl) <= 0) {
805 goto bail_out;
806 }
807
808 /*
809 * Make sure this is not just a trivial ACL.
810 */
811 #if !defined(HAVE_DARWIN_OS)
812 if (acltype == BACL_TYPE_ACCESS && AclIsTrivial(acl)) {
813 /*
814 * The ACLs simply reflect the (already known) standard permissions
815 * So we don't send an ACL stream to the SD.
816 */
817 goto bail_out;
818 }
819 #endif
820 #if defined(HAVE_FREEBSD_OS) && defined(_PC_ACL_NFS4)
821 if (acltype == BACL_TYPE_NFS4) {
822 int trivial;
823 if (acl_is_trivial_np(acl, &trivial) == 0) {
824 if (trivial == 1) {
825 /*
826 * The ACLs simply reflect the (already known) standard permissions
827 * So we don't send an ACL stream to the SD.
828 */
829 goto bail_out;
830 }
831 }
832 }
833 #endif
834
835 /*
836 * Convert the internal acl representation into a text representation.
837 */
838 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
839 acl_data->u.build->content_length =
840 PmStrcpy(acl_data->u.build->content, acl_text);
841 acl_free(acl);
842 acl_free(acl_text);
843 return bacl_exit_ok;
844 }
845
846 BErrNo be;
847 Mmsg2(jcr->errmsg,
848 _("acl_to_text error on file \"%s\": ERR=%s\n"),
849 acl_data->last_fname, be.bstrerror());
850 Dmsg2(100, "acl_to_text error file=%s ERR=%s\n",
851 acl_data->last_fname, be.bstrerror());
852
853 retval = bacl_exit_error;
854 goto bail_out;
855 } else {
856 BErrNo be;
857
858 /*
859 * Handle errors gracefully.
860 */
861 switch (errno) {
862 #if defined(BACL_ENOTSUP)
863 case BACL_ENOTSUP:
864 /*
865 * If the filesystem reports it doesn't support ACLs we clear the
866 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
867 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
868 * when we change from one filesystem to another.
869 */
870 acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
871 goto bail_out;
872 #endif
873 case ENOENT:
874 goto bail_out;
875 default:
876 /* Some real error */
877 Mmsg2(jcr->errmsg,
878 _("acl_get_file error on file \"%s\": ERR=%s\n"),
879 acl_data->last_fname, be.bstrerror());
880 Dmsg2(100, "acl_get_file error file=%s ERR=%s\n",
881 acl_data->last_fname, be.bstrerror());
882
883 retval = bacl_exit_error;
884 goto bail_out;
885 }
886 }
887
888 bail_out:
889 if (acl) {
890 acl_free(acl);
891 }
892 PmStrcpy(acl_data->u.build->content, "");
893 acl_data->u.build->content_length = 0;
894 return retval;
895 }
896
897 /**
898 * Generic wrapper around acl_set_file call.
899 */
generic_set_acl_on_os(JobControlRecord * jcr,acl_data_t * acl_data,bacl_type acltype,char * content,uint32_t content_length)900 static bacl_exit_code generic_set_acl_on_os(JobControlRecord *jcr,
901 acl_data_t *acl_data,
902 bacl_type acltype,
903 char *content,
904 uint32_t content_length)
905 {
906 acl_t acl;
907 acl_type_t ostype;
908
909 /*
910 * If we get empty default ACLs, clear ACLs now
911 */
912 ostype = BacToOsAcltype(acltype);
913 if (ostype == ACL_TYPE_DEFAULT && strlen(content) == 0) {
914 if (acl_delete_def_file(acl_data->last_fname) == 0) {
915 return bacl_exit_ok;
916 }
917 BErrNo be;
918
919 switch (errno) {
920 case ENOENT:
921 return bacl_exit_ok;
922 #if defined(BACL_ENOTSUP)
923 case BACL_ENOTSUP:
924 /*
925 * If the filesystem reports it doesn't support ACLs we clear the
926 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
927 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
928 * when we change from one filesystem to another.
929 */
930 acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
931 Mmsg1(jcr->errmsg,
932 _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"),
933 acl_data->last_fname);
934 return bacl_exit_error;
935 #endif
936 default:
937 Mmsg2(jcr->errmsg,
938 _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
939 acl_data->last_fname, be.bstrerror());
940 return bacl_exit_error;
941 }
942 }
943
944 acl = acl_from_text(content);
945 if (acl == NULL) {
946 BErrNo be;
947
948 Mmsg2(jcr->errmsg,
949 _("acl_from_text error on file \"%s\": ERR=%s\n"),
950 acl_data->last_fname, be.bstrerror());
951 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
952 content, acl_data->last_fname, be.bstrerror());
953 return bacl_exit_error;
954 }
955
956 /**
957 * Only validate POSIX acls the acl_valid interface is only implemented
958 * for checking POSIX acls on most platforms.
959 */
960 switch (acltype) {
961 case BACL_TYPE_NFS4:
962 /*
963 * Skip acl_valid tests on NFSv4 acls.
964 */
965 break;
966 default:
967 if (acl_valid(acl) != 0) {
968 BErrNo be;
969
970 Mmsg2(jcr->errmsg, _("acl_valid error on file \"%s\": ERR=%s\n"),
971 acl_data->last_fname, be.bstrerror());
972 Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",
973 content, acl_data->last_fname, be.bstrerror());
974 acl_free(acl);
975 return bacl_exit_error;
976 }
977 break;
978 }
979
980 /**
981 * Restore the ACLs, but don't complain about links which really should
982 * not have attributes, and the file it is linked to may not yet be restored.
983 * This is only true for the old acl streams as in the new implementation we
984 * don't save acls of symlinks (which cannot have acls anyhow)
985 */
986 if (acl_set_file(acl_data->last_fname, ostype, acl) != 0 && acl_data->filetype != FT_LNK) {
987 BErrNo be;
988
989 switch (errno) {
990 case ENOENT:
991 acl_free(acl);
992 return bacl_exit_ok;
993 #if defined(BACL_ENOTSUP)
994 case BACL_ENOTSUP:
995 /*
996 * If the filesystem reports it doesn't support ACLs we clear the
997 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
998 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
999 * when we change from one filesystem to another.
1000 */
1001 acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
1002 Mmsg1(jcr->errmsg,
1003 _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"),
1004 acl_data->last_fname);
1005 Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n",
1006 content, acl_data->last_fname);
1007 acl_free(acl);
1008 return bacl_exit_error;
1009 #endif
1010 default:
1011 Mmsg2(jcr->errmsg,
1012 _("acl_set_file error on file \"%s\": ERR=%s\n"),
1013 acl_data->last_fname, be.bstrerror());
1014 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
1015 content, acl_data->last_fname, be.bstrerror());
1016 acl_free(acl);
1017 return bacl_exit_error;
1018 }
1019 }
1020 acl_free(acl);
1021 return bacl_exit_ok;
1022 }
1023
1024 /**
1025 * OS specific functions for handling different types of acl streams.
1026 */
1027 #if defined(HAVE_DARWIN_OS)
1028 /**
1029 * Define the supported ACL streams for this OS
1030 */
1031 static int os_access_acl_streams[1] = {
1032 STREAM_ACL_DARWIN_ACCESS_ACL
1033 };
1034 static int os_default_acl_streams[1] = {
1035 -1
1036 };
1037
darwin_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)1038 static bacl_exit_code darwin_build_acl_streams(JobControlRecord *jcr,
1039 acl_data_t *acl_data,
1040 FindFilesPacket *ff_pkt)
1041 {
1042 #if defined(HAVE_ACL_TYPE_EXTENDED)
1043 /**
1044 * On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
1045 * and acl_get_file (name, ACL_TYPE_DEFAULT)
1046 * always return NULL / EINVAL. There is no point in making
1047 * these two useless calls. The real ACL is retrieved through
1048 * acl_get_file (name, ACL_TYPE_EXTENDED).
1049 *
1050 * Read access ACLs for files, dirs and links
1051 */
1052 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_EXTENDED) == bacl_exit_fatal)
1053 return bacl_exit_fatal;
1054 #else
1055 /**
1056 * Read access ACLs for files, dirs and links
1057 */
1058 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_ACCESS) == bacl_exit_fatal)
1059 return bacl_exit_fatal;
1060 #endif
1061
1062 if (acl_data->u.build->content_length > 0) {
1063 return SendAclStream(jcr, acl_data, STREAM_ACL_DARWIN_ACCESS_ACL);
1064 }
1065 return bacl_exit_ok;
1066 }
1067
darwin_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)1068 static bacl_exit_code darwin_parse_acl_streams(JobControlRecord *jcr,
1069 acl_data_t *acl_data,
1070 int stream,
1071 char *content,
1072 uint32_t content_length)
1073 {
1074 #if defined(HAVE_ACL_TYPE_EXTENDED)
1075 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_EXTENDED,
1076 content, content_length);
1077 #else
1078 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1079 content, content_length);
1080 #endif
1081 }
1082
1083 /**
1084 * For this OS setup the build and parse function pointer to the OS specific functions.
1085 */
1086 static bacl_exit_code (*os_build_acl_streams)
1087 (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1088 darwin_build_acl_streams;
1089 static bacl_exit_code (*os_parse_acl_streams)
1090 (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1091 darwin_parse_acl_streams;
1092
1093 #elif defined(HAVE_FREEBSD_OS)
1094 /**
1095 * Define the supported ACL streams for these OSes
1096 */
1097 static int os_access_acl_streams[2] = {
1098 STREAM_ACL_FREEBSD_ACCESS_ACL,
1099 STREAM_ACL_FREEBSD_NFS4_ACL
1100 };
1101 static int os_default_acl_streams[1] = {
1102 STREAM_ACL_FREEBSD_DEFAULT_ACL
1103 };
1104
freebsd_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)1105 static bacl_exit_code freebsd_build_acl_streams(JobControlRecord *jcr,
1106 acl_data_t *acl_data,
1107 FindFilesPacket *ff_pkt)
1108 {
1109 int acl_enabled = 0;
1110 bacl_type acltype = BACL_TYPE_NONE;
1111
1112 #if defined(_PC_ACL_NFS4)
1113 /*
1114 * See if filesystem supports NFS4 acls.
1115 */
1116 acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_NFS4);
1117 switch (acl_enabled) {
1118 case -1: {
1119 BErrNo be;
1120
1121 switch (errno) {
1122 case ENOENT:
1123 return bacl_exit_ok;
1124 default:
1125 Mmsg2(jcr->errmsg,
1126 _("pathconf error on file \"%s\": ERR=%s\n"),
1127 acl_data->last_fname, be.bstrerror());
1128 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
1129 acl_data->last_fname, be.bstrerror());
1130 return bacl_exit_error;
1131 }
1132 }
1133 case 0:
1134 break;
1135 default:
1136 acltype = BACL_TYPE_NFS4;
1137 break;
1138 }
1139 #endif
1140
1141 if (acl_enabled == 0) {
1142 /*
1143 * See if filesystem supports POSIX acls.
1144 */
1145 acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_EXTENDED);
1146 switch (acl_enabled) {
1147 case -1: {
1148 BErrNo be;
1149
1150 switch (errno) {
1151 case ENOENT:
1152 return bacl_exit_ok;
1153 default:
1154 Mmsg2(jcr->errmsg,
1155 _("pathconf error on file \"%s\": ERR=%s\n"),
1156 acl_data->last_fname, be.bstrerror());
1157 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
1158 acl_data->last_fname, be.bstrerror());
1159 return bacl_exit_error;
1160 }
1161 }
1162 case 0:
1163 break;
1164 default:
1165 acltype = BACL_TYPE_ACCESS;
1166 break;
1167 }
1168 }
1169
1170 /*
1171 * If the filesystem reports it doesn't support ACLs we clear the
1172 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1173 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1174 * when we change from one filesystem to another.
1175 */
1176 if (acl_enabled == 0) {
1177 acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1178 PmStrcpy(acl_data->u.build->content, "");
1179 acl_data->u.build->content_length = 0;
1180 return bacl_exit_ok;
1181 }
1182
1183 /*
1184 * Based on the supported ACLs retrieve and store them.
1185 */
1186 switch (acltype) {
1187 case BACL_TYPE_NFS4:
1188 /*
1189 * Read NFS4 ACLs for files, dirs and links
1190 */
1191 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_NFS4) == bacl_exit_fatal)
1192 return bacl_exit_fatal;
1193
1194 if (acl_data->u.build->content_length > 0) {
1195 if (SendAclStream(jcr, acl_data, STREAM_ACL_FREEBSD_NFS4_ACL) == bacl_exit_fatal)
1196 return bacl_exit_fatal;
1197 }
1198 break;
1199 case BACL_TYPE_ACCESS:
1200 /*
1201 * Read access ACLs for files, dirs and links
1202 */
1203 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_ACCESS) == bacl_exit_fatal)
1204 return bacl_exit_fatal;
1205
1206 if (acl_data->u.build->content_length > 0) {
1207 if (SendAclStream(jcr, acl_data, STREAM_ACL_FREEBSD_ACCESS_ACL) == bacl_exit_fatal)
1208 return bacl_exit_fatal;
1209 }
1210
1211 /*
1212 * Directories can have default ACLs too
1213 */
1214 if (acl_data->filetype == FT_DIREND) {
1215 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
1216 return bacl_exit_fatal;
1217 if (acl_data->u.build->content_length > 0) {
1218 if (SendAclStream(jcr, acl_data, STREAM_ACL_FREEBSD_DEFAULT_ACL) == bacl_exit_fatal)
1219 return bacl_exit_fatal;
1220 }
1221 }
1222 break;
1223 default:
1224 break;
1225 }
1226
1227 return bacl_exit_ok;
1228 }
1229
freebsd_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)1230 static bacl_exit_code freebsd_parse_acl_streams(JobControlRecord *jcr,
1231 acl_data_t *acl_data,
1232 int stream,
1233 char *content,
1234 uint32_t content_length)
1235 {
1236 int acl_enabled = 0;
1237 const char *acl_type_name;
1238
1239 /*
1240 * First make sure the filesystem supports acls.
1241 */
1242 switch (stream) {
1243 case STREAM_UNIX_ACCESS_ACL:
1244 case STREAM_ACL_FREEBSD_ACCESS_ACL:
1245 case STREAM_UNIX_DEFAULT_ACL:
1246 case STREAM_ACL_FREEBSD_DEFAULT_ACL:
1247 acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_EXTENDED);
1248 acl_type_name = "POSIX";
1249 break;
1250 case STREAM_ACL_FREEBSD_NFS4_ACL:
1251 #if defined(_PC_ACL_NFS4)
1252 acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_NFS4);
1253 #endif
1254 acl_type_name = "NFS4";
1255 break;
1256 default:
1257 acl_type_name = "unknown";
1258 break;
1259 }
1260
1261 switch (acl_enabled) {
1262 case -1: {
1263 BErrNo be;
1264
1265 switch (errno) {
1266 case ENOENT:
1267 return bacl_exit_ok;
1268 default:
1269 Mmsg2(jcr->errmsg,
1270 _("pathconf error on file \"%s\": ERR=%s\n"),
1271 acl_data->last_fname, be.bstrerror());
1272 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
1273 content, acl_data->last_fname, be.bstrerror());
1274 return bacl_exit_error;
1275 }
1276 }
1277 case 0:
1278 /*
1279 * If the filesystem reports it doesn't support ACLs we clear the
1280 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
1281 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
1282 * when we change from one filesystem to another.
1283 */
1284 acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1285 Mmsg2(jcr->errmsg,
1286 _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"),
1287 acl_data->last_fname, acl_type_name);
1288 return bacl_exit_error;
1289 default:
1290 break;
1291 }
1292
1293 /*
1294 * Restore the ACLs.
1295 */
1296 switch (stream) {
1297 case STREAM_UNIX_ACCESS_ACL:
1298 case STREAM_ACL_FREEBSD_ACCESS_ACL:
1299 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1300 content, content_length);
1301 case STREAM_UNIX_DEFAULT_ACL:
1302 case STREAM_ACL_FREEBSD_DEFAULT_ACL:
1303 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT,
1304 content, content_length);
1305 case STREAM_ACL_FREEBSD_NFS4_ACL:
1306 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_NFS4,
1307 content, content_length);
1308 default:
1309 break;
1310 }
1311 return bacl_exit_error;
1312 }
1313
1314 /**
1315 * For this OSes setup the build and parse function pointer to the OS specific functions.
1316 */
1317 static bacl_exit_code (*os_build_acl_streams)
1318 (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1319 freebsd_build_acl_streams;
1320 static bacl_exit_code (*os_parse_acl_streams)
1321 (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1322 freebsd_parse_acl_streams;
1323
1324 #elif defined(HAVE_IRIX_OS) || \
1325 defined(HAVE_LINUX_OS) || \
1326 defined(HAVE_HURD_OS)
1327 /**
1328 * Define the supported ACL streams for these OSes
1329 */
1330 #if defined(HAVE_IRIX_OS)
1331 static int os_access_acl_streams[1] = {
1332 STREAM_ACL_IRIX_ACCESS_ACL
1333 };
1334 static int os_default_acl_streams[1] = {
1335 STREAM_ACL_IRIX_DEFAULT_ACL
1336 };
1337 #elif defined(HAVE_LINUX_OS)
1338 static int os_access_acl_streams[1] = {
1339 STREAM_ACL_LINUX_ACCESS_ACL
1340 };
1341 static int os_default_acl_streams[1] = {
1342 STREAM_ACL_LINUX_DEFAULT_ACL
1343 };
1344 #elif defined(HAVE_HURD_OS)
1345 static int os_access_acl_streams[1] = {
1346 STREAM_ACL_HURD_ACCESS_ACL
1347 };
1348 static int os_default_acl_streams[1] = {
1349 STREAM_ACL_HURD_DEFAULT_ACL
1350 };
1351 #endif
1352
generic_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)1353 static bacl_exit_code generic_build_acl_streams(JobControlRecord *jcr,
1354 acl_data_t *acl_data,
1355 FindFilesPacket *ff_pkt)
1356 {
1357 /*
1358 * Read access ACLs for files, dirs and links
1359 */
1360 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_ACCESS) == bacl_exit_fatal)
1361 return bacl_exit_fatal;
1362
1363 if (acl_data->u.build->content_length > 0) {
1364 if (SendAclStream(jcr, acl_data, os_access_acl_streams[0]) == bacl_exit_fatal)
1365 return bacl_exit_fatal;
1366 }
1367
1368 /*
1369 * Directories can have default ACLs too
1370 */
1371 if (acl_data->filetype == FT_DIREND) {
1372 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_DEFAULT) == bacl_exit_fatal)
1373 return bacl_exit_fatal;
1374 if (acl_data->u.build->content_length > 0) {
1375 if (SendAclStream(jcr, acl_data, os_default_acl_streams[0]) == bacl_exit_fatal)
1376 return bacl_exit_fatal;
1377 }
1378 }
1379 return bacl_exit_ok;
1380 }
1381
generic_parse_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,int stream,char * content,uint32_t content_length)1382 static bacl_exit_code generic_parse_acl_streams(JobControlRecord *jcr,
1383 acl_data_t *acl_data,
1384 int stream,
1385 char *content,
1386 uint32_t content_length)
1387 {
1388 unsigned int cnt;
1389
1390 switch (stream) {
1391 case STREAM_UNIX_ACCESS_ACL:
1392 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1393 content, content_length);
1394 case STREAM_UNIX_DEFAULT_ACL:
1395 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT,
1396 content, content_length);
1397 default:
1398 /*
1399 * See what type of acl it is.
1400 */
1401 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
1402 if (os_access_acl_streams[cnt] == stream) {
1403 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1404 content, content_length);
1405 }
1406 }
1407 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
1408 if (os_default_acl_streams[cnt] == stream) {
1409 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT,
1410 content, content_length);
1411 }
1412 }
1413 break;
1414 }
1415 return bacl_exit_error;
1416 }
1417
1418 /**
1419 * For this OSes setup the build and parse function pointer to the OS specific functions.
1420 */
1421 static bacl_exit_code (*os_build_acl_streams)
1422 (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1423 generic_build_acl_streams;
1424 static bacl_exit_code (*os_parse_acl_streams)
1425 (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1426 generic_parse_acl_streams;
1427
1428 #elif defined(HAVE_OSF1_OS)
1429
1430 /**
1431 * Define the supported ACL streams for this OS
1432 */
1433 static int os_access_acl_streams[1] = {
1434 STREAM_ACL_TRU64_ACCESS_ACL
1435 };
1436 static int os_default_acl_streams[2] = {
1437 STREAM_ACL_TRU64_DEFAULT_ACL,
1438 STREAM_ACL_TRU64_DEFAULT_DIR_ACL
1439 };
1440
tru64_build_acl_streams(JobControlRecord * jcr,acl_data_t * acl_data,FindFilesPacket * ff_pkt)1441 static bacl_exit_code tru64_build_acl_streams(JobControlRecord *jcr,
1442 acl_data_t *acl_data,
1443 FindFilesPacket *ff_pkt)
1444 {
1445 /*
1446 * Read access ACLs for files, dirs and links
1447 */
1448 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_ACCESS) == bacl_exit_fatal) {
1449 return bacl_exit_error;
1450 if (acl_data->u.build->content_length > 0) {
1451 if (!SendAclStream(jcr, acl_data, STREAM_ACL_TRU64_ACCESS_ACL))
1452 return bacl_exit_error;
1453 }
1454 /*
1455 * Directories can have default ACLs too
1456 */
1457 if (acl_data->filetype == FT_DIREND) {
1458 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_DEFAULT) == bacl_exit_fatal) {
1459 return bacl_exit_error;
1460 if (acl_data->u.build->content_length > 0) {
1461 if (!SendAclStream(jcr, acl_data, STREAM_ACL_TRU64_DEFAULT_ACL))
1462 return bacl_exit_error;
1463 }
1464 /**
1465 * Tru64 has next to BACL_TYPE_DEFAULT also BACL_TYPE_DEFAULT_DIR acls.
1466 * This is an inherited acl for all subdirs.
1467 */
1468 if (generic_get_acl_from_os(jcr, acl_data, BACL_TYPE_DEFAULT_DIR) == bacl_exit_fatal) {
1469 return bacl_exit_error;
1470 if (acl_data->u.build->content_length > 0) {
1471 if (!SendAclStream(jcr, acl_data, STREAM_ACL_TRU64_DEFAULT_DIR_ACL))
1472 return bacl_exit_error;
1473 }
1474 }
1475 return bacl_exit_ok;
1476 }
1477
1478 static bacl_exit_code tru64_parse_acl_streams(JobControlRecord *jcr,
1479 acl_data_t *acl_data,
1480 int stream,
1481 char *content,
1482 uint32_t content_length)
1483 {
1484 switch (stream) {
1485 case STREAM_UNIX_ACCESS_ACL:
1486 case STREAM_ACL_TRU64_ACCESS_ACL:
1487 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_ACCESS,
1488 content, content_length);
1489 case STREAM_UNIX_DEFAULT_ACL:
1490 case STREAM_ACL_TRU64_DEFAULT_ACL:
1491 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT,
1492 content, content_length);
1493 case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
1494 return generic_set_acl_on_os(jcr, acl_data, BACL_TYPE_DEFAULT_DIR,
1495 content, content_length);
1496 }
1497
1498 /**
1499 * For this OS setup the build and parse function pointer to the OS specific functions.
1500 */
1501 static bacl_exit_code (*os_build_acl_streams)
1502 (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1503 tru64_build_acl_streams;
1504 static bacl_exit_code (*os_parse_acl_streams)
1505 (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1506 tru64_parse_acl_streams;
1507
1508 #endif
1509
1510 #elif defined(HAVE_HPUX_OS)
1511 #ifdef HAVE_SYS_ACL_H
1512 #include <sys/acl.h>
1513 #else
1514 #error "configure failed to detect availability of sys/acl.h"
1515 #endif
1516
1517 #include <acllib.h>
1518
1519 /**
1520 * Define the supported ACL streams for this OS
1521 */
1522 static int os_access_acl_streams[1] = {
1523 STREAM_ACL_HPUX_ACL_ENTRY
1524 };
1525 static int os_default_acl_streams[1] = {
1526 -1
1527 };
1528
1529 /**
1530 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
1531 * There is no need to store those acls as we already store the stat bits too.
1532 */
1533 static bool AclIsTrivial(int count, struct acl_entry *entries, struct stat sb)
1534 {
1535 int n;
1536 struct acl_entry ace
1537
1538 for (n = 0; n < count; n++) {
1539 ace = entries[n];
1540 /*
1541 * See if this acl just is the stat mode in acl form.
1542 */
1543 if (!((ace.uid == sb.st_uid && ace.gid == ACL_NSGROUP) ||
1544 (ace.uid == ACL_NSUSER && ace.gid == sb.st_gid) ||
1545 (ace.uid == ACL_NSUSER && ace.gid == ACL_NSGROUP)))
1546 return false;
1547 }
1548 return true;
1549 }
1550
1551 /**
1552 * OS specific functions for handling different types of acl streams.
1553 */
1554 static bacl_exit_code hpux_build_acl_streams(JobControlRecord *jcr,
1555 acl_data_t *acl_data
1556 FindFilesPacket *ff_pkt)
1557 {
1558 int n;
1559 struct acl_entry acls[NACLENTRIES];
1560 char *acl_text;
1561
1562 if ((n = getacl(acl_data->last_fname, 0, acls)) < 0) {
1563 BErrNo be;
1564
1565 switch (errno) {
1566 #if defined(BACL_ENOTSUP)
1567 case BACL_ENOTSUP:
1568 /*
1569 * Not supported, just pretend there is nothing to see
1570 *
1571 * If the filesystem reports it doesn't support ACLs we clear the
1572 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1573 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1574 * when we change from one filesystem to another.
1575 */
1576 acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1577 PmStrcpy(acl_data->u.build->content, "");
1578 acl_data->u.build->content_length = 0;
1579 return bacl_exit_ok;
1580 #endif
1581 case ENOENT:
1582 PmStrcpy(acl_data->u.build->content, "");
1583 acl_data->u.build->content_length = 0;
1584 return bacl_exit_ok;
1585 default:
1586 Mmsg2(jcr->errmsg,
1587 _("getacl error on file \"%s\": ERR=%s\n"),
1588 acl_data->last_fname, be.bstrerror());
1589 Dmsg2(100, "getacl error file=%s ERR=%s\n",
1590 acl_data->last_fname, be.bstrerror());
1591
1592 PmStrcpy(acl_data->u.build->content, "");
1593 acl_data->u.build->content_length = 0;
1594 return bacl_exit_error;
1595 }
1596 }
1597 if (n == 0) {
1598 PmStrcpy(acl_data->u.build->content, "");
1599 acl_data->u.build->content_length = 0;
1600 return bacl_exit_ok;
1601 }
1602 if ((n = getacl(acl_data->last_fname, n, acls)) > 0) {
1603 if (AclIsTrivial(n, acls, ff_pkt->statp)) {
1604 /*
1605 * The ACLs simply reflect the (already known) standard permissions
1606 * So we don't send an ACL stream to the SD.
1607 */
1608 PmStrcpy(acl_data->u.build->content, "");
1609 acl_data->u.build->content_length = 0;
1610 return bacl_exit_ok;
1611 }
1612 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
1613 acl_data->u.build->content_length =
1614 PmStrcpy(acl_data->u.build->content, acl_text);
1615 Actuallyfree(acl_text);
1616
1617 return SendAclStream(jcr, acl_data, STREAM_ACL_HPUX_ACL_ENTRY);
1618 }
1619
1620 BErrNo be;
1621 Mmsg2(jcr->errmsg,
1622 _("acltostr error on file \"%s\": ERR=%s\n"),
1623 acl_data->last_fname, be.bstrerror());
1624 Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",
1625 acl_data->u.build->content, acl_data->last_fname, be.bstrerror());
1626 return bacl_exit_error;
1627 }
1628 return bacl_exit_error;
1629 }
1630
1631 static bacl_exit_code hpux_parse_acl_streams(JobControlRecord *jcr,
1632 acl_data_t *acl_data,
1633 int stream,
1634 char *content,
1635 uint32_t content_length)
1636 {
1637 int n;
1638 struct acl_entry acls[NACLENTRIES];
1639
1640 n = strtoacl(content, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
1641 if (n <= 0) {
1642 BErrNo be;
1643
1644 Mmsg2(jcr->errmsg,
1645 _("strtoacl error on file \"%s\": ERR=%s\n"),
1646 acl_data->last_fname, be.bstrerror());
1647 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
1648 content, acl_data->last_fname, be.bstrerror());
1649 return bacl_exit_error;
1650 }
1651 if (strtoacl(content, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
1652 BErrNo be;
1653
1654 Mmsg2(jcr->errmsg,
1655 _("strtoacl error on file \"%s\": ERR=%s\n"),
1656 acl_data->last_fname, be.bstrerror());
1657 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
1658 content, acl_data->last_fname, be.bstrerror());
1659
1660 return bacl_exit_error;
1661 }
1662 /**
1663 * Restore the ACLs, but don't complain about links which really should
1664 * not have attributes, and the file it is linked to may not yet be restored.
1665 * This is only true for the old acl streams as in the new implementation we
1666 * don't save acls of symlinks (which cannot have acls anyhow)
1667 */
1668 if (setacl(acl_data->last_fname, n, acls) != 0 && acl_data->filetype != FT_LNK) {
1669 BErrNo be;
1670
1671 switch (errno) {
1672 case ENOENT:
1673 return bacl_exit_ok;
1674 #if defined(BACL_ENOTSUP)
1675 case BACL_ENOTSUP:
1676 /*
1677 * If the filesystem reports it doesn't support ACLs we clear the
1678 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
1679 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
1680 * when we change from one filesystem to another.
1681 */
1682 acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1683 Mmsg1(jcr->errmsg,
1684 _("setacl error on file \"%s\": filesystem doesn't support ACLs\n"),
1685 acl_data->last_fname);
1686 Dmsg2(100, "setacl error acl=%s file=%s filesystem doesn't support ACLs\n",
1687 content, acl_data->last_fname);
1688 return bacl_exit_error;
1689 #endif
1690 default:
1691 Mmsg2(jcr->errmsg,
1692 _("setacl error on file \"%s\": ERR=%s\n"),
1693 acl_data->last_fname, be.bstrerror());
1694 Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
1695 content, acl_data->last_fname, be.bstrerror());
1696 return bacl_exit_error;
1697 }
1698 }
1699 return bacl_exit_ok;
1700 }
1701
1702 /**
1703 * For this OS setup the build and parse function pointer to the OS specific functions.
1704 */
1705 static bacl_exit_code (*os_build_acl_streams)
1706 (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
1707 hpux_build_acl_streams;
1708 static bacl_exit_code (*os_parse_acl_streams)
1709 (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
1710 hpux_parse_acl_streams;
1711
1712 #elif defined(HAVE_SUN_OS)
1713 #ifdef HAVE_SYS_ACL_H
1714 #include <sys/acl.h>
1715 #else
1716 #error "configure failed to detect availability of sys/acl.h"
1717 #endif
1718
1719 #if defined(HAVE_EXTENDED_ACL)
1720 /**
1721 * We define some internals of the Solaris acl libs here as those
1722 * are not exposed yet. Probably because they want us to see the
1723 * acls as opague data. But as we need to support different platforms
1724 * and versions of Solaris we need to expose some data to be able
1725 * to determine the type of acl used to stuff it into the correct
1726 * data stream. I know this is far from portable, but maybe the
1727 * proper interface is exposed later on and we can get ride of
1728 * this kludge. Newer versions of Solaris include sys/acl_impl.h
1729 * which has implementation details of acls, if thats included we
1730 * don't have to define it ourself.
1731 */
1732 #if !defined(_SYS_ACL_IMPL_H)
1733 typedef enum acl_type {
1734 ACLENT_T = 0,
1735 ACE_T = 1
1736 } acl_type_t;
1737 #endif
1738
1739 /**
1740 * Two external references to functions in the libsec library function not in current include files.
1741 */
1742 extern "C" {
1743 int acl_type(acl_t *);
1744 char *acl_strerror(int);
1745 }
1746
1747 /**
1748 * Define the supported ACL streams for this OS
1749 */
1750 static int os_access_acl_streams[2] = {
1751 STREAM_ACL_SOLARIS_ACLENT,
1752 STREAM_ACL_SOLARIS_ACE
1753 };
1754 static int os_default_acl_streams[1] = {
1755 -1
1756 };
1757
1758 /**
1759 * As the new libsec interface with acl_totext and acl_fromtext also handles
1760 * the old format from acltotext we can use the new functions even
1761 * for acls retrieved and stored in the database with older fd versions. If the
1762 * new interface is not defined (Solaris 9 and older we fall back to the old code)
1763 */
1764 static bacl_exit_code solaris_build_acl_streams(JobControlRecord *jcr,
1765 acl_data_t *acl_data,
1766 FindFilesPacket *ff_pkt)
1767 {
1768 int acl_enabled, flags;
1769 acl_t *aclp;
1770 char *acl_text;
1771 bacl_exit_code stream_status = bacl_exit_error;
1772
1773 /*
1774 * See if filesystem supports acls.
1775 */
1776 acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_ENABLED);
1777 switch (acl_enabled) {
1778 case 0:
1779 /*
1780 * If the filesystem reports it doesn't support ACLs we clear the
1781 * BACL_FLAG_SAVE_NATIVE flag so we skip ACL saves on all other files
1782 * on the same filesystem. The BACL_FLAG_SAVE_NATIVE flag gets set again
1783 * when we change from one filesystem to another.
1784 */
1785 acl_data->flags &= ~BACL_FLAG_SAVE_NATIVE;
1786 PmStrcpy(acl_data->u.build->content, "");
1787 acl_data->u.build->content_length = 0;
1788 return bacl_exit_ok;
1789 case -1: {
1790 BErrNo be;
1791
1792 switch (errno) {
1793 case ENOENT:
1794 return bacl_exit_ok;
1795 default:
1796 Mmsg2(jcr->errmsg,
1797 _("pathconf error on file \"%s\": ERR=%s\n"),
1798 acl_data->last_fname, be.bstrerror());
1799 Dmsg2(100, "pathconf error file=%s ERR=%s\n",
1800 acl_data->last_fname, be.bstrerror());
1801 return bacl_exit_error;
1802 }
1803 }
1804 default:
1805 break;
1806 }
1807
1808 /*
1809 * Get ACL info: don't bother allocating space if there is only a trivial ACL.
1810 */
1811 if (acl_get(acl_data->last_fname, ACL_NO_TRIVIAL, &aclp) != 0) {
1812 BErrNo be;
1813
1814 switch (errno) {
1815 case ENOENT:
1816 return bacl_exit_ok;
1817 default:
1818 Mmsg2(jcr->errmsg,
1819 _("acl_get error on file \"%s\": ERR=%s\n"),
1820 acl_data->last_fname, acl_strerror(errno));
1821 Dmsg2(100, "acl_get error file=%s ERR=%s\n",
1822 acl_data->last_fname, acl_strerror(errno));
1823 return bacl_exit_error;
1824 }
1825 }
1826
1827 if (!aclp) {
1828 /*
1829 * The ACLs simply reflect the (already known) standard permissions
1830 * So we don't send an ACL stream to the SD.
1831 */
1832 PmStrcpy(acl_data->u.build->content, "");
1833 acl_data->u.build->content_length = 0;
1834 return bacl_exit_ok;
1835 }
1836
1837 #if defined(ACL_SID_FMT)
1838 /*
1839 * New format flag added in newer Solaris versions.
1840 */
1841 flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
1842 #else
1843 flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
1844 #endif /* ACL_SID_FMT */
1845
1846 if ((acl_text = acl_totext(aclp, flags)) != NULL) {
1847 acl_data->u.build->content_length =
1848 PmStrcpy(acl_data->u.build->content, acl_text);
1849 Actuallyfree(acl_text);
1850
1851 switch (acl_type(aclp)) {
1852 case ACLENT_T:
1853 stream_status = SendAclStream(jcr, acl_data, STREAM_ACL_SOLARIS_ACLENT);
1854 break;
1855 case ACE_T:
1856 stream_status = SendAclStream(jcr, acl_data, STREAM_ACL_SOLARIS_ACE);
1857 break;
1858 default:
1859 break;
1860 }
1861
1862 acl_free(aclp);
1863 }
1864 return stream_status;
1865 }
1866
1867 static bacl_exit_code solaris_parse_acl_streams(JobControlRecord *jcr,
1868 acl_data_t *acl_data,
1869 int stream,
1870 char *content,
1871 uint32_t content_length)
1872 {
1873 acl_t *aclp;
1874 int acl_enabled, error;
1875
1876 switch (stream) {
1877 case STREAM_UNIX_ACCESS_ACL:
1878 case STREAM_ACL_SOLARIS_ACLENT:
1879 case STREAM_ACL_SOLARIS_ACE:
1880 /*
1881 * First make sure the filesystem supports acls.
1882 */
1883 acl_enabled = pathconf(acl_data->last_fname, _PC_ACL_ENABLED);
1884 switch (acl_enabled) {
1885 case 0:
1886 /*
1887 * If the filesystem reports it doesn't support ACLs we clear the
1888 * BACL_FLAG_RESTORE_NATIVE flag so we skip ACL restores on all other files
1889 * on the same filesystem. The BACL_FLAG_RESTORE_NATIVE flag gets set again
1890 * when we change from one filesystem to another.
1891 */
1892 acl_data->flags &= ~BACL_FLAG_RESTORE_NATIVE;
1893 Mmsg1(jcr->errmsg,
1894 _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"),
1895 acl_data->last_fname);
1896 return bacl_exit_error;
1897 case -1: {
1898 BErrNo be;
1899
1900 switch (errno) {
1901 case ENOENT:
1902 return bacl_exit_ok;
1903 default:
1904 Mmsg2(jcr->errmsg,
1905 _("pathconf error on file \"%s\": ERR=%s\n"),
1906 acl_data->last_fname, be.bstrerror());
1907 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n",
1908 content, acl_data->last_fname, be.bstrerror());
1909 return bacl_exit_error;
1910 }
1911 }
1912 default:
1913 /*
1914 * On a filesystem with ACL support make sure this particular ACL type can be restored.
1915 */
1916 switch (stream) {
1917 case STREAM_ACL_SOLARIS_ACLENT:
1918 /*
1919 * An aclent can be restored on filesystems with _ACL_ACLENT_ENABLED or _ACL_ACE_ENABLED support.
1920 */
1921 if ((acl_enabled & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0) {
1922 Mmsg1(jcr->errmsg,
1923 _("Trying to restore POSIX acl on file \"%s\" on filesystem without aclent acl support\n"),
1924 acl_data->last_fname);
1925 return bacl_exit_error;
1926 }
1927 break;
1928 case STREAM_ACL_SOLARIS_ACE:
1929 /*
1930 * An ace can only be restored on a filesystem with _ACL_ACE_ENABLED support.
1931 */
1932 if ((acl_enabled & _ACL_ACE_ENABLED) == 0) {
1933 Mmsg1(jcr->errmsg,
1934 _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without ace acl support\n"),
1935 acl_data->last_fname);
1936 return bacl_exit_error;
1937 }
1938 break;
1939 default:
1940 /*
1941 * Stream id which doesn't describe the type of acl which is encoded.
1942 */
1943 break;
1944 }
1945 break;
1946 }
1947
1948 if ((error = acl_fromtext(content, &aclp)) != 0) {
1949 Mmsg2(jcr->errmsg,
1950 _("acl_fromtext error on file \"%s\": ERR=%s\n"),
1951 acl_data->last_fname, acl_strerror(error));
1952 Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n",
1953 content, acl_data->last_fname, acl_strerror(error));
1954 return bacl_exit_error;
1955 }
1956
1957 /*
1958 * Validate that the conversion gave us the correct acl type.
1959 */
1960 switch (stream) {
1961 case STREAM_ACL_SOLARIS_ACLENT:
1962 if (acl_type(aclp) != ACLENT_T) {
1963 Mmsg1(jcr->errmsg,
1964 _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1965 acl_data->last_fname);
1966 return bacl_exit_error;
1967 }
1968 break;
1969 case STREAM_ACL_SOLARIS_ACE:
1970 if (acl_type(aclp) != ACE_T) {
1971 Mmsg1(jcr->errmsg,
1972 _("wrong encoding of acl type in acl stream on file \"%s\"\n"),
1973 acl_data->last_fname);
1974 return bacl_exit_error;
1975 }
1976 break;
1977 default:
1978 /*
1979 * Stream id which doesn't describe the type of acl which is encoded.
1980 */
1981 break;
1982 }
1983
1984 /**
1985 * Restore the ACLs, but don't complain about links which really should
1986 * not have attributes, and the file it is linked to may not yet be restored.
1987 * This is only true for the old acl streams as in the new implementation we
1988 * don't save acls of symlinks (which cannot have acls anyhow)
1989 */
1990 if ((error = acl_set(acl_data->last_fname, aclp)) == -1 && acl_data->filetype != FT_LNK) {
1991 switch (errno) {
1992 case ENOENT:
1993 acl_free(aclp);
1994 return bacl_exit_ok;
1995 default:
1996 Mmsg2(jcr->errmsg,
1997 _("acl_set error on file \"%s\": ERR=%s\n"),
1998 acl_data->last_fname, acl_strerror(error));
1999 Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n",
2000 content, acl_data->last_fname, acl_strerror(error));
2001 acl_free(aclp);
2002 return bacl_exit_error;
2003 }
2004 }
2005
2006 acl_free(aclp);
2007 return bacl_exit_ok;
2008 default:
2009 return bacl_exit_error;
2010 } /* end switch (stream) */
2011 }
2012
2013 #else /* HAVE_EXTENDED_ACL */
2014
2015 /**
2016 * Define the supported ACL streams for this OS
2017 */
2018 static int os_access_acl_streams[1] = {
2019 STREAM_ACL_SOLARIS_ACLENT
2020 };
2021 static int os_default_acl_streams[1] = {
2022 -1
2023 };
2024
2025 /**
2026 * See if an acl is a trivial one (e.g. just the stat bits encoded as acl.)
2027 * There is no need to store those acls as we already store the stat bits too.
2028 */
2029 static bool AclIsTrivial(int count, aclent_t *entries)
2030 {
2031 int n;
2032 aclent_t *ace;
2033
2034 for (n = 0; n < count; n++) {
2035 ace = &entries[n];
2036
2037 if (!(ace->a_type == USER_OBJ ||
2038 ace->a_type == GROUP_OBJ ||
2039 ace->a_type == OTHER_OBJ ||
2040 ace->a_type == CLASS_OBJ))
2041 return false;
2042 }
2043 return true;
2044 }
2045
2046 /**
2047 * OS specific functions for handling different types of acl streams.
2048 */
2049 static bacl_exit_code solaris_build_acl_streams(JobControlRecord *jcr,
2050 acl_data_t *acl_data,
2051 FindFilesPacket *ff_pkt)
2052 {
2053 int n;
2054 aclent_t *acls;
2055 char *acl_text;
2056
2057 n = acl(acl_data->last_fname, GETACLCNT, 0, NULL);
2058 if (n < MIN_ACL_ENTRIES) {
2059 return bacl_exit_error;
2060 }
2061
2062 acls = (aclent_t *)malloc(n * sizeof(aclent_t));
2063 if (acl(acl_data->last_fname, GETACL, n, acls) == n) {
2064 if (AclIsTrivial(n, acls)) {
2065 /*
2066 * The ACLs simply reflect the (already known) standard permissions
2067 * So we don't send an ACL stream to the SD.
2068 */
2069 free(acls);
2070 PmStrcpy(acl_data->u.build->content, "");
2071 acl_data->u.build->content_length = 0;
2072 return bacl_exit_ok;
2073 }
2074
2075 if ((acl_text = acltotext(acls, n)) != NULL) {
2076 acl_data->u.build->content_length =
2077 PmStrcpy(acl_data->u.build->content, acl_text);
2078 Actuallyfree(acl_text);
2079 free(acls);
2080 return SendAclStream(jcr, acl_data, STREAM_ACL_SOLARIS_ACLENT);
2081 }
2082
2083 BErrNo be;
2084 Mmsg2(jcr->errmsg,
2085 _("acltotext error on file \"%s\": ERR=%s\n"),
2086 acl_data->last_fname, be.bstrerror());
2087 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
2088 acl_data->u.build->content, acl_data->last_fname, be.bstrerror());
2089 }
2090
2091 free(acls);
2092 return bacl_exit_error;
2093 }
2094
2095 static bacl_exit_code solaris_parse_acl_streams(JobControlRecord *jcr,
2096 acl_data_t *acl_data,
2097 int stream,
2098 char *content,
2099 uint32_t content_length)
2100 {
2101 int n;
2102 aclent_t *acls;
2103
2104 acls = aclfromtext(content, &n);
2105 if (!acls) {
2106 BErrNo be;
2107
2108 Mmsg2(jcr->errmsg,
2109 _("aclfromtext error on file \"%s\": ERR=%s\n"),
2110 acl_data->last_fname, be.bstrerror());
2111 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
2112 content, acl_data->last_fname, be.bstrerror());
2113 return bacl_exit_error;
2114 }
2115
2116 /*
2117 * Restore the ACLs, but don't complain about links which really should
2118 * not have attributes, and the file it is linked to may not yet be restored.
2119 */
2120 if (acl(acl_data->last_fname, SETACL, n, acls) == -1 && acl_data->filetype != FT_LNK) {
2121 BErrNo be;
2122
2123 switch (errno) {
2124 case ENOENT:
2125 Actuallyfree(acls);
2126 return bacl_exit_ok;
2127 default:
2128 Mmsg2(jcr->errmsg,
2129 _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
2130 acl_data->last_fname, be.bstrerror());
2131 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
2132 content, acl_data->last_fname, be.bstrerror());
2133 Actuallyfree(acls);
2134 return bacl_exit_error;
2135 }
2136 }
2137 Actuallyfree(acls);
2138 return bacl_exit_ok;
2139 }
2140 #endif /* HAVE_EXTENDED_ACL */
2141
2142 /**
2143 * For this OS setup the build and parse function pointer to the OS specific functions.
2144 */
2145 static bacl_exit_code (*os_build_acl_streams)
2146 (JobControlRecord *jcr, acl_data_t *acl_data, FindFilesPacket *ff_pkt) =
2147 solaris_build_acl_streams;
2148 static bacl_exit_code (*os_parse_acl_streams)
2149 (JobControlRecord *jcr, acl_data_t *acl_data, int stream, char *content, uint32_t content_length) =
2150 solaris_parse_acl_streams;
2151
2152 #endif /* HAVE_SUN_OS */
2153 #endif /* HAVE_ACL */
2154
2155 #if defined(HAVE_AFS_ACL)
2156
2157 #if defined(HAVE_AFS_AFSINT_H) && defined(HAVE_AFS_VENUS_H)
2158 #include <afs/afsint.h>
2159 #include <afs/venus.h>
2160 #else
2161 #error "configure failed to detect availability of afs/afsint.h and/or afs/venus.h"
2162 #endif
2163
2164 /**
2165 * External references to functions in the libsys library function not in current include files.
2166 */
2167 extern "C" {
2168 long pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow);
2169 }
2170
2171 static bacl_exit_code afs_build_acl_streams(JobControlRecord *jcr,
2172 acl_data_t *acl_data,
2173 FindFilesPacket *ff_pkt)
2174 {
2175 int error;
2176 struct ViceIoctl vip;
2177 char acl_text[BUFSIZ];
2178
2179 /*
2180 * AFS ACLs can only be set on a directory, so no need to try to
2181 * request them for anything other then that.
2182 */
2183 if (ff_pkt->type != FT_DIREND) {
2184 return bacl_exit_ok;
2185 }
2186
2187 vip.in = NULL;
2188 vip.in_size = 0;
2189 vip.out = acl_text;
2190 vip.out_size = sizeof(acl_text);
2191 memset((caddr_t)acl_text, 0, sizeof(acl_text));
2192
2193 if ((error = pioctl(acl_data->last_fname, VIOCGETAL, &vip, 0)) < 0) {
2194 BErrNo be;
2195
2196 Mmsg2(jcr->errmsg,
2197 _("pioctl VIOCGETAL error on file \"%s\": ERR=%s\n"),
2198 acl_data->last_fname, be.bstrerror());
2199 Dmsg2(100, "pioctl VIOCGETAL error file=%s ERR=%s\n",
2200 acl_data->last_fname, be.bstrerror());
2201 return bacl_exit_error;
2202 }
2203 acl_data->u.build->content_length =
2204 PmStrcpy(acl_data->u.build->content, acl_text);
2205 return SendAclStream(jcr, acl_data, STREAM_ACL_AFS_TEXT);
2206 }
2207
2208 static bacl_exit_code afs_parse_acl_stream(JobControlRecord *jcr,
2209 acl_data_t *acl_data,
2210 int stream,
2211 char *content,
2212 uint32_t content_length)
2213 {
2214 int error;
2215 struct ViceIoctl vip;
2216
2217 vip.in = content;
2218 vip.in_size = content_length;
2219 vip.out = NULL;
2220 vip.out_size = 0;
2221
2222 if ((error = pioctl(acl_data->last_fname, VIOCSETAL, &vip, 0)) < 0) {
2223 BErrNo be;
2224
2225 Mmsg2(jcr->errmsg,
2226 _("pioctl VIOCSETAL error on file \"%s\": ERR=%s\n"),
2227 acl_data->last_fname, be.bstrerror());
2228 Dmsg2(100, "pioctl VIOCSETAL error file=%s ERR=%s\n",
2229 acl_data->last_fname, be.bstrerror());
2230
2231 return bacl_exit_error;
2232 }
2233 return bacl_exit_ok;
2234 }
2235 #endif /* HAVE_AFS_ACL */
2236
2237 /**
2238 * Entry points when compiled with support for ACLs on a supported platform.
2239 */
2240
2241 /**
2242 * Read and send an ACL for the last encountered file.
2243 */
2244 bacl_exit_code BuildAclStreams(JobControlRecord *jcr,
2245 acl_data_t *acl_data,
2246 FindFilesPacket *ff_pkt)
2247 {
2248 /*
2249 * See if we are changing from one device to another.
2250 * We save the current device we are scanning and compare
2251 * it with the current st_dev in the last stat performed on
2252 * the file we are currently storing.
2253 */
2254 if (acl_data->current_dev != ff_pkt->statp.st_dev) {
2255 /*
2256 * Reset the acl save flags.
2257 */
2258 acl_data->flags = 0;
2259
2260 #if defined(HAVE_AFS_ACL)
2261 /*
2262 * AFS is a non OS specific filesystem so see if this path is on an AFS filesystem
2263 * Set the BACL_FLAG_SAVE_AFS flag if it is. If not set the BACL_FLAG_SAVE_NATIVE flag.
2264 */
2265 if (FstypeEquals(acl_data->last_fname, "afs")) {
2266 acl_data->flags |= BACL_FLAG_SAVE_AFS;
2267 } else {
2268 acl_data->flags |= BACL_FLAG_SAVE_NATIVE;
2269 }
2270 #else
2271 acl_data->flags |= BACL_FLAG_SAVE_NATIVE;
2272 #endif
2273
2274 /*
2275 * Save that we started scanning a new filesystem.
2276 */
2277 acl_data->current_dev = ff_pkt->statp.st_dev;
2278 }
2279
2280 #if defined(HAVE_AFS_ACL)
2281 /*
2282 * See if the BACL_FLAG_SAVE_AFS flag is set which lets us know if we should
2283 * save AFS ACLs.
2284 */
2285 if (acl_data->flags & BACL_FLAG_SAVE_AFS) {
2286 return afs_build_acl_streams(jcr, acl_data, ff_pkt);
2287 }
2288 #endif
2289 #if defined(HAVE_ACL)
2290 /*
2291 * See if the BACL_FLAG_SAVE_NATIVE flag is set which lets us know if we should
2292 * save native ACLs.
2293 */
2294 if (acl_data->flags & BACL_FLAG_SAVE_NATIVE) {
2295 /*
2296 * Call the appropriate function.
2297 */
2298 if (os_build_acl_streams) {
2299 return os_build_acl_streams(jcr, acl_data, ff_pkt);
2300 }
2301 } else {
2302 return bacl_exit_ok;
2303 }
2304 #endif
2305 return bacl_exit_error;
2306 }
2307
2308 bacl_exit_code parse_acl_streams(JobControlRecord *jcr,
2309 acl_data_t *acl_data,
2310 int stream,
2311 char *content,
2312 uint32_t content_length)
2313 {
2314 int ret;
2315 struct stat st;
2316 unsigned int cnt;
2317
2318 /*
2319 * See if we are changing from one device to another.
2320 * We save the current device we are restoring to and compare
2321 * it with the current st_dev in the last stat performed on
2322 * the file we are currently restoring.
2323 */
2324 ret = lstat(acl_data->last_fname, &st);
2325 switch (ret) {
2326 case -1: {
2327 BErrNo be;
2328
2329 switch (errno) {
2330 case ENOENT:
2331 return bacl_exit_ok;
2332 default:
2333 Mmsg2(jcr->errmsg,
2334 _("Unable to stat file \"%s\": ERR=%s\n"),
2335 acl_data->last_fname, be.bstrerror());
2336 Dmsg2(100, "Unable to stat file \"%s\": ERR=%s\n",
2337 acl_data->last_fname, be.bstrerror());
2338 return bacl_exit_error;
2339 }
2340 break;
2341 }
2342 case 0:
2343 break;
2344 }
2345 if (acl_data->current_dev != st.st_dev) {
2346 /*
2347 * Reset the acl save flags.
2348 */
2349 acl_data->flags = 0;
2350
2351 #if defined(HAVE_AFS_ACL)
2352 /*
2353 * AFS is a non OS specific filesystem so see if this path is on an AFS filesystem
2354 * Set the BACL_FLAG_RESTORE_AFS flag if it is. If not set the BACL_FLAG_RETORE_NATIVE flag.
2355 */
2356 if (FstypeEquals(acl_data->last_fname, "afs")) {
2357 acl_data->flags |= BACL_FLAG_RESTORE_AFS;
2358 } else {
2359 acl_data->flags |= BACL_FLAG_RESTORE_NATIVE;
2360 }
2361 #else
2362 acl_data->flags |= BACL_FLAG_RESTORE_NATIVE;
2363 #endif
2364
2365 /*
2366 * Save that we started restoring to a new filesystem.
2367 */
2368 acl_data->current_dev = st.st_dev;
2369 }
2370
2371 switch (stream) {
2372 #if defined(HAVE_AFS_ACL)
2373 case STREAM_ACL_AFS_TEXT:
2374 if (acl_data->flags & BACL_FLAG_RESTORE_AFS) {
2375 return afs_parse_acl_stream(jcr, acl_data, stream, content, content_length);
2376 } else {
2377 /*
2378 * Increment error count but don't log an error again for the same filesystem.
2379 */
2380 acl_data->u.parse->nr_errors++;
2381 return bacl_exit_ok;
2382 }
2383 #endif
2384 #if defined(HAVE_ACL)
2385 case STREAM_UNIX_ACCESS_ACL:
2386 case STREAM_UNIX_DEFAULT_ACL:
2387 /*
2388 * Handle legacy ACL streams.
2389 */
2390 if ((acl_data->flags & BACL_FLAG_RESTORE_NATIVE) && os_parse_acl_streams) {
2391 return os_parse_acl_streams(jcr, acl_data, stream, content, content_length);
2392 } else {
2393 /*
2394 * Increment error count but don't log an error again for the same filesystem.
2395 */
2396 acl_data->u.parse->nr_errors++;
2397 return bacl_exit_ok;
2398 }
2399 break;
2400 default:
2401 if ((acl_data->flags & BACL_FLAG_RESTORE_NATIVE) && os_parse_acl_streams) {
2402 /*
2403 * Walk the os_access_acl_streams array with the supported Access ACL streams for this OS.
2404 */
2405 for (cnt = 0; cnt < sizeof(os_access_acl_streams) / sizeof(int); cnt++) {
2406 if (os_access_acl_streams[cnt] == stream) {
2407 return os_parse_acl_streams(jcr, acl_data, stream, content, content_length);
2408 }
2409 }
2410 /*
2411 * Walk the os_default_acl_streams array with the supported Default ACL streams for this OS.
2412 */
2413 for (cnt = 0; cnt < sizeof(os_default_acl_streams) / sizeof(int); cnt++) {
2414 if (os_default_acl_streams[cnt] == stream) {
2415 return os_parse_acl_streams(jcr, acl_data, stream, content, content_length);
2416 }
2417 }
2418 } else {
2419 /*
2420 * Increment error count but don't log an error again for the same filesystem.
2421 */
2422 acl_data->u.parse->nr_errors++;
2423 return bacl_exit_ok;
2424 }
2425 break;
2426 #else
2427 default:
2428 break;
2429 #endif
2430 }
2431 Qmsg2(jcr, M_WARNING, 0,
2432 _("Can't restore ACLs of %s - incompatible acl stream encountered - %d\n"),
2433 acl_data->last_fname, stream);
2434 return bacl_exit_error;
2435 }
2436 #endif
2437