UNIXworkcode

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