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