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_freebsd.h"
31
32 #if defined(HAVE_FREEBSD_OS)
33
34 /* check if XATTR support is enabled */
35 #if defined(HAVE_XATTR)
36
37 /*
38 * Define the supported XATTR streams for this OS
39 */
40 static const int os_xattr_streams[] = {
41 STREAM_XACL_FREEBSD_XATTR,
42 0
43 };
44
45 static const int os_xattr_namespaces[] = {
46 EXTATTR_NAMESPACE_USER,
47 EXTATTR_NAMESPACE_SYSTEM,
48 -1
49 };
50
51 static const char *os_xattr_acl_skiplist[] = {
52 "system.posix1e.acl_access",
53 "system.posix1e.acl_default",
54 "system.nfs4.acl",
55 NULL
56 };
57
58 static const char *os_xattr_skiplist[] = {
59 NULL
60 };
61
62 /*
63 * OS specific constructor
64 */
BXATTR_FreeBSD()65 BXATTR_FreeBSD::BXATTR_FreeBSD()
66 {
67 set_xattr_streams(os_xattr_streams);
68 set_xattr_skiplists(os_xattr_skiplist, os_xattr_acl_skiplist);
69 };
70
71 /*
72 * Perform OS specific extended attribute backup
73 *
74 * in/out - check API at bxattr.h
75 */
os_backup_xattr(JCR * jcr,FF_PKT * ff_pkt)76 bRC_BXATTR BXATTR_FreeBSD::os_backup_xattr (JCR *jcr, FF_PKT *ff_pkt){
77
78 bRC_BXATTR rc;
79 POOLMEM *xlist;
80 uint32_t xlen;
81 char *name;
82 uint32_t name_len;
83 POOLMEM *value;
84 uint32_t value_len;
85 POOLMEM *name_gen;
86 uint32_t name_gen_len;
87 char * namespace_str;
88 int namespace_len;
89 bool skip;
90 alist *xattr_list = NULL;
91 int xattr_count = 0;
92 uint32_t len = 0;
93 BXATTR_xattr *xattr;
94 int a;
95
96 for (a = 0; os_xattr_namespaces[a] != -1; a++){ // loop through all available namespaces
97 /* xlist is allocated as POOLMEM by os_get_xattr_names_local */
98 rc = os_get_xattr_names_local(jcr, os_xattr_namespaces[a], &xlist, &xlen);
99 switch (rc){
100 case bRC_BXATTR_ok:
101 /* it's ok, so go further */
102 break;
103 case bRC_BXATTR_skip:
104 case bRC_BXATTR_cont:
105 /* no xattr available, so skip rest of it */
106 return bRC_BXATTR_ok;
107 default:
108 return rc;
109 }
110
111 /* get a string representation of the namespace */
112 if (extattr_namespace_to_string(os_xattr_namespaces[a], &namespace_str) != 0){
113 Mmsg2(jcr->errmsg, _("Failed to convert %d into namespace on file \"%s\"\n"), os_xattr_namespaces[a], jcr->last_fname);
114 Dmsg2(100, "Failed to convert %d into namespace on file \"%s\"\n", os_xattr_namespaces[a], jcr->last_fname);
115 goto bail_out;
116 }
117 namespace_len = strlen(namespace_str);
118
119 /* follow the list of xattr names and get the values */
120 for (name = xlist; (name - xlist) + 1 < xlen; name = strchr(name, '\0') + 1){
121 name_len = strlen(name);
122 name_gen = get_pool_memory(PM_FNAME);
123 name_gen = check_pool_memory_size(name_gen, name_len + namespace_len + 2);
124 bsnprintf(name_gen, name_len + namespace_len + 2, "%s.%s", namespace_str, name);
125 name_gen_len = strlen(name_gen);
126
127 skip = check_xattr_skiplists(jcr, ff_pkt, name_gen);
128 if (skip || name_len == 0){
129 Dmsg1(100, "Skipping xattr named %s\n", name_gen);
130 continue;
131 }
132
133 /* value is allocated as POOLMEM by os_get_xattr_value_local */
134 rc = os_get_xattr_value_local(jcr, os_xattr_namespaces[a], name, &value, &value_len);
135 switch (rc){
136 case bRC_BXATTR_ok:
137 /* it's ok, so go further */
138 break;
139 case bRC_BXATTR_skip:
140 /* no xattr available, so skip rest of it */
141 rc = bRC_BXATTR_ok;
142 goto bail_out;
143 default:
144 /* error / fatal */
145 goto bail_out;
146 }
147
148 /*
149 * we have a name of the extended attribute in the name variable
150 * and value of the extended attribute in the value variable
151 * so we need to build a list
152 */
153 xattr = (BXATTR_xattr*)malloc(sizeof(BXATTR_xattr));
154 xattr->name_len = name_gen_len;
155 xattr->name = name_gen;
156 xattr->value_len = value_len;
157 xattr->value = value;
158 /* magic name_len name value_len value */
159 len += sizeof(uint32_t) + sizeof(uint32_t) + name_gen_len + sizeof(uint32_t) + value_len;
160
161 if (xattr_list == NULL){
162 xattr_list = New(alist(10, not_owned_by_alist));
163 }
164 xattr_list->append(xattr);
165 xattr_count++;
166 }
167 if (xattr_count > 0){
168 /* serialize the stream */
169 rc = serialize_xattr_stream(jcr, len, xattr_list);
170 if (rc != bRC_BXATTR_ok){
171 Mmsg(jcr->errmsg, _("Failed to serialize extended attributes on file \"%s\"\n"), jcr->last_fname);
172 Dmsg1(100, "Failed to serialize extended attributes on file \"%s\"\n", jcr->last_fname);
173 goto bail_out;
174 } else {
175 /* send data to SD */
176 rc = send_xattr_stream(jcr, STREAM_XACL_FREEBSD_XATTR);
177 }
178 } else {
179 rc = bRC_BXATTR_ok;
180 }
181 }
182 bail_out:
183 /* free allocated data */
184 if (xattr_list != NULL){
185 foreach_alist(xattr, xattr_list){
186 if (xattr == NULL){
187 break;
188 }
189 if (xattr->name){
190 free_pool_memory(name_gen);
191 }
192 if (xattr->value){
193 free(xattr->value);
194 }
195 free(xattr);
196 }
197 delete xattr_list;
198 }
199 if (xlist != NULL){
200 free(xlist);
201 }
202
203 return rc;
204 };
205
206 /*
207 * Perform OS specific XATTR restore. Runtime is called only when stream is supported by OS.
208 *
209 * in/out - check API at bxattr.h
210 */
os_restore_xattr(JCR * jcr,int stream,char * content,uint32_t length)211 bRC_BXATTR BXATTR_FreeBSD::os_restore_xattr (JCR *jcr, int stream, char *content, uint32_t length){
212 return generic_restore_xattr(jcr, stream);
213 };
214
215 /*
216 * Return a list of xattr names in newly allocated pool memory and a length of the allocated buffer.
217 * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
218 * when not needed.
219 *
220 * in/out - check API at bxattr.h
221 *
222 * As a FreeBSD uses a different attributes name schema/format then this method is a very different
223 * from a standard generic method because it uses a namespace (ns) value for os dependent optimization.
224 */
os_get_xattr_names_local(JCR * jcr,int ns,POOLMEM ** pxlist,uint32_t * xlen)225 bRC_BXATTR BXATTR_FreeBSD::os_get_xattr_names_local (JCR *jcr, int ns, POOLMEM ** pxlist, uint32_t * xlen){
226
227 int len;
228 POOLMEM * list;
229 int a;
230 int stra;
231 POOLMEM * genlist;
232
233 /* check input data */
234 if (jcr == NULL || xlen == NULL || pxlist == NULL){
235 return bRC_BXATTR_inval;
236 }
237 /* get the length of the extended attributes */
238 len = extattr_list_link(jcr->last_fname, ns, NULL, 0);
239 switch (len){
240 case -1: {
241 berrno be;
242
243 switch (errno){
244 case ENOENT:
245 /* no file available, skip it */
246 return bRC_BXATTR_skip;
247 case EOPNOTSUPP:
248 /* no xattr supported on filesystem, clear a flag and skip it */
249 clear_flag(BXATTR_FLAG_NATIVE);
250 set_content(NULL);
251 return bRC_BXATTR_skip;
252 case EPERM:
253 if (ns == EXTATTR_NAMESPACE_SYSTEM){
254 return bRC_BXATTR_cont;
255 } /* else show error */
256 default:
257 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
258 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
259 return bRC_BXATTR_error;
260 }
261 break;
262 }
263 case 0:
264 /* xattr available but empty, skip it */
265 return bRC_BXATTR_skip;
266 default:
267 break;
268 }
269
270 /*
271 * allocate memory for the extented attribute list
272 * default size is a 4k for PM_BSOCK, which should be sufficient on almost all
273 * Linux system where xattrs a limited in size to single filesystem block ~4kB
274 * so we need to check required size
275 */
276 list = get_pool_memory(PM_BSOCK);
277 list = check_pool_memory_size(list, len + 1);
278 memset(list, 0, len + 1);
279
280 /* get the list of extended attributes names for a file */
281 len = extattr_list_link(jcr->last_fname, ns, list, len);
282 switch (len){
283 case -1: {
284 berrno be;
285
286 switch (errno){
287 case ENOENT:
288 /* no file available, skip it, first release allocated memory */
289 free_pool_memory(list);
290 return bRC_BXATTR_skip;
291 case EPERM:
292 if (ns == EXTATTR_NAMESPACE_SYSTEM){
293 return bRC_BXATTR_cont;
294 } /* else show error */
295 default:
296 Mmsg2(jcr->errmsg, _("extattr_list_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
297 Dmsg2(100, "extattr_list_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
298 free_pool_memory(list);
299 return bRC_BXATTR_error;
300 }
301 break;
302 }
303 default:
304 break;
305 }
306 /* convert FreeBSD list type to the generic one */
307 genlist = get_pool_memory(PM_BSOCK);
308 genlist = check_pool_memory_size(genlist, len + 1);
309 memset(genlist, 0, len + 1);
310 for (a = 0; a < len; a += list[a] + 1){
311 stra = list[a];
312 memcpy(genlist + a, list + a + 1, stra);
313 genlist[a + stra] = '\0';
314 }
315 free_pool_memory(list);
316 /* setup return data */
317 *pxlist = genlist;
318 *xlen = len;
319 return bRC_BXATTR_ok;
320 };
321
322 /*
323 * Return a value of the requested attribute name and a length of the allocated buffer.
324 * It allocates a memory with poolmem subroutines every time a function is called, so it must be freed
325 * when not needed.
326 *
327 * in/out - check API at bxattr.h
328 *
329 * As a FreeBSD uses a different attributes name schema/format then this method is a very different
330 * from a standard generic method because it uses a namespace (ns) value for os dependent optimization.
331 */
os_get_xattr_value_local(JCR * jcr,int ns,char * name,char ** pvalue,uint32_t * plen)332 bRC_BXATTR BXATTR_FreeBSD::os_get_xattr_value_local (JCR *jcr, int ns, char * name, char ** pvalue, uint32_t * plen){
333
334 int len;
335 POOLMEM * value;
336
337 /* check input data */
338 if (jcr == NULL || name == NULL || plen == NULL || pvalue == NULL){
339 return bRC_BXATTR_inval;
340 }
341 /* get the length of the value for extended attribute */
342 len = extattr_get_link(jcr->last_fname, ns, name, NULL, 0);
343 switch (len){
344 case -1: {
345 berrno be;
346
347 switch (errno){
348 case ENOENT:
349 /* no file available, skip it */
350 return bRC_BXATTR_skip;
351 default:
352 /* XXX: what about ENOATTR error value? */
353 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
354 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
355 return bRC_BXATTR_error;
356 }
357 break;
358 }
359 default:
360 break;
361 }
362
363 if (len > 0){
364 /*
365 * allocate memory for the extented attribute value
366 * default size is a 256B for PM_MESSAGE, so we need to check required size
367 */
368 value = get_pool_memory(PM_MESSAGE);
369 value = check_pool_memory_size(value, len + 1);
370 memset(value, 0, len + 1);
371 /* value is not empty, get a data */
372 len = extattr_get_link(jcr->last_fname, ns, name, value, len);
373 switch (len){
374 case -1: {
375 berrno be;
376
377 switch (errno){
378 case ENOENT:
379 /* no file available, skip it, first release allocated memory */
380 free_pool_memory(value);
381 return bRC_BXATTR_skip;
382 default:
383 Mmsg2(jcr->errmsg, _("extattr_get_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
384 Dmsg2(100, "extattr_get_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
385 free_pool_memory(value);
386 return bRC_BXATTR_error;
387 }
388 break;
389 }
390 default:
391 break;
392 }
393 /* ensure a value is nul terminated */
394 value[len] = '\0';
395 } else {
396 /* empty value */
397 value = NULL;
398 len = 0;
399 }
400 /* setup return data */
401 *pvalue = value;
402 *plen = len;
403 return bRC_BXATTR_ok;
404 };
405
406 /*
407 * Low level OS specific runtime to set extended attribute on file
408 *
409 * in/out - check API at bxattr.h
410 *
411 * xattr->name should be in '<namespace>.<name>' format which
412 * function handle without problem, otherwise it returns an error
413 * TODO: it is possible to handle a different attributes name format
414 * for OS portability where default namespace 'user' can be used
415 */
os_set_xattr(JCR * jcr,BXATTR_xattr * xattr)416 bRC_BXATTR BXATTR_FreeBSD::os_set_xattr (JCR *jcr, BXATTR_xattr *xattr){
417
418 char * name;
419 char * nspace;
420 int ns;
421 int rc;
422
423 /* check input data */
424 if (jcr == NULL || xattr == NULL){
425 return bRC_BXATTR_inval;
426 }
427
428 /* search for attribute namespace which is distinguished from attribute name by a dot '.' character */
429 if ((name = strchr(xattr->name, '.')) == (char *)NULL){
430 Mmsg2(jcr->errmsg, _("Failed to split %s into namespace and name part on file \"%s\"\n"), xattr->name, jcr->last_fname);
431 Dmsg2(100, "Failed to split %s into namespace and name part on file \"%s\"\n", xattr->name, jcr->last_fname);
432 return bRC_BXATTR_error;
433 }
434
435 /* split namespace and name of the attribute */
436 nspace = xattr->name;
437 *name++ = '\0';
438
439 /* check if namespace is valid on this system */
440 if (extattr_string_to_namespace(nspace, &ns) != 0){
441 Mmsg2(jcr->errmsg, _("Failed to convert %s into namespace on file \"%s\"\n"), nspace, jcr->last_fname);
442 Dmsg2(100, "Failed to convert %s into namespace on file \"%s\"\n", nspace, jcr->last_fname);
443 return bRC_BXATTR_error;
444 }
445
446 /* set extattr on file */
447 rc = extattr_set_link(jcr->last_fname, ns, name, xattr->value, xattr->value_len);
448 if (rc < 0 || rc != (int)xattr->value_len){
449 berrno be;
450
451 switch (errno){
452 case ENOENT:
453 break;
454 default:
455 Mmsg2(jcr->errmsg, _("extattr_set_link error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
456 Dmsg2(100, "extattr_set_link error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
457 return bRC_BXATTR_error;
458 }
459 }
460 return bRC_BXATTR_ok;
461 };
462
463 #endif /* HAVE_XATTR */
464
465 #endif /* HAVE_FREEBSD_OS */
466