archive_acl.c revision 313570
1/*- 2 * Copyright (c) 2003-2010 Tim Kientzle 3 * Copyright (c) 2016 Martin Matuska 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "archive_platform.h" 28__FBSDID("$FreeBSD$"); 29 30#ifdef HAVE_ERRNO_H 31#include <errno.h> 32#endif 33#ifdef HAVE_LIMITS_H 34#include <limits.h> 35#endif 36#ifdef HAVE_WCHAR_H 37#include <wchar.h> 38#endif 39 40#include "archive_acl_private.h" 41#include "archive_entry.h" 42#include "archive_private.h" 43 44#undef max 45#define max(a, b) ((a)>(b)?(a):(b)) 46 47#ifndef HAVE_WMEMCMP 48/* Good enough for simple equality testing, but not for sorting. */ 49#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) 50#endif 51 52static int acl_special(struct archive_acl *acl, 53 int type, int permset, int tag); 54static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl, 55 int type, int permset, int tag, int id); 56static int archive_acl_add_entry_len_l(struct archive_acl *acl, 57 int type, int permset, int tag, int id, const char *name, 58 size_t len, struct archive_string_conv *sc); 59static int archive_acl_text_want_type(struct archive_acl *acl, int flags); 60static ssize_t archive_acl_text_len(struct archive_acl *acl, int want_type, 61 int flags, int wide, struct archive *a, 62 struct archive_string_conv *sc); 63static int isint_w(const wchar_t *start, const wchar_t *end, int *result); 64static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); 65static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, 66 int *result); 67static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, 68 int *result); 69static void next_field_w(const wchar_t **wp, const wchar_t **start, 70 const wchar_t **end, wchar_t *sep); 71static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, 72 int tag, int flags, const wchar_t *wname, int perm, int id); 73static void append_id_w(wchar_t **wp, int id); 74static int isint(const char *start, const char *end, int *result); 75static int ismode(const char *start, const char *end, int *result); 76static int is_nfs4_flags(const char *start, const char *end, 77 int *result); 78static int is_nfs4_perms(const char *start, const char *end, 79 int *result); 80static void next_field(const char **p, const char **start, 81 const char **end, char *sep); 82static void append_entry(char **p, const char *prefix, int type, 83 int tag, int flags, const char *name, int perm, int id); 84static void append_id(char **p, int id); 85 86void 87archive_acl_clear(struct archive_acl *acl) 88{ 89 struct archive_acl_entry *ap; 90 91 while (acl->acl_head != NULL) { 92 ap = acl->acl_head->next; 93 archive_mstring_clean(&acl->acl_head->name); 94 free(acl->acl_head); 95 acl->acl_head = ap; 96 } 97 if (acl->acl_text_w != NULL) { 98 free(acl->acl_text_w); 99 acl->acl_text_w = NULL; 100 } 101 if (acl->acl_text != NULL) { 102 free(acl->acl_text); 103 acl->acl_text = NULL; 104 } 105 acl->acl_p = NULL; 106 acl->acl_types = 0; 107 acl->acl_state = 0; /* Not counting. */ 108} 109 110void 111archive_acl_copy(struct archive_acl *dest, struct archive_acl *src) 112{ 113 struct archive_acl_entry *ap, *ap2; 114 115 archive_acl_clear(dest); 116 117 dest->mode = src->mode; 118 ap = src->acl_head; 119 while (ap != NULL) { 120 ap2 = acl_new_entry(dest, 121 ap->type, ap->permset, ap->tag, ap->id); 122 if (ap2 != NULL) 123 archive_mstring_copy(&ap2->name, &ap->name); 124 ap = ap->next; 125 } 126} 127 128int 129archive_acl_add_entry(struct archive_acl *acl, 130 int type, int permset, int tag, int id, const char *name) 131{ 132 struct archive_acl_entry *ap; 133 134 if (acl_special(acl, type, permset, tag) == 0) 135 return ARCHIVE_OK; 136 ap = acl_new_entry(acl, type, permset, tag, id); 137 if (ap == NULL) { 138 /* XXX Error XXX */ 139 return ARCHIVE_FAILED; 140 } 141 if (name != NULL && *name != '\0') 142 archive_mstring_copy_mbs(&ap->name, name); 143 else 144 archive_mstring_clean(&ap->name); 145 return ARCHIVE_OK; 146} 147 148int 149archive_acl_add_entry_w_len(struct archive_acl *acl, 150 int type, int permset, int tag, int id, const wchar_t *name, size_t len) 151{ 152 struct archive_acl_entry *ap; 153 154 if (acl_special(acl, type, permset, tag) == 0) 155 return ARCHIVE_OK; 156 ap = acl_new_entry(acl, type, permset, tag, id); 157 if (ap == NULL) { 158 /* XXX Error XXX */ 159 return ARCHIVE_FAILED; 160 } 161 if (name != NULL && *name != L'\0' && len > 0) 162 archive_mstring_copy_wcs_len(&ap->name, name, len); 163 else 164 archive_mstring_clean(&ap->name); 165 return ARCHIVE_OK; 166} 167 168static int 169archive_acl_add_entry_len_l(struct archive_acl *acl, 170 int type, int permset, int tag, int id, const char *name, size_t len, 171 struct archive_string_conv *sc) 172{ 173 struct archive_acl_entry *ap; 174 int r; 175 176 if (acl_special(acl, type, permset, tag) == 0) 177 return ARCHIVE_OK; 178 ap = acl_new_entry(acl, type, permset, tag, id); 179 if (ap == NULL) { 180 /* XXX Error XXX */ 181 return ARCHIVE_FAILED; 182 } 183 if (name != NULL && *name != '\0' && len > 0) { 184 r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc); 185 } else { 186 r = 0; 187 archive_mstring_clean(&ap->name); 188 } 189 if (r == 0) 190 return (ARCHIVE_OK); 191 else if (errno == ENOMEM) 192 return (ARCHIVE_FATAL); 193 else 194 return (ARCHIVE_WARN); 195} 196 197/* 198 * If this ACL entry is part of the standard POSIX permissions set, 199 * store the permissions in the stat structure and return zero. 200 */ 201static int 202acl_special(struct archive_acl *acl, int type, int permset, int tag) 203{ 204 if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS 205 && ((permset & ~007) == 0)) { 206 switch (tag) { 207 case ARCHIVE_ENTRY_ACL_USER_OBJ: 208 acl->mode &= ~0700; 209 acl->mode |= (permset & 7) << 6; 210 return (0); 211 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 212 acl->mode &= ~0070; 213 acl->mode |= (permset & 7) << 3; 214 return (0); 215 case ARCHIVE_ENTRY_ACL_OTHER: 216 acl->mode &= ~0007; 217 acl->mode |= permset & 7; 218 return (0); 219 } 220 } 221 return (1); 222} 223 224/* 225 * Allocate and populate a new ACL entry with everything but the 226 * name. 227 */ 228static struct archive_acl_entry * 229acl_new_entry(struct archive_acl *acl, 230 int type, int permset, int tag, int id) 231{ 232 struct archive_acl_entry *ap, *aq; 233 234 /* Type argument must be a valid NFS4 or POSIX.1e type. 235 * The type must agree with anything already set and 236 * the permset must be compatible. */ 237 if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 238 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 239 return (NULL); 240 } 241 if (permset & 242 ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4 243 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) { 244 return (NULL); 245 } 246 } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 247 if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 248 return (NULL); 249 } 250 if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) { 251 return (NULL); 252 } 253 } else { 254 return (NULL); 255 } 256 257 /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */ 258 switch (tag) { 259 case ARCHIVE_ENTRY_ACL_USER: 260 case ARCHIVE_ENTRY_ACL_USER_OBJ: 261 case ARCHIVE_ENTRY_ACL_GROUP: 262 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 263 /* Tags valid in both NFS4 and POSIX.1e */ 264 break; 265 case ARCHIVE_ENTRY_ACL_MASK: 266 case ARCHIVE_ENTRY_ACL_OTHER: 267 /* Tags valid only in POSIX.1e. */ 268 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 269 return (NULL); 270 } 271 break; 272 case ARCHIVE_ENTRY_ACL_EVERYONE: 273 /* Tags valid only in NFS4. */ 274 if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 275 return (NULL); 276 } 277 break; 278 default: 279 /* No other values are valid. */ 280 return (NULL); 281 } 282 283 if (acl->acl_text_w != NULL) { 284 free(acl->acl_text_w); 285 acl->acl_text_w = NULL; 286 } 287 if (acl->acl_text != NULL) { 288 free(acl->acl_text); 289 acl->acl_text = NULL; 290 } 291 292 /* 293 * If there's a matching entry already in the list, overwrite it. 294 * NFSv4 entries may be repeated and are not overwritten. 295 * 296 * TODO: compare names of no id is provided (needs more rework) 297 */ 298 ap = acl->acl_head; 299 aq = NULL; 300 while (ap != NULL) { 301 if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) && 302 ap->type == type && ap->tag == tag && ap->id == id) { 303 if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER && 304 tag != ARCHIVE_ENTRY_ACL_GROUP)) { 305 ap->permset = permset; 306 return (ap); 307 } 308 } 309 aq = ap; 310 ap = ap->next; 311 } 312 313 /* Add a new entry to the end of the list. */ 314 ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap)); 315 if (ap == NULL) 316 return (NULL); 317 if (aq == NULL) 318 acl->acl_head = ap; 319 else 320 aq->next = ap; 321 ap->type = type; 322 ap->tag = tag; 323 ap->id = id; 324 ap->permset = permset; 325 acl->acl_types |= type; 326 return (ap); 327} 328 329/* 330 * Return a count of entries matching "want_type". 331 */ 332int 333archive_acl_count(struct archive_acl *acl, int want_type) 334{ 335 int count; 336 struct archive_acl_entry *ap; 337 338 count = 0; 339 ap = acl->acl_head; 340 while (ap != NULL) { 341 if ((ap->type & want_type) != 0) 342 count++; 343 ap = ap->next; 344 } 345 346 if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) 347 count += 3; 348 return (count); 349} 350 351/* 352 * Return a bitmask of stored ACL types in an ACL list 353 */ 354int 355archive_acl_types(struct archive_acl *acl) 356{ 357 return (acl->acl_types); 358} 359 360/* 361 * Prepare for reading entries from the ACL data. Returns a count 362 * of entries matching "want_type", or zero if there are no 363 * non-extended ACL entries of that type. 364 */ 365int 366archive_acl_reset(struct archive_acl *acl, int want_type) 367{ 368 int count, cutoff; 369 370 count = archive_acl_count(acl, want_type); 371 372 /* 373 * If the only entries are the three standard ones, 374 * then don't return any ACL data. (In this case, 375 * client can just use chmod(2) to set permissions.) 376 */ 377 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) 378 cutoff = 3; 379 else 380 cutoff = 0; 381 382 if (count > cutoff) 383 acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; 384 else 385 acl->acl_state = 0; 386 acl->acl_p = acl->acl_head; 387 return (count); 388} 389 390 391/* 392 * Return the next ACL entry in the list. Fake entries for the 393 * standard permissions and include them in the returned list. 394 */ 395int 396archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, 397 int *type, int *permset, int *tag, int *id, const char **name) 398{ 399 *name = NULL; 400 *id = -1; 401 402 /* 403 * The acl_state is either zero (no entries available), -1 404 * (reading from list), or an entry type (retrieve that type 405 * from ae_stat.aest_mode). 406 */ 407 if (acl->acl_state == 0) 408 return (ARCHIVE_WARN); 409 410 /* The first three access entries are special. */ 411 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 412 switch (acl->acl_state) { 413 case ARCHIVE_ENTRY_ACL_USER_OBJ: 414 *permset = (acl->mode >> 6) & 7; 415 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 416 *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 417 acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 418 return (ARCHIVE_OK); 419 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 420 *permset = (acl->mode >> 3) & 7; 421 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 422 *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 423 acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER; 424 return (ARCHIVE_OK); 425 case ARCHIVE_ENTRY_ACL_OTHER: 426 *permset = acl->mode & 7; 427 *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 428 *tag = ARCHIVE_ENTRY_ACL_OTHER; 429 acl->acl_state = -1; 430 acl->acl_p = acl->acl_head; 431 return (ARCHIVE_OK); 432 default: 433 break; 434 } 435 } 436 437 while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0) 438 acl->acl_p = acl->acl_p->next; 439 if (acl->acl_p == NULL) { 440 acl->acl_state = 0; 441 *type = 0; 442 *permset = 0; 443 *tag = 0; 444 *id = -1; 445 *name = NULL; 446 return (ARCHIVE_EOF); /* End of ACL entries. */ 447 } 448 *type = acl->acl_p->type; 449 *permset = acl->acl_p->permset; 450 *tag = acl->acl_p->tag; 451 *id = acl->acl_p->id; 452 if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) { 453 if (errno == ENOMEM) 454 return (ARCHIVE_FATAL); 455 *name = NULL; 456 } 457 acl->acl_p = acl->acl_p->next; 458 return (ARCHIVE_OK); 459} 460 461/* 462 * Determine what type of ACL do we want 463 */ 464static int 465archive_acl_text_want_type(struct archive_acl *acl, int flags) 466{ 467 int want_type; 468 469 /* Check if ACL is NFSv4 */ 470 if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 471 /* NFSv4 should never mix with POSIX.1e */ 472 if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) 473 return (0); 474 else 475 return (ARCHIVE_ENTRY_ACL_TYPE_NFS4); 476 } 477 478 /* Now deal with POSIX.1e ACLs */ 479 480 want_type = 0; 481 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) 482 want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 483 if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) 484 want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 485 486 /* By default we want both access and default ACLs */ 487 if (want_type == 0) 488 return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E); 489 490 return (want_type); 491} 492 493/* 494 * Calculate ACL text string length 495 */ 496static ssize_t 497archive_acl_text_len(struct archive_acl *acl, int want_type, int flags, 498 int wide, struct archive *a, struct archive_string_conv *sc) { 499 struct archive_acl_entry *ap; 500 const char *name; 501 const wchar_t *wname; 502 int count, idlen, tmp, r; 503 ssize_t length; 504 size_t len; 505 506 count = 0; 507 length = 0; 508 for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 509 if ((ap->type & want_type) == 0) 510 continue; 511 /* 512 * Filemode-mapping ACL entries are stored exclusively in 513 * ap->mode so they should not be in the list 514 */ 515 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) 516 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ 517 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ 518 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) 519 continue; 520 count++; 521 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 522 && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) 523 length += 8; /* "default:" */ 524 switch (ap->tag) { 525 case ARCHIVE_ENTRY_ACL_USER_OBJ: 526 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 527 length += 6; /* "owner@" */ 528 break; 529 } 530 /* FALLTHROUGH */ 531 case ARCHIVE_ENTRY_ACL_USER: 532 case ARCHIVE_ENTRY_ACL_MASK: 533 length += 4; /* "user", "mask" */ 534 break; 535 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 536 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 537 length += 6; /* "group@" */ 538 break; 539 } 540 /* FALLTHROUGH */ 541 case ARCHIVE_ENTRY_ACL_GROUP: 542 case ARCHIVE_ENTRY_ACL_OTHER: 543 length += 5; /* "group", "other" */ 544 break; 545 case ARCHIVE_ENTRY_ACL_EVERYONE: 546 length += 9; /* "everyone@" */ 547 break; 548 } 549 length += 1; /* colon after tag */ 550 if (ap->tag == ARCHIVE_ENTRY_ACL_USER || 551 ap->tag == ARCHIVE_ENTRY_ACL_GROUP) { 552 if (wide) { 553 r = archive_mstring_get_wcs(a, &ap->name, 554 &wname); 555 if (r == 0 && wname != NULL) 556 length += wcslen(wname); 557 else if (r < 0 && errno == ENOMEM) 558 return (0); 559 else 560 length += sizeof(uid_t) * 3 + 1; 561 } else { 562 r = archive_mstring_get_mbs_l(&ap->name, &name, 563 &len, sc); 564 if (r != 0) 565 return (0); 566 if (len > 0 && name != NULL) 567 length += len; 568 else 569 length += sizeof(uid_t) * 3 + 1; 570 } 571 length += 1; /* colon after user or group name */ 572 } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) 573 length += 1; /* 2nd colon empty user,group or other */ 574 575 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) 576 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) 577 && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER 578 || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) { 579 /* Solaris has no colon after other: and mask: */ 580 length = length - 1; 581 } 582 583 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 584 /* rwxpdDaARWcCos:fdinSFI:deny */ 585 length += 27; 586 if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0) 587 length += 1; /* allow, alarm, audit */ 588 } else 589 length += 3; /* rwx */ 590 591 if ((ap->tag == ARCHIVE_ENTRY_ACL_USER || 592 ap->tag == ARCHIVE_ENTRY_ACL_GROUP) && 593 (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) { 594 length += 1; /* colon */ 595 /* ID digit count */ 596 idlen = 1; 597 tmp = ap->id; 598 while (tmp > 9) { 599 tmp = tmp / 10; 600 idlen++; 601 } 602 length += idlen; 603 } 604 length ++; /* entry separator */ 605 } 606 607 /* Add filemode-mapping access entries to the length */ 608 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 609 if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) { 610 /* "user::rwx\ngroup::rwx\nother:rwx\n" */ 611 length += 31; 612 } else { 613 /* "user::rwx\ngroup::rwx\nother::rwx\n" */ 614 length += 32; 615 } 616 } else if (count == 0) 617 return (0); 618 619 /* The terminating character is included in count */ 620 return (length); 621} 622 623/* 624 * Generate a wide text version of the ACL. The flags parameter controls 625 * the type and style of the generated ACL. 626 */ 627wchar_t * 628archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, 629 struct archive *a) 630{ 631 int count; 632 ssize_t length; 633 size_t len; 634 const wchar_t *wname; 635 const wchar_t *prefix; 636 wchar_t separator; 637 struct archive_acl_entry *ap; 638 int id, r, want_type; 639 wchar_t *wp, *ws; 640 641 want_type = archive_acl_text_want_type(acl, flags); 642 643 /* Both NFSv4 and POSIX.1 types found */ 644 if (want_type == 0) 645 return (NULL); 646 647 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) 648 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; 649 650 length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL); 651 652 if (length == 0) 653 return (NULL); 654 655 if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) 656 separator = L','; 657 else 658 separator = L'\n'; 659 660 /* Now, allocate the string and actually populate it. */ 661 wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t)); 662 if (wp == NULL) { 663 if (errno == ENOMEM) 664 __archive_errx(1, "No memory"); 665 return (NULL); 666 } 667 count = 0; 668 669 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 670 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 671 ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, 672 acl->mode & 0700, -1); 673 *wp++ = separator; 674 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 675 ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, 676 acl->mode & 0070, -1); 677 *wp++ = separator; 678 append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 679 ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, 680 acl->mode & 0007, -1); 681 count += 3; 682 } 683 684 for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 685 if ((ap->type & want_type) == 0) 686 continue; 687 /* 688 * Filemode-mapping ACL entries are stored exclusively in 689 * ap->mode so they should not be in the list 690 */ 691 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) 692 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ 693 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ 694 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) 695 continue; 696 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT && 697 (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) 698 prefix = L"default:"; 699 else 700 prefix = NULL; 701 r = archive_mstring_get_wcs(a, &ap->name, &wname); 702 if (r == 0) { 703 if (count > 0) 704 *wp++ = separator; 705 if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 706 id = ap->id; 707 else 708 id = -1; 709 append_entry_w(&wp, prefix, ap->type, ap->tag, flags, 710 wname, ap->permset, id); 711 count++; 712 } else if (r < 0 && errno == ENOMEM) 713 return (NULL); 714 } 715 716 /* Add terminating character */ 717 *wp++ = L'\0'; 718 719 len = wcslen(ws); 720 721 if ((ssize_t)len > (length - 1)) 722 __archive_errx(1, "Buffer overrun"); 723 724 if (text_len != NULL) 725 *text_len = len; 726 727 return (ws); 728} 729 730static void 731append_id_w(wchar_t **wp, int id) 732{ 733 if (id < 0) 734 id = 0; 735 if (id > 9) 736 append_id_w(wp, id / 10); 737 *(*wp)++ = L"0123456789"[id % 10]; 738} 739 740static void 741append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, 742 int tag, int flags, const wchar_t *wname, int perm, int id) 743{ 744 if (prefix != NULL) { 745 wcscpy(*wp, prefix); 746 *wp += wcslen(*wp); 747 } 748 switch (tag) { 749 case ARCHIVE_ENTRY_ACL_USER_OBJ: 750 wname = NULL; 751 id = -1; 752 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 753 wcscpy(*wp, L"owner@"); 754 break; 755 } 756 /* FALLTHROUGH */ 757 case ARCHIVE_ENTRY_ACL_USER: 758 wcscpy(*wp, L"user"); 759 break; 760 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 761 wname = NULL; 762 id = -1; 763 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 764 wcscpy(*wp, L"group@"); 765 break; 766 } 767 /* FALLTHROUGH */ 768 case ARCHIVE_ENTRY_ACL_GROUP: 769 wcscpy(*wp, L"group"); 770 break; 771 case ARCHIVE_ENTRY_ACL_MASK: 772 wcscpy(*wp, L"mask"); 773 wname = NULL; 774 id = -1; 775 break; 776 case ARCHIVE_ENTRY_ACL_OTHER: 777 wcscpy(*wp, L"other"); 778 wname = NULL; 779 id = -1; 780 break; 781 case ARCHIVE_ENTRY_ACL_EVERYONE: 782 wcscpy(*wp, L"everyone@"); 783 wname = NULL; 784 id = -1; 785 break; 786 } 787 *wp += wcslen(*wp); 788 *(*wp)++ = L':'; 789 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || 790 tag == ARCHIVE_ENTRY_ACL_USER || 791 tag == ARCHIVE_ENTRY_ACL_GROUP) { 792 if (wname != NULL) { 793 wcscpy(*wp, wname); 794 *wp += wcslen(*wp); 795 } else if (tag == ARCHIVE_ENTRY_ACL_USER 796 || tag == ARCHIVE_ENTRY_ACL_GROUP) { 797 append_id_w(wp, id); 798 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) 799 id = -1; 800 } 801 /* Solaris style has no second colon after other and mask */ 802 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) 803 || (tag != ARCHIVE_ENTRY_ACL_OTHER 804 && tag != ARCHIVE_ENTRY_ACL_MASK)) 805 *(*wp)++ = L':'; 806 } 807 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { 808 /* POSIX.1e ACL perms */ 809 *(*wp)++ = (perm & 0444) ? L'r' : L'-'; 810 *(*wp)++ = (perm & 0222) ? L'w' : L'-'; 811 *(*wp)++ = (perm & 0111) ? L'x' : L'-'; 812 } else { 813 /* NFS4 ACL perms */ 814 *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_READ_DATA | 815 ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? L'r' : L'-'; 816 *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE_DATA | 817 ARCHIVE_ENTRY_ACL_ADD_FILE)) ? L'w' : L'-'; 818 *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_EXECUTE) ? L'x' : L'-'; 819 *(*wp)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA | 820 ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? L'p' : L'-'; 821 *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? L'd' : L'-'; 822 *(*wp)++ = (perm & 823 ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? L'D' : L'-'; 824 *(*wp)++ = (perm & 825 ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? L'a' : L'-'; 826 *(*wp)++ = (perm & 827 ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? L'A' : L'-'; 828 *(*wp)++ = (perm & 829 ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? L'R' : L'-'; 830 *(*wp)++ = (perm & 831 ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? L'W' : L'-'; 832 *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_READ_ACL) ? L'c' : L'-'; 833 *(*wp)++ = (perm & ARCHIVE_ENTRY_ACL_WRITE_ACL) ? L'C' : L'-'; 834 *(*wp)++ = (perm & 835 ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? L'o' : L'-'; 836 *(*wp)++ = (perm & 837 ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? L's' : L'-'; 838 *(*wp)++ = L':'; 839 *(*wp)++ = (perm & 840 ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? L'f' : L'-'; 841 *(*wp)++ = (perm & 842 ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? L'd' : L'-'; 843 *(*wp)++ = (perm & 844 ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? L'i' : L'-'; 845 *(*wp)++ = (perm & 846 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? L'n' : L'-'; 847 *(*wp)++ = (perm & 848 ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? L'S' : L'-'; 849 *(*wp)++ = (perm & 850 ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? L'F' : L'-'; 851 *(*wp)++ = (perm & 852 ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? L'I' : L'-'; 853 *(*wp)++ = L':'; 854 switch (type) { 855 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: 856 wcscpy(*wp, L"allow"); 857 break; 858 case ARCHIVE_ENTRY_ACL_TYPE_DENY: 859 wcscpy(*wp, L"deny"); 860 break; 861 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: 862 wcscpy(*wp, L"audit"); 863 break; 864 case ARCHIVE_ENTRY_ACL_TYPE_ALARM: 865 wcscpy(*wp, L"alarm"); 866 break; 867 default: 868 break; 869 } 870 *wp += wcslen(*wp); 871 } 872 if (id != -1) { 873 *(*wp)++ = L':'; 874 append_id_w(wp, id); 875 } 876} 877 878/* 879 * Generate a text version of the ACL. The flags parameter controls 880 * the type and style of the generated ACL. 881 */ 882char * 883archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, 884 struct archive_string_conv *sc) 885{ 886 int count; 887 ssize_t length; 888 size_t len; 889 const char *name; 890 const char *prefix; 891 char separator; 892 struct archive_acl_entry *ap; 893 int id, r, want_type; 894 char *p, *s; 895 896 want_type = archive_acl_text_want_type(acl, flags); 897 898 /* Both NFSv4 and POSIX.1 types found */ 899 if (want_type == 0) 900 return (NULL); 901 902 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) 903 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; 904 905 length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc); 906 907 if (length == 0) 908 return (NULL); 909 910 if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) 911 separator = ','; 912 else 913 separator = '\n'; 914 915 /* Now, allocate the string and actually populate it. */ 916 p = s = (char *)malloc(length * sizeof(char)); 917 if (p == NULL) { 918 if (errno == ENOMEM) 919 __archive_errx(1, "No memory"); 920 return (NULL); 921 } 922 count = 0; 923 924 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 925 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 926 ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, 927 acl->mode & 0700, -1); 928 *p++ = separator; 929 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 930 ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, 931 acl->mode & 0070, -1); 932 *p++ = separator; 933 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 934 ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, 935 acl->mode & 0007, -1); 936 count += 3; 937 } 938 939 for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 940 if ((ap->type & want_type) == 0) 941 continue; 942 /* 943 * Filemode-mapping ACL entries are stored exclusively in 944 * ap->mode so they should not be in the list 945 */ 946 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) 947 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ 948 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ 949 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) 950 continue; 951 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT && 952 (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) 953 prefix = "default:"; 954 else 955 prefix = NULL; 956 r = archive_mstring_get_mbs_l( 957 &ap->name, &name, &len, sc); 958 if (r != 0) 959 return (NULL); 960 if (count > 0) 961 *p++ = separator; 962 if (name == NULL || 963 (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { 964 id = ap->id; 965 } else { 966 id = -1; 967 } 968 append_entry(&p, prefix, ap->type, ap->tag, flags, name, 969 ap->permset, id); 970 count++; 971 } 972 973 /* Add terminating character */ 974 *p++ = '\0'; 975 976 len = strlen(s); 977 978 if ((ssize_t)len > (length - 1)) 979 __archive_errx(1, "Buffer overrun"); 980 981 if (text_len != NULL) 982 *text_len = len; 983 984 return (s); 985} 986 987static void 988append_id(char **p, int id) 989{ 990 if (id < 0) 991 id = 0; 992 if (id > 9) 993 append_id(p, id / 10); 994 *(*p)++ = "0123456789"[id % 10]; 995} 996 997static void 998append_entry(char **p, const char *prefix, int type, 999 int tag, int flags, const char *name, int perm, int id) 1000{ 1001 if (prefix != NULL) { 1002 strcpy(*p, prefix); 1003 *p += strlen(*p); 1004 } 1005 switch (tag) { 1006 case ARCHIVE_ENTRY_ACL_USER_OBJ: 1007 name = NULL; 1008 id = -1; 1009 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 1010 strcpy(*p, "owner@"); 1011 break; 1012 } 1013 /* FALLTHROUGH */ 1014 case ARCHIVE_ENTRY_ACL_USER: 1015 strcpy(*p, "user"); 1016 break; 1017 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1018 name = NULL; 1019 id = -1; 1020 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 1021 strcpy(*p, "group@"); 1022 break; 1023 } 1024 /* FALLTHROUGH */ 1025 case ARCHIVE_ENTRY_ACL_GROUP: 1026 strcpy(*p, "group"); 1027 break; 1028 case ARCHIVE_ENTRY_ACL_MASK: 1029 strcpy(*p, "mask"); 1030 name = NULL; 1031 id = -1; 1032 break; 1033 case ARCHIVE_ENTRY_ACL_OTHER: 1034 strcpy(*p, "other"); 1035 name = NULL; 1036 id = -1; 1037 break; 1038 case ARCHIVE_ENTRY_ACL_EVERYONE: 1039 strcpy(*p, "everyone@"); 1040 name = NULL; 1041 id = -1; 1042 break; 1043 } 1044 *p += strlen(*p); 1045 *(*p)++ = ':'; 1046 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || 1047 tag == ARCHIVE_ENTRY_ACL_USER || 1048 tag == ARCHIVE_ENTRY_ACL_GROUP) { 1049 if (name != NULL) { 1050 strcpy(*p, name); 1051 *p += strlen(*p); 1052 } else if (tag == ARCHIVE_ENTRY_ACL_USER 1053 || tag == ARCHIVE_ENTRY_ACL_GROUP) { 1054 append_id(p, id); 1055 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) 1056 id = -1; 1057 } 1058 /* Solaris style has no second colon after other and mask */ 1059 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) 1060 || (tag != ARCHIVE_ENTRY_ACL_OTHER 1061 && tag != ARCHIVE_ENTRY_ACL_MASK)) 1062 *(*p)++ = ':'; 1063 } 1064 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { 1065 /* POSIX.1e ACL perms */ 1066 *(*p)++ = (perm & 0444) ? 'r' : '-'; 1067 *(*p)++ = (perm & 0222) ? 'w' : '-'; 1068 *(*p)++ = (perm & 0111) ? 'x' : '-'; 1069 } else { 1070 /* NFS4 ACL perms */ 1071 *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_READ_DATA | 1072 ARCHIVE_ENTRY_ACL_LIST_DIRECTORY)) ? 'r' : '-'; 1073 *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_WRITE_DATA | 1074 ARCHIVE_ENTRY_ACL_ADD_FILE)) ? 'w' : '-'; 1075 *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_EXECUTE)) ? 'x' : '-'; 1076 *(*p)++ = (perm & (ARCHIVE_ENTRY_ACL_APPEND_DATA | 1077 ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY)) ? 'p' : '-'; 1078 *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE) ? 'd' : '-'; 1079 *(*p)++ = (perm & ARCHIVE_ENTRY_ACL_DELETE_CHILD) ? 'D' : '-'; 1080 *(*p)++ = (perm & 1081 ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES) ? 'a' : '-'; 1082 *(*p)++ = (perm & 1083 ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES) ? 'A' : '-'; 1084 *(*p)++ = (perm & 1085 ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS) ? 'R' : '-'; 1086 *(*p)++ = (perm & 1087 ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS) ? 'W' : '-'; 1088 *(*p)++ = (perm & 1089 ARCHIVE_ENTRY_ACL_READ_ACL) ? 'c' : '-'; 1090 *(*p)++ = (perm & 1091 ARCHIVE_ENTRY_ACL_WRITE_ACL) ? 'C' : '-'; 1092 *(*p)++ = (perm & 1093 ARCHIVE_ENTRY_ACL_WRITE_OWNER) ? 'o' : '-'; 1094 *(*p)++ = (perm & 1095 ARCHIVE_ENTRY_ACL_SYNCHRONIZE) ? 's' : '-'; 1096 *(*p)++ = ':'; 1097 *(*p)++ = (perm & 1098 ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT) ? 'f' : '-'; 1099 *(*p)++ = (perm & 1100 ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT) ? 'd' : '-'; 1101 *(*p)++ = (perm & 1102 ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY) ? 'i' : '-'; 1103 *(*p)++ = (perm & 1104 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT) ? 'n' : '-'; 1105 *(*p)++ = (perm & 1106 ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS) ? 'S' : '-'; 1107 *(*p)++ = (perm & 1108 ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS) ? 'F' : '-'; 1109 *(*p)++ = (perm & 1110 ARCHIVE_ENTRY_ACL_ENTRY_INHERITED) ? 'I' : '-'; 1111 *(*p)++ = ':'; 1112 switch (type) { 1113 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: 1114 strcpy(*p, "allow"); 1115 break; 1116 case ARCHIVE_ENTRY_ACL_TYPE_DENY: 1117 strcpy(*p, "deny"); 1118 break; 1119 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: 1120 strcpy(*p, "audit"); 1121 break; 1122 case ARCHIVE_ENTRY_ACL_TYPE_ALARM: 1123 strcpy(*p, "alarm"); 1124 break; 1125 } 1126 *p += strlen(*p); 1127 } 1128 if (id != -1) { 1129 *(*p)++ = ':'; 1130 append_id(p, id); 1131 } 1132} 1133 1134/* 1135 * Parse a wide ACL text string. 1136 * 1137 * The want_type argument may be one of the following: 1138 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS 1139 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT 1140 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL 1141 * 1142 * POSIX.1e ACL entries prefixed with "default:" are treated as 1143 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 1144 */ 1145int 1146archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, 1147 int want_type) 1148{ 1149 struct { 1150 const wchar_t *start; 1151 const wchar_t *end; 1152 } field[6], name; 1153 1154 const wchar_t *s, *st; 1155 1156 int numfields, fields, n, r, sol, ret; 1157 int type, types, tag, permset, id; 1158 size_t len; 1159 wchar_t sep; 1160 1161 ret = ARCHIVE_OK; 1162 types = 0; 1163 1164 switch (want_type) { 1165 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: 1166 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 1167 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 1168 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 1169 numfields = 5; 1170 break; 1171 case ARCHIVE_ENTRY_ACL_TYPE_NFS4: 1172 numfields = 6; 1173 break; 1174 default: 1175 return (ARCHIVE_FATAL); 1176 } 1177 1178 while (text != NULL && *text != L'\0') { 1179 /* 1180 * Parse the fields out of the next entry, 1181 * advance 'text' to start of next entry. 1182 */ 1183 fields = 0; 1184 do { 1185 const wchar_t *start, *end; 1186 next_field_w(&text, &start, &end, &sep); 1187 if (fields < numfields) { 1188 field[fields].start = start; 1189 field[fields].end = end; 1190 } 1191 ++fields; 1192 } while (sep == L':'); 1193 1194 /* Set remaining fields to blank. */ 1195 for (n = fields; n < numfields; ++n) 1196 field[n].start = field[n].end = NULL; 1197 1198 if (field[0].start != NULL && *(field[0].start) == L'#') { 1199 /* Comment, skip entry */ 1200 continue; 1201 } 1202 1203 n = 0; 1204 sol = 0; 1205 id = -1; 1206 permset = 0; 1207 name.start = name.end = NULL; 1208 1209 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 1210 /* POSIX.1e ACLs */ 1211 /* 1212 * Default keyword "default:user::rwx" 1213 * if found, we have one more field 1214 * 1215 * We also support old Solaris extension: 1216 * "defaultuser::rwx" is the default ACL corresponding 1217 * to "user::rwx", etc. valid only for first field 1218 */ 1219 s = field[0].start; 1220 len = field[0].end - field[0].start; 1221 if (*s == L'd' && (len == 1 || (len >= 7 1222 && wmemcmp((s + 1), L"efault", 6) == 0))) { 1223 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1224 if (len > 7) 1225 field[0].start += 7; 1226 else 1227 n = 1; 1228 } else 1229 type = want_type; 1230 1231 /* Check for a numeric ID in field n+1 or n+3. */ 1232 isint_w(field[n + 1].start, field[n + 1].end, &id); 1233 /* Field n+3 is optional. */ 1234 if (id == -1 && fields > n+3) 1235 isint_w(field[n + 3].start, field[n + 3].end, 1236 &id); 1237 1238 tag = 0; 1239 s = field[n].start; 1240 st = field[n].start + 1; 1241 len = field[n].end - field[n].start; 1242 1243 switch (*s) { 1244 case L'u': 1245 if (len == 1 || (len == 4 1246 && wmemcmp(st, L"ser", 3) == 0)) 1247 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1248 break; 1249 case L'g': 1250 if (len == 1 || (len == 5 1251 && wmemcmp(st, L"roup", 4) == 0)) 1252 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1253 break; 1254 case L'o': 1255 if (len == 1 || (len == 5 1256 && wmemcmp(st, L"ther", 4) == 0)) 1257 tag = ARCHIVE_ENTRY_ACL_OTHER; 1258 break; 1259 case L'm': 1260 if (len == 1 || (len == 4 1261 && wmemcmp(st, L"ask", 3) == 0)) 1262 tag = ARCHIVE_ENTRY_ACL_MASK; 1263 break; 1264 default: 1265 break; 1266 } 1267 1268 switch (tag) { 1269 case ARCHIVE_ENTRY_ACL_OTHER: 1270 case ARCHIVE_ENTRY_ACL_MASK: 1271 if (fields == (n + 2) 1272 && field[n + 1].start < field[n + 1].end 1273 && ismode_w(field[n + 1].start, 1274 field[n + 1].end, &permset)) { 1275 /* This is Solaris-style "other:rwx" */ 1276 sol = 1; 1277 } else if (fields == (n + 3) && 1278 field[n + 1].start < field[n + 1].end) { 1279 /* Invalid mask or other field */ 1280 ret = ARCHIVE_WARN; 1281 continue; 1282 } 1283 break; 1284 case ARCHIVE_ENTRY_ACL_USER_OBJ: 1285 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1286 if (id != -1 || 1287 field[n + 1].start < field[n + 1].end) { 1288 name = field[n + 1]; 1289 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) 1290 tag = ARCHIVE_ENTRY_ACL_USER; 1291 else 1292 tag = ARCHIVE_ENTRY_ACL_GROUP; 1293 } 1294 break; 1295 default: 1296 /* Invalid tag, skip entry */ 1297 ret = ARCHIVE_WARN; 1298 continue; 1299 } 1300 1301 /* 1302 * Without "default:" we expect mode in field 2 1303 * Exception: Solaris other and mask fields 1304 */ 1305 if (permset == 0 && !ismode_w(field[n + 2 - sol].start, 1306 field[n + 2 - sol].end, &permset)) { 1307 /* Invalid mode, skip entry */ 1308 ret = ARCHIVE_WARN; 1309 continue; 1310 } 1311 } else { 1312 /* NFS4 ACLs */ 1313 s = field[0].start; 1314 len = field[0].end - field[0].start; 1315 tag = 0; 1316 1317 switch (len) { 1318 case 4: 1319 if (wmemcmp(s, L"user", 4) == 0) 1320 tag = ARCHIVE_ENTRY_ACL_USER; 1321 break; 1322 case 5: 1323 if (wmemcmp(s, L"group", 5) == 0) 1324 tag = ARCHIVE_ENTRY_ACL_GROUP; 1325 break; 1326 case 6: 1327 if (wmemcmp(s, L"owner@", 6) == 0) 1328 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1329 else if (wmemcmp(s, L"group@", len) == 0) 1330 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1331 break; 1332 case 9: 1333 if (wmemcmp(s, L"everyone@", 9) == 0) 1334 tag = ARCHIVE_ENTRY_ACL_EVERYONE; 1335 default: 1336 break; 1337 } 1338 1339 if (tag == 0) { 1340 /* Invalid tag, skip entry */ 1341 ret = ARCHIVE_WARN; 1342 continue; 1343 } else if (tag == ARCHIVE_ENTRY_ACL_USER || 1344 tag == ARCHIVE_ENTRY_ACL_GROUP) { 1345 n = 1; 1346 name = field[1]; 1347 isint_w(name.start, name.end, &id); 1348 } else 1349 n = 0; 1350 1351 if (!is_nfs4_perms_w(field[1 + n].start, 1352 field[1 + n].end, &permset)) { 1353 /* Invalid NFSv4 perms, skip entry */ 1354 ret = ARCHIVE_WARN; 1355 continue; 1356 } 1357 if (!is_nfs4_flags_w(field[2 + n].start, 1358 field[2 + n].end, &permset)) { 1359 /* Invalid NFSv4 flags, skip entry */ 1360 ret = ARCHIVE_WARN; 1361 continue; 1362 } 1363 s = field[3 + n].start; 1364 len = field[3 + n].end - field[3 + n].start; 1365 type = 0; 1366 if (len == 4) { 1367 if (wmemcmp(s, L"deny", 4) == 0) 1368 type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 1369 } else if (len == 5) { 1370 if (wmemcmp(s, L"allow", 5) == 0) 1371 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 1372 else if (wmemcmp(s, L"audit", 5) == 0) 1373 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 1374 else if (wmemcmp(s, L"alarm", 5) == 0) 1375 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 1376 } 1377 if (type == 0) { 1378 /* Invalid entry type, skip entry */ 1379 ret = ARCHIVE_WARN; 1380 continue; 1381 } 1382 isint_w(field[4 + n].start, field[4 + n].end, &id); 1383 } 1384 1385 /* Add entry to the internal list. */ 1386 r = archive_acl_add_entry_w_len(acl, type, permset, 1387 tag, id, name.start, name.end - name.start); 1388 if (r < ARCHIVE_WARN) 1389 return (r); 1390 if (r != ARCHIVE_OK) 1391 ret = ARCHIVE_WARN; 1392 types |= type; 1393 } 1394 1395 /* Reset ACL */ 1396 archive_acl_reset(acl, types); 1397 1398 return (ret); 1399} 1400 1401/* 1402 * Parse a string to a positive decimal integer. Returns true if 1403 * the string is non-empty and consists only of decimal digits, 1404 * false otherwise. 1405 */ 1406static int 1407isint_w(const wchar_t *start, const wchar_t *end, int *result) 1408{ 1409 int n = 0; 1410 if (start >= end) 1411 return (0); 1412 while (start < end) { 1413 if (*start < '0' || *start > '9') 1414 return (0); 1415 if (n > (INT_MAX / 10) || 1416 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1417 n = INT_MAX; 1418 } else { 1419 n *= 10; 1420 n += *start - '0'; 1421 } 1422 start++; 1423 } 1424 *result = n; 1425 return (1); 1426} 1427 1428/* 1429 * Parse a string as a mode field. Returns true if 1430 * the string is non-empty and consists only of mode characters, 1431 * false otherwise. 1432 */ 1433static int 1434ismode_w(const wchar_t *start, const wchar_t *end, int *permset) 1435{ 1436 const wchar_t *p; 1437 1438 if (start >= end) 1439 return (0); 1440 p = start; 1441 *permset = 0; 1442 while (p < end) { 1443 switch (*p++) { 1444 case L'r': case L'R': 1445 *permset |= ARCHIVE_ENTRY_ACL_READ; 1446 break; 1447 case L'w': case L'W': 1448 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1449 break; 1450 case L'x': case L'X': 1451 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1452 break; 1453 case L'-': 1454 break; 1455 default: 1456 return (0); 1457 } 1458 } 1459 return (1); 1460} 1461 1462/* 1463 * Parse a string as a NFS4 ACL permission field. 1464 * Returns true if the string is non-empty and consists only of NFS4 ACL 1465 * permission characters, false otherwise 1466 */ 1467static int 1468is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset) 1469{ 1470 const wchar_t *p; 1471 1472 if (start >= end) 1473 return (0); 1474 p = start; 1475 while (p < end) { 1476 switch (*p++) { 1477 case L'r': 1478 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; 1479 break; 1480 case L'w': 1481 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; 1482 break; 1483 case L'x': 1484 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1485 break; 1486 case L'p': 1487 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; 1488 break; 1489 case L'D': 1490 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; 1491 break; 1492 case L'd': 1493 *permset |= ARCHIVE_ENTRY_ACL_DELETE; 1494 break; 1495 case L'a': 1496 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; 1497 break; 1498 case L'A': 1499 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; 1500 break; 1501 case L'R': 1502 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; 1503 break; 1504 case L'W': 1505 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; 1506 break; 1507 case L'c': 1508 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; 1509 break; 1510 case L'C': 1511 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; 1512 break; 1513 case L'o': 1514 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; 1515 break; 1516 case L's': 1517 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; 1518 break; 1519 case L'-': 1520 break; 1521 default: 1522 return(0); 1523 } 1524 } 1525 return (1); 1526} 1527 1528/* 1529 * Parse a string as a NFS4 ACL flags field. 1530 * Returns true if the string is non-empty and consists only of NFS4 ACL 1531 * flag characters, false otherwise 1532 */ 1533static int 1534is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset) 1535{ 1536 const wchar_t *p; 1537 1538 if (start >= end) 1539 return (0); 1540 p = start; 1541 while (p < end) { 1542 switch(*p++) { 1543 case L'f': 1544 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; 1545 break; 1546 case L'd': 1547 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; 1548 break; 1549 case L'i': 1550 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; 1551 break; 1552 case L'n': 1553 *permset |= 1554 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; 1555 break; 1556 case L'S': 1557 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; 1558 break; 1559 case L'F': 1560 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; 1561 break; 1562 case L'I': 1563 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; 1564 break; 1565 case L'-': 1566 break; 1567 default: 1568 return (0); 1569 } 1570 } 1571 return (1); 1572} 1573 1574/* 1575 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1576 * to point to just after the separator. *start points to the first 1577 * character of the matched text and *end just after the last 1578 * character of the matched identifier. In particular *end - *start 1579 * is the length of the field body, not including leading or trailing 1580 * whitespace. 1581 */ 1582static void 1583next_field_w(const wchar_t **wp, const wchar_t **start, 1584 const wchar_t **end, wchar_t *sep) 1585{ 1586 /* Skip leading whitespace to find start of field. */ 1587 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { 1588 (*wp)++; 1589 } 1590 *start = *wp; 1591 1592 /* Scan for the separator. */ 1593 while (**wp != L'\0' && **wp != L',' && **wp != L':' && 1594 **wp != L'\n') { 1595 (*wp)++; 1596 } 1597 *sep = **wp; 1598 1599 /* Trim trailing whitespace to locate end of field. */ 1600 *end = *wp - 1; 1601 while (**end == L' ' || **end == L'\t' || **end == L'\n') { 1602 (*end)--; 1603 } 1604 (*end)++; 1605 1606 /* Adjust scanner location. */ 1607 if (**wp != L'\0') 1608 (*wp)++; 1609} 1610 1611/* 1612 * Parse an ACL text string. 1613 * 1614 * The want_type argument may be one of the following: 1615 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS 1616 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT 1617 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL 1618 * 1619 * POSIX.1e ACL entries prefixed with "default:" are treated as 1620 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 1621 */ 1622int 1623archive_acl_from_text_l(struct archive_acl *acl, const char *text, 1624 int want_type, struct archive_string_conv *sc) 1625{ 1626 struct { 1627 const char *start; 1628 const char *end; 1629 } field[6], name; 1630 1631 const char *s, *st; 1632 int numfields, fields, n, r, sol, ret; 1633 int type, types, tag, permset, id; 1634 size_t len; 1635 char sep; 1636 1637 switch (want_type) { 1638 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: 1639 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 1640 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 1641 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 1642 numfields = 5; 1643 break; 1644 case ARCHIVE_ENTRY_ACL_TYPE_NFS4: 1645 numfields = 6; 1646 break; 1647 default: 1648 return (ARCHIVE_FATAL); 1649 } 1650 1651 ret = ARCHIVE_OK; 1652 types = 0; 1653 1654 while (text != NULL && *text != '\0') { 1655 /* 1656 * Parse the fields out of the next entry, 1657 * advance 'text' to start of next entry. 1658 */ 1659 fields = 0; 1660 do { 1661 const char *start, *end; 1662 next_field(&text, &start, &end, &sep); 1663 if (fields < numfields) { 1664 field[fields].start = start; 1665 field[fields].end = end; 1666 } 1667 ++fields; 1668 } while (sep == ':'); 1669 1670 /* Set remaining fields to blank. */ 1671 for (n = fields; n < numfields; ++n) 1672 field[n].start = field[n].end = NULL; 1673 1674 if (field[0].start != NULL && *(field[0].start) == '#') { 1675 /* Comment, skip entry */ 1676 continue; 1677 } 1678 1679 n = 0; 1680 sol = 0; 1681 id = -1; 1682 permset = 0; 1683 name.start = name.end = NULL; 1684 1685 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 1686 /* POSIX.1e ACLs */ 1687 /* 1688 * Default keyword "default:user::rwx" 1689 * if found, we have one more field 1690 * 1691 * We also support old Solaris extension: 1692 * "defaultuser::rwx" is the default ACL corresponding 1693 * to "user::rwx", etc. valid only for first field 1694 */ 1695 s = field[0].start; 1696 len = field[0].end - field[0].start; 1697 if (*s == 'd' && (len == 1 || (len >= 7 1698 && memcmp((s + 1), "efault", 6) == 0))) { 1699 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1700 if (len > 7) 1701 field[0].start += 7; 1702 else 1703 n = 1; 1704 } else 1705 type = want_type; 1706 1707 /* Check for a numeric ID in field n+1 or n+3. */ 1708 isint(field[n + 1].start, field[n + 1].end, &id); 1709 /* Field n+3 is optional. */ 1710 if (id == -1 && fields > (n + 3)) 1711 isint(field[n + 3].start, field[n + 3].end, 1712 &id); 1713 1714 tag = 0; 1715 s = field[n].start; 1716 st = field[n].start + 1; 1717 len = field[n].end - field[n].start; 1718 1719 switch (*s) { 1720 case 'u': 1721 if (len == 1 || (len == 4 1722 && memcmp(st, "ser", 3) == 0)) 1723 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1724 break; 1725 case 'g': 1726 if (len == 1 || (len == 5 1727 && memcmp(st, "roup", 4) == 0)) 1728 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1729 break; 1730 case 'o': 1731 if (len == 1 || (len == 5 1732 && memcmp(st, "ther", 4) == 0)) 1733 tag = ARCHIVE_ENTRY_ACL_OTHER; 1734 break; 1735 case 'm': 1736 if (len == 1 || (len == 4 1737 && memcmp(st, "ask", 3) == 0)) 1738 tag = ARCHIVE_ENTRY_ACL_MASK; 1739 break; 1740 default: 1741 break; 1742 } 1743 1744 switch (tag) { 1745 case ARCHIVE_ENTRY_ACL_OTHER: 1746 case ARCHIVE_ENTRY_ACL_MASK: 1747 if (fields == (n + 2) 1748 && field[n + 1].start < field[n + 1].end 1749 && ismode(field[n + 1].start, 1750 field[n + 1].end, &permset)) { 1751 /* This is Solaris-style "other:rwx" */ 1752 sol = 1; 1753 } else if (fields == (n + 3) && 1754 field[n + 1].start < field[n + 1].end) { 1755 /* Invalid mask or other field */ 1756 ret = ARCHIVE_WARN; 1757 continue; 1758 } 1759 break; 1760 case ARCHIVE_ENTRY_ACL_USER_OBJ: 1761 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1762 if (id != -1 || 1763 field[n + 1].start < field[n + 1].end) { 1764 name = field[n + 1]; 1765 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) 1766 tag = ARCHIVE_ENTRY_ACL_USER; 1767 else 1768 tag = ARCHIVE_ENTRY_ACL_GROUP; 1769 } 1770 break; 1771 default: 1772 /* Invalid tag, skip entry */ 1773 ret = ARCHIVE_WARN; 1774 continue; 1775 } 1776 1777 /* 1778 * Without "default:" we expect mode in field 3 1779 * Exception: Solaris other and mask fields 1780 */ 1781 if (permset == 0 && !ismode(field[n + 2 - sol].start, 1782 field[n + 2 - sol].end, &permset)) { 1783 /* Invalid mode, skip entry */ 1784 ret = ARCHIVE_WARN; 1785 continue; 1786 } 1787 } else { 1788 /* NFS4 ACLs */ 1789 s = field[0].start; 1790 len = field[0].end - field[0].start; 1791 tag = 0; 1792 1793 switch (len) { 1794 case 4: 1795 if (memcmp(s, "user", 4) == 0) 1796 tag = ARCHIVE_ENTRY_ACL_USER; 1797 break; 1798 case 5: 1799 if (memcmp(s, "group", 5) == 0) 1800 tag = ARCHIVE_ENTRY_ACL_GROUP; 1801 break; 1802 case 6: 1803 if (memcmp(s, "owner@", 6) == 0) 1804 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1805 else if (memcmp(s, "group@", 6) == 0) 1806 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1807 break; 1808 case 9: 1809 if (memcmp(s, "everyone@", 9) == 0) 1810 tag = ARCHIVE_ENTRY_ACL_EVERYONE; 1811 break; 1812 default: 1813 break; 1814 } 1815 1816 if (tag == 0) { 1817 /* Invalid tag, skip entry */ 1818 ret = ARCHIVE_WARN; 1819 continue; 1820 } else if (tag == ARCHIVE_ENTRY_ACL_USER || 1821 tag == ARCHIVE_ENTRY_ACL_GROUP) { 1822 n = 1; 1823 name = field[1]; 1824 isint(name.start, name.end, &id); 1825 } else 1826 n = 0; 1827 1828 if (!is_nfs4_perms(field[1 + n].start, 1829 field[1 + n].end, &permset)) { 1830 /* Invalid NFSv4 perms, skip entry */ 1831 ret = ARCHIVE_WARN; 1832 continue; 1833 } 1834 if (!is_nfs4_flags(field[2 + n].start, 1835 field[2 + n].end, &permset)) { 1836 /* Invalid NFSv4 flags, skip entry */ 1837 ret = ARCHIVE_WARN; 1838 continue; 1839 } 1840 s = field[3 + n].start; 1841 len = field[3 + n].end - field[3 + n].start; 1842 type = 0; 1843 if (len == 4) { 1844 if (memcmp(s, "deny", 4) == 0) 1845 type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 1846 } else if (len == 5) { 1847 if (memcmp(s, "allow", 5) == 0) 1848 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 1849 else if (memcmp(s, "audit", 5) == 0) 1850 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 1851 else if (memcmp(s, "alarm", 5) == 0) 1852 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 1853 } 1854 if (type == 0) { 1855 /* Invalid entry type, skip entry */ 1856 ret = ARCHIVE_WARN; 1857 continue; 1858 } 1859 isint(field[4 + n].start, field[4 + n].end, 1860 &id); 1861 } 1862 1863 /* Add entry to the internal list. */ 1864 r = archive_acl_add_entry_len_l(acl, type, permset, 1865 tag, id, name.start, name.end - name.start, sc); 1866 if (r < ARCHIVE_WARN) 1867 return (r); 1868 if (r != ARCHIVE_OK) 1869 ret = ARCHIVE_WARN; 1870 types |= type; 1871 } 1872 1873 /* Reset ACL */ 1874 archive_acl_reset(acl, types); 1875 1876 return (ret); 1877} 1878 1879/* 1880 * Parse a string to a positive decimal integer. Returns true if 1881 * the string is non-empty and consists only of decimal digits, 1882 * false otherwise. 1883 */ 1884static int 1885isint(const char *start, const char *end, int *result) 1886{ 1887 int n = 0; 1888 if (start >= end) 1889 return (0); 1890 while (start < end) { 1891 if (*start < '0' || *start > '9') 1892 return (0); 1893 if (n > (INT_MAX / 10) || 1894 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1895 n = INT_MAX; 1896 } else { 1897 n *= 10; 1898 n += *start - '0'; 1899 } 1900 start++; 1901 } 1902 *result = n; 1903 return (1); 1904} 1905 1906/* 1907 * Parse a string as a mode field. Returns true if 1908 * the string is non-empty and consists only of mode characters, 1909 * false otherwise. 1910 */ 1911static int 1912ismode(const char *start, const char *end, int *permset) 1913{ 1914 const char *p; 1915 1916 if (start >= end) 1917 return (0); 1918 p = start; 1919 *permset = 0; 1920 while (p < end) { 1921 switch (*p++) { 1922 case 'r': case 'R': 1923 *permset |= ARCHIVE_ENTRY_ACL_READ; 1924 break; 1925 case 'w': case 'W': 1926 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1927 break; 1928 case 'x': case 'X': 1929 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1930 break; 1931 case '-': 1932 break; 1933 default: 1934 return (0); 1935 } 1936 } 1937 return (1); 1938} 1939 1940/* 1941 * Parse a string as a NFS4 ACL permission field. 1942 * Returns true if the string is non-empty and consists only of NFS4 ACL 1943 * permission characters, false otherwise 1944 */ 1945static int 1946is_nfs4_perms(const char *start, const char *end, int *permset) 1947{ 1948 const char *p; 1949 1950 if (start >= end) 1951 return (0); 1952 p = start; 1953 while (p < end) { 1954 switch (*p++) { 1955 case 'r': 1956 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; 1957 break; 1958 case 'w': 1959 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; 1960 break; 1961 case 'x': 1962 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1963 break; 1964 case 'p': 1965 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; 1966 break; 1967 case 'D': 1968 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; 1969 break; 1970 case 'd': 1971 *permset |= ARCHIVE_ENTRY_ACL_DELETE; 1972 break; 1973 case 'a': 1974 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; 1975 break; 1976 case 'A': 1977 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; 1978 break; 1979 case 'R': 1980 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; 1981 break; 1982 case 'W': 1983 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; 1984 break; 1985 case 'c': 1986 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; 1987 break; 1988 case 'C': 1989 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; 1990 break; 1991 case 'o': 1992 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; 1993 break; 1994 case 's': 1995 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; 1996 break; 1997 case '-': 1998 break; 1999 default: 2000 return(0); 2001 } 2002 } 2003 return (1); 2004} 2005 2006/* 2007 * Parse a string as a NFS4 ACL flags field. 2008 * Returns true if the string is non-empty and consists only of NFS4 ACL 2009 * flag characters, false otherwise 2010 */ 2011static int 2012is_nfs4_flags(const char *start, const char *end, int *permset) 2013{ 2014 const char *p; 2015 2016 if (start >= end) 2017 return (0); 2018 p = start; 2019 while (p < end) { 2020 switch(*p++) { 2021 case 'f': 2022 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; 2023 break; 2024 case 'd': 2025 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; 2026 break; 2027 case 'i': 2028 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; 2029 break; 2030 case 'n': 2031 *permset |= 2032 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; 2033 break; 2034 case 'S': 2035 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; 2036 break; 2037 case 'F': 2038 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; 2039 break; 2040 case 'I': 2041 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; 2042 break; 2043 case '-': 2044 break; 2045 default: 2046 return (0); 2047 } 2048 } 2049 return (1); 2050} 2051 2052/* 2053 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 2054 * to point to just after the separator. *start points to the first 2055 * character of the matched text and *end just after the last 2056 * character of the matched identifier. In particular *end - *start 2057 * is the length of the field body, not including leading or trailing 2058 * whitespace. 2059 */ 2060static void 2061next_field(const char **p, const char **start, 2062 const char **end, char *sep) 2063{ 2064 /* Skip leading whitespace to find start of field. */ 2065 while (**p == ' ' || **p == '\t' || **p == '\n') { 2066 (*p)++; 2067 } 2068 *start = *p; 2069 2070 /* Scan for the separator. */ 2071 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { 2072 (*p)++; 2073 } 2074 *sep = **p; 2075 2076 /* Trim trailing whitespace to locate end of field. */ 2077 *end = *p - 1; 2078 while (**end == ' ' || **end == '\t' || **end == '\n') { 2079 (*end)--; 2080 } 2081 (*end)++; 2082 2083 /* Adjust scanner location. */ 2084 if (**p != '\0') 2085 (*p)++; 2086} 2087