|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2018 Olaf Wintermann. All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #define _GNU_SOURCE |
|
30 |
|
31 #include <stdio.h> |
|
32 #include <stdlib.h> |
|
33 |
|
34 #include "libxattr.h" |
|
35 |
|
36 #include <errno.h> |
|
37 #include <sys/types.h> |
|
38 |
|
39 #include <string.h> |
|
40 |
|
41 #define LIST_BUF_LEN 1024 |
|
42 #define LIST_ARRAY_LEN 8 |
|
43 #define ATTR_BUF_LEN 1024 |
|
44 |
|
45 #define ARRAY_ADD(array, pos, len, obj) if(pos >= len) { \ |
|
46 len *= 2; /* TODO: missing error handling for realloc() */ \ |
|
47 array = realloc(array, len * sizeof(char*)); \ |
|
48 } \ |
|
49 array[pos] = obj; \ |
|
50 pos++; |
|
51 |
|
52 static void* libxattr_malloc(void *unused, size_t size) { |
|
53 return malloc(size); |
|
54 } |
|
55 |
|
56 static void libxattr_free(void *unused, void *ptr) { |
|
57 free(ptr); |
|
58 } |
|
59 |
|
60 |
|
61 #ifdef __linux__ |
|
62 #define XATTR_SUPPORTED |
|
63 #include <sys/xattr.h> |
|
64 |
|
65 static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { |
|
66 size_t arraylen = LIST_ARRAY_LEN; |
|
67 size_t arraypos = 0; |
|
68 char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); |
|
69 |
|
70 char *begin = buf; |
|
71 char *name = NULL; |
|
72 for(int i=0;i<length;i++) { |
|
73 if(!name && buf[i] == '.') { |
|
74 int nslen = (buf+i-begin); |
|
75 //printf("%.*s\n", nslen, begin); |
|
76 name = buf + i + 1; |
|
77 } |
|
78 if(buf[i] == '\0') { |
|
79 char *attrname = strdup(name); |
|
80 ARRAY_ADD(array, arraypos, arraylen, attrname); |
|
81 begin = buf + i + 1; |
|
82 name = 0; |
|
83 } |
|
84 } |
|
85 |
|
86 if(arraypos == 0) { |
|
87 free(array); |
|
88 array = NULL; |
|
89 } |
|
90 |
|
91 *nelm = arraypos; |
|
92 return array; |
|
93 } |
|
94 |
|
95 char ** xattr_list(const char *path, ssize_t *nelm) { |
|
96 char *list = malloc(LIST_BUF_LEN); |
|
97 ssize_t len = listxattr(path, list, LIST_BUF_LEN); |
|
98 if(len == -1) { |
|
99 switch(errno) { |
|
100 case ERANGE: { |
|
101 // buffer too, get size of attribute list |
|
102 ssize_t newlen = listxattr(path, NULL, 0); |
|
103 if(newlen > 0) { |
|
104 // second try |
|
105 list = realloc(list, newlen); |
|
106 len = listxattr(path, list, newlen); |
|
107 if(len != -1) { |
|
108 // this time it worked |
|
109 break; |
|
110 } |
|
111 } |
|
112 } |
|
113 default: { |
|
114 free(list); |
|
115 *nelm = -1; |
|
116 return NULL; |
|
117 } |
|
118 } |
|
119 } |
|
120 |
|
121 char **ret = parse_xattrlist(list, len, nelm); |
|
122 free(list); |
|
123 return ret; |
|
124 } |
|
125 |
|
126 static char* name2nsname(const char *name) { |
|
127 // add the 'user' namespace to the name |
|
128 size_t namelen = strlen(name); |
|
129 char *attrname = malloc(8 + namelen); |
|
130 if(!attrname) { |
|
131 return NULL; |
|
132 } |
|
133 memcpy(attrname, "user.", 5); |
|
134 memcpy(attrname+5, name, namelen + 1); |
|
135 return attrname; |
|
136 } |
|
137 |
|
138 char * xattr_get_alloc( |
|
139 void *pool, |
|
140 libxattr_malloc_func malloc_func, |
|
141 libxattr_free_func free_func, |
|
142 const char *path, |
|
143 const char *attr, |
|
144 ssize_t *len) |
|
145 { |
|
146 char *buf = malloc_func(pool, ATTR_BUF_LEN); |
|
147 if(!buf) { |
|
148 *len = -1; |
|
149 return NULL; |
|
150 } |
|
151 |
|
152 char *attrname = name2nsname(attr); |
|
153 if(!attrname) { |
|
154 free_func(pool, buf); |
|
155 *len = -1; |
|
156 return NULL; |
|
157 } |
|
158 |
|
159 ssize_t vlen = getxattr(path, attrname, buf, ATTR_BUF_LEN - 1); |
|
160 if(vlen < 0) { |
|
161 switch(errno) { |
|
162 case ERANGE: { |
|
163 ssize_t attrlen = getxattr(path, attrname, NULL, 0); |
|
164 if(attrlen > 0) { |
|
165 free_func(pool, buf); |
|
166 buf = malloc_func(pool, attrlen + 1); |
|
167 if(!buf) { |
|
168 free(attrname); |
|
169 *len = -1; |
|
170 return NULL; |
|
171 } |
|
172 vlen = getxattr(path, attrname, buf, attrlen); |
|
173 if(vlen > 0) { |
|
174 break; |
|
175 } |
|
176 } |
|
177 } |
|
178 default: { |
|
179 *len = -1; |
|
180 free_func(pool, buf); |
|
181 free(attrname); |
|
182 return NULL; |
|
183 } |
|
184 } |
|
185 } |
|
186 buf[vlen] = 0; |
|
187 |
|
188 free(attrname); |
|
189 *len = vlen; |
|
190 return buf; |
|
191 } |
|
192 |
|
193 int xattr_set(const char *path, const char *name, const void *value, size_t len) { |
|
194 char *attrname = name2nsname(name); |
|
195 int ret = setxattr(path, attrname, value, len, 0); |
|
196 free(attrname); |
|
197 return ret; |
|
198 } |
|
199 |
|
200 int xattr_remove(const char *path, const char *name) { |
|
201 char *attrname = name2nsname(name); |
|
202 int ret = removexattr(path, attrname); |
|
203 free(attrname); |
|
204 return ret; |
|
205 } |
|
206 |
|
207 #endif /* Linux */ |
|
208 |
|
209 #ifdef __APPLE__ |
|
210 #define XATTR_SUPPORTED |
|
211 #include <sys/xattr.h> |
|
212 |
|
213 static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { |
|
214 size_t arraylen = LIST_ARRAY_LEN; |
|
215 size_t arraypos = 0; |
|
216 char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); |
|
217 |
|
218 char *name = buf; |
|
219 for(int i=0;i<length;i++) { |
|
220 if(buf[i] == '\0') { |
|
221 char *attrname = strdup(name); |
|
222 ARRAY_ADD(array, arraypos, arraylen, attrname); |
|
223 name = buf + i + 1; |
|
224 } |
|
225 } |
|
226 |
|
227 if(arraypos == 0) { |
|
228 free(array); |
|
229 array = NULL; |
|
230 } |
|
231 |
|
232 *nelm = arraypos; |
|
233 return array; |
|
234 } |
|
235 |
|
236 char ** xattr_list(const char *path, ssize_t *nelm) { |
|
237 char *list = malloc(LIST_BUF_LEN); |
|
238 ssize_t len = listxattr(path, list, LIST_BUF_LEN, 0); |
|
239 if(len == -1) { |
|
240 switch(errno) { |
|
241 case ERANGE: { |
|
242 // buffer too, get size of attribute list |
|
243 ssize_t newlen = listxattr(path, NULL, 0, 0); |
|
244 if(newlen > 0) { |
|
245 // second try |
|
246 list = realloc(list, newlen); |
|
247 len = listxattr(path, list, newlen, 0); |
|
248 if(len != -1) { |
|
249 // this time it worked |
|
250 break; |
|
251 } |
|
252 } |
|
253 } |
|
254 default: { |
|
255 free(list); |
|
256 *nelm = -1; |
|
257 return NULL; |
|
258 } |
|
259 } |
|
260 } |
|
261 |
|
262 char **ret = parse_xattrlist(list, len, nelm); |
|
263 free(list); |
|
264 return ret; |
|
265 } |
|
266 |
|
267 char * xattr_get_alloc( |
|
268 void *pool, |
|
269 libxattr_malloc_func malloc_func, |
|
270 libxattr_free_func free_func, |
|
271 const char *path, |
|
272 const char *attr, |
|
273 ssize_t *len) |
|
274 { |
|
275 // get attribute length |
|
276 ssize_t attrlen = getxattr(path, attr, NULL, 0, 0, 0); |
|
277 if(attrlen < 0) { |
|
278 *len = -1; |
|
279 return NULL; |
|
280 } |
|
281 |
|
282 char *buf = malloc_func(pool, attrlen + 1); |
|
283 if(!buf) { |
|
284 *len = -1; |
|
285 return NULL; |
|
286 } |
|
287 |
|
288 ssize_t vlen = getxattr(path, attr, buf, attrlen, 0, 0); |
|
289 if(vlen < 0) { |
|
290 *len = -1; |
|
291 free_func(pool, buf); |
|
292 return NULL; |
|
293 } |
|
294 buf[attrlen] = 0; |
|
295 |
|
296 *len = vlen; |
|
297 return buf; |
|
298 } |
|
299 |
|
300 int xattr_set(const char *path, const char *name, const void *value, size_t len) { |
|
301 int ret = setxattr(path, name, value, len, 0, 0); |
|
302 return ret; |
|
303 } |
|
304 |
|
305 int xattr_remove(const char *path, const char *name) { |
|
306 return removexattr(path, name, 0); |
|
307 } |
|
308 |
|
309 #endif /* Apple */ |
|
310 |
|
311 #ifdef __sun |
|
312 #define XATTR_SUPPORTED |
|
313 #include <unistd.h> |
|
314 #include <sys/types.h> |
|
315 #include <sys/stat.h> |
|
316 #include <dirent.h> |
|
317 #include <fcntl.h> |
|
318 |
|
319 static int open_attrfile(const char *path, const char *attr, int oflag) { |
|
320 int file = open(path, O_RDONLY); |
|
321 if(file == -1) { |
|
322 return -1; |
|
323 } |
|
324 |
|
325 int attrfile = openat(file, attr, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); |
|
326 close(file); |
|
327 return attrfile; |
|
328 } |
|
329 |
|
330 char ** xattr_list(const char *path, ssize_t *nelm) { |
|
331 *nelm = -1; |
|
332 |
|
333 int attrdir = open_attrfile(path, ".", O_RDONLY|O_XATTR); |
|
334 if(attrdir == -1) { |
|
335 return NULL; |
|
336 } |
|
337 |
|
338 DIR *dir = fdopendir(attrdir); |
|
339 if(!dir) { |
|
340 close(attrdir); |
|
341 return NULL; |
|
342 } |
|
343 |
|
344 size_t arraylen = LIST_ARRAY_LEN; |
|
345 size_t arraypos = 0; |
|
346 char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); |
|
347 |
|
348 struct dirent *ent; |
|
349 while((ent = readdir(dir)) != NULL) { |
|
350 if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "SUNWattr_ro") || !strcmp(ent->d_name, "SUNWattr_rw")) { |
|
351 continue; |
|
352 } |
|
353 char *name = strdup(ent->d_name); |
|
354 ARRAY_ADD(array, arraypos, arraylen, name); |
|
355 } |
|
356 closedir(dir); |
|
357 |
|
358 *nelm = arraypos; |
|
359 return array; |
|
360 } |
|
361 |
|
362 char * xattr_get_alloc( |
|
363 void *pool, |
|
364 libxattr_malloc_func malloc_func, |
|
365 libxattr_free_func free_func, |
|
366 const char *path, |
|
367 const char *attr, |
|
368 ssize_t *len) |
|
369 { |
|
370 *len = -1; |
|
371 |
|
372 int attrfile = open_attrfile(path, attr, O_RDONLY|O_XATTR); |
|
373 if(attrfile == -1) { |
|
374 return NULL; |
|
375 } |
|
376 |
|
377 struct stat s; |
|
378 if(fstat(attrfile, &s)) { |
|
379 close(attrfile); |
|
380 return NULL; |
|
381 } |
|
382 |
|
383 size_t bufsize = (size_t)s.st_size; |
|
384 char *buf = malloc_func(pool, bufsize); |
|
385 |
|
386 char *b = buf; |
|
387 size_t cur = 0; |
|
388 while(cur < bufsize) { |
|
389 ssize_t r = read(attrfile, buf + cur, bufsize - cur); |
|
390 if(r <= 0) { |
|
391 break; |
|
392 } |
|
393 cur += r; |
|
394 } |
|
395 |
|
396 close(attrfile); |
|
397 if(cur != bufsize) { |
|
398 free_func(pool, buf); |
|
399 return NULL; |
|
400 } |
|
401 |
|
402 *len = (ssize_t)bufsize; |
|
403 return buf; |
|
404 } |
|
405 |
|
406 int xattr_set(const char *path, const char *name, const void *value, size_t len) { |
|
407 int attrfile = open_attrfile(path, name, O_CREAT|O_WRONLY|O_XATTR|O_TRUNC); |
|
408 if(attrfile == -1) { |
|
409 return -1; |
|
410 } |
|
411 |
|
412 const char *p = value; |
|
413 size_t remaining = len; |
|
414 while(remaining > 0) { |
|
415 ssize_t w = write(attrfile, p, remaining); |
|
416 if(w <= 0) { |
|
417 break; |
|
418 } |
|
419 p += w; |
|
420 remaining -= w; |
|
421 } |
|
422 |
|
423 close(attrfile); |
|
424 |
|
425 return remaining > 0 ? -1 : 0; |
|
426 } |
|
427 |
|
428 int xattr_remove(const char *path, const char *name) { |
|
429 int attrdir = open_attrfile(path, ".", O_RDONLY|O_XATTR); |
|
430 if(attrdir == -1) { |
|
431 return -1; |
|
432 } |
|
433 |
|
434 int ret = unlinkat(attrdir, name, 0); |
|
435 close(attrdir); |
|
436 return ret; |
|
437 } |
|
438 |
|
439 #endif /* Sun */ |
|
440 |
|
441 |
|
442 #ifdef __FreeBSD__ |
|
443 #define XATTR_SUPPORTED |
|
444 |
|
445 #include <sys/types.h> |
|
446 #include <sys/extattr.h> |
|
447 |
|
448 static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { |
|
449 size_t arraylen = LIST_ARRAY_LEN; |
|
450 size_t arraypos = 0; |
|
451 char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); |
|
452 |
|
453 char *name = buf; |
|
454 for(int i=0;i<length;i++) { |
|
455 char namelen = buf[i]; |
|
456 char *name = buf + i + 1; |
|
457 char *attrname = malloc(namelen + 1); |
|
458 memcpy(attrname, name, namelen); |
|
459 attrname[namelen] = 0; |
|
460 ARRAY_ADD(array, arraypos, arraylen, attrname); |
|
461 i += namelen; |
|
462 } |
|
463 |
|
464 if(arraypos == 0) { |
|
465 free(array); |
|
466 array = NULL; |
|
467 } |
|
468 |
|
469 *nelm = arraypos; |
|
470 return array; |
|
471 } |
|
472 |
|
473 char ** xattr_list(const char *path, ssize_t *nelm) { |
|
474 *nelm = -1; |
|
475 ssize_t lslen = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0); |
|
476 if(lslen <= 0) { |
|
477 if(lslen == 0) { |
|
478 *nelm = 0; |
|
479 } |
|
480 return NULL; |
|
481 } |
|
482 |
|
483 char *list = malloc(lslen); |
|
484 ssize_t len = extattr_list_file(path, EXTATTR_NAMESPACE_USER, list, lslen); |
|
485 if(len == -1) { |
|
486 free(list); |
|
487 return NULL; |
|
488 } |
|
489 |
|
490 char **ret = parse_xattrlist(list, len, nelm); |
|
491 free(list); |
|
492 return ret; |
|
493 } |
|
494 |
|
495 char * xattr_get_alloc( |
|
496 void *pool, |
|
497 libxattr_malloc_func malloc_func, |
|
498 libxattr_free_func free_func, |
|
499 const char *path, |
|
500 const char *attr, |
|
501 ssize_t *len) |
|
502 { |
|
503 // get attribute length |
|
504 ssize_t attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, attr, NULL, 0); |
|
505 if(attrlen < 0) { |
|
506 *len = -1; |
|
507 return NULL; |
|
508 } |
|
509 |
|
510 char *buf = malloc_func(pool, attrlen + 1); |
|
511 ssize_t vlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, attr, buf, attrlen); |
|
512 if(vlen < 0) { |
|
513 *len = -1; |
|
514 free_func(pool, buf); |
|
515 return NULL; |
|
516 } |
|
517 buf[attrlen] = 0; |
|
518 |
|
519 *len = vlen; |
|
520 return buf; |
|
521 } |
|
522 |
|
523 int xattr_set(const char *path, const char *name, const void *value, size_t len) { |
|
524 int ret = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, value, len); |
|
525 return ret; |
|
526 } |
|
527 |
|
528 int xattr_remove(const char *path, const char *name) { |
|
529 return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name); |
|
530 } |
|
531 |
|
532 #endif /* FreeBSD */ |
|
533 |
|
534 |
|
535 #ifndef XATTR_SUPPORTED |
|
536 |
|
537 char ** xattr_list(const char *path, ssize_t *nelm) { |
|
538 *nelm = -1; |
|
539 return NULL; |
|
540 } |
|
541 |
|
542 char * xattr_get_alloc( |
|
543 void *pool, |
|
544 libxattr_malloc_func malloc_func, |
|
545 libxattr_free_func free_func, |
|
546 const char *path, |
|
547 const char *attr, |
|
548 ssize_t *len) |
|
549 { |
|
550 *len = -1; |
|
551 return NULL; |
|
552 } |
|
553 |
|
554 int xattr_set(const char *path, const char *name, const void *value, size_t len) { |
|
555 return -1; |
|
556 } |
|
557 |
|
558 int xattr_remove(const char *path, const char *name) { |
|
559 return -1; |
|
560 } |
|
561 |
|
562 #endif /* unsupported platform */ |
|
563 |
|
564 |
|
565 char * xattr_get(const char *path, const char *attr, ssize_t *len) { |
|
566 return xattr_get_alloc(NULL, libxattr_malloc, libxattr_free, path, attr, len); |
|
567 } |
|
568 |
|
569 void xattr_free_list(char **attrnames, ssize_t nelm) { |
|
570 if(attrnames) { |
|
571 for(int i=0;i<nelm;i++) { |
|
572 free(attrnames[i]); |
|
573 } |
|
574 free(attrnames); |
|
575 } |
|
576 } |