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