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