1231200Smm/*- 2231200Smm * Copyright (c) 2003-2010 Tim Kientzle 3313570Smm * Copyright (c) 2016 Martin Matuska 4231200Smm * All rights reserved. 5231200Smm * 6231200Smm * Redistribution and use in source and binary forms, with or without 7231200Smm * modification, are permitted provided that the following conditions 8231200Smm * are met: 9231200Smm * 1. Redistributions of source code must retain the above copyright 10231200Smm * notice, this list of conditions and the following disclaimer. 11231200Smm * 2. Redistributions in binary form must reproduce the above copyright 12231200Smm * notice, this list of conditions and the following disclaimer in the 13231200Smm * documentation and/or other materials provided with the distribution. 14231200Smm * 15231200Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16231200Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17231200Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18231200Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19231200Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20231200Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21231200Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22231200Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23231200Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24231200Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25231200Smm */ 26231200Smm 27231200Smm#include "archive_platform.h" 28231200Smm__FBSDID("$FreeBSD$"); 29231200Smm 30231200Smm#ifdef HAVE_ERRNO_H 31231200Smm#include <errno.h> 32231200Smm#endif 33231200Smm#ifdef HAVE_LIMITS_H 34231200Smm#include <limits.h> 35231200Smm#endif 36231200Smm#ifdef HAVE_WCHAR_H 37231200Smm#include <wchar.h> 38231200Smm#endif 39231200Smm 40231200Smm#include "archive_acl_private.h" 41231200Smm#include "archive_entry.h" 42231200Smm#include "archive_private.h" 43231200Smm 44231200Smm#undef max 45231200Smm#define max(a, b) ((a)>(b)?(a):(b)) 46231200Smm 47231200Smm#ifndef HAVE_WMEMCMP 48231200Smm/* Good enough for simple equality testing, but not for sorting. */ 49231200Smm#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) 50231200Smm#endif 51231200Smm 52231200Smmstatic int acl_special(struct archive_acl *acl, 53231200Smm int type, int permset, int tag); 54231200Smmstatic struct archive_acl_entry *acl_new_entry(struct archive_acl *acl, 55231200Smm int type, int permset, int tag, int id); 56232153Smmstatic int archive_acl_add_entry_len_l(struct archive_acl *acl, 57232153Smm int type, int permset, int tag, int id, const char *name, 58232153Smm size_t len, struct archive_string_conv *sc); 59313570Smmstatic int archive_acl_text_want_type(struct archive_acl *acl, int flags); 60313570Smmstatic ssize_t archive_acl_text_len(struct archive_acl *acl, int want_type, 61313570Smm int flags, int wide, struct archive *a, 62313570Smm struct archive_string_conv *sc); 63231200Smmstatic int isint_w(const wchar_t *start, const wchar_t *end, int *result); 64231200Smmstatic int ismode_w(const wchar_t *start, const wchar_t *end, int *result); 65313570Smmstatic int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, 66313570Smm int *result); 67313570Smmstatic int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, 68313570Smm int *result); 69231200Smmstatic void next_field_w(const wchar_t **wp, const wchar_t **start, 70231200Smm const wchar_t **end, wchar_t *sep); 71313570Smmstatic void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, 72313570Smm int tag, int flags, const wchar_t *wname, int perm, int id); 73231200Smmstatic void append_id_w(wchar_t **wp, int id); 74231200Smmstatic int isint(const char *start, const char *end, int *result); 75231200Smmstatic int ismode(const char *start, const char *end, int *result); 76313570Smmstatic int is_nfs4_flags(const char *start, const char *end, 77313570Smm int *result); 78313570Smmstatic int is_nfs4_perms(const char *start, const char *end, 79313570Smm int *result); 80231200Smmstatic void next_field(const char **p, const char **start, 81231200Smm const char **end, char *sep); 82313570Smmstatic void append_entry(char **p, const char *prefix, int type, 83313570Smm int tag, int flags, const char *name, int perm, int id); 84231200Smmstatic void append_id(char **p, int id); 85231200Smm 86313926Smmstatic const struct { 87313926Smm const int perm; 88313926Smm const char c; 89313926Smm const wchar_t wc; 90313926Smm} nfsv4_acl_perm_map[] = { 91313926Smm { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r', 92313926Smm L'r' }, 93313926Smm { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w', 94313926Smm L'w' }, 95313926Smm { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' }, 96313926Smm { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, 97313926Smm 'p', L'p' }, 98313926Smm { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' }, 99313926Smm { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' }, 100313926Smm { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' }, 101313926Smm { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' }, 102313926Smm { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' }, 103313926Smm { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' }, 104313926Smm { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' }, 105313926Smm { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' }, 106313926Smm { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' }, 107313926Smm { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' } 108313926Smm}; 109313926Smm 110313926Smmstatic const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) / 111313926Smm sizeof(nfsv4_acl_perm_map[0])); 112313926Smm 113313926Smmstatic const struct { 114313926Smm const int perm; 115313926Smm const char c; 116313926Smm const wchar_t wc; 117313926Smm} nfsv4_acl_flag_map[] = { 118313926Smm { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' }, 119313926Smm { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' }, 120313926Smm { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' }, 121313926Smm { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' }, 122313926Smm { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' }, 123313926Smm { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' }, 124313926Smm { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' } 125313926Smm}; 126313926Smm 127313926Smmstatic const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) / 128313926Smm sizeof(nfsv4_acl_flag_map[0])); 129313926Smm 130231200Smmvoid 131231200Smmarchive_acl_clear(struct archive_acl *acl) 132231200Smm{ 133231200Smm struct archive_acl_entry *ap; 134231200Smm 135231200Smm while (acl->acl_head != NULL) { 136231200Smm ap = acl->acl_head->next; 137231200Smm archive_mstring_clean(&acl->acl_head->name); 138231200Smm free(acl->acl_head); 139231200Smm acl->acl_head = ap; 140231200Smm } 141344673Smm free(acl->acl_text_w); 142344673Smm acl->acl_text_w = NULL; 143344673Smm free(acl->acl_text); 144344673Smm acl->acl_text = NULL; 145231200Smm acl->acl_p = NULL; 146311041Smm acl->acl_types = 0; 147231200Smm acl->acl_state = 0; /* Not counting. */ 148231200Smm} 149231200Smm 150231200Smmvoid 151231200Smmarchive_acl_copy(struct archive_acl *dest, struct archive_acl *src) 152231200Smm{ 153231200Smm struct archive_acl_entry *ap, *ap2; 154231200Smm 155231200Smm archive_acl_clear(dest); 156231200Smm 157231200Smm dest->mode = src->mode; 158231200Smm ap = src->acl_head; 159231200Smm while (ap != NULL) { 160231200Smm ap2 = acl_new_entry(dest, 161231200Smm ap->type, ap->permset, ap->tag, ap->id); 162231200Smm if (ap2 != NULL) 163231200Smm archive_mstring_copy(&ap2->name, &ap->name); 164231200Smm ap = ap->next; 165231200Smm } 166231200Smm} 167231200Smm 168231200Smmint 169231200Smmarchive_acl_add_entry(struct archive_acl *acl, 170231200Smm int type, int permset, int tag, int id, const char *name) 171231200Smm{ 172231200Smm struct archive_acl_entry *ap; 173231200Smm 174231200Smm if (acl_special(acl, type, permset, tag) == 0) 175231200Smm return ARCHIVE_OK; 176231200Smm ap = acl_new_entry(acl, type, permset, tag, id); 177231200Smm if (ap == NULL) { 178231200Smm /* XXX Error XXX */ 179231200Smm return ARCHIVE_FAILED; 180231200Smm } 181231200Smm if (name != NULL && *name != '\0') 182231200Smm archive_mstring_copy_mbs(&ap->name, name); 183231200Smm else 184231200Smm archive_mstring_clean(&ap->name); 185231200Smm return ARCHIVE_OK; 186231200Smm} 187231200Smm 188231200Smmint 189231200Smmarchive_acl_add_entry_w_len(struct archive_acl *acl, 190231200Smm int type, int permset, int tag, int id, const wchar_t *name, size_t len) 191231200Smm{ 192231200Smm struct archive_acl_entry *ap; 193231200Smm 194231200Smm if (acl_special(acl, type, permset, tag) == 0) 195231200Smm return ARCHIVE_OK; 196231200Smm ap = acl_new_entry(acl, type, permset, tag, id); 197231200Smm if (ap == NULL) { 198231200Smm /* XXX Error XXX */ 199231200Smm return ARCHIVE_FAILED; 200231200Smm } 201231200Smm if (name != NULL && *name != L'\0' && len > 0) 202231200Smm archive_mstring_copy_wcs_len(&ap->name, name, len); 203231200Smm else 204231200Smm archive_mstring_clean(&ap->name); 205231200Smm return ARCHIVE_OK; 206231200Smm} 207231200Smm 208232153Smmstatic int 209231200Smmarchive_acl_add_entry_len_l(struct archive_acl *acl, 210231200Smm int type, int permset, int tag, int id, const char *name, size_t len, 211231200Smm struct archive_string_conv *sc) 212231200Smm{ 213231200Smm struct archive_acl_entry *ap; 214231200Smm int r; 215231200Smm 216231200Smm if (acl_special(acl, type, permset, tag) == 0) 217231200Smm return ARCHIVE_OK; 218231200Smm ap = acl_new_entry(acl, type, permset, tag, id); 219231200Smm if (ap == NULL) { 220231200Smm /* XXX Error XXX */ 221231200Smm return ARCHIVE_FAILED; 222231200Smm } 223231200Smm if (name != NULL && *name != '\0' && len > 0) { 224231200Smm r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc); 225231200Smm } else { 226231200Smm r = 0; 227231200Smm archive_mstring_clean(&ap->name); 228231200Smm } 229231200Smm if (r == 0) 230231200Smm return (ARCHIVE_OK); 231231200Smm else if (errno == ENOMEM) 232231200Smm return (ARCHIVE_FATAL); 233231200Smm else 234231200Smm return (ARCHIVE_WARN); 235231200Smm} 236231200Smm 237231200Smm/* 238231200Smm * If this ACL entry is part of the standard POSIX permissions set, 239231200Smm * store the permissions in the stat structure and return zero. 240231200Smm */ 241231200Smmstatic int 242231200Smmacl_special(struct archive_acl *acl, int type, int permset, int tag) 243231200Smm{ 244231200Smm if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS 245231200Smm && ((permset & ~007) == 0)) { 246231200Smm switch (tag) { 247231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 248231200Smm acl->mode &= ~0700; 249231200Smm acl->mode |= (permset & 7) << 6; 250231200Smm return (0); 251231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 252231200Smm acl->mode &= ~0070; 253231200Smm acl->mode |= (permset & 7) << 3; 254231200Smm return (0); 255231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 256231200Smm acl->mode &= ~0007; 257231200Smm acl->mode |= permset & 7; 258231200Smm return (0); 259231200Smm } 260231200Smm } 261231200Smm return (1); 262231200Smm} 263231200Smm 264231200Smm/* 265231200Smm * Allocate and populate a new ACL entry with everything but the 266231200Smm * name. 267231200Smm */ 268231200Smmstatic struct archive_acl_entry * 269231200Smmacl_new_entry(struct archive_acl *acl, 270231200Smm int type, int permset, int tag, int id) 271231200Smm{ 272231200Smm struct archive_acl_entry *ap, *aq; 273231200Smm 274231200Smm /* Type argument must be a valid NFS4 or POSIX.1e type. 275231200Smm * The type must agree with anything already set and 276231200Smm * the permset must be compatible. */ 277231200Smm if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 278231200Smm if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 279231200Smm return (NULL); 280231200Smm } 281231200Smm if (permset & 282231200Smm ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4 283231200Smm | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) { 284231200Smm return (NULL); 285231200Smm } 286231200Smm } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 287231200Smm if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 288231200Smm return (NULL); 289231200Smm } 290231200Smm if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) { 291231200Smm return (NULL); 292231200Smm } 293231200Smm } else { 294231200Smm return (NULL); 295231200Smm } 296231200Smm 297231200Smm /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */ 298231200Smm switch (tag) { 299231200Smm case ARCHIVE_ENTRY_ACL_USER: 300231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 301231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 302231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 303231200Smm /* Tags valid in both NFS4 and POSIX.1e */ 304231200Smm break; 305231200Smm case ARCHIVE_ENTRY_ACL_MASK: 306231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 307231200Smm /* Tags valid only in POSIX.1e. */ 308231200Smm if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 309231200Smm return (NULL); 310231200Smm } 311231200Smm break; 312231200Smm case ARCHIVE_ENTRY_ACL_EVERYONE: 313231200Smm /* Tags valid only in NFS4. */ 314231200Smm if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 315231200Smm return (NULL); 316231200Smm } 317231200Smm break; 318231200Smm default: 319231200Smm /* No other values are valid. */ 320231200Smm return (NULL); 321231200Smm } 322231200Smm 323344673Smm free(acl->acl_text_w); 324344673Smm acl->acl_text_w = NULL; 325344673Smm free(acl->acl_text); 326344673Smm acl->acl_text = NULL; 327231200Smm 328311041Smm /* 329311041Smm * If there's a matching entry already in the list, overwrite it. 330311041Smm * NFSv4 entries may be repeated and are not overwritten. 331311041Smm * 332311041Smm * TODO: compare names of no id is provided (needs more rework) 333311041Smm */ 334231200Smm ap = acl->acl_head; 335231200Smm aq = NULL; 336231200Smm while (ap != NULL) { 337311041Smm if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) && 338311041Smm ap->type == type && ap->tag == tag && ap->id == id) { 339311041Smm if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER && 340311041Smm tag != ARCHIVE_ENTRY_ACL_GROUP)) { 341311041Smm ap->permset = permset; 342311041Smm return (ap); 343311041Smm } 344231200Smm } 345231200Smm aq = ap; 346231200Smm ap = ap->next; 347231200Smm } 348231200Smm 349231200Smm /* Add a new entry to the end of the list. */ 350311041Smm ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap)); 351231200Smm if (ap == NULL) 352231200Smm return (NULL); 353231200Smm if (aq == NULL) 354231200Smm acl->acl_head = ap; 355231200Smm else 356231200Smm aq->next = ap; 357231200Smm ap->type = type; 358231200Smm ap->tag = tag; 359231200Smm ap->id = id; 360231200Smm ap->permset = permset; 361231200Smm acl->acl_types |= type; 362231200Smm return (ap); 363231200Smm} 364231200Smm 365231200Smm/* 366231200Smm * Return a count of entries matching "want_type". 367231200Smm */ 368231200Smmint 369231200Smmarchive_acl_count(struct archive_acl *acl, int want_type) 370231200Smm{ 371231200Smm int count; 372231200Smm struct archive_acl_entry *ap; 373231200Smm 374231200Smm count = 0; 375231200Smm ap = acl->acl_head; 376231200Smm while (ap != NULL) { 377231200Smm if ((ap->type & want_type) != 0) 378231200Smm count++; 379231200Smm ap = ap->next; 380231200Smm } 381231200Smm 382231200Smm if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) 383231200Smm count += 3; 384231200Smm return (count); 385231200Smm} 386231200Smm 387231200Smm/* 388313570Smm * Return a bitmask of stored ACL types in an ACL list 389313570Smm */ 390313570Smmint 391313570Smmarchive_acl_types(struct archive_acl *acl) 392313570Smm{ 393313570Smm return (acl->acl_types); 394313570Smm} 395313570Smm 396313570Smm/* 397231200Smm * Prepare for reading entries from the ACL data. Returns a count 398231200Smm * of entries matching "want_type", or zero if there are no 399231200Smm * non-extended ACL entries of that type. 400231200Smm */ 401231200Smmint 402231200Smmarchive_acl_reset(struct archive_acl *acl, int want_type) 403231200Smm{ 404231200Smm int count, cutoff; 405231200Smm 406231200Smm count = archive_acl_count(acl, want_type); 407231200Smm 408231200Smm /* 409231200Smm * If the only entries are the three standard ones, 410231200Smm * then don't return any ACL data. (In this case, 411231200Smm * client can just use chmod(2) to set permissions.) 412231200Smm */ 413231200Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) 414231200Smm cutoff = 3; 415231200Smm else 416231200Smm cutoff = 0; 417231200Smm 418231200Smm if (count > cutoff) 419231200Smm acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; 420231200Smm else 421231200Smm acl->acl_state = 0; 422231200Smm acl->acl_p = acl->acl_head; 423231200Smm return (count); 424231200Smm} 425231200Smm 426231200Smm 427231200Smm/* 428231200Smm * Return the next ACL entry in the list. Fake entries for the 429231200Smm * standard permissions and include them in the returned list. 430231200Smm */ 431231200Smmint 432313570Smmarchive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, 433313570Smm int *type, int *permset, int *tag, int *id, const char **name) 434231200Smm{ 435231200Smm *name = NULL; 436231200Smm *id = -1; 437231200Smm 438231200Smm /* 439231200Smm * The acl_state is either zero (no entries available), -1 440231200Smm * (reading from list), or an entry type (retrieve that type 441231200Smm * from ae_stat.aest_mode). 442231200Smm */ 443231200Smm if (acl->acl_state == 0) 444231200Smm return (ARCHIVE_WARN); 445231200Smm 446231200Smm /* The first three access entries are special. */ 447231200Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 448231200Smm switch (acl->acl_state) { 449231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 450231200Smm *permset = (acl->mode >> 6) & 7; 451231200Smm *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 452231200Smm *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 453231200Smm acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 454231200Smm return (ARCHIVE_OK); 455231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 456231200Smm *permset = (acl->mode >> 3) & 7; 457231200Smm *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 458231200Smm *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 459231200Smm acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER; 460231200Smm return (ARCHIVE_OK); 461231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 462231200Smm *permset = acl->mode & 7; 463231200Smm *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 464231200Smm *tag = ARCHIVE_ENTRY_ACL_OTHER; 465231200Smm acl->acl_state = -1; 466231200Smm acl->acl_p = acl->acl_head; 467231200Smm return (ARCHIVE_OK); 468231200Smm default: 469231200Smm break; 470231200Smm } 471231200Smm } 472231200Smm 473231200Smm while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0) 474231200Smm acl->acl_p = acl->acl_p->next; 475231200Smm if (acl->acl_p == NULL) { 476231200Smm acl->acl_state = 0; 477231200Smm *type = 0; 478231200Smm *permset = 0; 479231200Smm *tag = 0; 480231200Smm *id = -1; 481231200Smm *name = NULL; 482231200Smm return (ARCHIVE_EOF); /* End of ACL entries. */ 483231200Smm } 484231200Smm *type = acl->acl_p->type; 485231200Smm *permset = acl->acl_p->permset; 486231200Smm *tag = acl->acl_p->tag; 487231200Smm *id = acl->acl_p->id; 488238856Smm if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) { 489238856Smm if (errno == ENOMEM) 490238856Smm return (ARCHIVE_FATAL); 491231200Smm *name = NULL; 492238856Smm } 493231200Smm acl->acl_p = acl->acl_p->next; 494231200Smm return (ARCHIVE_OK); 495231200Smm} 496231200Smm 497231200Smm/* 498313570Smm * Determine what type of ACL do we want 499231200Smm */ 500313570Smmstatic int 501313570Smmarchive_acl_text_want_type(struct archive_acl *acl, int flags) 502231200Smm{ 503313570Smm int want_type; 504231200Smm 505313570Smm /* Check if ACL is NFSv4 */ 506313570Smm if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 507313570Smm /* NFSv4 should never mix with POSIX.1e */ 508313570Smm if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) 509313570Smm return (0); 510313570Smm else 511313570Smm return (ARCHIVE_ENTRY_ACL_TYPE_NFS4); 512231200Smm } 513231200Smm 514313570Smm /* Now deal with POSIX.1e ACLs */ 515313570Smm 516313570Smm want_type = 0; 517313570Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) 518313570Smm want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 519313570Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) 520313570Smm want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 521313570Smm 522313570Smm /* By default we want both access and default ACLs */ 523313570Smm if (want_type == 0) 524313570Smm return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E); 525313570Smm 526313570Smm return (want_type); 527313570Smm} 528313570Smm 529313570Smm/* 530313570Smm * Calculate ACL text string length 531313570Smm */ 532313570Smmstatic ssize_t 533313570Smmarchive_acl_text_len(struct archive_acl *acl, int want_type, int flags, 534313570Smm int wide, struct archive *a, struct archive_string_conv *sc) { 535313570Smm struct archive_acl_entry *ap; 536313570Smm const char *name; 537313570Smm const wchar_t *wname; 538313570Smm int count, idlen, tmp, r; 539313570Smm ssize_t length; 540313570Smm size_t len; 541313570Smm 542231200Smm count = 0; 543231200Smm length = 0; 544313570Smm for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 545313570Smm if ((ap->type & want_type) == 0) 546313570Smm continue; 547313570Smm /* 548313570Smm * Filemode-mapping ACL entries are stored exclusively in 549313570Smm * ap->mode so they should not be in the list 550313570Smm */ 551313570Smm if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) 552313570Smm && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ 553313570Smm || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ 554313570Smm || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) 555313570Smm continue; 556313570Smm count++; 557313570Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 558313570Smm && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) 559313570Smm length += 8; /* "default:" */ 560313570Smm switch (ap->tag) { 561313570Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 562313570Smm if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 563313570Smm length += 6; /* "owner@" */ 564313570Smm break; 565313570Smm } 566313570Smm /* FALLTHROUGH */ 567313570Smm case ARCHIVE_ENTRY_ACL_USER: 568313570Smm case ARCHIVE_ENTRY_ACL_MASK: 569313570Smm length += 4; /* "user", "mask" */ 570313570Smm break; 571313570Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 572313570Smm if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 573313570Smm length += 6; /* "group@" */ 574313570Smm break; 575313570Smm } 576313570Smm /* FALLTHROUGH */ 577313570Smm case ARCHIVE_ENTRY_ACL_GROUP: 578313570Smm case ARCHIVE_ENTRY_ACL_OTHER: 579313570Smm length += 5; /* "group", "other" */ 580313570Smm break; 581313570Smm case ARCHIVE_ENTRY_ACL_EVERYONE: 582313570Smm length += 9; /* "everyone@" */ 583313570Smm break; 584313570Smm } 585313570Smm length += 1; /* colon after tag */ 586313570Smm if (ap->tag == ARCHIVE_ENTRY_ACL_USER || 587313570Smm ap->tag == ARCHIVE_ENTRY_ACL_GROUP) { 588313570Smm if (wide) { 589313570Smm r = archive_mstring_get_wcs(a, &ap->name, 590313570Smm &wname); 591313570Smm if (r == 0 && wname != NULL) 592313570Smm length += wcslen(wname); 593313570Smm else if (r < 0 && errno == ENOMEM) 594313570Smm return (0); 595313570Smm else 596313570Smm length += sizeof(uid_t) * 3 + 1; 597313570Smm } else { 598368707Smm r = archive_mstring_get_mbs_l(a, &ap->name, &name, 599313570Smm &len, sc); 600313570Smm if (r != 0) 601313570Smm return (0); 602313570Smm if (len > 0 && name != NULL) 603313570Smm length += len; 604313570Smm else 605313570Smm length += sizeof(uid_t) * 3 + 1; 606313570Smm } 607313570Smm length += 1; /* colon after user or group name */ 608313570Smm } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) 609313570Smm length += 1; /* 2nd colon empty user,group or other */ 610313570Smm 611313570Smm if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) 612313570Smm && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) 613313570Smm && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER 614313570Smm || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) { 615313570Smm /* Solaris has no colon after other: and mask: */ 616313570Smm length = length - 1; 617313570Smm } 618313570Smm 619313570Smm if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 620313570Smm /* rwxpdDaARWcCos:fdinSFI:deny */ 621313570Smm length += 27; 622313570Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0) 623313570Smm length += 1; /* allow, alarm, audit */ 624313570Smm } else 625231200Smm length += 3; /* rwx */ 626313570Smm 627313570Smm if ((ap->tag == ARCHIVE_ENTRY_ACL_USER || 628313570Smm ap->tag == ARCHIVE_ENTRY_ACL_GROUP) && 629313570Smm (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) { 630231200Smm length += 1; /* colon */ 631313570Smm /* ID digit count */ 632313570Smm idlen = 1; 633313570Smm tmp = ap->id; 634313570Smm while (tmp > 9) { 635313570Smm tmp = tmp / 10; 636313570Smm idlen++; 637313570Smm } 638313570Smm length += idlen; 639231200Smm } 640313570Smm length ++; /* entry separator */ 641231200Smm } 642231200Smm 643313570Smm /* Add filemode-mapping access entries to the length */ 644313570Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 645313570Smm if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) { 646313570Smm /* "user::rwx\ngroup::rwx\nother:rwx\n" */ 647313570Smm length += 31; 648313570Smm } else { 649313570Smm /* "user::rwx\ngroup::rwx\nother::rwx\n" */ 650313570Smm length += 32; 651313570Smm } 652313570Smm } else if (count == 0) 653313570Smm return (0); 654231200Smm 655313570Smm /* The terminating character is included in count */ 656313570Smm return (length); 657313570Smm} 658313570Smm 659313570Smm/* 660313570Smm * Generate a wide text version of the ACL. The flags parameter controls 661313570Smm * the type and style of the generated ACL. 662313570Smm */ 663313570Smmwchar_t * 664313570Smmarchive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, 665313570Smm struct archive *a) 666313570Smm{ 667313570Smm int count; 668313570Smm ssize_t length; 669313570Smm size_t len; 670313570Smm const wchar_t *wname; 671313570Smm const wchar_t *prefix; 672313570Smm wchar_t separator; 673313570Smm struct archive_acl_entry *ap; 674313570Smm int id, r, want_type; 675313570Smm wchar_t *wp, *ws; 676313570Smm 677313570Smm want_type = archive_acl_text_want_type(acl, flags); 678313570Smm 679313570Smm /* Both NFSv4 and POSIX.1 types found */ 680313570Smm if (want_type == 0) 681231200Smm return (NULL); 682231200Smm 683313570Smm if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) 684313570Smm flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; 685313570Smm 686313570Smm length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL); 687313570Smm 688313570Smm if (length == 0) 689313570Smm return (NULL); 690313570Smm 691313570Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) 692313570Smm separator = L','; 693313570Smm else 694313570Smm separator = L'\n'; 695313570Smm 696231200Smm /* Now, allocate the string and actually populate it. */ 697313570Smm wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t)); 698313570Smm if (wp == NULL) { 699313570Smm if (errno == ENOMEM) 700313570Smm __archive_errx(1, "No memory"); 701238856Smm return (NULL); 702313570Smm } 703231200Smm count = 0; 704313570Smm 705313570Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 706313570Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 707313570Smm ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, 708231200Smm acl->mode & 0700, -1); 709313570Smm *wp++ = separator; 710313570Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 711313570Smm ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, 712231200Smm acl->mode & 0070, -1); 713313570Smm *wp++ = separator; 714313570Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 715313570Smm ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, 716231200Smm acl->mode & 0007, -1); 717231200Smm count += 3; 718231200Smm } 719231200Smm 720313570Smm for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 721313570Smm if ((ap->type & want_type) == 0) 722313570Smm continue; 723313570Smm /* 724313570Smm * Filemode-mapping ACL entries are stored exclusively in 725313570Smm * ap->mode so they should not be in the list 726313570Smm */ 727313570Smm if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) 728313570Smm && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ 729313570Smm || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ 730313570Smm || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) 731313570Smm continue; 732313570Smm if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT && 733313570Smm (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) 734231200Smm prefix = L"default:"; 735231200Smm else 736231200Smm prefix = NULL; 737313570Smm r = archive_mstring_get_wcs(a, &ap->name, &wname); 738313570Smm if (r == 0) { 739313570Smm if (count > 0) 740313570Smm *wp++ = separator; 741313570Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 742313570Smm id = ap->id; 743313570Smm else 744313570Smm id = -1; 745313570Smm append_entry_w(&wp, prefix, ap->type, ap->tag, flags, 746313570Smm wname, ap->permset, id); 747313570Smm count++; 748342360Smm } else if (r < 0 && errno == ENOMEM) { 749342360Smm free(ws); 750313570Smm return (NULL); 751342360Smm } 752231200Smm } 753231200Smm 754313570Smm /* Add terminating character */ 755313570Smm *wp++ = L'\0'; 756313570Smm 757313570Smm len = wcslen(ws); 758313570Smm 759313570Smm if ((ssize_t)len > (length - 1)) 760313570Smm __archive_errx(1, "Buffer overrun"); 761313570Smm 762313570Smm if (text_len != NULL) 763313570Smm *text_len = len; 764313570Smm 765313570Smm return (ws); 766231200Smm} 767231200Smm 768231200Smmstatic void 769231200Smmappend_id_w(wchar_t **wp, int id) 770231200Smm{ 771231200Smm if (id < 0) 772231200Smm id = 0; 773231200Smm if (id > 9) 774231200Smm append_id_w(wp, id / 10); 775231200Smm *(*wp)++ = L"0123456789"[id % 10]; 776231200Smm} 777231200Smm 778231200Smmstatic void 779313570Smmappend_entry_w(wchar_t **wp, const wchar_t *prefix, int type, 780313570Smm int tag, int flags, const wchar_t *wname, int perm, int id) 781231200Smm{ 782313926Smm int i; 783313926Smm 784231200Smm if (prefix != NULL) { 785231200Smm wcscpy(*wp, prefix); 786231200Smm *wp += wcslen(*wp); 787231200Smm } 788231200Smm switch (tag) { 789231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 790231200Smm wname = NULL; 791231200Smm id = -1; 792313570Smm if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 793313570Smm wcscpy(*wp, L"owner@"); 794313570Smm break; 795313570Smm } 796231200Smm /* FALLTHROUGH */ 797231200Smm case ARCHIVE_ENTRY_ACL_USER: 798231200Smm wcscpy(*wp, L"user"); 799231200Smm break; 800231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 801231200Smm wname = NULL; 802231200Smm id = -1; 803313570Smm if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 804313570Smm wcscpy(*wp, L"group@"); 805313570Smm break; 806313570Smm } 807231200Smm /* FALLTHROUGH */ 808231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 809231200Smm wcscpy(*wp, L"group"); 810231200Smm break; 811231200Smm case ARCHIVE_ENTRY_ACL_MASK: 812231200Smm wcscpy(*wp, L"mask"); 813231200Smm wname = NULL; 814231200Smm id = -1; 815231200Smm break; 816231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 817231200Smm wcscpy(*wp, L"other"); 818231200Smm wname = NULL; 819231200Smm id = -1; 820231200Smm break; 821313570Smm case ARCHIVE_ENTRY_ACL_EVERYONE: 822313570Smm wcscpy(*wp, L"everyone@"); 823313570Smm wname = NULL; 824313570Smm id = -1; 825313570Smm break; 826231200Smm } 827231200Smm *wp += wcslen(*wp); 828231200Smm *(*wp)++ = L':'; 829313570Smm if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || 830313570Smm tag == ARCHIVE_ENTRY_ACL_USER || 831313570Smm tag == ARCHIVE_ENTRY_ACL_GROUP) { 832313570Smm if (wname != NULL) { 833313570Smm wcscpy(*wp, wname); 834313570Smm *wp += wcslen(*wp); 835313570Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER 836313570Smm || tag == ARCHIVE_ENTRY_ACL_GROUP) { 837313570Smm append_id_w(wp, id); 838313570Smm if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) 839313570Smm id = -1; 840313570Smm } 841313570Smm /* Solaris style has no second colon after other and mask */ 842313570Smm if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) 843313570Smm || (tag != ARCHIVE_ENTRY_ACL_OTHER 844313570Smm && tag != ARCHIVE_ENTRY_ACL_MASK)) 845313570Smm *(*wp)++ = L':'; 846313570Smm } 847313570Smm if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { 848313570Smm /* POSIX.1e ACL perms */ 849313570Smm *(*wp)++ = (perm & 0444) ? L'r' : L'-'; 850313570Smm *(*wp)++ = (perm & 0222) ? L'w' : L'-'; 851313570Smm *(*wp)++ = (perm & 0111) ? L'x' : L'-'; 852313570Smm } else { 853313926Smm /* NFSv4 ACL perms */ 854313926Smm for (i = 0; i < nfsv4_acl_perm_map_size; i++) { 855313926Smm if (perm & nfsv4_acl_perm_map[i].perm) 856313926Smm *(*wp)++ = nfsv4_acl_perm_map[i].wc; 857313926Smm else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) 858313926Smm *(*wp)++ = L'-'; 859313926Smm } 860313570Smm *(*wp)++ = L':'; 861313926Smm for (i = 0; i < nfsv4_acl_flag_map_size; i++) { 862313926Smm if (perm & nfsv4_acl_flag_map[i].perm) 863313926Smm *(*wp)++ = nfsv4_acl_flag_map[i].wc; 864313926Smm else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) 865313926Smm *(*wp)++ = L'-'; 866313926Smm } 867313570Smm *(*wp)++ = L':'; 868313570Smm switch (type) { 869313570Smm case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: 870313570Smm wcscpy(*wp, L"allow"); 871313570Smm break; 872313570Smm case ARCHIVE_ENTRY_ACL_TYPE_DENY: 873313570Smm wcscpy(*wp, L"deny"); 874313570Smm break; 875313570Smm case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: 876313570Smm wcscpy(*wp, L"audit"); 877313570Smm break; 878313570Smm case ARCHIVE_ENTRY_ACL_TYPE_ALARM: 879313570Smm wcscpy(*wp, L"alarm"); 880313570Smm break; 881313570Smm default: 882313570Smm break; 883313570Smm } 884231200Smm *wp += wcslen(*wp); 885231200Smm } 886231200Smm if (id != -1) { 887231200Smm *(*wp)++ = L':'; 888231200Smm append_id_w(wp, id); 889231200Smm } 890231200Smm} 891231200Smm 892313570Smm/* 893313570Smm * Generate a text version of the ACL. The flags parameter controls 894313570Smm * the type and style of the generated ACL. 895313570Smm */ 896313570Smmchar * 897313570Smmarchive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, 898231200Smm struct archive_string_conv *sc) 899231200Smm{ 900231200Smm int count; 901313570Smm ssize_t length; 902313570Smm size_t len; 903231200Smm const char *name; 904231200Smm const char *prefix; 905231200Smm char separator; 906231200Smm struct archive_acl_entry *ap; 907313570Smm int id, r, want_type; 908313570Smm char *p, *s; 909231200Smm 910313570Smm want_type = archive_acl_text_want_type(acl, flags); 911231200Smm 912313570Smm /* Both NFSv4 and POSIX.1 types found */ 913313570Smm if (want_type == 0) 914313570Smm return (NULL); 915231200Smm 916313570Smm if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) 917313570Smm flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; 918231200Smm 919313570Smm length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc); 920231200Smm 921313570Smm if (length == 0) 922313570Smm return (NULL); 923313570Smm 924313570Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) 925313570Smm separator = ','; 926313570Smm else 927313570Smm separator = '\n'; 928313570Smm 929231200Smm /* Now, allocate the string and actually populate it. */ 930313570Smm p = s = (char *)malloc(length * sizeof(char)); 931313570Smm if (p == NULL) { 932313570Smm if (errno == ENOMEM) 933313570Smm __archive_errx(1, "No memory"); 934313570Smm return (NULL); 935313570Smm } 936231200Smm count = 0; 937313570Smm 938313570Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 939313570Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 940313570Smm ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, 941231200Smm acl->mode & 0700, -1); 942313570Smm *p++ = separator; 943313570Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 944313570Smm ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, 945231200Smm acl->mode & 0070, -1); 946313570Smm *p++ = separator; 947313570Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 948313570Smm ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, 949231200Smm acl->mode & 0007, -1); 950231200Smm count += 3; 951231200Smm } 952231200Smm 953313570Smm for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 954313570Smm if ((ap->type & want_type) == 0) 955313570Smm continue; 956313570Smm /* 957313570Smm * Filemode-mapping ACL entries are stored exclusively in 958313570Smm * ap->mode so they should not be in the list 959313570Smm */ 960313570Smm if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) 961313570Smm && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ 962313570Smm || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ 963313570Smm || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) 964313570Smm continue; 965313570Smm if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT && 966313570Smm (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0) 967231200Smm prefix = "default:"; 968231200Smm else 969231200Smm prefix = NULL; 970313570Smm r = archive_mstring_get_mbs_l( 971368707Smm NULL, &ap->name, &name, &len, sc); 972342360Smm if (r != 0) { 973342360Smm free(s); 974313570Smm return (NULL); 975342360Smm } 976313570Smm if (count > 0) 977313570Smm *p++ = separator; 978313570Smm if (name == NULL || 979313570Smm (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { 980313570Smm id = ap->id; 981313570Smm } else { 982313570Smm id = -1; 983231200Smm } 984313570Smm append_entry(&p, prefix, ap->type, ap->tag, flags, name, 985313570Smm ap->permset, id); 986313570Smm count++; 987231200Smm } 988231200Smm 989313570Smm /* Add terminating character */ 990313570Smm *p++ = '\0'; 991313570Smm 992313570Smm len = strlen(s); 993313570Smm 994313570Smm if ((ssize_t)len > (length - 1)) 995313570Smm __archive_errx(1, "Buffer overrun"); 996313570Smm 997313570Smm if (text_len != NULL) 998313570Smm *text_len = len; 999313570Smm 1000313570Smm return (s); 1001231200Smm} 1002231200Smm 1003231200Smmstatic void 1004231200Smmappend_id(char **p, int id) 1005231200Smm{ 1006231200Smm if (id < 0) 1007231200Smm id = 0; 1008231200Smm if (id > 9) 1009231200Smm append_id(p, id / 10); 1010231200Smm *(*p)++ = "0123456789"[id % 10]; 1011231200Smm} 1012231200Smm 1013231200Smmstatic void 1014313570Smmappend_entry(char **p, const char *prefix, int type, 1015313570Smm int tag, int flags, const char *name, int perm, int id) 1016231200Smm{ 1017313926Smm int i; 1018313926Smm 1019231200Smm if (prefix != NULL) { 1020231200Smm strcpy(*p, prefix); 1021231200Smm *p += strlen(*p); 1022231200Smm } 1023231200Smm switch (tag) { 1024231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 1025231200Smm name = NULL; 1026231200Smm id = -1; 1027313570Smm if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 1028313570Smm strcpy(*p, "owner@"); 1029313570Smm break; 1030313570Smm } 1031231200Smm /* FALLTHROUGH */ 1032231200Smm case ARCHIVE_ENTRY_ACL_USER: 1033231200Smm strcpy(*p, "user"); 1034231200Smm break; 1035231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1036231200Smm name = NULL; 1037231200Smm id = -1; 1038313570Smm if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { 1039313570Smm strcpy(*p, "group@"); 1040313570Smm break; 1041313570Smm } 1042231200Smm /* FALLTHROUGH */ 1043231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 1044231200Smm strcpy(*p, "group"); 1045231200Smm break; 1046231200Smm case ARCHIVE_ENTRY_ACL_MASK: 1047231200Smm strcpy(*p, "mask"); 1048231200Smm name = NULL; 1049231200Smm id = -1; 1050231200Smm break; 1051231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 1052231200Smm strcpy(*p, "other"); 1053231200Smm name = NULL; 1054231200Smm id = -1; 1055231200Smm break; 1056313570Smm case ARCHIVE_ENTRY_ACL_EVERYONE: 1057313570Smm strcpy(*p, "everyone@"); 1058313570Smm name = NULL; 1059313570Smm id = -1; 1060313570Smm break; 1061231200Smm } 1062231200Smm *p += strlen(*p); 1063231200Smm *(*p)++ = ':'; 1064313570Smm if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || 1065313570Smm tag == ARCHIVE_ENTRY_ACL_USER || 1066313570Smm tag == ARCHIVE_ENTRY_ACL_GROUP) { 1067313570Smm if (name != NULL) { 1068313570Smm strcpy(*p, name); 1069313570Smm *p += strlen(*p); 1070313570Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER 1071313570Smm || tag == ARCHIVE_ENTRY_ACL_GROUP) { 1072313570Smm append_id(p, id); 1073313570Smm if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) 1074313570Smm id = -1; 1075313570Smm } 1076313570Smm /* Solaris style has no second colon after other and mask */ 1077313570Smm if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) 1078313570Smm || (tag != ARCHIVE_ENTRY_ACL_OTHER 1079313570Smm && tag != ARCHIVE_ENTRY_ACL_MASK)) 1080313570Smm *(*p)++ = ':'; 1081313570Smm } 1082313570Smm if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { 1083313570Smm /* POSIX.1e ACL perms */ 1084313570Smm *(*p)++ = (perm & 0444) ? 'r' : '-'; 1085313570Smm *(*p)++ = (perm & 0222) ? 'w' : '-'; 1086313570Smm *(*p)++ = (perm & 0111) ? 'x' : '-'; 1087313570Smm } else { 1088313926Smm /* NFSv4 ACL perms */ 1089313926Smm for (i = 0; i < nfsv4_acl_perm_map_size; i++) { 1090313926Smm if (perm & nfsv4_acl_perm_map[i].perm) 1091313926Smm *(*p)++ = nfsv4_acl_perm_map[i].c; 1092313926Smm else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) 1093313926Smm *(*p)++ = '-'; 1094313926Smm } 1095313570Smm *(*p)++ = ':'; 1096313926Smm for (i = 0; i < nfsv4_acl_flag_map_size; i++) { 1097313926Smm if (perm & nfsv4_acl_flag_map[i].perm) 1098313926Smm *(*p)++ = nfsv4_acl_flag_map[i].c; 1099313926Smm else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) 1100313926Smm *(*p)++ = '-'; 1101313926Smm } 1102313570Smm *(*p)++ = ':'; 1103313570Smm switch (type) { 1104313570Smm case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: 1105313570Smm strcpy(*p, "allow"); 1106313570Smm break; 1107313570Smm case ARCHIVE_ENTRY_ACL_TYPE_DENY: 1108313570Smm strcpy(*p, "deny"); 1109313570Smm break; 1110313570Smm case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: 1111313570Smm strcpy(*p, "audit"); 1112313570Smm break; 1113313570Smm case ARCHIVE_ENTRY_ACL_TYPE_ALARM: 1114313570Smm strcpy(*p, "alarm"); 1115313570Smm break; 1116313570Smm } 1117231200Smm *p += strlen(*p); 1118231200Smm } 1119231200Smm if (id != -1) { 1120231200Smm *(*p)++ = ':'; 1121231200Smm append_id(p, id); 1122231200Smm } 1123231200Smm} 1124231200Smm 1125231200Smm/* 1126313570Smm * Parse a wide ACL text string. 1127313570Smm * 1128313570Smm * The want_type argument may be one of the following: 1129313570Smm * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS 1130313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT 1131313570Smm * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL 1132313570Smm * 1133313570Smm * POSIX.1e ACL entries prefixed with "default:" are treated as 1134313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 1135231200Smm */ 1136231200Smmint 1137313570Smmarchive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, 1138313570Smm int want_type) 1139231200Smm{ 1140231200Smm struct { 1141231200Smm const wchar_t *start; 1142231200Smm const wchar_t *end; 1143313570Smm } field[6], name; 1144231200Smm 1145313570Smm const wchar_t *s, *st; 1146313570Smm 1147313570Smm int numfields, fields, n, r, sol, ret; 1148313570Smm int type, types, tag, permset, id; 1149313570Smm size_t len; 1150231200Smm wchar_t sep; 1151231200Smm 1152313570Smm ret = ARCHIVE_OK; 1153313570Smm types = 0; 1154313570Smm 1155313570Smm switch (want_type) { 1156313570Smm case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: 1157313570Smm want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 1158328827Smm __LA_FALLTHROUGH; 1159313570Smm case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 1160313570Smm case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 1161313570Smm numfields = 5; 1162313570Smm break; 1163313570Smm case ARCHIVE_ENTRY_ACL_TYPE_NFS4: 1164313570Smm numfields = 6; 1165313570Smm break; 1166313570Smm default: 1167313570Smm return (ARCHIVE_FATAL); 1168313570Smm } 1169313570Smm 1170313570Smm while (text != NULL && *text != L'\0') { 1171231200Smm /* 1172231200Smm * Parse the fields out of the next entry, 1173231200Smm * advance 'text' to start of next entry. 1174231200Smm */ 1175231200Smm fields = 0; 1176231200Smm do { 1177231200Smm const wchar_t *start, *end; 1178231200Smm next_field_w(&text, &start, &end, &sep); 1179313570Smm if (fields < numfields) { 1180231200Smm field[fields].start = start; 1181231200Smm field[fields].end = end; 1182231200Smm } 1183231200Smm ++fields; 1184231200Smm } while (sep == L':'); 1185231200Smm 1186231200Smm /* Set remaining fields to blank. */ 1187313570Smm for (n = fields; n < numfields; ++n) 1188231200Smm field[n].start = field[n].end = NULL; 1189231200Smm 1190313570Smm if (field[0].start != NULL && *(field[0].start) == L'#') { 1191313570Smm /* Comment, skip entry */ 1192313570Smm continue; 1193313570Smm } 1194313570Smm 1195313570Smm n = 0; 1196313570Smm sol = 0; 1197231200Smm id = -1; 1198313570Smm permset = 0; 1199313570Smm name.start = name.end = NULL; 1200231200Smm 1201313570Smm if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 1202313570Smm /* POSIX.1e ACLs */ 1203313570Smm /* 1204313570Smm * Default keyword "default:user::rwx" 1205313570Smm * if found, we have one more field 1206313570Smm * 1207313570Smm * We also support old Solaris extension: 1208313570Smm * "defaultuser::rwx" is the default ACL corresponding 1209313570Smm * to "user::rwx", etc. valid only for first field 1210313570Smm */ 1211313570Smm s = field[0].start; 1212313570Smm len = field[0].end - field[0].start; 1213313570Smm if (*s == L'd' && (len == 1 || (len >= 7 1214313570Smm && wmemcmp((s + 1), L"efault", 6) == 0))) { 1215313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1216313570Smm if (len > 7) 1217313570Smm field[0].start += 7; 1218313570Smm else 1219313570Smm n = 1; 1220313570Smm } else 1221313570Smm type = want_type; 1222231200Smm 1223313570Smm /* Check for a numeric ID in field n+1 or n+3. */ 1224313570Smm isint_w(field[n + 1].start, field[n + 1].end, &id); 1225313570Smm /* Field n+3 is optional. */ 1226313570Smm if (id == -1 && fields > n+3) 1227313570Smm isint_w(field[n + 3].start, field[n + 3].end, 1228313570Smm &id); 1229313570Smm 1230313570Smm tag = 0; 1231313570Smm s = field[n].start; 1232313570Smm st = field[n].start + 1; 1233313570Smm len = field[n].end - field[n].start; 1234313570Smm 1235313570Smm switch (*s) { 1236313570Smm case L'u': 1237313570Smm if (len == 1 || (len == 4 1238313570Smm && wmemcmp(st, L"ser", 3) == 0)) 1239313570Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1240313570Smm break; 1241313570Smm case L'g': 1242313570Smm if (len == 1 || (len == 5 1243313570Smm && wmemcmp(st, L"roup", 4) == 0)) 1244313570Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1245313570Smm break; 1246313570Smm case L'o': 1247313570Smm if (len == 1 || (len == 5 1248313570Smm && wmemcmp(st, L"ther", 4) == 0)) 1249313570Smm tag = ARCHIVE_ENTRY_ACL_OTHER; 1250313570Smm break; 1251313570Smm case L'm': 1252313570Smm if (len == 1 || (len == 4 1253313570Smm && wmemcmp(st, L"ask", 3) == 0)) 1254313570Smm tag = ARCHIVE_ENTRY_ACL_MASK; 1255313570Smm break; 1256313570Smm default: 1257313570Smm break; 1258313570Smm } 1259313570Smm 1260313570Smm switch (tag) { 1261313570Smm case ARCHIVE_ENTRY_ACL_OTHER: 1262313570Smm case ARCHIVE_ENTRY_ACL_MASK: 1263313570Smm if (fields == (n + 2) 1264313570Smm && field[n + 1].start < field[n + 1].end 1265313570Smm && ismode_w(field[n + 1].start, 1266313570Smm field[n + 1].end, &permset)) { 1267313570Smm /* This is Solaris-style "other:rwx" */ 1268313570Smm sol = 1; 1269313570Smm } else if (fields == (n + 3) && 1270313570Smm field[n + 1].start < field[n + 1].end) { 1271313570Smm /* Invalid mask or other field */ 1272313570Smm ret = ARCHIVE_WARN; 1273313570Smm continue; 1274313570Smm } 1275313570Smm break; 1276313570Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 1277313570Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1278313570Smm if (id != -1 || 1279313570Smm field[n + 1].start < field[n + 1].end) { 1280313570Smm name = field[n + 1]; 1281313570Smm if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) 1282313570Smm tag = ARCHIVE_ENTRY_ACL_USER; 1283313570Smm else 1284313570Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 1285313570Smm } 1286313570Smm break; 1287313570Smm default: 1288313570Smm /* Invalid tag, skip entry */ 1289313570Smm ret = ARCHIVE_WARN; 1290313570Smm continue; 1291313570Smm } 1292313570Smm 1293313570Smm /* 1294313570Smm * Without "default:" we expect mode in field 2 1295313570Smm * Exception: Solaris other and mask fields 1296313570Smm */ 1297313570Smm if (permset == 0 && !ismode_w(field[n + 2 - sol].start, 1298313570Smm field[n + 2 - sol].end, &permset)) { 1299313570Smm /* Invalid mode, skip entry */ 1300313570Smm ret = ARCHIVE_WARN; 1301313570Smm continue; 1302313570Smm } 1303313570Smm } else { 1304313570Smm /* NFS4 ACLs */ 1305313570Smm s = field[0].start; 1306313570Smm len = field[0].end - field[0].start; 1307313570Smm tag = 0; 1308313570Smm 1309313570Smm switch (len) { 1310313570Smm case 4: 1311313570Smm if (wmemcmp(s, L"user", 4) == 0) 1312313570Smm tag = ARCHIVE_ENTRY_ACL_USER; 1313313570Smm break; 1314313570Smm case 5: 1315313570Smm if (wmemcmp(s, L"group", 5) == 0) 1316313570Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 1317313570Smm break; 1318313570Smm case 6: 1319313570Smm if (wmemcmp(s, L"owner@", 6) == 0) 1320313570Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1321313570Smm else if (wmemcmp(s, L"group@", len) == 0) 1322313570Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1323313570Smm break; 1324313570Smm case 9: 1325313570Smm if (wmemcmp(s, L"everyone@", 9) == 0) 1326313570Smm tag = ARCHIVE_ENTRY_ACL_EVERYONE; 1327313570Smm default: 1328313570Smm break; 1329313570Smm } 1330313570Smm 1331313570Smm if (tag == 0) { 1332313570Smm /* Invalid tag, skip entry */ 1333313570Smm ret = ARCHIVE_WARN; 1334313570Smm continue; 1335313570Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER || 1336313570Smm tag == ARCHIVE_ENTRY_ACL_GROUP) { 1337313570Smm n = 1; 1338231200Smm name = field[1]; 1339313570Smm isint_w(name.start, name.end, &id); 1340231200Smm } else 1341313570Smm n = 0; 1342231200Smm 1343313570Smm if (!is_nfs4_perms_w(field[1 + n].start, 1344313570Smm field[1 + n].end, &permset)) { 1345313570Smm /* Invalid NFSv4 perms, skip entry */ 1346313570Smm ret = ARCHIVE_WARN; 1347313570Smm continue; 1348313570Smm } 1349313570Smm if (!is_nfs4_flags_w(field[2 + n].start, 1350313570Smm field[2 + n].end, &permset)) { 1351313570Smm /* Invalid NFSv4 flags, skip entry */ 1352313570Smm ret = ARCHIVE_WARN; 1353313570Smm continue; 1354313570Smm } 1355313570Smm s = field[3 + n].start; 1356313570Smm len = field[3 + n].end - field[3 + n].start; 1357313570Smm type = 0; 1358313570Smm if (len == 4) { 1359313570Smm if (wmemcmp(s, L"deny", 4) == 0) 1360313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 1361313570Smm } else if (len == 5) { 1362313570Smm if (wmemcmp(s, L"allow", 5) == 0) 1363313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 1364313570Smm else if (wmemcmp(s, L"audit", 5) == 0) 1365313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 1366313570Smm else if (wmemcmp(s, L"alarm", 5) == 0) 1367313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 1368313570Smm } 1369313570Smm if (type == 0) { 1370313570Smm /* Invalid entry type, skip entry */ 1371313570Smm ret = ARCHIVE_WARN; 1372313570Smm continue; 1373313570Smm } 1374313570Smm isint_w(field[4 + n].start, field[4 + n].end, &id); 1375313570Smm } 1376313570Smm 1377231200Smm /* Add entry to the internal list. */ 1378313570Smm r = archive_acl_add_entry_w_len(acl, type, permset, 1379231200Smm tag, id, name.start, name.end - name.start); 1380313570Smm if (r < ARCHIVE_WARN) 1381313570Smm return (r); 1382313570Smm if (r != ARCHIVE_OK) 1383313570Smm ret = ARCHIVE_WARN; 1384313570Smm types |= type; 1385231200Smm } 1386313570Smm 1387313570Smm /* Reset ACL */ 1388313570Smm archive_acl_reset(acl, types); 1389313570Smm 1390313570Smm return (ret); 1391231200Smm} 1392231200Smm 1393231200Smm/* 1394231200Smm * Parse a string to a positive decimal integer. Returns true if 1395231200Smm * the string is non-empty and consists only of decimal digits, 1396231200Smm * false otherwise. 1397231200Smm */ 1398231200Smmstatic int 1399231200Smmisint_w(const wchar_t *start, const wchar_t *end, int *result) 1400231200Smm{ 1401231200Smm int n = 0; 1402231200Smm if (start >= end) 1403231200Smm return (0); 1404231200Smm while (start < end) { 1405368707Smm if (*start < L'0' || *start > L'9') 1406231200Smm return (0); 1407231200Smm if (n > (INT_MAX / 10) || 1408368707Smm (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) { 1409231200Smm n = INT_MAX; 1410231200Smm } else { 1411231200Smm n *= 10; 1412368707Smm n += *start - L'0'; 1413231200Smm } 1414231200Smm start++; 1415231200Smm } 1416231200Smm *result = n; 1417231200Smm return (1); 1418231200Smm} 1419231200Smm 1420231200Smm/* 1421231200Smm * Parse a string as a mode field. Returns true if 1422231200Smm * the string is non-empty and consists only of mode characters, 1423231200Smm * false otherwise. 1424231200Smm */ 1425231200Smmstatic int 1426231200Smmismode_w(const wchar_t *start, const wchar_t *end, int *permset) 1427231200Smm{ 1428231200Smm const wchar_t *p; 1429231200Smm 1430231200Smm if (start >= end) 1431231200Smm return (0); 1432231200Smm p = start; 1433231200Smm *permset = 0; 1434231200Smm while (p < end) { 1435231200Smm switch (*p++) { 1436313570Smm case L'r': case L'R': 1437231200Smm *permset |= ARCHIVE_ENTRY_ACL_READ; 1438231200Smm break; 1439313570Smm case L'w': case L'W': 1440231200Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1441231200Smm break; 1442313570Smm case L'x': case L'X': 1443231200Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1444231200Smm break; 1445313570Smm case L'-': 1446231200Smm break; 1447231200Smm default: 1448231200Smm return (0); 1449231200Smm } 1450231200Smm } 1451231200Smm return (1); 1452231200Smm} 1453231200Smm 1454231200Smm/* 1455313570Smm * Parse a string as a NFS4 ACL permission field. 1456313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL 1457313570Smm * permission characters, false otherwise 1458313570Smm */ 1459313570Smmstatic int 1460313570Smmis_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset) 1461313570Smm{ 1462313926Smm const wchar_t *p = start; 1463313570Smm 1464313570Smm while (p < end) { 1465313570Smm switch (*p++) { 1466313570Smm case L'r': 1467313570Smm *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; 1468313570Smm break; 1469313570Smm case L'w': 1470313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; 1471313570Smm break; 1472313570Smm case L'x': 1473313570Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1474313570Smm break; 1475313570Smm case L'p': 1476313570Smm *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; 1477313570Smm break; 1478313570Smm case L'D': 1479313570Smm *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; 1480313570Smm break; 1481313570Smm case L'd': 1482313570Smm *permset |= ARCHIVE_ENTRY_ACL_DELETE; 1483313570Smm break; 1484313570Smm case L'a': 1485313570Smm *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; 1486313570Smm break; 1487313570Smm case L'A': 1488313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; 1489313570Smm break; 1490313570Smm case L'R': 1491313570Smm *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; 1492313570Smm break; 1493313570Smm case L'W': 1494313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; 1495313570Smm break; 1496313570Smm case L'c': 1497313570Smm *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; 1498313570Smm break; 1499313570Smm case L'C': 1500313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; 1501313570Smm break; 1502313570Smm case L'o': 1503313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; 1504313570Smm break; 1505313570Smm case L's': 1506313570Smm *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; 1507313570Smm break; 1508313570Smm case L'-': 1509313570Smm break; 1510313570Smm default: 1511313570Smm return(0); 1512313570Smm } 1513313570Smm } 1514313570Smm return (1); 1515313570Smm} 1516313570Smm 1517313570Smm/* 1518313570Smm * Parse a string as a NFS4 ACL flags field. 1519313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL 1520313570Smm * flag characters, false otherwise 1521313570Smm */ 1522313570Smmstatic int 1523313570Smmis_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset) 1524313570Smm{ 1525313926Smm const wchar_t *p = start; 1526313570Smm 1527313570Smm while (p < end) { 1528313570Smm switch(*p++) { 1529313570Smm case L'f': 1530313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; 1531313570Smm break; 1532313570Smm case L'd': 1533313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; 1534313570Smm break; 1535313570Smm case L'i': 1536313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; 1537313570Smm break; 1538313570Smm case L'n': 1539313570Smm *permset |= 1540313570Smm ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; 1541313570Smm break; 1542313570Smm case L'S': 1543313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; 1544313570Smm break; 1545313570Smm case L'F': 1546313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; 1547313570Smm break; 1548313570Smm case L'I': 1549313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; 1550313570Smm break; 1551313570Smm case L'-': 1552313570Smm break; 1553313570Smm default: 1554313570Smm return (0); 1555313570Smm } 1556313570Smm } 1557313570Smm return (1); 1558313570Smm} 1559313570Smm 1560313570Smm/* 1561231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1562231200Smm * to point to just after the separator. *start points to the first 1563231200Smm * character of the matched text and *end just after the last 1564231200Smm * character of the matched identifier. In particular *end - *start 1565231200Smm * is the length of the field body, not including leading or trailing 1566231200Smm * whitespace. 1567231200Smm */ 1568231200Smmstatic void 1569231200Smmnext_field_w(const wchar_t **wp, const wchar_t **start, 1570231200Smm const wchar_t **end, wchar_t *sep) 1571231200Smm{ 1572231200Smm /* Skip leading whitespace to find start of field. */ 1573231200Smm while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { 1574231200Smm (*wp)++; 1575231200Smm } 1576231200Smm *start = *wp; 1577231200Smm 1578231200Smm /* Scan for the separator. */ 1579231200Smm while (**wp != L'\0' && **wp != L',' && **wp != L':' && 1580342360Smm **wp != L'\n' && **wp != L'#') { 1581231200Smm (*wp)++; 1582231200Smm } 1583231200Smm *sep = **wp; 1584231200Smm 1585342360Smm /* Locate end of field, trim trailing whitespace if necessary */ 1586342360Smm if (*wp == *start) { 1587342360Smm *end = *wp; 1588342360Smm } else { 1589342360Smm *end = *wp - 1; 1590342360Smm while (**end == L' ' || **end == L'\t' || **end == L'\n') { 1591342360Smm (*end)--; 1592342360Smm } 1593342360Smm (*end)++; 1594231200Smm } 1595231200Smm 1596342360Smm /* Handle in-field comments */ 1597342360Smm if (*sep == L'#') { 1598342360Smm while (**wp != L'\0' && **wp != L',' && **wp != L'\n') { 1599342360Smm (*wp)++; 1600342360Smm } 1601342360Smm *sep = **wp; 1602342360Smm } 1603342360Smm 1604231200Smm /* Adjust scanner location. */ 1605231200Smm if (**wp != L'\0') 1606231200Smm (*wp)++; 1607231200Smm} 1608231200Smm 1609231200Smm/* 1610313570Smm * Parse an ACL text string. 1611313570Smm * 1612313570Smm * The want_type argument may be one of the following: 1613313570Smm * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS 1614313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT 1615313570Smm * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL 1616313570Smm * 1617313570Smm * POSIX.1e ACL entries prefixed with "default:" are treated as 1618313570Smm * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4 1619231200Smm */ 1620231200Smmint 1621313570Smmarchive_acl_from_text_l(struct archive_acl *acl, const char *text, 1622313570Smm int want_type, struct archive_string_conv *sc) 1623231200Smm{ 1624231200Smm struct { 1625231200Smm const char *start; 1626231200Smm const char *end; 1627313570Smm } field[6], name; 1628231200Smm 1629313570Smm const char *s, *st; 1630313570Smm int numfields, fields, n, r, sol, ret; 1631313570Smm int type, types, tag, permset, id; 1632313570Smm size_t len; 1633231200Smm char sep; 1634231200Smm 1635313570Smm switch (want_type) { 1636313570Smm case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: 1637313570Smm want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 1638328827Smm __LA_FALLTHROUGH; 1639313570Smm case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 1640313570Smm case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 1641313570Smm numfields = 5; 1642313570Smm break; 1643313570Smm case ARCHIVE_ENTRY_ACL_TYPE_NFS4: 1644313570Smm numfields = 6; 1645313570Smm break; 1646313570Smm default: 1647313570Smm return (ARCHIVE_FATAL); 1648313570Smm } 1649313570Smm 1650313570Smm ret = ARCHIVE_OK; 1651313570Smm types = 0; 1652313570Smm 1653342360Smm while (text != NULL && *text != '\0') { 1654231200Smm /* 1655231200Smm * Parse the fields out of the next entry, 1656231200Smm * advance 'text' to start of next entry. 1657231200Smm */ 1658231200Smm fields = 0; 1659231200Smm do { 1660231200Smm const char *start, *end; 1661231200Smm next_field(&text, &start, &end, &sep); 1662313570Smm if (fields < numfields) { 1663231200Smm field[fields].start = start; 1664231200Smm field[fields].end = end; 1665231200Smm } 1666231200Smm ++fields; 1667231200Smm } while (sep == ':'); 1668231200Smm 1669231200Smm /* Set remaining fields to blank. */ 1670313570Smm for (n = fields; n < numfields; ++n) 1671231200Smm field[n].start = field[n].end = NULL; 1672231200Smm 1673313570Smm if (field[0].start != NULL && *(field[0].start) == '#') { 1674313570Smm /* Comment, skip entry */ 1675313570Smm continue; 1676313570Smm } 1677313570Smm 1678313570Smm n = 0; 1679313570Smm sol = 0; 1680231200Smm id = -1; 1681313570Smm permset = 0; 1682313570Smm name.start = name.end = NULL; 1683231200Smm 1684313570Smm if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 1685313570Smm /* POSIX.1e ACLs */ 1686313570Smm /* 1687313570Smm * Default keyword "default:user::rwx" 1688313570Smm * if found, we have one more field 1689313570Smm * 1690313570Smm * We also support old Solaris extension: 1691313570Smm * "defaultuser::rwx" is the default ACL corresponding 1692313570Smm * to "user::rwx", etc. valid only for first field 1693313570Smm */ 1694313570Smm s = field[0].start; 1695313570Smm len = field[0].end - field[0].start; 1696313570Smm if (*s == 'd' && (len == 1 || (len >= 7 1697313570Smm && memcmp((s + 1), "efault", 6) == 0))) { 1698313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1699313570Smm if (len > 7) 1700313570Smm field[0].start += 7; 1701313570Smm else 1702313570Smm n = 1; 1703313570Smm } else 1704313570Smm type = want_type; 1705231200Smm 1706313570Smm /* Check for a numeric ID in field n+1 or n+3. */ 1707313570Smm isint(field[n + 1].start, field[n + 1].end, &id); 1708313570Smm /* Field n+3 is optional. */ 1709313570Smm if (id == -1 && fields > (n + 3)) 1710313570Smm isint(field[n + 3].start, field[n + 3].end, 1711313570Smm &id); 1712313570Smm 1713313570Smm tag = 0; 1714313570Smm s = field[n].start; 1715313570Smm st = field[n].start + 1; 1716313570Smm len = field[n].end - field[n].start; 1717313570Smm 1718342360Smm if (len == 0) { 1719342360Smm ret = ARCHIVE_WARN; 1720342360Smm continue; 1721342360Smm } 1722342360Smm 1723313570Smm switch (*s) { 1724313570Smm case 'u': 1725313570Smm if (len == 1 || (len == 4 1726313570Smm && memcmp(st, "ser", 3) == 0)) 1727313570Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1728313570Smm break; 1729313570Smm case 'g': 1730313570Smm if (len == 1 || (len == 5 1731313570Smm && memcmp(st, "roup", 4) == 0)) 1732313570Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1733313570Smm break; 1734313570Smm case 'o': 1735313570Smm if (len == 1 || (len == 5 1736313570Smm && memcmp(st, "ther", 4) == 0)) 1737313570Smm tag = ARCHIVE_ENTRY_ACL_OTHER; 1738313570Smm break; 1739313570Smm case 'm': 1740313570Smm if (len == 1 || (len == 4 1741313570Smm && memcmp(st, "ask", 3) == 0)) 1742313570Smm tag = ARCHIVE_ENTRY_ACL_MASK; 1743313570Smm break; 1744313570Smm default: 1745313570Smm break; 1746313570Smm } 1747313570Smm 1748313570Smm switch (tag) { 1749313570Smm case ARCHIVE_ENTRY_ACL_OTHER: 1750313570Smm case ARCHIVE_ENTRY_ACL_MASK: 1751313570Smm if (fields == (n + 2) 1752313570Smm && field[n + 1].start < field[n + 1].end 1753313570Smm && ismode(field[n + 1].start, 1754313570Smm field[n + 1].end, &permset)) { 1755313570Smm /* This is Solaris-style "other:rwx" */ 1756313570Smm sol = 1; 1757313570Smm } else if (fields == (n + 3) && 1758313570Smm field[n + 1].start < field[n + 1].end) { 1759313570Smm /* Invalid mask or other field */ 1760313570Smm ret = ARCHIVE_WARN; 1761313570Smm continue; 1762313570Smm } 1763313570Smm break; 1764313570Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 1765313570Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 1766313570Smm if (id != -1 || 1767313570Smm field[n + 1].start < field[n + 1].end) { 1768313570Smm name = field[n + 1]; 1769313570Smm if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) 1770313570Smm tag = ARCHIVE_ENTRY_ACL_USER; 1771313570Smm else 1772313570Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 1773313570Smm } 1774313570Smm break; 1775313570Smm default: 1776313570Smm /* Invalid tag, skip entry */ 1777313570Smm ret = ARCHIVE_WARN; 1778313570Smm continue; 1779313570Smm } 1780313570Smm 1781313570Smm /* 1782313570Smm * Without "default:" we expect mode in field 3 1783313570Smm * Exception: Solaris other and mask fields 1784313570Smm */ 1785313570Smm if (permset == 0 && !ismode(field[n + 2 - sol].start, 1786313570Smm field[n + 2 - sol].end, &permset)) { 1787313570Smm /* Invalid mode, skip entry */ 1788313570Smm ret = ARCHIVE_WARN; 1789313570Smm continue; 1790313570Smm } 1791313570Smm } else { 1792313570Smm /* NFS4 ACLs */ 1793313570Smm s = field[0].start; 1794313570Smm len = field[0].end - field[0].start; 1795313570Smm tag = 0; 1796313570Smm 1797313570Smm switch (len) { 1798313570Smm case 4: 1799313570Smm if (memcmp(s, "user", 4) == 0) 1800313570Smm tag = ARCHIVE_ENTRY_ACL_USER; 1801313570Smm break; 1802313570Smm case 5: 1803313570Smm if (memcmp(s, "group", 5) == 0) 1804313570Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 1805313570Smm break; 1806313570Smm case 6: 1807313570Smm if (memcmp(s, "owner@", 6) == 0) 1808313570Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1809313570Smm else if (memcmp(s, "group@", 6) == 0) 1810313570Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1811313570Smm break; 1812313570Smm case 9: 1813313570Smm if (memcmp(s, "everyone@", 9) == 0) 1814313570Smm tag = ARCHIVE_ENTRY_ACL_EVERYONE; 1815313570Smm break; 1816313570Smm default: 1817313570Smm break; 1818313570Smm } 1819313570Smm 1820313570Smm if (tag == 0) { 1821313570Smm /* Invalid tag, skip entry */ 1822313570Smm ret = ARCHIVE_WARN; 1823313570Smm continue; 1824313570Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER || 1825313570Smm tag == ARCHIVE_ENTRY_ACL_GROUP) { 1826313570Smm n = 1; 1827231200Smm name = field[1]; 1828313570Smm isint(name.start, name.end, &id); 1829231200Smm } else 1830313570Smm n = 0; 1831231200Smm 1832313570Smm if (!is_nfs4_perms(field[1 + n].start, 1833313570Smm field[1 + n].end, &permset)) { 1834313570Smm /* Invalid NFSv4 perms, skip entry */ 1835313570Smm ret = ARCHIVE_WARN; 1836313570Smm continue; 1837313570Smm } 1838313570Smm if (!is_nfs4_flags(field[2 + n].start, 1839313570Smm field[2 + n].end, &permset)) { 1840313570Smm /* Invalid NFSv4 flags, skip entry */ 1841313570Smm ret = ARCHIVE_WARN; 1842313570Smm continue; 1843313570Smm } 1844313570Smm s = field[3 + n].start; 1845313570Smm len = field[3 + n].end - field[3 + n].start; 1846313570Smm type = 0; 1847313570Smm if (len == 4) { 1848313570Smm if (memcmp(s, "deny", 4) == 0) 1849313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 1850313570Smm } else if (len == 5) { 1851313570Smm if (memcmp(s, "allow", 5) == 0) 1852313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 1853313570Smm else if (memcmp(s, "audit", 5) == 0) 1854313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 1855313570Smm else if (memcmp(s, "alarm", 5) == 0) 1856313570Smm type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 1857313570Smm } 1858313570Smm if (type == 0) { 1859313570Smm /* Invalid entry type, skip entry */ 1860313570Smm ret = ARCHIVE_WARN; 1861313570Smm continue; 1862313570Smm } 1863313570Smm isint(field[4 + n].start, field[4 + n].end, 1864313570Smm &id); 1865313570Smm } 1866313570Smm 1867231200Smm /* Add entry to the internal list. */ 1868231200Smm r = archive_acl_add_entry_len_l(acl, type, permset, 1869231200Smm tag, id, name.start, name.end - name.start, sc); 1870231200Smm if (r < ARCHIVE_WARN) 1871231200Smm return (r); 1872231200Smm if (r != ARCHIVE_OK) 1873231200Smm ret = ARCHIVE_WARN; 1874313570Smm types |= type; 1875231200Smm } 1876313570Smm 1877313570Smm /* Reset ACL */ 1878313570Smm archive_acl_reset(acl, types); 1879313570Smm 1880231200Smm return (ret); 1881231200Smm} 1882231200Smm 1883231200Smm/* 1884231200Smm * Parse a string to a positive decimal integer. Returns true if 1885231200Smm * the string is non-empty and consists only of decimal digits, 1886231200Smm * false otherwise. 1887231200Smm */ 1888231200Smmstatic int 1889231200Smmisint(const char *start, const char *end, int *result) 1890231200Smm{ 1891231200Smm int n = 0; 1892231200Smm if (start >= end) 1893231200Smm return (0); 1894231200Smm while (start < end) { 1895231200Smm if (*start < '0' || *start > '9') 1896231200Smm return (0); 1897231200Smm if (n > (INT_MAX / 10) || 1898231200Smm (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1899231200Smm n = INT_MAX; 1900231200Smm } else { 1901231200Smm n *= 10; 1902231200Smm n += *start - '0'; 1903231200Smm } 1904231200Smm start++; 1905231200Smm } 1906231200Smm *result = n; 1907231200Smm return (1); 1908231200Smm} 1909231200Smm 1910231200Smm/* 1911231200Smm * Parse a string as a mode field. Returns true if 1912231200Smm * the string is non-empty and consists only of mode characters, 1913231200Smm * false otherwise. 1914231200Smm */ 1915231200Smmstatic int 1916231200Smmismode(const char *start, const char *end, int *permset) 1917231200Smm{ 1918231200Smm const char *p; 1919231200Smm 1920231200Smm if (start >= end) 1921231200Smm return (0); 1922231200Smm p = start; 1923231200Smm *permset = 0; 1924231200Smm while (p < end) { 1925231200Smm switch (*p++) { 1926231200Smm case 'r': case 'R': 1927231200Smm *permset |= ARCHIVE_ENTRY_ACL_READ; 1928231200Smm break; 1929231200Smm case 'w': case 'W': 1930231200Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1931231200Smm break; 1932231200Smm case 'x': case 'X': 1933231200Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1934231200Smm break; 1935231200Smm case '-': 1936231200Smm break; 1937231200Smm default: 1938231200Smm return (0); 1939231200Smm } 1940231200Smm } 1941231200Smm return (1); 1942231200Smm} 1943231200Smm 1944231200Smm/* 1945313570Smm * Parse a string as a NFS4 ACL permission field. 1946313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL 1947313570Smm * permission characters, false otherwise 1948313570Smm */ 1949313570Smmstatic int 1950313570Smmis_nfs4_perms(const char *start, const char *end, int *permset) 1951313570Smm{ 1952313926Smm const char *p = start; 1953313570Smm 1954313570Smm while (p < end) { 1955313570Smm switch (*p++) { 1956313570Smm case 'r': 1957313570Smm *permset |= ARCHIVE_ENTRY_ACL_READ_DATA; 1958313570Smm break; 1959313570Smm case 'w': 1960313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA; 1961313570Smm break; 1962313570Smm case 'x': 1963313570Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1964313570Smm break; 1965313570Smm case 'p': 1966313570Smm *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA; 1967313570Smm break; 1968313570Smm case 'D': 1969313570Smm *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD; 1970313570Smm break; 1971313570Smm case 'd': 1972313570Smm *permset |= ARCHIVE_ENTRY_ACL_DELETE; 1973313570Smm break; 1974313570Smm case 'a': 1975313570Smm *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES; 1976313570Smm break; 1977313570Smm case 'A': 1978313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES; 1979313570Smm break; 1980313570Smm case 'R': 1981313570Smm *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS; 1982313570Smm break; 1983313570Smm case 'W': 1984313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS; 1985313570Smm break; 1986313570Smm case 'c': 1987313570Smm *permset |= ARCHIVE_ENTRY_ACL_READ_ACL; 1988313570Smm break; 1989313570Smm case 'C': 1990313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL; 1991313570Smm break; 1992313570Smm case 'o': 1993313570Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER; 1994313570Smm break; 1995313570Smm case 's': 1996313570Smm *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE; 1997313570Smm break; 1998313570Smm case '-': 1999313570Smm break; 2000313570Smm default: 2001313570Smm return(0); 2002313570Smm } 2003313570Smm } 2004313570Smm return (1); 2005313570Smm} 2006313570Smm 2007313570Smm/* 2008313570Smm * Parse a string as a NFS4 ACL flags field. 2009313570Smm * Returns true if the string is non-empty and consists only of NFS4 ACL 2010313570Smm * flag characters, false otherwise 2011313570Smm */ 2012313570Smmstatic int 2013313570Smmis_nfs4_flags(const char *start, const char *end, int *permset) 2014313570Smm{ 2015313926Smm const char *p = start; 2016313570Smm 2017313570Smm while (p < end) { 2018313570Smm switch(*p++) { 2019313570Smm case 'f': 2020313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT; 2021313570Smm break; 2022313570Smm case 'd': 2023313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT; 2024313570Smm break; 2025313570Smm case 'i': 2026313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY; 2027313570Smm break; 2028313570Smm case 'n': 2029313570Smm *permset |= 2030313570Smm ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT; 2031313570Smm break; 2032313570Smm case 'S': 2033313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS; 2034313570Smm break; 2035313570Smm case 'F': 2036313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS; 2037313570Smm break; 2038313570Smm case 'I': 2039313570Smm *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED; 2040313570Smm break; 2041313570Smm case '-': 2042313570Smm break; 2043313570Smm default: 2044313570Smm return (0); 2045313570Smm } 2046313570Smm } 2047313570Smm return (1); 2048313570Smm} 2049313570Smm 2050313570Smm/* 2051231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 2052231200Smm * to point to just after the separator. *start points to the first 2053231200Smm * character of the matched text and *end just after the last 2054231200Smm * character of the matched identifier. In particular *end - *start 2055231200Smm * is the length of the field body, not including leading or trailing 2056231200Smm * whitespace. 2057231200Smm */ 2058231200Smmstatic void 2059231200Smmnext_field(const char **p, const char **start, 2060231200Smm const char **end, char *sep) 2061231200Smm{ 2062231200Smm /* Skip leading whitespace to find start of field. */ 2063231200Smm while (**p == ' ' || **p == '\t' || **p == '\n') { 2064231200Smm (*p)++; 2065231200Smm } 2066231200Smm *start = *p; 2067231200Smm 2068231200Smm /* Scan for the separator. */ 2069342360Smm while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' && 2070342360Smm **p != '#') { 2071231200Smm (*p)++; 2072231200Smm } 2073231200Smm *sep = **p; 2074231200Smm 2075342360Smm /* Locate end of field, trim trailing whitespace if necessary */ 2076342360Smm if (*p == *start) { 2077339005Smm *end = *p; 2078342360Smm } else { 2079342360Smm *end = *p - 1; 2080342360Smm while (**end == ' ' || **end == '\t' || **end == '\n') { 2081342360Smm (*end)--; 2082342360Smm } 2083342360Smm (*end)++; 2084339005Smm } 2085339005Smm 2086342360Smm /* Handle in-field comments */ 2087342360Smm if (*sep == '#') { 2088342360Smm while (**p != '\0' && **p != ',' && **p != '\n') { 2089342360Smm (*p)++; 2090342360Smm } 2091342360Smm *sep = **p; 2092231200Smm } 2093231200Smm 2094231200Smm /* Adjust scanner location. */ 2095231200Smm if (**p != '\0') 2096231200Smm (*p)++; 2097231200Smm} 2098