UNIXworkcode

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