archive_acl.c revision 305754
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 (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { 711 id = ap->id; 712 } else { 713 id = -1; 714 } 715 append_entry(&p, NULL, ap->tag, name, 716 ap->permset, id); 717 count++; 718 } 719 } 720 721 722 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 723 if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) 724 prefix = "default:"; 725 else 726 prefix = NULL; 727 count = 0; 728 for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 729 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0) 730 continue; 731 r = archive_mstring_get_mbs_l( 732 &ap->name, &name, &len, sc); 733 if (r != 0) 734 return (-1); 735 if (count > 0) 736 *p++ = separator; 737 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 738 id = ap->id; 739 else 740 id = -1; 741 append_entry(&p, prefix, ap->tag, 742 name, ap->permset, id); 743 count ++; 744 } 745 } 746 747 *acl_text = acl->acl_text; 748 if (acl_text_len != NULL) 749 *acl_text_len = strlen(acl->acl_text); 750 return (0); 751} 752 753static void 754append_id(char **p, int id) 755{ 756 if (id < 0) 757 id = 0; 758 if (id > 9) 759 append_id(p, id / 10); 760 *(*p)++ = "0123456789"[id % 10]; 761} 762 763static void 764append_entry(char **p, const char *prefix, int tag, 765 const char *name, int perm, int id) 766{ 767 if (prefix != NULL) { 768 strcpy(*p, prefix); 769 *p += strlen(*p); 770 } 771 switch (tag) { 772 case ARCHIVE_ENTRY_ACL_USER_OBJ: 773 name = NULL; 774 id = -1; 775 /* FALLTHROUGH */ 776 case ARCHIVE_ENTRY_ACL_USER: 777 strcpy(*p, "user"); 778 break; 779 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 780 name = NULL; 781 id = -1; 782 /* FALLTHROUGH */ 783 case ARCHIVE_ENTRY_ACL_GROUP: 784 strcpy(*p, "group"); 785 break; 786 case ARCHIVE_ENTRY_ACL_MASK: 787 strcpy(*p, "mask"); 788 name = NULL; 789 id = -1; 790 break; 791 case ARCHIVE_ENTRY_ACL_OTHER: 792 strcpy(*p, "other"); 793 name = NULL; 794 id = -1; 795 break; 796 } 797 *p += strlen(*p); 798 *(*p)++ = ':'; 799 if (name != NULL) { 800 strcpy(*p, name); 801 *p += strlen(*p); 802 } else if (tag == ARCHIVE_ENTRY_ACL_USER 803 || tag == ARCHIVE_ENTRY_ACL_GROUP) { 804 append_id(p, id); 805 id = -1; 806 } 807 *(*p)++ = ':'; 808 *(*p)++ = (perm & 0444) ? 'r' : '-'; 809 *(*p)++ = (perm & 0222) ? 'w' : '-'; 810 *(*p)++ = (perm & 0111) ? 'x' : '-'; 811 if (id != -1) { 812 *(*p)++ = ':'; 813 append_id(p, id); 814 } 815 **p = '\0'; 816} 817 818/* 819 * Parse a textual ACL. This automatically recognizes and supports 820 * extensions described above. The 'type' argument is used to 821 * indicate the type that should be used for any entries not 822 * explicitly marked as "default:". 823 */ 824int 825archive_acl_parse_w(struct archive_acl *acl, 826 const wchar_t *text, int default_type) 827{ 828 struct { 829 const wchar_t *start; 830 const wchar_t *end; 831 } field[4], name; 832 833 int fields, n; 834 int type, tag, permset, id; 835 wchar_t sep; 836 837 while (text != NULL && *text != L'\0') { 838 /* 839 * Parse the fields out of the next entry, 840 * advance 'text' to start of next entry. 841 */ 842 fields = 0; 843 do { 844 const wchar_t *start, *end; 845 next_field_w(&text, &start, &end, &sep); 846 if (fields < 4) { 847 field[fields].start = start; 848 field[fields].end = end; 849 } 850 ++fields; 851 } while (sep == L':'); 852 853 /* Set remaining fields to blank. */ 854 for (n = fields; n < 4; ++n) 855 field[n].start = field[n].end = NULL; 856 857 /* Check for a numeric ID in field 1 or 3. */ 858 id = -1; 859 isint_w(field[1].start, field[1].end, &id); 860 /* Field 3 is optional. */ 861 if (id == -1 && fields > 3) 862 isint_w(field[3].start, field[3].end, &id); 863 864 /* 865 * Solaris extension: "defaultuser::rwx" is the 866 * default ACL corresponding to "user::rwx", etc. 867 */ 868 if (field[0].end - field[0].start > 7 869 && wmemcmp(field[0].start, L"default", 7) == 0) { 870 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 871 field[0].start += 7; 872 } else 873 type = default_type; 874 875 name.start = name.end = NULL; 876 if (prefix_w(field[0].start, field[0].end, L"user")) { 877 if (!ismode_w(field[2].start, field[2].end, &permset)) 878 return (ARCHIVE_WARN); 879 if (id != -1 || field[1].start < field[1].end) { 880 tag = ARCHIVE_ENTRY_ACL_USER; 881 name = field[1]; 882 } else 883 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 884 } else if (prefix_w(field[0].start, field[0].end, L"group")) { 885 if (!ismode_w(field[2].start, field[2].end, &permset)) 886 return (ARCHIVE_WARN); 887 if (id != -1 || field[1].start < field[1].end) { 888 tag = ARCHIVE_ENTRY_ACL_GROUP; 889 name = field[1]; 890 } else 891 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 892 } else if (prefix_w(field[0].start, field[0].end, L"other")) { 893 if (fields == 2 894 && field[1].start < field[1].end 895 && ismode_w(field[1].start, field[1].end, &permset)) { 896 /* This is Solaris-style "other:rwx" */ 897 } else if (fields == 3 898 && field[1].start == field[1].end 899 && field[2].start < field[2].end 900 && ismode_w(field[2].start, field[2].end, &permset)) { 901 /* This is FreeBSD-style "other::rwx" */ 902 } else 903 return (ARCHIVE_WARN); 904 tag = ARCHIVE_ENTRY_ACL_OTHER; 905 } else if (prefix_w(field[0].start, field[0].end, L"mask")) { 906 if (fields == 2 907 && field[1].start < field[1].end 908 && ismode_w(field[1].start, field[1].end, &permset)) { 909 /* This is Solaris-style "mask:rwx" */ 910 } else if (fields == 3 911 && field[1].start == field[1].end 912 && field[2].start < field[2].end 913 && ismode_w(field[2].start, field[2].end, &permset)) { 914 /* This is FreeBSD-style "mask::rwx" */ 915 } else 916 return (ARCHIVE_WARN); 917 tag = ARCHIVE_ENTRY_ACL_MASK; 918 } else 919 return (ARCHIVE_WARN); 920 921 /* Add entry to the internal list. */ 922 archive_acl_add_entry_w_len(acl, type, permset, 923 tag, id, name.start, name.end - name.start); 924 } 925 return (ARCHIVE_OK); 926} 927 928/* 929 * Parse a string to a positive decimal integer. Returns true if 930 * the string is non-empty and consists only of decimal digits, 931 * false otherwise. 932 */ 933static int 934isint_w(const wchar_t *start, const wchar_t *end, int *result) 935{ 936 int n = 0; 937 if (start >= end) 938 return (0); 939 while (start < end) { 940 if (*start < '0' || *start > '9') 941 return (0); 942 if (n > (INT_MAX / 10) || 943 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 944 n = INT_MAX; 945 } else { 946 n *= 10; 947 n += *start - '0'; 948 } 949 start++; 950 } 951 *result = n; 952 return (1); 953} 954 955/* 956 * Parse a string as a mode field. Returns true if 957 * the string is non-empty and consists only of mode characters, 958 * false otherwise. 959 */ 960static int 961ismode_w(const wchar_t *start, const wchar_t *end, int *permset) 962{ 963 const wchar_t *p; 964 965 if (start >= end) 966 return (0); 967 p = start; 968 *permset = 0; 969 while (p < end) { 970 switch (*p++) { 971 case 'r': case 'R': 972 *permset |= ARCHIVE_ENTRY_ACL_READ; 973 break; 974 case 'w': case 'W': 975 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 976 break; 977 case 'x': case 'X': 978 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 979 break; 980 case '-': 981 break; 982 default: 983 return (0); 984 } 985 } 986 return (1); 987} 988 989/* 990 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 991 * to point to just after the separator. *start points to the first 992 * character of the matched text and *end just after the last 993 * character of the matched identifier. In particular *end - *start 994 * is the length of the field body, not including leading or trailing 995 * whitespace. 996 */ 997static void 998next_field_w(const wchar_t **wp, const wchar_t **start, 999 const wchar_t **end, wchar_t *sep) 1000{ 1001 /* Skip leading whitespace to find start of field. */ 1002 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { 1003 (*wp)++; 1004 } 1005 *start = *wp; 1006 1007 /* Scan for the separator. */ 1008 while (**wp != L'\0' && **wp != L',' && **wp != L':' && 1009 **wp != L'\n') { 1010 (*wp)++; 1011 } 1012 *sep = **wp; 1013 1014 /* Trim trailing whitespace to locate end of field. */ 1015 *end = *wp - 1; 1016 while (**end == L' ' || **end == L'\t' || **end == L'\n') { 1017 (*end)--; 1018 } 1019 (*end)++; 1020 1021 /* Adjust scanner location. */ 1022 if (**wp != L'\0') 1023 (*wp)++; 1024} 1025 1026/* 1027 * Return true if the characters [start...end) are a prefix of 'test'. 1028 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1029 */ 1030static int 1031prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) 1032{ 1033 if (start == end) 1034 return (0); 1035 1036 if (*start++ != *test++) 1037 return (0); 1038 1039 while (start < end && *start++ == *test++) 1040 ; 1041 1042 if (start < end) 1043 return (0); 1044 1045 return (1); 1046} 1047 1048/* 1049 * Parse a textual ACL. This automatically recognizes and supports 1050 * extensions described above. The 'type' argument is used to 1051 * indicate the type that should be used for any entries not 1052 * explicitly marked as "default:". 1053 */ 1054int 1055archive_acl_parse_l(struct archive_acl *acl, 1056 const char *text, int default_type, struct archive_string_conv *sc) 1057{ 1058 struct { 1059 const char *start; 1060 const char *end; 1061 } field[4], name; 1062 1063 int fields, n, r, ret = ARCHIVE_OK; 1064 int type, tag, permset, id; 1065 char sep; 1066 1067 while (text != NULL && *text != '\0') { 1068 /* 1069 * Parse the fields out of the next entry, 1070 * advance 'text' to start of next entry. 1071 */ 1072 fields = 0; 1073 do { 1074 const char *start, *end; 1075 next_field(&text, &start, &end, &sep); 1076 if (fields < 4) { 1077 field[fields].start = start; 1078 field[fields].end = end; 1079 } 1080 ++fields; 1081 } while (sep == ':'); 1082 1083 /* Set remaining fields to blank. */ 1084 for (n = fields; n < 4; ++n) 1085 field[n].start = field[n].end = NULL; 1086 1087 /* Check for a numeric ID in field 1 or 3. */ 1088 id = -1; 1089 isint(field[1].start, field[1].end, &id); 1090 /* Field 3 is optional. */ 1091 if (id == -1 && fields > 3) 1092 isint(field[3].start, field[3].end, &id); 1093 1094 /* 1095 * Solaris extension: "defaultuser::rwx" is the 1096 * default ACL corresponding to "user::rwx", etc. 1097 */ 1098 if (field[0].end - field[0].start > 7 1099 && memcmp(field[0].start, "default", 7) == 0) { 1100 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1101 field[0].start += 7; 1102 } else 1103 type = default_type; 1104 1105 name.start = name.end = NULL; 1106 if (prefix_c(field[0].start, field[0].end, "user")) { 1107 if (!ismode(field[2].start, field[2].end, &permset)) 1108 return (ARCHIVE_WARN); 1109 if (id != -1 || field[1].start < field[1].end) { 1110 tag = ARCHIVE_ENTRY_ACL_USER; 1111 name = field[1]; 1112 } else 1113 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1114 } else if (prefix_c(field[0].start, field[0].end, "group")) { 1115 if (!ismode(field[2].start, field[2].end, &permset)) 1116 return (ARCHIVE_WARN); 1117 if (id != -1 || field[1].start < field[1].end) { 1118 tag = ARCHIVE_ENTRY_ACL_GROUP; 1119 name = field[1]; 1120 } else 1121 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1122 } else if (prefix_c(field[0].start, field[0].end, "other")) { 1123 if (fields == 2 1124 && field[1].start < field[1].end 1125 && ismode(field[1].start, field[1].end, &permset)) { 1126 /* This is Solaris-style "other:rwx" */ 1127 } else if (fields == 3 1128 && field[1].start == field[1].end 1129 && field[2].start < field[2].end 1130 && ismode(field[2].start, field[2].end, &permset)) { 1131 /* This is FreeBSD-style "other::rwx" */ 1132 } else 1133 return (ARCHIVE_WARN); 1134 tag = ARCHIVE_ENTRY_ACL_OTHER; 1135 } else if (prefix_c(field[0].start, field[0].end, "mask")) { 1136 if (fields == 2 1137 && field[1].start < field[1].end 1138 && ismode(field[1].start, field[1].end, &permset)) { 1139 /* This is Solaris-style "mask:rwx" */ 1140 } else if (fields == 3 1141 && field[1].start == field[1].end 1142 && field[2].start < field[2].end 1143 && ismode(field[2].start, field[2].end, &permset)) { 1144 /* This is FreeBSD-style "mask::rwx" */ 1145 } else 1146 return (ARCHIVE_WARN); 1147 tag = ARCHIVE_ENTRY_ACL_MASK; 1148 } else 1149 return (ARCHIVE_WARN); 1150 1151 /* Add entry to the internal list. */ 1152 r = archive_acl_add_entry_len_l(acl, type, permset, 1153 tag, id, name.start, name.end - name.start, sc); 1154 if (r < ARCHIVE_WARN) 1155 return (r); 1156 if (r != ARCHIVE_OK) 1157 ret = ARCHIVE_WARN; 1158 } 1159 return (ret); 1160} 1161 1162/* 1163 * Parse a string to a positive decimal integer. Returns true if 1164 * the string is non-empty and consists only of decimal digits, 1165 * false otherwise. 1166 */ 1167static int 1168isint(const char *start, const char *end, int *result) 1169{ 1170 int n = 0; 1171 if (start >= end) 1172 return (0); 1173 while (start < end) { 1174 if (*start < '0' || *start > '9') 1175 return (0); 1176 if (n > (INT_MAX / 10) || 1177 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1178 n = INT_MAX; 1179 } else { 1180 n *= 10; 1181 n += *start - '0'; 1182 } 1183 start++; 1184 } 1185 *result = n; 1186 return (1); 1187} 1188 1189/* 1190 * Parse a string as a mode field. Returns true if 1191 * the string is non-empty and consists only of mode characters, 1192 * false otherwise. 1193 */ 1194static int 1195ismode(const char *start, const char *end, int *permset) 1196{ 1197 const char *p; 1198 1199 if (start >= end) 1200 return (0); 1201 p = start; 1202 *permset = 0; 1203 while (p < end) { 1204 switch (*p++) { 1205 case 'r': case 'R': 1206 *permset |= ARCHIVE_ENTRY_ACL_READ; 1207 break; 1208 case 'w': case 'W': 1209 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1210 break; 1211 case 'x': case 'X': 1212 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1213 break; 1214 case '-': 1215 break; 1216 default: 1217 return (0); 1218 } 1219 } 1220 return (1); 1221} 1222 1223/* 1224 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1225 * to point to just after the separator. *start points to the first 1226 * character of the matched text and *end just after the last 1227 * character of the matched identifier. In particular *end - *start 1228 * is the length of the field body, not including leading or trailing 1229 * whitespace. 1230 */ 1231static void 1232next_field(const char **p, const char **start, 1233 const char **end, char *sep) 1234{ 1235 /* Skip leading whitespace to find start of field. */ 1236 while (**p == ' ' || **p == '\t' || **p == '\n') { 1237 (*p)++; 1238 } 1239 *start = *p; 1240 1241 /* Scan for the separator. */ 1242 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { 1243 (*p)++; 1244 } 1245 *sep = **p; 1246 1247 /* Trim trailing whitespace to locate end of field. */ 1248 *end = *p - 1; 1249 while (**end == ' ' || **end == '\t' || **end == '\n') { 1250 (*end)--; 1251 } 1252 (*end)++; 1253 1254 /* Adjust scanner location. */ 1255 if (**p != '\0') 1256 (*p)++; 1257} 1258 1259/* 1260 * Return true if the characters [start...end) are a prefix of 'test'. 1261 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1262 */ 1263static int 1264prefix_c(const char *start, const char *end, const char *test) 1265{ 1266 if (start == end) 1267 return (0); 1268 1269 if (*start++ != *test++) 1270 return (0); 1271 1272 while (start < end && *start++ == *test++) 1273 ; 1274 1275 if (start < end) 1276 return (0); 1277 1278 return (1); 1279} 1280