archive_acl.c revision 342360
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 free(ws); 758 return (NULL); 759 } 760 } 761 762 /* Add terminating character */ 763 *wp++ = L'\0'; 764 765 len = wcslen(ws); 766 767 if ((ssize_t)len > (length - 1)) 768 __archive_errx(1, "Buffer overrun"); 769 770 if (text_len != NULL) 771 *text_len = len; 772 773 return (ws); 774} 775 776static void 777append_id_w(wchar_t **wp, int id) 778{ 779 if (id < 0) 780 id = 0; 781 if (id > 9) 782 append_id_w(wp, id / 10); 783 *(*wp)++ = L"0123456789"[id % 10]; 784} 785 786static void 787append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, 788 int tag, int flags, const wchar_t *wname, int perm, int id) 789{ 790 int i; 791 792 if (prefix != NULL) { 793 wcscpy(*wp, prefix); 794 *wp += wcslen(*wp); 795 } 796 switch (tag) { 797 case ARCHIVE_ENTRY_ACL_USER_OBJ: 798 wname = NULL; 799 id = -1; 800 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 801 wcscpy(*wp, L"owner@"); 802 break; 803 } 804 /* FALLTHROUGH */ 805 case ARCHIVE_ENTRY_ACL_USER: 806 wcscpy(*wp, L"user"); 807 break; 808 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 809 wname = NULL; 810 id = -1; 811 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 812 wcscpy(*wp, L"group@"); 813 break; 814 } 815 /* FALLTHROUGH */ 816 case ARCHIVE_ENTRY_ACL_GROUP: 817 wcscpy(*wp, L"group"); 818 break; 819 case ARCHIVE_ENTRY_ACL_MASK: 820 wcscpy(*wp, L"mask"); 821 wname = NULL; 822 id = -1; 823 break; 824 case ARCHIVE_ENTRY_ACL_OTHER: 825 wcscpy(*wp, L"other"); 826 wname = NULL; 827 id = -1; 828 break; 829 case ARCHIVE_ENTRY_ACL_EVERYONE: 830 wcscpy(*wp, L"everyone@"); 831 wname = NULL; 832 id = -1; 833 break; 834 } 835 *wp += wcslen(*wp); 836 *(*wp)++ = L':'; 837 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || 838 tag == ARCHIVE_ENTRY_ACL_USER || 839 tag == ARCHIVE_ENTRY_ACL_GROUP) { 840 if (wname != NULL) { 841 wcscpy(*wp, wname); 842 *wp += wcslen(*wp); 843 } else if (tag == ARCHIVE_ENTRY_ACL_USER 844 || tag == ARCHIVE_ENTRY_ACL_GROUP) { 845 append_id_w(wp, id); 846 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) 847 id = -1; 848 } 849 /* Solaris style has no second colon after other and mask */ 850 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) 851 || (tag != ARCHIVE_ENTRY_ACL_OTHER 852 && tag != ARCHIVE_ENTRY_ACL_MASK)) 853 *(*wp)++ = L':'; 854 } 855 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { 856 /* POSIX.1e ACL perms */ 857 *(*wp)++ = (perm & 0444) ? L'r' : L'-'; 858 *(*wp)++ = (perm & 0222) ? L'w' : L'-'; 859 *(*wp)++ = (perm & 0111) ? L'x' : L'-'; 860 } else { 861 /* NFSv4 ACL perms */ 862 for (i = 0; i < nfsv4_acl_perm_map_size; i++) { 863 if (perm & nfsv4_acl_perm_map[i].perm) 864 *(*wp)++ = nfsv4_acl_perm_map[i].wc; 865 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) 866 *(*wp)++ = L'-'; 867 } 868 *(*wp)++ = L':'; 869 for (i = 0; i < nfsv4_acl_flag_map_size; i++) { 870 if (perm & nfsv4_acl_flag_map[i].perm) 871 *(*wp)++ = nfsv4_acl_flag_map[i].wc; 872 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) 873 *(*wp)++ = L'-'; 874 } 875 *(*wp)++ = L':'; 876 switch (type) { 877 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: 878 wcscpy(*wp, L"allow"); 879 break; 880 case ARCHIVE_ENTRY_ACL_TYPE_DENY: 881 wcscpy(*wp, L"deny"); 882 break; 883 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: 884 wcscpy(*wp, L"audit"); 885 break; 886 case ARCHIVE_ENTRY_ACL_TYPE_ALARM: 887 wcscpy(*wp, L"alarm"); 888 break; 889 default: 890 break; 891 } 892 *wp += wcslen(*wp); 893 } 894 if (id != -1) { 895 *(*wp)++ = L':'; 896 append_id_w(wp, id); 897 } 898} 899 900/* 901 * Generate a text version of the ACL. The flags parameter controls 902 * the type and style of the generated ACL. 903 */ 904char * 905archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, 906 struct archive_string_conv *sc) 907{ 908 int count; 909 ssize_t length; 910 size_t len; 911 const char *name; 912 const char *prefix; 913 char separator; 914 struct archive_acl_entry *ap; 915 int id, r, want_type; 916 char *p, *s; 917 918 want_type = archive_acl_text_want_type(acl, flags); 919 920 /* Both NFSv4 and POSIX.1 types found */ 921 if (want_type == 0) 922 return (NULL); 923 924 if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) 925 flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; 926 927 length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc); 928 929 if (length == 0) 930 return (NULL); 931 932 if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) 933 separator = ','; 934 else 935 separator = '\n'; 936 937 /* Now, allocate the string and actually populate it. */ 938 p = s = (char *)malloc(length * sizeof(char)); 939 if (p == NULL) { 940 if (errno == ENOMEM) 941 __archive_errx(1, "No memory"); 942 return (NULL); 943 } 944 count = 0; 945 946 if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 947 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 948 ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, 949 acl->mode & 0700, -1); 950 *p++ = separator; 951 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 952 ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, 953 acl->mode & 0070, -1); 954 *p++ = separator; 955 append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 956 ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, 957 acl->mode & 0007, -1); 958 count += 3; 959 } 960 961 for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 962 if ((ap->type & want_type) == 0) 963 continue; 964 /* 965 * Filemode-mapping ACL entries are stored exclusively in 966 * ap->mode so they should not be in the list 967 */ 968 if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) 969 && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ 970 || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ 971 || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) 972 continue; 973 if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT && 974 (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) 975 prefix = "default:"; 976 else 977 prefix = NULL; 978 r = archive_mstring_get_mbs_l( 979 &ap->name, &name, &len, sc); 980 if (r != 0) { 981 free(s); 982 return (NULL); 983 } 984 if (count > 0) 985 *p++ = separator; 986 if (name == NULL || 987 (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { 988 id = ap->id; 989 } else { 990 id = -1; 991 } 992 append_entry(&p, prefix, ap->type, ap->tag, flags, name, 993 ap->permset, id); 994 count++; 995 } 996 997 /* Add terminating character */ 998 *p++ = '\0'; 999 1000 len = strlen(s); 1001 1002 if ((ssize_t)len > (length - 1)) 1003 __archive_errx(1, "Buffer overrun"); 1004 1005 if (text_len != NULL) 1006 *text_len = len; 1007 1008 return (s); 1009} 1010 1011static void 1012append_id(char **p, int id) 1013{ 1014 if (id < 0) 1015 id = 0; 1016 if (id > 9) 1017 append_id(p, id / 10); 1018 *(*p)++ = "0123456789"[id % 10]; 1019} 1020 1021static void 1022append_entry(char **p, const char *prefix, int type, 1023 int tag, int flags, const char *name, int perm, int id) 1024{ 1025 int i; 1026 1027 if (prefix != NULL) { 1028 strcpy(*p, prefix); 1029 *p += strlen(*p); 1030 } 1031 switch (tag) { 1032 case ARCHIVE_ENTRY_ACL_USER_OBJ: 1033 name = NULL; 1034 id = -1; 1035 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 1036 strcpy(*p, "owner@"); 1037 break; 1038 } 1039 /* FALLTHROUGH */ 1040 case ARCHIVE_ENTRY_ACL_USER: 1041 strcpy(*p, "user"); 1042 break; 1043 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1044 name = NULL; 1045 id = -1; 1046 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 1047 strcpy(*p, "group@"); 1048 break; 1049 } 1050 /* FALLTHROUGH */ 1051 case ARCHIVE_ENTRY_ACL_GROUP: 1052 strcpy(*p, "group"); 1053 break; 1054 case ARCHIVE_ENTRY_ACL_MASK: 1055 strcpy(*p, "mask"); 1056 name = NULL; 1057 id = -1; 1058 break; 1059 case ARCHIVE_ENTRY_ACL_OTHER: 1060 strcpy(*p, "other"); 1061 name = NULL; 1062 id = -1; 1063 break; 1064 case ARCHIVE_ENTRY_ACL_EVERYONE: 1065 strcpy(*p, "everyone@"); 1066 name = NULL; 1067 id = -1; 1068 break; 1069 } 1070 *p += strlen(*p); 1071 *(*p)++ = ':'; 1072 if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || 1073 tag == ARCHIVE_ENTRY_ACL_USER || 1074 tag == ARCHIVE_ENTRY_ACL_GROUP) { 1075 if (name != NULL) { 1076 strcpy(*p, name); 1077 *p += strlen(*p); 1078 } else if (tag == ARCHIVE_ENTRY_ACL_USER 1079 || tag == ARCHIVE_ENTRY_ACL_GROUP) { 1080 append_id(p, id); 1081 if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) 1082 id = -1; 1083 } 1084 /* Solaris style has no second colon after other and mask */ 1085 if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) 1086 || (tag != ARCHIVE_ENTRY_ACL_OTHER 1087 && tag != ARCHIVE_ENTRY_ACL_MASK)) 1088 *(*p)++ = ':'; 1089 } 1090 if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { 1091 /* POSIX.1e ACL perms */ 1092 *(*p)++ = (perm & 0444) ? 'r' : '-'; 1093 *(*p)++ = (perm & 0222) ? 'w' : '-'; 1094 *(*p)++ = (perm & 0111) ? 'x' : '-'; 1095 } else { 1096 /* NFSv4 ACL perms */ 1097 for (i = 0; i < nfsv4_acl_perm_map_size; i++) { 1098 if (perm & nfsv4_acl_perm_map[i].perm) 1099 *(*p)++ = nfsv4_acl_perm_map[i].c; 1100 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) 1101 *(*p)++ = '-'; 1102 } 1103 *(*p)++ = ':'; 1104 for (i = 0; i < nfsv4_acl_flag_map_size; i++) { 1105 if (perm & nfsv4_acl_flag_map[i].perm) 1106 *(*p)++ = nfsv4_acl_flag_map[i].c; 1107 else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) 1108 *(*p)++ = '-'; 1109 } 1110 *(*p)++ = ':'; 1111 switch (type) { 1112 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: 1113 strcpy(*p, "allow"); 1114 break; 1115 case ARCHIVE_ENTRY_ACL_TYPE_DENY: 1116 strcpy(*p, "deny"); 1117 break; 1118 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: 1119 strcpy(*p, "audit"); 1120 break; 1121 case ARCHIVE_ENTRY_ACL_TYPE_ALARM: 1122 strcpy(*p, "alarm"); 1123 break; 1124 } 1125 *p += strlen(*p); 1126 } 1127 if (id != -1) { 1128 *(*p)++ = ':'; 1129 append_id(p, id); 1130 } 1131} 1132 1133/* 1134 * Parse a wide ACL text string. 1135 * 1136 * The want_type argument may be one of the following: 1137 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS 1138 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT 1139 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL 1140 * 1141 * POSIX.1e ACL entries prefixed with "default:" are treated as 1142 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 1143 */ 1144int 1145archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, 1146 int want_type) 1147{ 1148 struct { 1149 const wchar_t *start; 1150 const wchar_t *end; 1151 } field[6], name; 1152 1153 const wchar_t *s, *st; 1154 1155 int numfields, fields, n, r, sol, ret; 1156 int type, types, tag, permset, id; 1157 size_t len; 1158 wchar_t sep; 1159 1160 ret = ARCHIVE_OK; 1161 types = 0; 1162 1163 switch (want_type) { 1164 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: 1165 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 1166 __LA_FALLTHROUGH; 1167 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 1168 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 1169 numfields = 5; 1170 break; 1171 case ARCHIVE_ENTRY_ACL_TYPE_NFS4: 1172 numfields = 6; 1173 break; 1174 default: 1175 return (ARCHIVE_FATAL); 1176 } 1177 1178 while (text != NULL && *text != L'\0') { 1179 /* 1180 * Parse the fields out of the next entry, 1181 * advance 'text' to start of next entry. 1182 */ 1183 fields = 0; 1184 do { 1185 const wchar_t *start, *end; 1186 next_field_w(&text, &start, &end, &sep); 1187 if (fields < numfields) { 1188 field[fields].start = start; 1189 field[fields].end = end; 1190 } 1191 ++fields; 1192 } while (sep == L':'); 1193 1194 /* Set remaining fields to blank. */ 1195 for (n = fields; n < numfields; ++n) 1196 field[n].start = field[n].end = NULL; 1197 1198 if (field[0].start != NULL && *(field[0].start) == L'#') { 1199 /* Comment, skip entry */ 1200 continue; 1201 } 1202 1203 n = 0; 1204 sol = 0; 1205 id = -1; 1206 permset = 0; 1207 name.start = name.end = NULL; 1208 1209 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 1210 /* POSIX.1e ACLs */ 1211 /* 1212 * Default keyword "default:user::rwx" 1213 * if found, we have one more field 1214 * 1215 * We also support old Solaris extension: 1216 * "defaultuser::rwx" is the default ACL corresponding 1217 * to "user::rwx", etc. valid only for first field 1218 */ 1219 s = field[0].start; 1220 len = field[0].end - field[0].start; 1221 if (*s == L'd' && (len == 1 || (len >= 7 1222 && wmemcmp((s + 1), L"efault", 6) == 0))) { 1223 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1224 if (len > 7) 1225 field[0].start += 7; 1226 else 1227 n = 1; 1228 } else 1229 type = want_type; 1230 1231 /* Check for a numeric ID in field n+1 or n+3. */ 1232 isint_w(field[n + 1].start, field[n + 1].end, &id); 1233 /* Field n+3 is optional. */ 1234 if (id == -1 && fields > n+3) 1235 isint_w(field[n + 3].start, field[n + 3].end, 1236 &id); 1237 1238 tag = 0; 1239 s = field[n].start; 1240 st = field[n].start + 1; 1241 len = field[n].end - field[n].start; 1242 1243 switch (*s) { 1244 case L'u': 1245 if (len == 1 || (len == 4 1246 && wmemcmp(st, L"ser", 3) == 0)) 1247 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1248 break; 1249 case L'g': 1250 if (len == 1 || (len == 5 1251 && wmemcmp(st, L"roup", 4) == 0)) 1252 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1253 break; 1254 case L'o': 1255 if (len == 1 || (len == 5 1256 && wmemcmp(st, L"ther", 4) == 0)) 1257 tag = ARCHIVE_ENTRY_ACL_OTHER; 1258 break; 1259 case L'm': 1260 if (len == 1 || (len == 4 1261 && wmemcmp(st, L"ask", 3) == 0)) 1262 tag = ARCHIVE_ENTRY_ACL_MASK; 1263 break; 1264 default: 1265 break; 1266 } 1267 1268 switch (tag) { 1269 case ARCHIVE_ENTRY_ACL_OTHER: 1270 case ARCHIVE_ENTRY_ACL_MASK: 1271 if (fields == (n + 2) 1272 && field[n + 1].start < field[n + 1].end 1273 && ismode_w(field[n + 1].start, 1274 field[n + 1].end, &permset)) { 1275 /* This is Solaris-style "other:rwx" */ 1276 sol = 1; 1277 } else if (fields == (n + 3) && 1278 field[n + 1].start < field[n + 1].end) { 1279 /* Invalid mask or other field */ 1280 ret = ARCHIVE_WARN; 1281 continue; 1282 } 1283 break; 1284 case ARCHIVE_ENTRY_ACL_USER_OBJ: 1285 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1286 if (id != -1 || 1287 field[n + 1].start < field[n + 1].end) { 1288 name = field[n + 1]; 1289 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) 1290 tag = ARCHIVE_ENTRY_ACL_USER; 1291 else 1292 tag = ARCHIVE_ENTRY_ACL_GROUP; 1293 } 1294 break; 1295 default: 1296 /* Invalid tag, skip entry */ 1297 ret = ARCHIVE_WARN; 1298 continue; 1299 } 1300 1301 /* 1302 * Without "default:" we expect mode in field 2 1303 * Exception: Solaris other and mask fields 1304 */ 1305 if (permset == 0 && !ismode_w(field[n + 2 - sol].start, 1306 field[n + 2 - sol].end, &permset)) { 1307 /* Invalid mode, skip entry */ 1308 ret = ARCHIVE_WARN; 1309 continue; 1310 } 1311 } else { 1312 /* NFS4 ACLs */ 1313 s = field[0].start; 1314 len = field[0].end - field[0].start; 1315 tag = 0; 1316 1317 switch (len) { 1318 case 4: 1319 if (wmemcmp(s, L"user", 4) == 0) 1320 tag = ARCHIVE_ENTRY_ACL_USER; 1321 break; 1322 case 5: 1323 if (wmemcmp(s, L"group", 5) == 0) 1324 tag = ARCHIVE_ENTRY_ACL_GROUP; 1325 break; 1326 case 6: 1327 if (wmemcmp(s, L"owner@", 6) == 0) 1328 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1329 else if (wmemcmp(s, L"group@", len) == 0) 1330 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1331 break; 1332 case 9: 1333 if (wmemcmp(s, L"everyone@", 9) == 0) 1334 tag = ARCHIVE_ENTRY_ACL_EVERYONE; 1335 default: 1336 break; 1337 } 1338 1339 if (tag == 0) { 1340 /* Invalid tag, skip entry */ 1341 ret = ARCHIVE_WARN; 1342 continue; 1343 } else if (tag == ARCHIVE_ENTRY_ACL_USER || 1344 tag == ARCHIVE_ENTRY_ACL_GROUP) { 1345 n = 1; 1346 name = field[1]; 1347 isint_w(name.start, name.end, &id); 1348 } else 1349 n = 0; 1350 1351 if (!is_nfs4_perms_w(field[1 + n].start, 1352 field[1 + n].end, &permset)) { 1353 /* Invalid NFSv4 perms, skip entry */ 1354 ret = ARCHIVE_WARN; 1355 continue; 1356 } 1357 if (!is_nfs4_flags_w(field[2 + n].start, 1358 field[2 + n].end, &permset)) { 1359 /* Invalid NFSv4 flags, skip entry */ 1360 ret = ARCHIVE_WARN; 1361 continue; 1362 } 1363 s = field[3 + n].start; 1364 len = field[3 + n].end - field[3 + n].start; 1365 type = 0; 1366 if (len == 4) { 1367 if (wmemcmp(s, L"deny", 4) == 0) 1368 type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 1369 } else if (len == 5) { 1370 if (wmemcmp(s, L"allow", 5) == 0) 1371 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 1372 else if (wmemcmp(s, L"audit", 5) == 0) 1373 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 1374 else if (wmemcmp(s, L"alarm", 5) == 0) 1375 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 1376 } 1377 if (type == 0) { 1378 /* Invalid entry type, skip entry */ 1379 ret = ARCHIVE_WARN; 1380 continue; 1381 } 1382 isint_w(field[4 + n].start, field[4 + n].end, &id); 1383 } 1384 1385 /* Add entry to the internal list. */ 1386 r = archive_acl_add_entry_w_len(acl, type, permset, 1387 tag, id, name.start, name.end - name.start); 1388 if (r < ARCHIVE_WARN) 1389 return (r); 1390 if (r != ARCHIVE_OK) 1391 ret = ARCHIVE_WARN; 1392 types |= type; 1393 } 1394 1395 /* Reset ACL */ 1396 archive_acl_reset(acl, types); 1397 1398 return (ret); 1399} 1400 1401/* 1402 * Parse a string to a positive decimal integer. Returns true if 1403 * the string is non-empty and consists only of decimal digits, 1404 * false otherwise. 1405 */ 1406static int 1407isint_w(const wchar_t *start, const wchar_t *end, int *result) 1408{ 1409 int n = 0; 1410 if (start >= end) 1411 return (0); 1412 while (start < end) { 1413 if (*start < '0' || *start > '9') 1414 return (0); 1415 if (n > (INT_MAX / 10) || 1416 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1417 n = INT_MAX; 1418 } else { 1419 n *= 10; 1420 n += *start - '0'; 1421 } 1422 start++; 1423 } 1424 *result = n; 1425 return (1); 1426} 1427 1428/* 1429 * Parse a string as a mode field. Returns true if 1430 * the string is non-empty and consists only of mode characters, 1431 * false otherwise. 1432 */ 1433static int 1434ismode_w(const wchar_t *start, const wchar_t *end, int *permset) 1435{ 1436 const wchar_t *p; 1437 1438 if (start >= end) 1439 return (0); 1440 p = start; 1441 *permset = 0; 1442 while (p < end) { 1443 switch (*p++) { 1444 case L'r': case L'R': 1445 *permset |= ARCHIVE_ENTRY_ACL_READ; 1446 break; 1447 case L'w': case L'W': 1448 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1449 break; 1450 case L'x': case L'X': 1451 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1452 break; 1453 case L'-': 1454 break; 1455 default: 1456 return (0); 1457 } 1458 } 1459 return (1); 1460} 1461 1462/* 1463 * Parse a string as a NFS4 ACL permission field. 1464 * Returns true if the string is non-empty and consists only of NFS4 ACL 1465 * permission characters, false otherwise 1466 */ 1467static int 1468is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset) 1469{ 1470 const wchar_t *p = start; 1471 1472 while (p < end) { 1473 switch (*p++) { 1474 case L'r': 1475 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; 1476 break; 1477 case L'w': 1478 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; 1479 break; 1480 case L'x': 1481 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1482 break; 1483 case L'p': 1484 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; 1485 break; 1486 case L'D': 1487 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; 1488 break; 1489 case L'd': 1490 *permset |= ARCHIVE_ENTRY_ACL_DELETE; 1491 break; 1492 case L'a': 1493 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; 1494 break; 1495 case L'A': 1496 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; 1497 break; 1498 case L'R': 1499 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; 1500 break; 1501 case L'W': 1502 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; 1503 break; 1504 case L'c': 1505 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; 1506 break; 1507 case L'C': 1508 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; 1509 break; 1510 case L'o': 1511 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; 1512 break; 1513 case L's': 1514 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; 1515 break; 1516 case L'-': 1517 break; 1518 default: 1519 return(0); 1520 } 1521 } 1522 return (1); 1523} 1524 1525/* 1526 * Parse a string as a NFS4 ACL flags field. 1527 * Returns true if the string is non-empty and consists only of NFS4 ACL 1528 * flag characters, false otherwise 1529 */ 1530static int 1531is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset) 1532{ 1533 const wchar_t *p = start; 1534 1535 while (p < end) { 1536 switch(*p++) { 1537 case L'f': 1538 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; 1539 break; 1540 case L'd': 1541 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; 1542 break; 1543 case L'i': 1544 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; 1545 break; 1546 case L'n': 1547 *permset |= 1548 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; 1549 break; 1550 case L'S': 1551 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; 1552 break; 1553 case L'F': 1554 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; 1555 break; 1556 case L'I': 1557 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; 1558 break; 1559 case L'-': 1560 break; 1561 default: 1562 return (0); 1563 } 1564 } 1565 return (1); 1566} 1567 1568/* 1569 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1570 * to point to just after the separator. *start points to the first 1571 * character of the matched text and *end just after the last 1572 * character of the matched identifier. In particular *end - *start 1573 * is the length of the field body, not including leading or trailing 1574 * whitespace. 1575 */ 1576static void 1577next_field_w(const wchar_t **wp, const wchar_t **start, 1578 const wchar_t **end, wchar_t *sep) 1579{ 1580 /* Skip leading whitespace to find start of field. */ 1581 while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { 1582 (*wp)++; 1583 } 1584 *start = *wp; 1585 1586 /* Scan for the separator. */ 1587 while (**wp != L'\0' && **wp != L',' && **wp != L':' && 1588 **wp != L'\n' && **wp != L'#') { 1589 (*wp)++; 1590 } 1591 *sep = **wp; 1592 1593 /* Locate end of field, trim trailing whitespace if necessary */ 1594 if (*wp == *start) { 1595 *end = *wp; 1596 } else { 1597 *end = *wp - 1; 1598 while (**end == L' ' || **end == L'\t' || **end == L'\n') { 1599 (*end)--; 1600 } 1601 (*end)++; 1602 } 1603 1604 /* Handle in-field comments */ 1605 if (*sep == L'#') { 1606 while (**wp != L'\0' && **wp != L',' && **wp != L'\n') { 1607 (*wp)++; 1608 } 1609 *sep = **wp; 1610 } 1611 1612 /* Adjust scanner location. */ 1613 if (**wp != L'\0') 1614 (*wp)++; 1615} 1616 1617/* 1618 * Parse an ACL text string. 1619 * 1620 * The want_type argument may be one of the following: 1621 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS 1622 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT 1623 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL 1624 * 1625 * POSIX.1e ACL entries prefixed with "default:" are treated as 1626 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 1627 */ 1628int 1629archive_acl_from_text_l(struct archive_acl *acl, const char *text, 1630 int want_type, struct archive_string_conv *sc) 1631{ 1632 struct { 1633 const char *start; 1634 const char *end; 1635 } field[6], name; 1636 1637 const char *s, *st; 1638 int numfields, fields, n, r, sol, ret; 1639 int type, types, tag, permset, id; 1640 size_t len; 1641 char sep; 1642 1643 switch (want_type) { 1644 case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: 1645 want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 1646 __LA_FALLTHROUGH; 1647 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 1648 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 1649 numfields = 5; 1650 break; 1651 case ARCHIVE_ENTRY_ACL_TYPE_NFS4: 1652 numfields = 6; 1653 break; 1654 default: 1655 return (ARCHIVE_FATAL); 1656 } 1657 1658 ret = ARCHIVE_OK; 1659 types = 0; 1660 1661 while (text != NULL && *text != '\0') { 1662 /* 1663 * Parse the fields out of the next entry, 1664 * advance 'text' to start of next entry. 1665 */ 1666 fields = 0; 1667 do { 1668 const char *start, *end; 1669 next_field(&text, &start, &end, &sep); 1670 if (fields < numfields) { 1671 field[fields].start = start; 1672 field[fields].end = end; 1673 } 1674 ++fields; 1675 } while (sep == ':'); 1676 1677 /* Set remaining fields to blank. */ 1678 for (n = fields; n < numfields; ++n) 1679 field[n].start = field[n].end = NULL; 1680 1681 if (field[0].start != NULL && *(field[0].start) == '#') { 1682 /* Comment, skip entry */ 1683 continue; 1684 } 1685 1686 n = 0; 1687 sol = 0; 1688 id = -1; 1689 permset = 0; 1690 name.start = name.end = NULL; 1691 1692 if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 1693 /* POSIX.1e ACLs */ 1694 /* 1695 * Default keyword "default:user::rwx" 1696 * if found, we have one more field 1697 * 1698 * We also support old Solaris extension: 1699 * "defaultuser::rwx" is the default ACL corresponding 1700 * to "user::rwx", etc. valid only for first field 1701 */ 1702 s = field[0].start; 1703 len = field[0].end - field[0].start; 1704 if (*s == 'd' && (len == 1 || (len >= 7 1705 && memcmp((s + 1), "efault", 6) == 0))) { 1706 type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1707 if (len > 7) 1708 field[0].start += 7; 1709 else 1710 n = 1; 1711 } else 1712 type = want_type; 1713 1714 /* Check for a numeric ID in field n+1 or n+3. */ 1715 isint(field[n + 1].start, field[n + 1].end, &id); 1716 /* Field n+3 is optional. */ 1717 if (id == -1 && fields > (n + 3)) 1718 isint(field[n + 3].start, field[n + 3].end, 1719 &id); 1720 1721 tag = 0; 1722 s = field[n].start; 1723 st = field[n].start + 1; 1724 len = field[n].end - field[n].start; 1725 1726 if (len == 0) { 1727 ret = ARCHIVE_WARN; 1728 continue; 1729 } 1730 1731 switch (*s) { 1732 case 'u': 1733 if (len == 1 || (len == 4 1734 && memcmp(st, "ser", 3) == 0)) 1735 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1736 break; 1737 case 'g': 1738 if (len == 1 || (len == 5 1739 && memcmp(st, "roup", 4) == 0)) 1740 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1741 break; 1742 case 'o': 1743 if (len == 1 || (len == 5 1744 && memcmp(st, "ther", 4) == 0)) 1745 tag = ARCHIVE_ENTRY_ACL_OTHER; 1746 break; 1747 case 'm': 1748 if (len == 1 || (len == 4 1749 && memcmp(st, "ask", 3) == 0)) 1750 tag = ARCHIVE_ENTRY_ACL_MASK; 1751 break; 1752 default: 1753 break; 1754 } 1755 1756 switch (tag) { 1757 case ARCHIVE_ENTRY_ACL_OTHER: 1758 case ARCHIVE_ENTRY_ACL_MASK: 1759 if (fields == (n + 2) 1760 && field[n + 1].start < field[n + 1].end 1761 && ismode(field[n + 1].start, 1762 field[n + 1].end, &permset)) { 1763 /* This is Solaris-style "other:rwx" */ 1764 sol = 1; 1765 } else if (fields == (n + 3) && 1766 field[n + 1].start < field[n + 1].end) { 1767 /* Invalid mask or other field */ 1768 ret = ARCHIVE_WARN; 1769 continue; 1770 } 1771 break; 1772 case ARCHIVE_ENTRY_ACL_USER_OBJ: 1773 case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1774 if (id != -1 || 1775 field[n + 1].start < field[n + 1].end) { 1776 name = field[n + 1]; 1777 if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) 1778 tag = ARCHIVE_ENTRY_ACL_USER; 1779 else 1780 tag = ARCHIVE_ENTRY_ACL_GROUP; 1781 } 1782 break; 1783 default: 1784 /* Invalid tag, skip entry */ 1785 ret = ARCHIVE_WARN; 1786 continue; 1787 } 1788 1789 /* 1790 * Without "default:" we expect mode in field 3 1791 * Exception: Solaris other and mask fields 1792 */ 1793 if (permset == 0 && !ismode(field[n + 2 - sol].start, 1794 field[n + 2 - sol].end, &permset)) { 1795 /* Invalid mode, skip entry */ 1796 ret = ARCHIVE_WARN; 1797 continue; 1798 } 1799 } else { 1800 /* NFS4 ACLs */ 1801 s = field[0].start; 1802 len = field[0].end - field[0].start; 1803 tag = 0; 1804 1805 switch (len) { 1806 case 4: 1807 if (memcmp(s, "user", 4) == 0) 1808 tag = ARCHIVE_ENTRY_ACL_USER; 1809 break; 1810 case 5: 1811 if (memcmp(s, "group", 5) == 0) 1812 tag = ARCHIVE_ENTRY_ACL_GROUP; 1813 break; 1814 case 6: 1815 if (memcmp(s, "owner@", 6) == 0) 1816 tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1817 else if (memcmp(s, "group@", 6) == 0) 1818 tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1819 break; 1820 case 9: 1821 if (memcmp(s, "everyone@", 9) == 0) 1822 tag = ARCHIVE_ENTRY_ACL_EVERYONE; 1823 break; 1824 default: 1825 break; 1826 } 1827 1828 if (tag == 0) { 1829 /* Invalid tag, skip entry */ 1830 ret = ARCHIVE_WARN; 1831 continue; 1832 } else if (tag == ARCHIVE_ENTRY_ACL_USER || 1833 tag == ARCHIVE_ENTRY_ACL_GROUP) { 1834 n = 1; 1835 name = field[1]; 1836 isint(name.start, name.end, &id); 1837 } else 1838 n = 0; 1839 1840 if (!is_nfs4_perms(field[1 + n].start, 1841 field[1 + n].end, &permset)) { 1842 /* Invalid NFSv4 perms, skip entry */ 1843 ret = ARCHIVE_WARN; 1844 continue; 1845 } 1846 if (!is_nfs4_flags(field[2 + n].start, 1847 field[2 + n].end, &permset)) { 1848 /* Invalid NFSv4 flags, skip entry */ 1849 ret = ARCHIVE_WARN; 1850 continue; 1851 } 1852 s = field[3 + n].start; 1853 len = field[3 + n].end - field[3 + n].start; 1854 type = 0; 1855 if (len == 4) { 1856 if (memcmp(s, "deny", 4) == 0) 1857 type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 1858 } else if (len == 5) { 1859 if (memcmp(s, "allow", 5) == 0) 1860 type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 1861 else if (memcmp(s, "audit", 5) == 0) 1862 type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 1863 else if (memcmp(s, "alarm", 5) == 0) 1864 type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 1865 } 1866 if (type == 0) { 1867 /* Invalid entry type, skip entry */ 1868 ret = ARCHIVE_WARN; 1869 continue; 1870 } 1871 isint(field[4 + n].start, field[4 + n].end, 1872 &id); 1873 } 1874 1875 /* Add entry to the internal list. */ 1876 r = archive_acl_add_entry_len_l(acl, type, permset, 1877 tag, id, name.start, name.end - name.start, sc); 1878 if (r < ARCHIVE_WARN) 1879 return (r); 1880 if (r != ARCHIVE_OK) 1881 ret = ARCHIVE_WARN; 1882 types |= type; 1883 } 1884 1885 /* Reset ACL */ 1886 archive_acl_reset(acl, types); 1887 1888 return (ret); 1889} 1890 1891/* 1892 * Parse a string to a positive decimal integer. Returns true if 1893 * the string is non-empty and consists only of decimal digits, 1894 * false otherwise. 1895 */ 1896static int 1897isint(const char *start, const char *end, int *result) 1898{ 1899 int n = 0; 1900 if (start >= end) 1901 return (0); 1902 while (start < end) { 1903 if (*start < '0' || *start > '9') 1904 return (0); 1905 if (n > (INT_MAX / 10) || 1906 (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1907 n = INT_MAX; 1908 } else { 1909 n *= 10; 1910 n += *start - '0'; 1911 } 1912 start++; 1913 } 1914 *result = n; 1915 return (1); 1916} 1917 1918/* 1919 * Parse a string as a mode field. Returns true if 1920 * the string is non-empty and consists only of mode characters, 1921 * false otherwise. 1922 */ 1923static int 1924ismode(const char *start, const char *end, int *permset) 1925{ 1926 const char *p; 1927 1928 if (start >= end) 1929 return (0); 1930 p = start; 1931 *permset = 0; 1932 while (p < end) { 1933 switch (*p++) { 1934 case 'r': case 'R': 1935 *permset |= ARCHIVE_ENTRY_ACL_READ; 1936 break; 1937 case 'w': case 'W': 1938 *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1939 break; 1940 case 'x': case 'X': 1941 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1942 break; 1943 case '-': 1944 break; 1945 default: 1946 return (0); 1947 } 1948 } 1949 return (1); 1950} 1951 1952/* 1953 * Parse a string as a NFS4 ACL permission field. 1954 * Returns true if the string is non-empty and consists only of NFS4 ACL 1955 * permission characters, false otherwise 1956 */ 1957static int 1958is_nfs4_perms(const char *start, const char *end, int *permset) 1959{ 1960 const char *p = start; 1961 1962 while (p < end) { 1963 switch (*p++) { 1964 case 'r': 1965 *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; 1966 break; 1967 case 'w': 1968 *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; 1969 break; 1970 case 'x': 1971 *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1972 break; 1973 case 'p': 1974 *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; 1975 break; 1976 case 'D': 1977 *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; 1978 break; 1979 case 'd': 1980 *permset |= ARCHIVE_ENTRY_ACL_DELETE; 1981 break; 1982 case 'a': 1983 *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; 1984 break; 1985 case 'A': 1986 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; 1987 break; 1988 case 'R': 1989 *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; 1990 break; 1991 case 'W': 1992 *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; 1993 break; 1994 case 'c': 1995 *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; 1996 break; 1997 case 'C': 1998 *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; 1999 break; 2000 case 'o': 2001 *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; 2002 break; 2003 case 's': 2004 *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; 2005 break; 2006 case '-': 2007 break; 2008 default: 2009 return(0); 2010 } 2011 } 2012 return (1); 2013} 2014 2015/* 2016 * Parse a string as a NFS4 ACL flags field. 2017 * Returns true if the string is non-empty and consists only of NFS4 ACL 2018 * flag characters, false otherwise 2019 */ 2020static int 2021is_nfs4_flags(const char *start, const char *end, int *permset) 2022{ 2023 const char *p = start; 2024 2025 while (p < end) { 2026 switch(*p++) { 2027 case 'f': 2028 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; 2029 break; 2030 case 'd': 2031 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; 2032 break; 2033 case 'i': 2034 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; 2035 break; 2036 case 'n': 2037 *permset |= 2038 ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; 2039 break; 2040 case 'S': 2041 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; 2042 break; 2043 case 'F': 2044 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; 2045 break; 2046 case 'I': 2047 *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; 2048 break; 2049 case '-': 2050 break; 2051 default: 2052 return (0); 2053 } 2054 } 2055 return (1); 2056} 2057 2058/* 2059 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 2060 * to point to just after the separator. *start points to the first 2061 * character of the matched text and *end just after the last 2062 * character of the matched identifier. In particular *end - *start 2063 * is the length of the field body, not including leading or trailing 2064 * whitespace. 2065 */ 2066static void 2067next_field(const char **p, const char **start, 2068 const char **end, char *sep) 2069{ 2070 /* Skip leading whitespace to find start of field. */ 2071 while (**p == ' ' || **p == '\t' || **p == '\n') { 2072 (*p)++; 2073 } 2074 *start = *p; 2075 2076 /* Scan for the separator. */ 2077 while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' && 2078 **p != '#') { 2079 (*p)++; 2080 } 2081 *sep = **p; 2082 2083 /* Locate end of field, trim trailing whitespace if necessary */ 2084 if (*p == *start) { 2085 *end = *p; 2086 } else { 2087 *end = *p - 1; 2088 while (**end == ' ' || **end == '\t' || **end == '\n') { 2089 (*end)--; 2090 } 2091 (*end)++; 2092 } 2093 2094 /* Handle in-field comments */ 2095 if (*sep == '#') { 2096 while (**p != '\0' && **p != ',' && **p != '\n') { 2097 (*p)++; 2098 } 2099 *sep = **p; 2100 } 2101 2102 /* Adjust scanner location. */ 2103 if (**p != '\0') 2104 (*p)++; 2105} 2106