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 *name = NULL; 82 for(int i=0;i<length;i++) { 83 if(!name && buf[i] == '.') { 84 name = buf + i + 1; 85 } 86 if(buf[i] == '\0') { 87 char *attrname = strdup(name); 88 if(!attrname) { 89 strarray_free(array, arraypos); 90 *nelm = -1; 91 return NULL; 92 } 93 if(strarray_add(&array, &arraypos, &arraylen, attrname)) { 94 strarray_free(array, arraypos); 95 *nelm = -1; 96 return NULL; 97 } 98 name = NULL; 99 } 100 } 101 102 if(arraypos == 0) { 103 free(array); 104 array = NULL; 105 } 106 107 *nelm = arraypos; 108 return array; 109 } 110 111 char ** xattr_list(const char *path, ssize_t *nelm) { 112 char *list = malloc(LIST_BUF_LEN); 113 if(!list) { 114 *nelm = -1; 115 return NULL; 116 } 117 ssize_t len = listxattr(path, list, LIST_BUF_LEN); 118 if(len == -1) { 119 switch(errno) { 120 case ERANGE: { 121 // buffer too, get size of attribute list 122 ssize_t newlen = listxattr(path, NULL, 0); 123 if(newlen > 0) { 124 // second try 125 char *new_list = realloc(list, newlen); 126 if(!new_list) { 127 free(list); 128 *nelm = -1; 129 return NULL; 130 } 131 list = new_list; 132 len = listxattr(path, list, newlen); 133 if(len != -1) { 134 // this time it worked 135 break; 136 } 137 } 138 } 139 default: { 140 free(list); 141 *nelm = -1; 142 return NULL; 143 } 144 } 145 } 146 147 char **ret = parse_xattrlist(list, len, nelm); 148 free(list); 149 return ret; 150 } 151 152 static char* name2nsname(const char *name) { 153 // add the 'user' namespace to the name 154 size_t namelen = strlen(name); 155 char *attrname = malloc(8 + namelen); 156 if(!attrname) { 157 return NULL; 158 } 159 memcpy(attrname, "user.", 5); 160 memcpy(attrname+5, name, namelen + 1); 161 return attrname; 162 } 163 164 char * xattr_get(const char *path, const char *attr, ssize_t *len) { 165 char *attrname = name2nsname(attr); 166 if(!attrname) { 167 *len = -1; 168 return NULL; 169 } 170 171 char *buf = malloc(ATTR_BUF_LEN); 172 if(!buf) { 173 *len = -1; 174 free(attrname); 175 return NULL; 176 } 177 ssize_t vlen = getxattr(path, attrname, buf, ATTR_BUF_LEN - 1); 178 if(vlen < 0) { 179 switch(errno) { 180 case ERANGE: { 181 ssize_t attrlen = getxattr(path, attrname, NULL, 0); 182 if(attrlen > 0) { 183 free(buf); 184 buf = malloc(attrlen + 1); 185 if(!buf) { 186 *len = -1; 187 free(attrname); 188 return NULL; 189 } 190 vlen = getxattr(path, attrname, buf, attrlen); 191 if(vlen > 0) { 192 break; 193 } 194 } 195 } 196 default: { 197 *len = -1; 198 free(buf); 199 free(attrname); 200 return NULL; 201 } 202 } 203 } 204 buf[vlen] = 0; 205 206 free(attrname); 207 *len = vlen; 208 return buf; 209 } 210 211 int xattr_set(const char *path, const char *name, const void *value, size_t len) { 212 char *attrname = name2nsname(name); 213 if(!attrname) { 214 return 1; 215 } 216 int ret = setxattr(path, attrname, value, len, 0); 217 free(attrname); 218 return ret; 219 } 220 221 int xattr_remove(const char *path, const char *name) { 222 char *attrname = name2nsname(name); 223 if(!attrname) { 224 return 1; 225 } 226 int ret = removexattr(path, attrname); 227 free(attrname); 228 return ret; 229 } 230 231 #endif /* Linux */ 232 233 #ifdef __APPLE__ 234 #define XATTR_SUPPORTED 235 #include <sys/xattr.h> 236 237 static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { 238 size_t arraylen = LIST_ARRAY_LEN; 239 size_t arraypos = 0; 240 char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); 241 242 char *name = buf; 243 for(int i=0;i<length;i++) { 244 if(buf[i] == '\0') { 245 char *attrname = strdup(name); 246 if(strarray_add(&array, &arraypos, &arraylen, attrname)) { 247 strarray_free(array, arraypos); 248 *nelm = -1; 249 return NULL; 250 } 251 name = buf + i + 1; 252 } 253 } 254 255 if(arraypos == 0) { 256 free(array); 257 array = NULL; 258 } 259 260 *nelm = arraypos; 261 return array; 262 } 263 264 char ** xattr_list(const char *path, ssize_t *nelm) { 265 char *list = malloc(LIST_BUF_LEN); 266 if(!list) { 267 *nelm = -1; 268 return NULL; 269 } 270 ssize_t len = listxattr(path, list, LIST_BUF_LEN, 0); 271 if(len == -1) { 272 switch(errno) { 273 case ERANGE: { 274 // buffer too, get size of attribute list 275 ssize_t newlen = listxattr(path, NULL, 0, 0); 276 if(newlen > 0) { 277 // second try 278 char *new_list = realloc(list, newlen); 279 if(!new_list) { 280 free(list); 281 *nelm = -1; 282 return NULL; 283 } 284 list = new_list; 285 len = listxattr(path, list, newlen, 0); 286 if(len != -1) { 287 // this time it worked 288 break; 289 } 290 } 291 } 292 default: { 293 free(list); 294 *nelm = -1; 295 return NULL; 296 } 297 } 298 } 299 300 char **ret = parse_xattrlist(list, len, nelm); 301 free(list); 302 return ret; 303 } 304 305 char * xattr_get(const char *path, const char *attr, ssize_t *len) { 306 // get attribute length 307 ssize_t attrlen = getxattr(path, attr, NULL, 0, 0, 0); 308 if(attrlen < 0) { 309 *len = -1; 310 return NULL; 311 } 312 313 char *buf = malloc(attrlen + 1); 314 if(!buf) { 315 *len = -1; 316 return NULL; 317 } 318 ssize_t vlen = getxattr(path, attr, buf, attrlen, 0, 0); 319 if(vlen < 0) { 320 *len = -1; 321 free(buf); 322 return NULL; 323 } 324 buf[vlen] = 0; 325 326 *len = vlen; 327 return buf; 328 } 329 330 int xattr_set(const char *path, const char *name, const void *value, size_t len) { 331 int ret = setxattr(path, name, value, len, 0, 0); 332 return ret; 333 } 334 335 int xattr_remove(const char *path, const char *name) { 336 return removexattr(path, name, 0); 337 } 338 339 #endif /* Apple */ 340 341 #ifdef __sun 342 #define XATTR_SUPPORTED 343 #include <unistd.h> 344 #include <sys/types.h> 345 #include <sys/stat.h> 346 #include <dirent.h> 347 #include <fcntl.h> 348 349 static int open_attrfile(const char *path, const char *attr, int oflag) { 350 int file = open(path, O_RDONLY); 351 if(file == -1) { 352 return -1; 353 } 354 355 int attrfile = openat(file, attr, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 356 close(file); 357 return attrfile; 358 } 359 360 char ** xattr_list(const char *path, ssize_t *nelm) { 361 *nelm = -1; 362 363 int attrdir = open_attrfile(path, ".", O_RDONLY|O_XATTR); 364 if(attrdir == -1) { 365 return NULL; 366 } 367 368 DIR *dir = fdopendir(attrdir); 369 if(!dir) { 370 close(attrdir); 371 return NULL; 372 } 373 374 size_t arraylen = LIST_ARRAY_LEN; 375 size_t arraypos = 0; 376 char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); 377 if(!array) { 378 closedir(dir); 379 *nelm = -1; 380 return NULL; 381 } 382 383 struct dirent *ent; 384 while((ent = readdir(dir)) != NULL) { 385 if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "SUNWattr_ro") || !strcmp(ent->d_name, "SUNWattr_rw")) { 386 continue; 387 } 388 char *name = strdup(ent->d_name); 389 if(strarray_add(&array, &arraypos, &arraylen, name)) { 390 strarray_free(array, arraypos); 391 *nelm = -1; 392 closedir(dir); 393 return NULL; 394 } 395 } 396 closedir(dir); 397 398 *nelm = arraypos; 399 return array; 400 } 401 402 char * xattr_get(const char *path, const char *attr, ssize_t *len) { 403 *len = -1; 404 405 int attrfile = open_attrfile(path, attr, O_RDONLY|O_XATTR); 406 if(attrfile == -1) { 407 return NULL; 408 } 409 410 struct stat s; 411 if(fstat(attrfile, &s)) { 412 close(attrfile); 413 return NULL; 414 } 415 416 size_t bufsize = (size_t)s.st_size; 417 char *buf = malloc(bufsize + 1); 418 if(!buf) { 419 close(attrfile); 420 return NULL; 421 } 422 423 char *b = buf; 424 size_t cur = 0; 425 while(cur < bufsize) { 426 ssize_t r = read(attrfile, buf + cur, bufsize - cur); 427 if(r <= 0) { 428 break; 429 } 430 cur += r; 431 } 432 433 close(attrfile); 434 if(cur != bufsize) { 435 free(buf); 436 return NULL; 437 } 438 439 buf[cur] = 0; 440 *len = (ssize_t)bufsize; 441 return buf; 442 } 443 444 int xattr_set(const char *path, const char *name, const void *value, size_t len) { 445 int attrfile = open_attrfile(path, name, O_CREAT|O_WRONLY|O_XATTR|O_TRUNC); 446 if(attrfile == -1) { 447 return -1; 448 } 449 450 const char *p = value; 451 size_t remaining = len; 452 while(remaining > 0) { 453 ssize_t w = write(attrfile, p, remaining); 454 if(w <= 0) { 455 break; 456 } 457 p += w; 458 remaining -= w; 459 } 460 461 close(attrfile); 462 463 return remaining > 0 ? -1 : 0; 464 } 465 466 int xattr_remove(const char *path, const char *name) { 467 int attrdir = open_attrfile(path, ".", O_RDONLY|O_XATTR); 468 if(attrdir == -1) { 469 return -1; 470 } 471 472 int ret = unlinkat(attrdir, name, 0); 473 close(attrdir); 474 return ret; 475 } 476 477 #endif /* Sun */ 478 479 480 #ifdef __FreeBSD__ 481 #define XATTR_SUPPORTED 482 483 #include <sys/types.h> 484 #include <sys/extattr.h> 485 486 static char ** parse_xattrlist(char *buf, ssize_t length, ssize_t *nelm) { 487 size_t arraylen = LIST_ARRAY_LEN; 488 size_t arraypos = 0; 489 char **array = malloc(LIST_ARRAY_LEN * sizeof(char*)); 490 if(!array) { 491 *nelm = -1; 492 return NULL; 493 } 494 495 for(int i=0;i<length;i++) { 496 int namelen = buf[i]; 497 char *name = buf + i + 1; 498 char *attrname = malloc(namelen + 1); 499 if(!attrname) { 500 strarray_free(array, arraypos); 501 *nelm = -1; 502 return NULL; 503 } 504 memcpy(attrname, name, namelen); 505 attrname[namelen] = 0; 506 if(strarray_add(&array, &arraypos, &arraylen, attrname)) { 507 strarray_free(array, arraypos); 508 *nelm = -1; 509 return NULL; 510 } 511 i += namelen; 512 } 513 514 if(arraypos == 0) { 515 free(array); 516 array = NULL; 517 } 518 519 *nelm = arraypos; 520 return array; 521 } 522 523 char ** xattr_list(const char *path, ssize_t *nelm) { 524 *nelm = -1; 525 ssize_t lslen = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0); 526 if(lslen <= 0) { 527 if(lslen == 0) { 528 *nelm = 0; 529 } 530 return NULL; 531 } 532 533 char *list = malloc(lslen); 534 if(!list) { 535 return NULL; 536 } 537 ssize_t len = extattr_list_file(path, EXTATTR_NAMESPACE_USER, list, lslen); 538 if(len == -1) { 539 free(list); 540 return NULL; 541 } 542 543 char **ret = parse_xattrlist(list, len, nelm); 544 free(list); 545 return ret; 546 } 547 548 char * xattr_get(const char *path, const char *attr, ssize_t *len) { 549 // get attribute length 550 ssize_t attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, attr, NULL, 0); 551 if(attrlen < 0) { 552 *len = -1; 553 return NULL; 554 } 555 556 char *buf = malloc(attrlen + 1); 557 ssize_t vlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, attr, buf, attrlen); 558 if(vlen < 0) { 559 *len = -1; 560 free(buf); 561 return NULL; 562 } 563 buf[attrlen] = 0; 564 *len = vlen; 565 return buf; 566 } 567 568 int xattr_set(const char *path, const char *name, const void *value, size_t len) { 569 int ret = extattr_set_file(path, EXTATTR_NAMESPACE_USER, name, value, len); 570 return ret >= 0 ? 0 : ret; 571 } 572 573 int xattr_remove(const char *path, const char *name) { 574 return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, name); 575 } 576 577 #endif /* FreeBSD */ 578 579 580 #ifndef XATTR_SUPPORTED 581 582 char ** xattr_list(const char *path, ssize_t *nelm) { 583 *nelm = -1; 584 return NULL; 585 } 586 587 char * xattr_get(const char *path, const char *attr, ssize_t *len) { 588 *len = -1; 589 return NULL; 590 } 591 592 int xattr_set(const char *path, const char *name, const void *value, size_t len) { 593 return -1; 594 } 595 596 int xattr_remove(const char *path, const char *name) { 597 return -1; 598 } 599 600 #endif /* unsupported platform */ 601 602 void xattr_free_list(char **attrnames, ssize_t nelm) { 603 if(attrnames) { 604 for(int i=0;i<nelm;i++) { 605 free(attrnames[i]); 606 } 607 free(attrnames); 608 } 609 } 610