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