1/*- 2 * Copyright (c) 2003-2010 Tim Kientzle 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "archive_platform.h" 27__FBSDID("$FreeBSD$"); 28 29#ifdef HAVE_ERRNO_H 30#include <errno.h> 31#endif 32#ifdef HAVE_LIMITS_H 33#include <limits.h> 34#endif 35#ifdef HAVE_WCHAR_H 36#include <wchar.h> 37#endif 38 39#include "archive_acl_private.h" 40#include "archive_entry.h" 41#include "archive_private.h" 42 43#undef max 44#define max(a, b) ((a)>(b)?(a):(b)) 45 46#ifndef HAVE_WMEMCMP 47/* Good enough for simple equality testing, but not for sorting. */ 48#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) 49#endif 50 51static int acl_special(struct archive_acl *acl, 52 int type, int permset, int tag); 53static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl, 54 int type, int permset, int tag, int id); 55static int archive_acl_add_entry_len_l(struct archive_acl *acl, 56 int type, int permset, int tag, int id, const char *name, 57 size_t len, struct archive_string_conv *sc); 58static int isint_w(const wchar_t *start, const wchar_t *end, int *result); 59static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); 60static void next_field_w(const wchar_t **wp, const wchar_t **start, 61 const wchar_t **end, wchar_t *sep); 62static int prefix_w(const wchar_t *start, const wchar_t *end, 63 const wchar_t *test); 64static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, 65 const wchar_t *wname, int perm, int id); 66static void append_id_w(wchar_t **wp, int id); 67static int isint(const char *start, const char *end, int *result); 68static int ismode(const char *start, const char *end, int *result); 69static void next_field(const char **p, const char **start, 70 const char **end, char *sep); 71static int prefix_c(const char *start, const char *end, 72 const char *test); 73static void append_entry(char **p, const char *prefix, int tag, 74 const char *name, int perm, int id); 75static void append_id(char **p, int id); 76 77void 78archive_acl_clear(struct archive_acl *acl) 79{ 80 struct archive_acl_entry *ap; 81 82 while (acl->acl_head != NULL) { 83 ap = acl->acl_head->next; 84 archive_mstring_clean(&acl->acl_head->name); 85 free(acl->acl_head); 86 acl->acl_head = ap; 87 } 88 if (acl->acl_text_w != NULL) { 89 free(acl->acl_text_w); 90 acl->acl_text_w = NULL; 91 } 92 if (acl->acl_text != NULL) { 93 free(acl->acl_text); 94 acl->acl_text = NULL; 95 } 96 acl->acl_p = NULL; 97 acl->acl_state = 0; /* Not counting. */ 98} 99 100void 101archive_acl_copy(struct archive_acl *dest, struct archive_acl *src) 102{ 103 struct archive_acl_entry *ap, *ap2; 104 105 archive_acl_clear(dest); 106 107 dest->mode = src->mode; 108 ap = src->acl_head; 109 while (ap != NULL) { 110 ap2 = acl_new_entry(dest, 111 ap->type, ap->permset, ap->tag, ap->id); 112 if (ap2 != NULL) 113 archive_mstring_copy(&ap2->name, &ap->name); 114 ap = ap->next; 115 } 116} 117 118int 119archive_acl_add_entry(struct archive_acl *acl, 120 int type, int permset, int tag, int id, const char *name) 121{ 122 struct archive_acl_entry *ap; 123 124 if (acl_special(acl, type, permset, tag) == 0) 125 return ARCHIVE_OK; 126 ap = acl_new_entry(acl, type, permset, tag, id); 127 if (ap == NULL) { 128 /* XXX Error XXX */ 129 return ARCHIVE_FAILED; 130 } 131 if (name != NULL && *name != '\0') 132 archive_mstring_copy_mbs(&ap->name, name); 133 else 134 archive_mstring_clean(&ap->name); 135 return ARCHIVE_OK; 136} 137 138int 139archive_acl_add_entry_w_len(struct archive_acl *acl, 140 int type, int permset, int tag, int id, const wchar_t *name, size_t len) 141{ 142 struct archive_acl_entry *ap; 143 144 if (acl_special(acl, type, permset, tag) == 0) 145 return ARCHIVE_OK; 146 ap = acl_new_entry(acl, type, permset, tag, id); 147 if (ap == NULL) { 148 /* XXX Error XXX */ 149 return ARCHIVE_FAILED; 150 } 151 if (name != NULL && *name != L'\0' && len > 0) 152 archive_mstring_copy_wcs_len(&ap->name, name, len); 153 else 154 archive_mstring_clean(&ap->name); 155 return ARCHIVE_OK; 156} 157 158static int 159archive_acl_add_entry_len_l(struct archive_acl *acl, 160 int type, int permset, int tag, int id, const char *name, size_t len, 161 struct archive_string_conv *sc) 162{ 163 struct archive_acl_entry *ap; 164 int r; 165 166 if (acl_special(acl, type, permset, tag) == 0) 167 return ARCHIVE_OK; 168 ap = acl_new_entry(acl, type, permset, tag, id); 169 if (ap == NULL) { 170 /* XXX Error XXX */ 171 return ARCHIVE_FAILED; 172 } 173 if (name != NULL && *name != '\0' && len > 0) { 174 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc); 175 } else { 176 r = 0; 177 archive_mstring_clean(&ap->name); 178 } 179 if (r == 0) 180 return (ARCHIVE_OK); 181 else if (errno == ENOMEM) 182 return (ARCHIVE_FATAL); 183 else 184 return (ARCHIVE_WARN); 185} 186 187/* 188 * If this ACL entry is part of the standard POSIX permissions set, 189 * store the permissions in the stat structure and return zero. 190 */ 191static int 192acl_special(struct archive_acl *acl, int type, int permset, int tag) 193{ 194 if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS 195 && ((permset & ~007) == 0)) { 196 switch (tag) { 197 case ARCHIVE_ENTRY_ACL_USER_OBJ: 198 acl->mode &= ~0700; 199 acl->mode |= (permset & 7) << 6; 200 return (0); 201 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 202 acl->mode &= ~0070; 203 acl->mode |= (permset & 7) << 3; 204 return (0); 205 case ARCHIVE_ENTRY_ACL_OTHER: 206 acl->mode &= ~0007; 207 acl->mode |= permset & 7; 208 return (0); 209 } 210 } 211 return (1); 212} 213 214/* 215 * Allocate and populate a new ACL entry with everything but the 216 * name. 217 */ 218static struct archive_acl_entry * 219acl_new_entry(struct archive_acl *acl, 220 int type, int permset, int tag, int id) 221{ 222 struct archive_acl_entry *ap, *aq; 223 224 /* Type argument must be a valid NFS4 or POSIX.1e type. 225 * The type must agree with anything already set and 226 * the permset must be compatible. */ 227 if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 228 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 229 return (NULL); 230 } 231 if (permset & 232 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4 233 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) { 234 return (NULL); 235 } 236 } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 237 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 238 return (NULL); 239 } 240 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) { 241 return (NULL); 242 } 243 } else { 244 return (NULL); 245 } 246 247 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */ 248 switch (tag) { 249 case ARCHIVE_ENTRY_ACL_USER: 250 case ARCHIVE_ENTRY_ACL_USER_OBJ: 251 case ARCHIVE_ENTRY_ACL_GROUP: 252 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 253 /* Tags valid in both NFS4 and POSIX.1e */ 254 break; 255 case ARCHIVE_ENTRY_ACL_MASK: 256 case ARCHIVE_ENTRY_ACL_OTHER: 257 /* Tags valid only in POSIX.1e. */ 258 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 259 return (NULL); 260 } 261 break; 262 case ARCHIVE_ENTRY_ACL_EVERYONE: 263 /* Tags valid only in NFS4. */ 264 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 265 return (NULL); 266 } 267 break; 268 default: 269 /* No other values are valid. */ 270 return (NULL); 271 } 272 273 if (acl->acl_text_w != NULL) { 274 free(acl->acl_text_w); 275 acl->acl_text_w = NULL; 276 } 277 if (acl->acl_text != NULL) { 278 free(acl->acl_text); 279 acl->acl_text = NULL; 280 } 281 282 /* If there's a matching entry already in the list, overwrite it. */ 283 ap = acl->acl_head; 284 aq = NULL; 285 while (ap != NULL) { 286 if (ap->type == type && ap->tag == tag && ap->id == id) { 287 ap->permset = permset; 288 return (ap); 289 } 290 aq = ap; 291 ap = ap->next; 292 } 293 294 /* Add a new entry to the end of the list. */ 295 ap = (struct archive_acl_entry *)malloc(sizeof(*ap)); 296 if (ap == NULL) 297 return (NULL); 298 memset(ap, 0, sizeof(*ap)); 299 if (aq == NULL) 300 acl->acl_head = ap; 301 else 302 aq->next = ap; 303 ap->type = type; 304 ap->tag = tag; 305 ap->id = id; 306 ap->permset = permset; 307 acl->acl_types |= type; 308 return (ap); 309} 310 311/* 312 * Return a count of entries matching "want_type". 313 */ 314int 315archive_acl_count(struct archive_acl *acl, int want_type) 316{ 317 int count; 318 struct archive_acl_entry *ap; 319 320 count = 0; 321 ap = acl->acl_head; 322 while (ap != NULL) { 323 if ((ap->type & want_type) != 0) 324 count++; 325 ap = ap->next; 326 } 327 328 if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) 329 count += 3; 330 return (count); 331} 332 333/* 334 * Prepare for reading entries from the ACL data. Returns a count 335 * of entries matching "want_type", or zero if there are no 336 * non-extended ACL entries of that type. 337 */ 338int 339archive_acl_reset(struct archive_acl *acl, int want_type) 340{ 341 int count, cutoff; 342 343 count = archive_acl_count(acl, want_type); 344 345 /* 346 * If the only entries are the three standard ones, 347 * then don't return any ACL data. (In this case, 348 * client can just use chmod(2) to set permissions.) 349 */ 350 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) 351 cutoff = 3; 352 else 353 cutoff = 0; 354 355 if (count > cutoff) 356 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; 357 else 358 acl->acl_state = 0; 359 acl->acl_p = acl->acl_head; 360 return (count); 361} 362 363 364/* 365 * Return the next ACL entry in the list. Fake entries for the 366 * standard permissions and include them in the returned list. 367 */ 368int 369archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type, 370 int *permset, int *tag, int *id, const char **name) 371{ 372 *name = NULL; 373 *id = -1; 374 375 /* 376 * The acl_state is either zero (no entries available), -1 377 * (reading from list), or an entry type (retrieve that type 378 * from ae_stat.aest_mode). 379 */ 380 if (acl->acl_state == 0) 381 return (ARCHIVE_WARN); 382 383 /* The first three access entries are special. */ 384 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 385 switch (acl->acl_state) { 386 case ARCHIVE_ENTRY_ACL_USER_OBJ: 387 *permset = (acl->mode >> 6) & 7; 388 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 389 *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 390 acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 391 return (ARCHIVE_OK); 392 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 393 *permset = (acl->mode >> 3) & 7; 394 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 395 *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 396 acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER; 397 return (ARCHIVE_OK); 398 case ARCHIVE_ENTRY_ACL_OTHER: 399 *permset = acl->mode & 7; 400 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 401 *tag = ARCHIVE_ENTRY_ACL_OTHER; 402 acl->acl_state = -1; 403 acl->acl_p = acl->acl_head; 404 return (ARCHIVE_OK); 405 default: 406 break; 407 } 408 } 409 410 while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0) 411 acl->acl_p = acl->acl_p->next; 412 if (acl->acl_p == NULL) { 413 acl->acl_state = 0; 414 *type = 0; 415 *permset = 0; 416 *tag = 0; 417 *id = -1; 418 *name = NULL; 419 return (ARCHIVE_EOF); /* End of ACL entries. */ 420 } 421 *type = acl->acl_p->type; 422 *permset = acl->acl_p->permset; 423 *tag = acl->acl_p->tag; 424 *id = acl->acl_p->id; 425 if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) { 426 if (errno == ENOMEM) 427 return (ARCHIVE_FATAL); 428 *name = NULL; 429 } 430 acl->acl_p = acl->acl_p->next; 431 return (ARCHIVE_OK); 432} 433 434/* 435 * Generate a text version of the ACL. The flags parameter controls 436 * the style of the generated ACL. 437 */ 438const wchar_t * 439archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags) 440{ 441 int count; 442 size_t length; 443 const wchar_t *wname; 444 const wchar_t *prefix; 445 wchar_t separator; 446 struct archive_acl_entry *ap; 447 int id, r; 448 wchar_t *wp; 449 450 if (acl->acl_text_w != NULL) { 451 free (acl->acl_text_w); 452 acl->acl_text_w = NULL; 453 } 454 455 separator = L','; 456 count = 0; 457 length = 0; 458 ap = acl->acl_head; 459 while (ap != NULL) { 460 if ((ap->type & flags) != 0) { 461 count++; 462 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && 463 (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) 464 length += 8; /* "default:" */ 465 length += 5; /* tag name */ 466 length += 1; /* colon */ 467 r = archive_mstring_get_wcs(a, &ap->name, &wname); 468 if (r == 0 && wname != NULL) 469 length += wcslen(wname); 470 else if (r < 0 && errno == ENOMEM) 471 return (NULL); 472 else 473 length += sizeof(uid_t) * 3 + 1; 474 length ++; /* colon */ 475 length += 3; /* rwx */ 476 length += 1; /* colon */ 477 length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; 478 length ++; /* newline */ 479 } 480 ap = ap->next; 481 } 482 483 if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { 484 length += 10; /* "user::rwx\n" */ 485 length += 11; /* "group::rwx\n" */ 486 length += 11; /* "other::rwx\n" */ 487 } 488 489 if (count == 0) 490 return (NULL); 491 492 /* Now, allocate the string and actually populate it. */ 493 wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); 494 if (wp == NULL) 495 return (NULL); 496 count = 0; 497 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 498 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, 499 acl->mode & 0700, -1); 500 *wp++ = ','; 501 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, 502 acl->mode & 0070, -1); 503 *wp++ = ','; 504 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, 505 acl->mode & 0007, -1); 506 count += 3; 507 508 ap = acl->acl_head; 509 while (ap != NULL) { 510 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 511 r = archive_mstring_get_wcs(a, &ap->name, &wname); 512 if (r == 0) { 513 *wp++ = separator; 514 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 515 id = ap->id; 516 else 517 id = -1; 518 append_entry_w(&wp, NULL, ap->tag, wname, 519 ap->permset, id); 520 count++; 521 } else if (r < 0 && errno == ENOMEM) 522 return (NULL); 523 } 524 ap = ap->next; 525 } 526 } 527 528 529 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 530 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) 531 prefix = L"default:"; 532 else 533 prefix = NULL; 534 ap = acl->acl_head; 535 count = 0; 536 while (ap != NULL) { 537 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 538 r = archive_mstring_get_wcs(a, &ap->name, &wname); 539 if (r == 0) { 540 if (count > 0) 541 *wp++ = separator; 542 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 543 id = ap->id; 544 else 545 id = -1; 546 append_entry_w(&wp, prefix, ap->tag, 547 wname, ap->permset, id); 548 count ++; 549 } else if (r < 0 && errno == ENOMEM) 550 return (NULL); 551 } 552 ap = ap->next; 553 } 554 } 555 556 return (acl->acl_text_w); 557} 558 559 560static void 561append_id_w(wchar_t **wp, int id) 562{ 563 if (id < 0) 564 id = 0; 565 if (id > 9) 566 append_id_w(wp, id / 10); 567 *(*wp)++ = L"0123456789"[id % 10]; 568} 569 570static void 571append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, 572 const wchar_t *wname, int perm, int id) 573{ 574 if (prefix != NULL) { 575 wcscpy(*wp, prefix); 576 *wp += wcslen(*wp); 577 } 578 switch (tag) { 579 case ARCHIVE_ENTRY_ACL_USER_OBJ: 580 wname = NULL; 581 id = -1; 582 /* FALLTHROUGH */ 583 case ARCHIVE_ENTRY_ACL_USER: 584 wcscpy(*wp, L"user"); 585 break; 586 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 587 wname = NULL; 588 id = -1; 589 /* FALLTHROUGH */ 590 case ARCHIVE_ENTRY_ACL_GROUP: 591 wcscpy(*wp, L"group"); 592 break; 593 case ARCHIVE_ENTRY_ACL_MASK: 594 wcscpy(*wp, L"mask"); 595 wname = NULL; 596 id = -1; 597 break; 598 case ARCHIVE_ENTRY_ACL_OTHER: 599 wcscpy(*wp, L"other"); 600 wname = NULL; 601 id = -1; 602 break; 603 } 604 *wp += wcslen(*wp); 605 *(*wp)++ = L':'; 606 if (wname != NULL) { 607 wcscpy(*wp, wname); 608 *wp += wcslen(*wp); 609 } else if (tag == ARCHIVE_ENTRY_ACL_USER 610 || tag == ARCHIVE_ENTRY_ACL_GROUP) { 611 append_id_w(wp, id); 612 id = -1; 613 } 614 *(*wp)++ = L':'; 615 *(*wp)++ = (perm & 0444) ? L'r' : L'-'; 616 *(*wp)++ = (perm & 0222) ? L'w' : L'-'; 617 *(*wp)++ = (perm & 0111) ? L'x' : L'-'; 618 if (id != -1) { 619 *(*wp)++ = L':'; 620 append_id_w(wp, id); 621 } 622 **wp = L'\0'; 623} 624 625int 626archive_acl_text_l(struct archive_acl *acl, int flags, 627 const char **acl_text, size_t *acl_text_len, 628 struct archive_string_conv *sc) 629{ 630 int count; 631 size_t length; 632 const char *name; 633 const char *prefix; 634 char separator; 635 struct archive_acl_entry *ap; 636 size_t len; 637 int id, r; 638 char *p; 639 640 if (acl->acl_text != NULL) { 641 free (acl->acl_text); 642 acl->acl_text = NULL; 643 } 644 645 *acl_text = NULL; 646 if (acl_text_len != NULL) 647 *acl_text_len = 0; 648 separator = ','; 649 count = 0; 650 length = 0; 651 ap = acl->acl_head; 652 while (ap != NULL) { 653 if ((ap->type & flags) != 0) { 654 count++; 655 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && 656 (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) 657 length += 8; /* "default:" */ 658 length += 5; /* tag name */ 659 length += 1; /* colon */ 660 r = archive_mstring_get_mbs_l( 661 &ap->name, &name, &len, sc); 662 if (r != 0) 663 return (-1); 664 if (len > 0 && name != NULL) 665 length += len; 666 else 667 length += sizeof(uid_t) * 3 + 1; 668 length ++; /* colon */ 669 length += 3; /* rwx */ 670 length += 1; /* colon */ 671 length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; 672 length ++; /* newline */ 673 } 674 ap = ap->next; 675 } 676 677 if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { 678 length += 10; /* "user::rwx\n" */ 679 length += 11; /* "group::rwx\n" */ 680 length += 11; /* "other::rwx\n" */ 681 } 682 683 if (count == 0) 684 return (0); 685 686 /* Now, allocate the string and actually populate it. */ 687 p = acl->acl_text = (char *)malloc(length); 688 if (p == NULL) 689 return (-1); 690 count = 0; 691 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 692 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, 693 acl->mode & 0700, -1); 694 *p++ = ','; 695 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, 696 acl->mode & 0070, -1); 697 *p++ = ','; 698 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, 699 acl->mode & 0007, -1); 700 count += 3; 701 702 for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 703 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0) 704 continue; 705 r = archive_mstring_get_mbs_l( 706 &ap->name, &name, &len, sc); 707 if (r != 0) 708 return (-1); 709 *p++ = separator; 710 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 711 id = ap->id; 712 else 713 id = -1; 714 append_entry(&p, NULL, ap->tag, name, 715 ap->permset, id); 716 count++; 717 } 718 } 719 720 721 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 722 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) 723 prefix = "default:"; 724 else 725 prefix = NULL; 726 count = 0; 727 for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 728 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0) 729 continue; 730 r = archive_mstring_get_mbs_l( 731 &ap->name, &name, &len, sc); 732 if (r != 0) 733 return (-1); 734 if (count > 0) 735 *p++ = separator; 736 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 737 id = ap->id; 738 else 739 id = -1; 740 append_entry(&p, prefix, ap->tag, 741 name, ap->permset, id); 742 count ++; 743 } 744 } 745 746 *acl_text = acl->acl_text; 747 if (acl_text_len != NULL) 748 *acl_text_len = strlen(acl->acl_text); 749 return (0); 750} 751 752static void 753append_id(char **p, int id) 754{ 755 if (id < 0) 756 id = 0; 757 if (id > 9) 758 append_id(p, id / 10); 759 *(*p)++ = "0123456789"[id % 10]; 760} 761 762static void 763append_entry(char **p, const char *prefix, int tag, 764 const char *name, int perm, int id) 765{ 766 if (prefix != NULL) { 767 strcpy(*p, prefix); 768 *p += strlen(*p); 769 } 770 switch (tag) { 771 case ARCHIVE_ENTRY_ACL_USER_OBJ: 772 name = NULL; 773 id = -1; 774 /* FALLTHROUGH */ 775 case ARCHIVE_ENTRY_ACL_USER: 776 strcpy(*p, "user"); 777 break; 778 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 779 name = NULL; 780 id = -1; 781 /* FALLTHROUGH */ 782 case ARCHIVE_ENTRY_ACL_GROUP: 783 strcpy(*p, "group"); 784 break; 785 case ARCHIVE_ENTRY_ACL_MASK: 786 strcpy(*p, "mask"); 787 name = NULL; 788 id = -1; 789 break; 790 case ARCHIVE_ENTRY_ACL_OTHER: 791 strcpy(*p, "other"); 792 name = NULL; 793 id = -1; 794 break; 795 } 796 *p += strlen(*p); 797 *(*p)++ = ':'; 798 if (name != NULL) { 799 strcpy(*p, name); 800 *p += strlen(*p); 801 } else if (tag == ARCHIVE_ENTRY_ACL_USER 802 || tag == ARCHIVE_ENTRY_ACL_GROUP) { 803 append_id(p, id); 804 id = -1; 805 } 806 *(*p)++ = ':'; 807 *(*p)++ = (perm & 0444) ? 'r' : '-'; 808 *(*p)++ = (perm & 0222) ? 'w' : '-'; 809 *(*p)++ = (perm & 0111) ? 'x' : '-'; 810 if (id != -1) { 811 *(*p)++ = ':'; 812 append_id(p, id); 813 } 814 **p = '\0'; 815} 816 817/* 818 * Parse a textual ACL. This automatically recognizes and supports 819 * extensions described above. The 'type' argument is used to 820 * indicate the type that should be used for any entries not 821 * explicitly marked as "default:". 822 */ 823int 824archive_acl_parse_w(struct archive_acl *acl, 825 const wchar_t *text, int default_type) 826{ 827 struct { 828 const wchar_t *start; 829 const wchar_t *end; 830 } field[4], name; 831 832 int fields, n; 833 int type, tag, permset, id; 834 wchar_t sep; 835 836 while (text != NULL && *text != L'\0') { 837 /* 838 * Parse the fields out of the next entry, 839 * advance 'text' to start of next entry. 840 */ 841 fields = 0; 842 do { 843 const wchar_t *start, *end; 844 next_field_w(&text, &start, &end, &sep); 845 if (fields < 4) { 846 field[fields].start = start; 847 field[fields].end = end; 848 } 849 ++fields; 850 } while (sep == L':'); 851 852 /* Set remaining fields to blank. */ 853 for (n = fields; n < 4; ++n) 854 field[n].start = field[n].end = NULL; 855 856 /* Check for a numeric ID in field 1 or 3. */ 857 id = -1; 858 isint_w(field[1].start, field[1].end, &id); 859 /* Field 3 is optional. */ 860 if (id == -1 && fields > 3) 861 isint_w(field[3].start, field[3].end, &id); 862 863 /* 864 * Solaris extension: "defaultuser::rwx" is the 865 * default ACL corresponding to "user::rwx", etc. 866 */ 867 if (field[0].end - field[0].start > 7 868 && wmemcmp(field[0].start, L"default", 7) == 0) { 869 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 870 field[0].start += 7; 871 } else 872 type = default_type; 873 874 name.start = name.end = NULL; 875 if (prefix_w(field[0].start, field[0].end, L"user")) { 876 if (!ismode_w(field[2].start, field[2].end, &permset)) 877 return (ARCHIVE_WARN); 878 if (id != -1 || field[1].start < field[1].end) { 879 tag = ARCHIVE_ENTRY_ACL_USER; 880 name = field[1]; 881 } else 882 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 883 } else if (prefix_w(field[0].start, field[0].end, L"group")) { 884 if (!ismode_w(field[2].start, field[2].end, &permset)) 885 return (ARCHIVE_WARN); 886 if (id != -1 || field[1].start < field[1].end) { 887 tag = ARCHIVE_ENTRY_ACL_GROUP; 888 name = field[1]; 889 } else 890 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 891 } else if (prefix_w(field[0].start, field[0].end, L"other")) { 892 if (fields == 2 893 && field[1].start < field[1].end 894 && ismode_w(field[1].start, field[1].end, &permset)) { 895 /* This is Solaris-style "other:rwx" */ 896 } else if (fields == 3 897 && field[1].start == field[1].end 898 && field[2].start < field[2].end 899 && ismode_w(field[2].start, field[2].end, &permset)) { 900 /* This is FreeBSD-style "other::rwx" */ 901 } else 902 return (ARCHIVE_WARN); 903 tag = ARCHIVE_ENTRY_ACL_OTHER; 904 } else if (prefix_w(field[0].start, field[0].end, L"mask")) { 905 if (fields == 2 906 && field[1].start < field[1].end 907 && ismode_w(field[1].start, field[1].end, &permset)) { 908 /* This is Solaris-style "mask:rwx" */ 909 } else if (fields == 3 910 && field[1].start == field[1].end 911 && field[2].start < field[2].end 912 && ismode_w(field[2].start, field[2].end, &permset)) { 913 /* This is FreeBSD-style "mask::rwx" */ 914 } else 915 return (ARCHIVE_WARN); 916 tag = ARCHIVE_ENTRY_ACL_MASK; 917 } else 918 return (ARCHIVE_WARN); 919 920 /* Add entry to the internal list. */ 921 archive_acl_add_entry_w_len(acl, type, permset, 922 tag, id, name.start, name.end - name.start); 923 } 924 return (ARCHIVE_OK); 925} 926 927/* 928 * Parse a string to a positive decimal integer. Returns true if 929 * the string is non-empty and consists only of decimal digits, 930 * false otherwise. 931 */ 932static int 933isint_w(const wchar_t *start, const wchar_t *end, int *result) 934{ 935 int n = 0; 936 if (start >= end) 937 return (0); 938 while (start < end) { 939 if (*start < '0' || *start > '9') 940 return (0); 941 if (n > (INT_MAX / 10) || 942 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 943 n = INT_MAX; 944 } else { 945 n *= 10; 946 n += *start - '0'; 947 } 948 start++; 949 } 950 *result = n; 951 return (1); 952} 953 954/* 955 * Parse a string as a mode field. Returns true if 956 * the string is non-empty and consists only of mode characters, 957 * false otherwise. 958 */ 959static int 960ismode_w(const wchar_t *start, const wchar_t *end, int *permset) 961{ 962 const wchar_t *p; 963 964 if (start >= end) 965 return (0); 966 p = start; 967 *permset = 0; 968 while (p < end) { 969 switch (*p++) { 970 case 'r': case 'R': 971 *permset |= ARCHIVE_ENTRY_ACL_READ; 972 break; 973 case 'w': case 'W': 974 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 975 break; 976 case 'x': case 'X': 977 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 978 break; 979 case '-': 980 break; 981 default: 982 return (0); 983 } 984 } 985 return (1); 986} 987 988/* 989 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 990 * to point to just after the separator. *start points to the first 991 * character of the matched text and *end just after the last 992 * character of the matched identifier. In particular *end - *start 993 * is the length of the field body, not including leading or trailing 994 * whitespace. 995 */ 996static void 997next_field_w(const wchar_t **wp, const wchar_t **start, 998 const wchar_t **end, wchar_t *sep) 999{ 1000 /* Skip leading whitespace to find start of field. */ 1001 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { 1002 (*wp)++; 1003 } 1004 *start = *wp; 1005 1006 /* Scan for the separator. */ 1007 while (**wp != L'\0' && **wp != L',' && **wp != L':' && 1008 **wp != L'\n') { 1009 (*wp)++; 1010 } 1011 *sep = **wp; 1012 1013 /* Trim trailing whitespace to locate end of field. */ 1014 *end = *wp - 1; 1015 while (**end == L' ' || **end == L'\t' || **end == L'\n') { 1016 (*end)--; 1017 } 1018 (*end)++; 1019 1020 /* Adjust scanner location. */ 1021 if (**wp != L'\0') 1022 (*wp)++; 1023} 1024 1025/* 1026 * Return true if the characters [start...end) are a prefix of 'test'. 1027 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1028 */ 1029static int 1030prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) 1031{ 1032 if (start == end) 1033 return (0); 1034 1035 if (*start++ != *test++) 1036 return (0); 1037 1038 while (start < end && *start++ == *test++) 1039 ; 1040 1041 if (start < end) 1042 return (0); 1043 1044 return (1); 1045} 1046 1047/* 1048 * Parse a textual ACL. This automatically recognizes and supports 1049 * extensions described above. The 'type' argument is used to 1050 * indicate the type that should be used for any entries not 1051 * explicitly marked as "default:". 1052 */ 1053int 1054archive_acl_parse_l(struct archive_acl *acl, 1055 const char *text, int default_type, struct archive_string_conv *sc) 1056{ 1057 struct { 1058 const char *start; 1059 const char *end; 1060 } field[4], name; 1061 1062 int fields, n, r, ret = ARCHIVE_OK; 1063 int type, tag, permset, id; 1064 char sep; 1065 1066 while (text != NULL && *text != '\0') { 1067 /* 1068 * Parse the fields out of the next entry, 1069 * advance 'text' to start of next entry. 1070 */ 1071 fields = 0; 1072 do { 1073 const char *start, *end; 1074 next_field(&text, &start, &end, &sep); 1075 if (fields < 4) { 1076 field[fields].start = start; 1077 field[fields].end = end; 1078 } 1079 ++fields; 1080 } while (sep == ':'); 1081 1082 /* Set remaining fields to blank. */ 1083 for (n = fields; n < 4; ++n) 1084 field[n].start = field[n].end = NULL; 1085 1086 /* Check for a numeric ID in field 1 or 3. */ 1087 id = -1; 1088 isint(field[1].start, field[1].end, &id); 1089 /* Field 3 is optional. */ 1090 if (id == -1 && fields > 3) 1091 isint(field[3].start, field[3].end, &id); 1092 1093 /* 1094 * Solaris extension: "defaultuser::rwx" is the 1095 * default ACL corresponding to "user::rwx", etc. 1096 */ 1097 if (field[0].end - field[0].start > 7 1098 && memcmp(field[0].start, "default", 7) == 0) { 1099 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1100 field[0].start += 7; 1101 } else 1102 type = default_type; 1103 1104 name.start = name.end = NULL; 1105 if (prefix_c(field[0].start, field[0].end, "user")) { 1106 if (!ismode(field[2].start, field[2].end, &permset)) 1107 return (ARCHIVE_WARN); 1108 if (id != -1 || field[1].start < field[1].end) { 1109 tag = ARCHIVE_ENTRY_ACL_USER; 1110 name = field[1]; 1111 } else 1112 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1113 } else if (prefix_c(field[0].start, field[0].end, "group")) { 1114 if (!ismode(field[2].start, field[2].end, &permset)) 1115 return (ARCHIVE_WARN); 1116 if (id != -1 || field[1].start < field[1].end) { 1117 tag = ARCHIVE_ENTRY_ACL_GROUP; 1118 name = field[1]; 1119 } else 1120 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1121 } else if (prefix_c(field[0].start, field[0].end, "other")) { 1122 if (fields == 2 1123 && field[1].start < field[1].end 1124 && ismode(field[1].start, field[1].end, &permset)) { 1125 /* This is Solaris-style "other:rwx" */ 1126 } else if (fields == 3 1127 && field[1].start == field[1].end 1128 && field[2].start < field[2].end 1129 && ismode(field[2].start, field[2].end, &permset)) { 1130 /* This is FreeBSD-style "other::rwx" */ 1131 } else 1132 return (ARCHIVE_WARN); 1133 tag = ARCHIVE_ENTRY_ACL_OTHER; 1134 } else if (prefix_c(field[0].start, field[0].end, "mask")) { 1135 if (fields == 2 1136 && field[1].start < field[1].end 1137 && ismode(field[1].start, field[1].end, &permset)) { 1138 /* This is Solaris-style "mask:rwx" */ 1139 } else if (fields == 3 1140 && field[1].start == field[1].end 1141 && field[2].start < field[2].end 1142 && ismode(field[2].start, field[2].end, &permset)) { 1143 /* This is FreeBSD-style "mask::rwx" */ 1144 } else 1145 return (ARCHIVE_WARN); 1146 tag = ARCHIVE_ENTRY_ACL_MASK; 1147 } else 1148 return (ARCHIVE_WARN); 1149 1150 /* Add entry to the internal list. */ 1151 r = archive_acl_add_entry_len_l(acl, type, permset, 1152 tag, id, name.start, name.end - name.start, sc); 1153 if (r < ARCHIVE_WARN) 1154 return (r); 1155 if (r != ARCHIVE_OK) 1156 ret = ARCHIVE_WARN; 1157 } 1158 return (ret); 1159} 1160 1161/* 1162 * Parse a string to a positive decimal integer. Returns true if 1163 * the string is non-empty and consists only of decimal digits, 1164 * false otherwise. 1165 */ 1166static int 1167isint(const char *start, const char *end, int *result) 1168{ 1169 int n = 0; 1170 if (start >= end) 1171 return (0); 1172 while (start < end) { 1173 if (*start < '0' || *start > '9') 1174 return (0); 1175 if (n > (INT_MAX / 10) || 1176 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1177 n = INT_MAX; 1178 } else { 1179 n *= 10; 1180 n += *start - '0'; 1181 } 1182 start++; 1183 } 1184 *result = n; 1185 return (1); 1186} 1187 1188/* 1189 * Parse a string as a mode field. Returns true if 1190 * the string is non-empty and consists only of mode characters, 1191 * false otherwise. 1192 */ 1193static int 1194ismode(const char *start, const char *end, int *permset) 1195{ 1196 const char *p; 1197 1198 if (start >= end) 1199 return (0); 1200 p = start; 1201 *permset = 0; 1202 while (p < end) { 1203 switch (*p++) { 1204 case 'r': case 'R': 1205 *permset |= ARCHIVE_ENTRY_ACL_READ; 1206 break; 1207 case 'w': case 'W': 1208 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1209 break; 1210 case 'x': case 'X': 1211 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1212 break; 1213 case '-': 1214 break; 1215 default: 1216 return (0); 1217 } 1218 } 1219 return (1); 1220} 1221 1222/* 1223 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1224 * to point to just after the separator. *start points to the first 1225 * character of the matched text and *end just after the last 1226 * character of the matched identifier. In particular *end - *start 1227 * is the length of the field body, not including leading or trailing 1228 * whitespace. 1229 */ 1230static void 1231next_field(const char **p, const char **start, 1232 const char **end, char *sep) 1233{ 1234 /* Skip leading whitespace to find start of field. */ 1235 while (**p == ' ' || **p == '\t' || **p == '\n') { 1236 (*p)++; 1237 } 1238 *start = *p; 1239 1240 /* Scan for the separator. */ 1241 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { 1242 (*p)++; 1243 } 1244 *sep = **p; 1245 1246 /* Trim trailing whitespace to locate end of field. */ 1247 *end = *p - 1; 1248 while (**end == ' ' || **end == '\t' || **end == '\n') { 1249 (*end)--; 1250 } 1251 (*end)++; 1252 1253 /* Adjust scanner location. */ 1254 if (**p != '\0') 1255 (*p)++; 1256} 1257 1258/* 1259 * Return true if the characters [start...end) are a prefix of 'test'. 1260 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1261 */ 1262static int 1263prefix_c(const char *start, const char *end, const char *test) 1264{ 1265 if (start == end) 1266 return (0); 1267 1268 if (*start++ != *test++) 1269 return (0); 1270 1271 while (start < end && *start++ == *test++) 1272 ; 1273 1274 if (start < end) 1275 return (0); 1276 1277 return (1); 1278} 1279