archive_acl.c revision 313927
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 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 154void 155archive_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 172int 173archive_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 192int 193archive_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 212static int 213archive_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 */ 245static int 246acl_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 */ 272static struct archive_acl_entry * 273acl_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 */ 376int 377archive_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 */ 398int 399archive_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 */ 409int 410archive_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 */ 439int 440archive_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 */ 508static int 509archive_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 */ 540static ssize_t 541archive_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 */ 671wchar_t * 672archive_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 774static void 775append_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 784static void 785append_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 */ 902char * 903archive_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 1007static void 1008append_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 1017static void 1018append_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 */ 1140int 1141archive_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 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 1163 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 1164 numfields = 5; 1165 break; 1166 case ARCHIVE_ENTRY_ACL_TYPE_NFS4: 1167 numfields = 6; 1168 break; 1169 default: 1170 return (ARCHIVE_FATAL); 1171 } 1172 1173 while (text != NULL && *text != L'\0') { 1174 /* 1175 * Parse the fields out of the next entry, 1176 * advance 'text' to start of next entry. 1177 */ 1178 fields = 0; 1179 do { 1180 const wchar_t *start, *end; 1181 next_field_w(&text, &start, &end, &sep); 1182 if (fields < numfields) { 1183 field[fields].start = start; 1184 field[fields].end = end; 1185 } 1186 ++fields; 1187 } while (sep == L':'); 1188 1189 /* Set remaining fields to blank. */ 1190 for (n = fields; n < numfields; ++n) 1191 field[n].start = field[n].end = NULL; 1192 1193 if (field[0].start != NULL && *(field[0].start) == L'#') { 1194 /* Comment, skip entry */ 1195 continue; 1196 } 1197 1198 n = 0; 1199 sol = 0; 1200 id = -1; 1201 permset = 0; 1202 name.start = name.end = NULL; 1203 1204 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 1205 /* POSIX.1e ACLs */ 1206 /* 1207 * Default keyword "default:user::rwx" 1208 * if found, we have one more field 1209 * 1210 * We also support old Solaris extension: 1211 * "defaultuser::rwx" is the default ACL corresponding 1212 * to "user::rwx", etc. valid only for first field 1213 */ 1214 s = field[0].start; 1215 len = field[0].end - field[0].start; 1216 if (*s == L'd' && (len == 1 || (len >= 7 1217 && wmemcmp((s + 1), L"efault", 6) == 0))) { 1218 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1219 if (len > 7) 1220 field[0].start += 7; 1221 else 1222 n = 1; 1223 } else 1224 type = want_type; 1225 1226 /* Check for a numeric ID in field n+1 or n+3. */ 1227 isint_w(field[n + 1].start, field[n + 1].end, &id); 1228 /* Field n+3 is optional. */ 1229 if (id == -1 && fields > n+3) 1230 isint_w(field[n + 3].start, field[n + 3].end, 1231 &id); 1232 1233 tag = 0; 1234 s = field[n].start; 1235 st = field[n].start + 1; 1236 len = field[n].end - field[n].start; 1237 1238 switch (*s) { 1239 case L'u': 1240 if (len == 1 || (len == 4 1241 && wmemcmp(st, L"ser", 3) == 0)) 1242 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1243 break; 1244 case L'g': 1245 if (len == 1 || (len == 5 1246 && wmemcmp(st, L"roup", 4) == 0)) 1247 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1248 break; 1249 case L'o': 1250 if (len == 1 || (len == 5 1251 && wmemcmp(st, L"ther", 4) == 0)) 1252 tag = ARCHIVE_ENTRY_ACL_OTHER; 1253 break; 1254 case L'm': 1255 if (len == 1 || (len == 4 1256 && wmemcmp(st, L"ask", 3) == 0)) 1257 tag = ARCHIVE_ENTRY_ACL_MASK; 1258 break; 1259 default: 1260 break; 1261 } 1262 1263 switch (tag) { 1264 case ARCHIVE_ENTRY_ACL_OTHER: 1265 case ARCHIVE_ENTRY_ACL_MASK: 1266 if (fields == (n + 2) 1267 && field[n + 1].start < field[n + 1].end 1268 && ismode_w(field[n + 1].start, 1269 field[n + 1].end, &permset)) { 1270 /* This is Solaris-style "other:rwx" */ 1271 sol = 1; 1272 } else if (fields == (n + 3) && 1273 field[n + 1].start < field[n + 1].end) { 1274 /* Invalid mask or other field */ 1275 ret = ARCHIVE_WARN; 1276 continue; 1277 } 1278 break; 1279 case ARCHIVE_ENTRY_ACL_USER_OBJ: 1280 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1281 if (id != -1 || 1282 field[n + 1].start < field[n + 1].end) { 1283 name = field[n + 1]; 1284 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) 1285 tag = ARCHIVE_ENTRY_ACL_USER; 1286 else 1287 tag = ARCHIVE_ENTRY_ACL_GROUP; 1288 } 1289 break; 1290 default: 1291 /* Invalid tag, skip entry */ 1292 ret = ARCHIVE_WARN; 1293 continue; 1294 } 1295 1296 /* 1297 * Without "default:" we expect mode in field 2 1298 * Exception: Solaris other and mask fields 1299 */ 1300 if (permset == 0 && !ismode_w(field[n + 2 - sol].start, 1301 field[n + 2 - sol].end, &permset)) { 1302 /* Invalid mode, skip entry */ 1303 ret = ARCHIVE_WARN; 1304 continue; 1305 } 1306 } else { 1307 /* NFS4 ACLs */ 1308 s = field[0].start; 1309 len = field[0].end - field[0].start; 1310 tag = 0; 1311 1312 switch (len) { 1313 case 4: 1314 if (wmemcmp(s, L"user", 4) == 0) 1315 tag = ARCHIVE_ENTRY_ACL_USER; 1316 break; 1317 case 5: 1318 if (wmemcmp(s, L"group", 5) == 0) 1319 tag = ARCHIVE_ENTRY_ACL_GROUP; 1320 break; 1321 case 6: 1322 if (wmemcmp(s, L"owner@", 6) == 0) 1323 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1324 else if (wmemcmp(s, L"group@", len) == 0) 1325 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1326 break; 1327 case 9: 1328 if (wmemcmp(s, L"everyone@", 9) == 0) 1329 tag = ARCHIVE_ENTRY_ACL_EVERYONE; 1330 default: 1331 break; 1332 } 1333 1334 if (tag == 0) { 1335 /* Invalid tag, skip entry */ 1336 ret = ARCHIVE_WARN; 1337 continue; 1338 } else if (tag == ARCHIVE_ENTRY_ACL_USER || 1339 tag == ARCHIVE_ENTRY_ACL_GROUP) { 1340 n = 1; 1341 name = field[1]; 1342 isint_w(name.start, name.end, &id); 1343 } else 1344 n = 0; 1345 1346 if (!is_nfs4_perms_w(field[1 + n].start, 1347 field[1 + n].end, &permset)) { 1348 /* Invalid NFSv4 perms, skip entry */ 1349 ret = ARCHIVE_WARN; 1350 continue; 1351 } 1352 if (!is_nfs4_flags_w(field[2 + n].start, 1353 field[2 + n].end, &permset)) { 1354 /* Invalid NFSv4 flags, skip entry */ 1355 ret = ARCHIVE_WARN; 1356 continue; 1357 } 1358 s = field[3 + n].start; 1359 len = field[3 + n].end - field[3 + n].start; 1360 type = 0; 1361 if (len == 4) { 1362 if (wmemcmp(s, L"deny", 4) == 0) 1363 type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 1364 } else if (len == 5) { 1365 if (wmemcmp(s, L"allow", 5) == 0) 1366 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 1367 else if (wmemcmp(s, L"audit", 5) == 0) 1368 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 1369 else if (wmemcmp(s, L"alarm", 5) == 0) 1370 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 1371 } 1372 if (type == 0) { 1373 /* Invalid entry type, skip entry */ 1374 ret = ARCHIVE_WARN; 1375 continue; 1376 } 1377 isint_w(field[4 + n].start, field[4 + n].end, &id); 1378 } 1379 1380 /* Add entry to the internal list. */ 1381 r = archive_acl_add_entry_w_len(acl, type, permset, 1382 tag, id, name.start, name.end - name.start); 1383 if (r < ARCHIVE_WARN) 1384 return (r); 1385 if (r != ARCHIVE_OK) 1386 ret = ARCHIVE_WARN; 1387 types |= type; 1388 } 1389 1390 /* Reset ACL */ 1391 archive_acl_reset(acl, types); 1392 1393 return (ret); 1394} 1395 1396/* 1397 * Parse a string to a positive decimal integer. Returns true if 1398 * the string is non-empty and consists only of decimal digits, 1399 * false otherwise. 1400 */ 1401static int 1402isint_w(const wchar_t *start, const wchar_t *end, int *result) 1403{ 1404 int n = 0; 1405 if (start >= end) 1406 return (0); 1407 while (start < end) { 1408 if (*start < '0' || *start > '9') 1409 return (0); 1410 if (n > (INT_MAX / 10) || 1411 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1412 n = INT_MAX; 1413 } else { 1414 n *= 10; 1415 n += *start - '0'; 1416 } 1417 start++; 1418 } 1419 *result = n; 1420 return (1); 1421} 1422 1423/* 1424 * Parse a string as a mode field. Returns true if 1425 * the string is non-empty and consists only of mode characters, 1426 * false otherwise. 1427 */ 1428static int 1429ismode_w(const wchar_t *start, const wchar_t *end, int *permset) 1430{ 1431 const wchar_t *p; 1432 1433 if (start >= end) 1434 return (0); 1435 p = start; 1436 *permset = 0; 1437 while (p < end) { 1438 switch (*p++) { 1439 case L'r': case L'R': 1440 *permset |= ARCHIVE_ENTRY_ACL_READ; 1441 break; 1442 case L'w': case L'W': 1443 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1444 break; 1445 case L'x': case L'X': 1446 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1447 break; 1448 case L'-': 1449 break; 1450 default: 1451 return (0); 1452 } 1453 } 1454 return (1); 1455} 1456 1457/* 1458 * Parse a string as a NFS4 ACL permission field. 1459 * Returns true if the string is non-empty and consists only of NFS4 ACL 1460 * permission characters, false otherwise 1461 */ 1462static int 1463is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset) 1464{ 1465 const wchar_t *p = start; 1466 1467 while (p < end) { 1468 switch (*p++) { 1469 case L'r': 1470 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; 1471 break; 1472 case L'w': 1473 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; 1474 break; 1475 case L'x': 1476 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1477 break; 1478 case L'p': 1479 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; 1480 break; 1481 case L'D': 1482 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; 1483 break; 1484 case L'd': 1485 *permset |= ARCHIVE_ENTRY_ACL_DELETE; 1486 break; 1487 case L'a': 1488 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; 1489 break; 1490 case L'A': 1491 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; 1492 break; 1493 case L'R': 1494 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; 1495 break; 1496 case L'W': 1497 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; 1498 break; 1499 case L'c': 1500 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; 1501 break; 1502 case L'C': 1503 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; 1504 break; 1505 case L'o': 1506 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; 1507 break; 1508 case L's': 1509 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; 1510 break; 1511 case L'-': 1512 break; 1513 default: 1514 return(0); 1515 } 1516 } 1517 return (1); 1518} 1519 1520/* 1521 * Parse a string as a NFS4 ACL flags field. 1522 * Returns true if the string is non-empty and consists only of NFS4 ACL 1523 * flag characters, false otherwise 1524 */ 1525static int 1526is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset) 1527{ 1528 const wchar_t *p = start; 1529 1530 while (p < end) { 1531 switch(*p++) { 1532 case L'f': 1533 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; 1534 break; 1535 case L'd': 1536 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; 1537 break; 1538 case L'i': 1539 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; 1540 break; 1541 case L'n': 1542 *permset |= 1543 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; 1544 break; 1545 case L'S': 1546 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; 1547 break; 1548 case L'F': 1549 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; 1550 break; 1551 case L'I': 1552 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; 1553 break; 1554 case L'-': 1555 break; 1556 default: 1557 return (0); 1558 } 1559 } 1560 return (1); 1561} 1562 1563/* 1564 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1565 * to point to just after the separator. *start points to the first 1566 * character of the matched text and *end just after the last 1567 * character of the matched identifier. In particular *end - *start 1568 * is the length of the field body, not including leading or trailing 1569 * whitespace. 1570 */ 1571static void 1572next_field_w(const wchar_t **wp, const wchar_t **start, 1573 const wchar_t **end, wchar_t *sep) 1574{ 1575 /* Skip leading whitespace to find start of field. */ 1576 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { 1577 (*wp)++; 1578 } 1579 *start = *wp; 1580 1581 /* Scan for the separator. */ 1582 while (**wp != L'\0' && **wp != L',' && **wp != L':' && 1583 **wp != L'\n') { 1584 (*wp)++; 1585 } 1586 *sep = **wp; 1587 1588 /* Trim trailing whitespace to locate end of field. */ 1589 *end = *wp - 1; 1590 while (**end == L' ' || **end == L'\t' || **end == L'\n') { 1591 (*end)--; 1592 } 1593 (*end)++; 1594 1595 /* Adjust scanner location. */ 1596 if (**wp != L'\0') 1597 (*wp)++; 1598} 1599 1600/* 1601 * Parse an ACL text string. 1602 * 1603 * The want_type argument may be one of the following: 1604 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS 1605 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT 1606 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL 1607 * 1608 * POSIX.1e ACL entries prefixed with "default:" are treated as 1609 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 1610 */ 1611int 1612archive_acl_from_text_l(struct archive_acl *acl, const char *text, 1613 int want_type, struct archive_string_conv *sc) 1614{ 1615 struct { 1616 const char *start; 1617 const char *end; 1618 } field[6], name; 1619 1620 const char *s, *st; 1621 int numfields, fields, n, r, sol, ret; 1622 int type, types, tag, permset, id; 1623 size_t len; 1624 char sep; 1625 1626 switch (want_type) { 1627 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: 1628 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 1629 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 1630 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 1631 numfields = 5; 1632 break; 1633 case ARCHIVE_ENTRY_ACL_TYPE_NFS4: 1634 numfields = 6; 1635 break; 1636 default: 1637 return (ARCHIVE_FATAL); 1638 } 1639 1640 ret = ARCHIVE_OK; 1641 types = 0; 1642 1643 while (text != NULL && *text != '\0') { 1644 /* 1645 * Parse the fields out of the next entry, 1646 * advance 'text' to start of next entry. 1647 */ 1648 fields = 0; 1649 do { 1650 const char *start, *end; 1651 next_field(&text, &start, &end, &sep); 1652 if (fields < numfields) { 1653 field[fields].start = start; 1654 field[fields].end = end; 1655 } 1656 ++fields; 1657 } while (sep == ':'); 1658 1659 /* Set remaining fields to blank. */ 1660 for (n = fields; n < numfields; ++n) 1661 field[n].start = field[n].end = NULL; 1662 1663 if (field[0].start != NULL && *(field[0].start) == '#') { 1664 /* Comment, skip entry */ 1665 continue; 1666 } 1667 1668 n = 0; 1669 sol = 0; 1670 id = -1; 1671 permset = 0; 1672 name.start = name.end = NULL; 1673 1674 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 1675 /* POSIX.1e ACLs */ 1676 /* 1677 * Default keyword "default:user::rwx" 1678 * if found, we have one more field 1679 * 1680 * We also support old Solaris extension: 1681 * "defaultuser::rwx" is the default ACL corresponding 1682 * to "user::rwx", etc. valid only for first field 1683 */ 1684 s = field[0].start; 1685 len = field[0].end - field[0].start; 1686 if (*s == 'd' && (len == 1 || (len >= 7 1687 && memcmp((s + 1), "efault", 6) == 0))) { 1688 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1689 if (len > 7) 1690 field[0].start += 7; 1691 else 1692 n = 1; 1693 } else 1694 type = want_type; 1695 1696 /* Check for a numeric ID in field n+1 or n+3. */ 1697 isint(field[n + 1].start, field[n + 1].end, &id); 1698 /* Field n+3 is optional. */ 1699 if (id == -1 && fields > (n + 3)) 1700 isint(field[n + 3].start, field[n + 3].end, 1701 &id); 1702 1703 tag = 0; 1704 s = field[n].start; 1705 st = field[n].start + 1; 1706 len = field[n].end - field[n].start; 1707 1708 switch (*s) { 1709 case 'u': 1710 if (len == 1 || (len == 4 1711 && memcmp(st, "ser", 3) == 0)) 1712 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1713 break; 1714 case 'g': 1715 if (len == 1 || (len == 5 1716 && memcmp(st, "roup", 4) == 0)) 1717 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1718 break; 1719 case 'o': 1720 if (len == 1 || (len == 5 1721 && memcmp(st, "ther", 4) == 0)) 1722 tag = ARCHIVE_ENTRY_ACL_OTHER; 1723 break; 1724 case 'm': 1725 if (len == 1 || (len == 4 1726 && memcmp(st, "ask", 3) == 0)) 1727 tag = ARCHIVE_ENTRY_ACL_MASK; 1728 break; 1729 default: 1730 break; 1731 } 1732 1733 switch (tag) { 1734 case ARCHIVE_ENTRY_ACL_OTHER: 1735 case ARCHIVE_ENTRY_ACL_MASK: 1736 if (fields == (n + 2) 1737 && field[n + 1].start < field[n + 1].end 1738 && ismode(field[n + 1].start, 1739 field[n + 1].end, &permset)) { 1740 /* This is Solaris-style "other:rwx" */ 1741 sol = 1; 1742 } else if (fields == (n + 3) && 1743 field[n + 1].start < field[n + 1].end) { 1744 /* Invalid mask or other field */ 1745 ret = ARCHIVE_WARN; 1746 continue; 1747 } 1748 break; 1749 case ARCHIVE_ENTRY_ACL_USER_OBJ: 1750 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1751 if (id != -1 || 1752 field[n + 1].start < field[n + 1].end) { 1753 name = field[n + 1]; 1754 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) 1755 tag = ARCHIVE_ENTRY_ACL_USER; 1756 else 1757 tag = ARCHIVE_ENTRY_ACL_GROUP; 1758 } 1759 break; 1760 default: 1761 /* Invalid tag, skip entry */ 1762 ret = ARCHIVE_WARN; 1763 continue; 1764 } 1765 1766 /* 1767 * Without "default:" we expect mode in field 3 1768 * Exception: Solaris other and mask fields 1769 */ 1770 if (permset == 0 && !ismode(field[n + 2 - sol].start, 1771 field[n + 2 - sol].end, &permset)) { 1772 /* Invalid mode, skip entry */ 1773 ret = ARCHIVE_WARN; 1774 continue; 1775 } 1776 } else { 1777 /* NFS4 ACLs */ 1778 s = field[0].start; 1779 len = field[0].end - field[0].start; 1780 tag = 0; 1781 1782 switch (len) { 1783 case 4: 1784 if (memcmp(s, "user", 4) == 0) 1785 tag = ARCHIVE_ENTRY_ACL_USER; 1786 break; 1787 case 5: 1788 if (memcmp(s, "group", 5) == 0) 1789 tag = ARCHIVE_ENTRY_ACL_GROUP; 1790 break; 1791 case 6: 1792 if (memcmp(s, "owner@", 6) == 0) 1793 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1794 else if (memcmp(s, "group@", 6) == 0) 1795 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1796 break; 1797 case 9: 1798 if (memcmp(s, "everyone@", 9) == 0) 1799 tag = ARCHIVE_ENTRY_ACL_EVERYONE; 1800 break; 1801 default: 1802 break; 1803 } 1804 1805 if (tag == 0) { 1806 /* Invalid tag, skip entry */ 1807 ret = ARCHIVE_WARN; 1808 continue; 1809 } else if (tag == ARCHIVE_ENTRY_ACL_USER || 1810 tag == ARCHIVE_ENTRY_ACL_GROUP) { 1811 n = 1; 1812 name = field[1]; 1813 isint(name.start, name.end, &id); 1814 } else 1815 n = 0; 1816 1817 if (!is_nfs4_perms(field[1 + n].start, 1818 field[1 + n].end, &permset)) { 1819 /* Invalid NFSv4 perms, skip entry */ 1820 ret = ARCHIVE_WARN; 1821 continue; 1822 } 1823 if (!is_nfs4_flags(field[2 + n].start, 1824 field[2 + n].end, &permset)) { 1825 /* Invalid NFSv4 flags, skip entry */ 1826 ret = ARCHIVE_WARN; 1827 continue; 1828 } 1829 s = field[3 + n].start; 1830 len = field[3 + n].end - field[3 + n].start; 1831 type = 0; 1832 if (len == 4) { 1833 if (memcmp(s, "deny", 4) == 0) 1834 type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 1835 } else if (len == 5) { 1836 if (memcmp(s, "allow", 5) == 0) 1837 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 1838 else if (memcmp(s, "audit", 5) == 0) 1839 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 1840 else if (memcmp(s, "alarm", 5) == 0) 1841 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 1842 } 1843 if (type == 0) { 1844 /* Invalid entry type, skip entry */ 1845 ret = ARCHIVE_WARN; 1846 continue; 1847 } 1848 isint(field[4 + n].start, field[4 + n].end, 1849 &id); 1850 } 1851 1852 /* Add entry to the internal list. */ 1853 r = archive_acl_add_entry_len_l(acl, type, permset, 1854 tag, id, name.start, name.end - name.start, sc); 1855 if (r < ARCHIVE_WARN) 1856 return (r); 1857 if (r != ARCHIVE_OK) 1858 ret = ARCHIVE_WARN; 1859 types |= type; 1860 } 1861 1862 /* Reset ACL */ 1863 archive_acl_reset(acl, types); 1864 1865 return (ret); 1866} 1867 1868/* 1869 * Parse a string to a positive decimal integer. Returns true if 1870 * the string is non-empty and consists only of decimal digits, 1871 * false otherwise. 1872 */ 1873static int 1874isint(const char *start, const char *end, int *result) 1875{ 1876 int n = 0; 1877 if (start >= end) 1878 return (0); 1879 while (start < end) { 1880 if (*start < '0' || *start > '9') 1881 return (0); 1882 if (n > (INT_MAX / 10) || 1883 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1884 n = INT_MAX; 1885 } else { 1886 n *= 10; 1887 n += *start - '0'; 1888 } 1889 start++; 1890 } 1891 *result = n; 1892 return (1); 1893} 1894 1895/* 1896 * Parse a string as a mode field. Returns true if 1897 * the string is non-empty and consists only of mode characters, 1898 * false otherwise. 1899 */ 1900static int 1901ismode(const char *start, const char *end, int *permset) 1902{ 1903 const char *p; 1904 1905 if (start >= end) 1906 return (0); 1907 p = start; 1908 *permset = 0; 1909 while (p < end) { 1910 switch (*p++) { 1911 case 'r': case 'R': 1912 *permset |= ARCHIVE_ENTRY_ACL_READ; 1913 break; 1914 case 'w': case 'W': 1915 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1916 break; 1917 case 'x': case 'X': 1918 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1919 break; 1920 case '-': 1921 break; 1922 default: 1923 return (0); 1924 } 1925 } 1926 return (1); 1927} 1928 1929/* 1930 * Parse a string as a NFS4 ACL permission field. 1931 * Returns true if the string is non-empty and consists only of NFS4 ACL 1932 * permission characters, false otherwise 1933 */ 1934static int 1935is_nfs4_perms(const char *start, const char *end, int *permset) 1936{ 1937 const char *p = start; 1938 1939 while (p < end) { 1940 switch (*p++) { 1941 case 'r': 1942 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; 1943 break; 1944 case 'w': 1945 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; 1946 break; 1947 case 'x': 1948 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1949 break; 1950 case 'p': 1951 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; 1952 break; 1953 case 'D': 1954 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; 1955 break; 1956 case 'd': 1957 *permset |= ARCHIVE_ENTRY_ACL_DELETE; 1958 break; 1959 case 'a': 1960 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; 1961 break; 1962 case 'A': 1963 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; 1964 break; 1965 case 'R': 1966 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; 1967 break; 1968 case 'W': 1969 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; 1970 break; 1971 case 'c': 1972 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; 1973 break; 1974 case 'C': 1975 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; 1976 break; 1977 case 'o': 1978 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; 1979 break; 1980 case 's': 1981 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; 1982 break; 1983 case '-': 1984 break; 1985 default: 1986 return(0); 1987 } 1988 } 1989 return (1); 1990} 1991 1992/* 1993 * Parse a string as a NFS4 ACL flags field. 1994 * Returns true if the string is non-empty and consists only of NFS4 ACL 1995 * flag characters, false otherwise 1996 */ 1997static int 1998is_nfs4_flags(const char *start, const char *end, int *permset) 1999{ 2000 const char *p = start; 2001 2002 while (p < end) { 2003 switch(*p++) { 2004 case 'f': 2005 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; 2006 break; 2007 case 'd': 2008 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; 2009 break; 2010 case 'i': 2011 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; 2012 break; 2013 case 'n': 2014 *permset |= 2015 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; 2016 break; 2017 case 'S': 2018 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; 2019 break; 2020 case 'F': 2021 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; 2022 break; 2023 case 'I': 2024 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; 2025 break; 2026 case '-': 2027 break; 2028 default: 2029 return (0); 2030 } 2031 } 2032 return (1); 2033} 2034 2035/* 2036 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 2037 * to point to just after the separator. *start points to the first 2038 * character of the matched text and *end just after the last 2039 * character of the matched identifier. In particular *end - *start 2040 * is the length of the field body, not including leading or trailing 2041 * whitespace. 2042 */ 2043static void 2044next_field(const char **p, const char **start, 2045 const char **end, char *sep) 2046{ 2047 /* Skip leading whitespace to find start of field. */ 2048 while (**p == ' ' || **p == '\t' || **p == '\n') { 2049 (*p)++; 2050 } 2051 *start = *p; 2052 2053 /* Scan for the separator. */ 2054 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { 2055 (*p)++; 2056 } 2057 *sep = **p; 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