1231200Smm/*- 2231200Smm * Copyright (c) 2003-2010 Tim Kientzle 3231200Smm * All rights reserved. 4231200Smm * 5231200Smm * Redistribution and use in source and binary forms, with or without 6231200Smm * modification, are permitted provided that the following conditions 7231200Smm * are met: 8231200Smm * 1. Redistributions of source code must retain the above copyright 9231200Smm * notice, this list of conditions and the following disclaimer. 10231200Smm * 2. Redistributions in binary form must reproduce the above copyright 11231200Smm * notice, this list of conditions and the following disclaimer in the 12231200Smm * documentation and/or other materials provided with the distribution. 13231200Smm * 14231200Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15231200Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16231200Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17231200Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18231200Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19231200Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20231200Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21231200Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22231200Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23231200Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24231200Smm */ 25231200Smm 26231200Smm#include "archive_platform.h" 27231200Smm__FBSDID("$FreeBSD$"); 28231200Smm 29231200Smm#ifdef HAVE_ERRNO_H 30231200Smm#include <errno.h> 31231200Smm#endif 32231200Smm#ifdef HAVE_LIMITS_H 33231200Smm#include <limits.h> 34231200Smm#endif 35231200Smm#ifdef HAVE_WCHAR_H 36231200Smm#include <wchar.h> 37231200Smm#endif 38231200Smm 39231200Smm#include "archive_acl_private.h" 40231200Smm#include "archive_entry.h" 41231200Smm#include "archive_private.h" 42231200Smm 43231200Smm#undef max 44231200Smm#define max(a, b) ((a)>(b)?(a):(b)) 45231200Smm 46231200Smm#ifndef HAVE_WMEMCMP 47231200Smm/* Good enough for simple equality testing, but not for sorting. */ 48231200Smm#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) 49231200Smm#endif 50231200Smm 51231200Smmstatic int acl_special(struct archive_acl *acl, 52231200Smm int type, int permset, int tag); 53231200Smmstatic struct archive_acl_entry *acl_new_entry(struct archive_acl *acl, 54231200Smm int type, int permset, int tag, int id); 55232153Smmstatic int archive_acl_add_entry_len_l(struct archive_acl *acl, 56232153Smm int type, int permset, int tag, int id, const char *name, 57232153Smm size_t len, struct archive_string_conv *sc); 58231200Smmstatic int isint_w(const wchar_t *start, const wchar_t *end, int *result); 59231200Smmstatic int ismode_w(const wchar_t *start, const wchar_t *end, int *result); 60231200Smmstatic void next_field_w(const wchar_t **wp, const wchar_t **start, 61231200Smm const wchar_t **end, wchar_t *sep); 62231200Smmstatic int prefix_w(const wchar_t *start, const wchar_t *end, 63231200Smm const wchar_t *test); 64231200Smmstatic void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, 65231200Smm const wchar_t *wname, int perm, int id); 66231200Smmstatic void append_id_w(wchar_t **wp, int id); 67231200Smmstatic int isint(const char *start, const char *end, int *result); 68231200Smmstatic int ismode(const char *start, const char *end, int *result); 69231200Smmstatic void next_field(const char **p, const char **start, 70231200Smm const char **end, char *sep); 71232153Smmstatic int prefix_c(const char *start, const char *end, 72231200Smm const char *test); 73231200Smmstatic void append_entry(char **p, const char *prefix, int tag, 74231200Smm const char *name, int perm, int id); 75231200Smmstatic void append_id(char **p, int id); 76231200Smm 77231200Smmvoid 78231200Smmarchive_acl_clear(struct archive_acl *acl) 79231200Smm{ 80231200Smm struct archive_acl_entry *ap; 81231200Smm 82231200Smm while (acl->acl_head != NULL) { 83231200Smm ap = acl->acl_head->next; 84231200Smm archive_mstring_clean(&acl->acl_head->name); 85231200Smm free(acl->acl_head); 86231200Smm acl->acl_head = ap; 87231200Smm } 88231200Smm if (acl->acl_text_w != NULL) { 89231200Smm free(acl->acl_text_w); 90231200Smm acl->acl_text_w = NULL; 91231200Smm } 92231200Smm if (acl->acl_text != NULL) { 93231200Smm free(acl->acl_text); 94231200Smm acl->acl_text = NULL; 95231200Smm } 96231200Smm acl->acl_p = NULL; 97231200Smm acl->acl_state = 0; /* Not counting. */ 98231200Smm} 99231200Smm 100231200Smmvoid 101231200Smmarchive_acl_copy(struct archive_acl *dest, struct archive_acl *src) 102231200Smm{ 103231200Smm struct archive_acl_entry *ap, *ap2; 104231200Smm 105231200Smm archive_acl_clear(dest); 106231200Smm 107231200Smm dest->mode = src->mode; 108231200Smm ap = src->acl_head; 109231200Smm while (ap != NULL) { 110231200Smm ap2 = acl_new_entry(dest, 111231200Smm ap->type, ap->permset, ap->tag, ap->id); 112231200Smm if (ap2 != NULL) 113231200Smm archive_mstring_copy(&ap2->name, &ap->name); 114231200Smm ap = ap->next; 115231200Smm } 116231200Smm} 117231200Smm 118231200Smmint 119231200Smmarchive_acl_add_entry(struct archive_acl *acl, 120231200Smm int type, int permset, int tag, int id, const char *name) 121231200Smm{ 122231200Smm struct archive_acl_entry *ap; 123231200Smm 124231200Smm if (acl_special(acl, type, permset, tag) == 0) 125231200Smm return ARCHIVE_OK; 126231200Smm ap = acl_new_entry(acl, type, permset, tag, id); 127231200Smm if (ap == NULL) { 128231200Smm /* XXX Error XXX */ 129231200Smm return ARCHIVE_FAILED; 130231200Smm } 131231200Smm if (name != NULL && *name != '\0') 132231200Smm archive_mstring_copy_mbs(&ap->name, name); 133231200Smm else 134231200Smm archive_mstring_clean(&ap->name); 135231200Smm return ARCHIVE_OK; 136231200Smm} 137231200Smm 138231200Smmint 139231200Smmarchive_acl_add_entry_w_len(struct archive_acl *acl, 140231200Smm int type, int permset, int tag, int id, const wchar_t *name, size_t len) 141231200Smm{ 142231200Smm struct archive_acl_entry *ap; 143231200Smm 144231200Smm if (acl_special(acl, type, permset, tag) == 0) 145231200Smm return ARCHIVE_OK; 146231200Smm ap = acl_new_entry(acl, type, permset, tag, id); 147231200Smm if (ap == NULL) { 148231200Smm /* XXX Error XXX */ 149231200Smm return ARCHIVE_FAILED; 150231200Smm } 151231200Smm if (name != NULL && *name != L'\0' && len > 0) 152231200Smm archive_mstring_copy_wcs_len(&ap->name, name, len); 153231200Smm else 154231200Smm archive_mstring_clean(&ap->name); 155231200Smm return ARCHIVE_OK; 156231200Smm} 157231200Smm 158232153Smmstatic int 159231200Smmarchive_acl_add_entry_len_l(struct archive_acl *acl, 160231200Smm int type, int permset, int tag, int id, const char *name, size_t len, 161231200Smm struct archive_string_conv *sc) 162231200Smm{ 163231200Smm struct archive_acl_entry *ap; 164231200Smm int r; 165231200Smm 166231200Smm if (acl_special(acl, type, permset, tag) == 0) 167231200Smm return ARCHIVE_OK; 168231200Smm ap = acl_new_entry(acl, type, permset, tag, id); 169231200Smm if (ap == NULL) { 170231200Smm /* XXX Error XXX */ 171231200Smm return ARCHIVE_FAILED; 172231200Smm } 173231200Smm if (name != NULL && *name != '\0' && len > 0) { 174231200Smm r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc); 175231200Smm } else { 176231200Smm r = 0; 177231200Smm archive_mstring_clean(&ap->name); 178231200Smm } 179231200Smm if (r == 0) 180231200Smm return (ARCHIVE_OK); 181231200Smm else if (errno == ENOMEM) 182231200Smm return (ARCHIVE_FATAL); 183231200Smm else 184231200Smm return (ARCHIVE_WARN); 185231200Smm} 186231200Smm 187231200Smm/* 188231200Smm * If this ACL entry is part of the standard POSIX permissions set, 189231200Smm * store the permissions in the stat structure and return zero. 190231200Smm */ 191231200Smmstatic int 192231200Smmacl_special(struct archive_acl *acl, int type, int permset, int tag) 193231200Smm{ 194231200Smm if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS 195231200Smm && ((permset & ~007) == 0)) { 196231200Smm switch (tag) { 197231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 198231200Smm acl->mode &= ~0700; 199231200Smm acl->mode |= (permset & 7) << 6; 200231200Smm return (0); 201231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 202231200Smm acl->mode &= ~0070; 203231200Smm acl->mode |= (permset & 7) << 3; 204231200Smm return (0); 205231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 206231200Smm acl->mode &= ~0007; 207231200Smm acl->mode |= permset & 7; 208231200Smm return (0); 209231200Smm } 210231200Smm } 211231200Smm return (1); 212231200Smm} 213231200Smm 214231200Smm/* 215231200Smm * Allocate and populate a new ACL entry with everything but the 216231200Smm * name. 217231200Smm */ 218231200Smmstatic struct archive_acl_entry * 219231200Smmacl_new_entry(struct archive_acl *acl, 220231200Smm int type, int permset, int tag, int id) 221231200Smm{ 222231200Smm struct archive_acl_entry *ap, *aq; 223231200Smm 224231200Smm /* Type argument must be a valid NFS4 or POSIX.1e type. 225231200Smm * The type must agree with anything already set and 226231200Smm * the permset must be compatible. */ 227231200Smm if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 228231200Smm if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 229231200Smm return (NULL); 230231200Smm } 231231200Smm if (permset & 232231200Smm ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4 233231200Smm | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) { 234231200Smm return (NULL); 235231200Smm } 236231200Smm } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 237231200Smm if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 238231200Smm return (NULL); 239231200Smm } 240231200Smm if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) { 241231200Smm return (NULL); 242231200Smm } 243231200Smm } else { 244231200Smm return (NULL); 245231200Smm } 246231200Smm 247231200Smm /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */ 248231200Smm switch (tag) { 249231200Smm case ARCHIVE_ENTRY_ACL_USER: 250231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 251231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 252231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 253231200Smm /* Tags valid in both NFS4 and POSIX.1e */ 254231200Smm break; 255231200Smm case ARCHIVE_ENTRY_ACL_MASK: 256231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 257231200Smm /* Tags valid only in POSIX.1e. */ 258231200Smm if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 259231200Smm return (NULL); 260231200Smm } 261231200Smm break; 262231200Smm case ARCHIVE_ENTRY_ACL_EVERYONE: 263231200Smm /* Tags valid only in NFS4. */ 264231200Smm if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 265231200Smm return (NULL); 266231200Smm } 267231200Smm break; 268231200Smm default: 269231200Smm /* No other values are valid. */ 270231200Smm return (NULL); 271231200Smm } 272231200Smm 273231200Smm if (acl->acl_text_w != NULL) { 274231200Smm free(acl->acl_text_w); 275231200Smm acl->acl_text_w = NULL; 276231200Smm } 277231200Smm if (acl->acl_text != NULL) { 278231200Smm free(acl->acl_text); 279231200Smm acl->acl_text = NULL; 280231200Smm } 281231200Smm 282231200Smm /* If there's a matching entry already in the list, overwrite it. */ 283231200Smm ap = acl->acl_head; 284231200Smm aq = NULL; 285231200Smm while (ap != NULL) { 286231200Smm if (ap->type == type && ap->tag == tag && ap->id == id) { 287231200Smm ap->permset = permset; 288231200Smm return (ap); 289231200Smm } 290231200Smm aq = ap; 291231200Smm ap = ap->next; 292231200Smm } 293231200Smm 294231200Smm /* Add a new entry to the end of the list. */ 295231200Smm ap = (struct archive_acl_entry *)malloc(sizeof(*ap)); 296231200Smm if (ap == NULL) 297231200Smm return (NULL); 298231200Smm memset(ap, 0, sizeof(*ap)); 299231200Smm if (aq == NULL) 300231200Smm acl->acl_head = ap; 301231200Smm else 302231200Smm aq->next = ap; 303231200Smm ap->type = type; 304231200Smm ap->tag = tag; 305231200Smm ap->id = id; 306231200Smm ap->permset = permset; 307231200Smm acl->acl_types |= type; 308231200Smm return (ap); 309231200Smm} 310231200Smm 311231200Smm/* 312231200Smm * Return a count of entries matching "want_type". 313231200Smm */ 314231200Smmint 315231200Smmarchive_acl_count(struct archive_acl *acl, int want_type) 316231200Smm{ 317231200Smm int count; 318231200Smm struct archive_acl_entry *ap; 319231200Smm 320231200Smm count = 0; 321231200Smm ap = acl->acl_head; 322231200Smm while (ap != NULL) { 323231200Smm if ((ap->type & want_type) != 0) 324231200Smm count++; 325231200Smm ap = ap->next; 326231200Smm } 327231200Smm 328231200Smm if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) 329231200Smm count += 3; 330231200Smm return (count); 331231200Smm} 332231200Smm 333231200Smm/* 334231200Smm * Prepare for reading entries from the ACL data. Returns a count 335231200Smm * of entries matching "want_type", or zero if there are no 336231200Smm * non-extended ACL entries of that type. 337231200Smm */ 338231200Smmint 339231200Smmarchive_acl_reset(struct archive_acl *acl, int want_type) 340231200Smm{ 341231200Smm int count, cutoff; 342231200Smm 343231200Smm count = archive_acl_count(acl, want_type); 344231200Smm 345231200Smm /* 346231200Smm * If the only entries are the three standard ones, 347231200Smm * then don't return any ACL data. (In this case, 348231200Smm * client can just use chmod(2) to set permissions.) 349231200Smm */ 350231200Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) 351231200Smm cutoff = 3; 352231200Smm else 353231200Smm cutoff = 0; 354231200Smm 355231200Smm if (count > cutoff) 356231200Smm acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; 357231200Smm else 358231200Smm acl->acl_state = 0; 359231200Smm acl->acl_p = acl->acl_head; 360231200Smm return (count); 361231200Smm} 362231200Smm 363231200Smm 364231200Smm/* 365231200Smm * Return the next ACL entry in the list. Fake entries for the 366231200Smm * standard permissions and include them in the returned list. 367231200Smm */ 368231200Smmint 369231200Smmarchive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type, 370231200Smm int *permset, int *tag, int *id, const char **name) 371231200Smm{ 372231200Smm *name = NULL; 373231200Smm *id = -1; 374231200Smm 375231200Smm /* 376231200Smm * The acl_state is either zero (no entries available), -1 377231200Smm * (reading from list), or an entry type (retrieve that type 378231200Smm * from ae_stat.aest_mode). 379231200Smm */ 380231200Smm if (acl->acl_state == 0) 381231200Smm return (ARCHIVE_WARN); 382231200Smm 383231200Smm /* The first three access entries are special. */ 384231200Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 385231200Smm switch (acl->acl_state) { 386231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 387231200Smm *permset = (acl->mode >> 6) & 7; 388231200Smm *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 389231200Smm *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 390231200Smm acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 391231200Smm return (ARCHIVE_OK); 392231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 393231200Smm *permset = (acl->mode >> 3) & 7; 394231200Smm *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 395231200Smm *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 396231200Smm acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER; 397231200Smm return (ARCHIVE_OK); 398231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 399231200Smm *permset = acl->mode & 7; 400231200Smm *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 401231200Smm *tag = ARCHIVE_ENTRY_ACL_OTHER; 402231200Smm acl->acl_state = -1; 403231200Smm acl->acl_p = acl->acl_head; 404231200Smm return (ARCHIVE_OK); 405231200Smm default: 406231200Smm break; 407231200Smm } 408231200Smm } 409231200Smm 410231200Smm while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0) 411231200Smm acl->acl_p = acl->acl_p->next; 412231200Smm if (acl->acl_p == NULL) { 413231200Smm acl->acl_state = 0; 414231200Smm *type = 0; 415231200Smm *permset = 0; 416231200Smm *tag = 0; 417231200Smm *id = -1; 418231200Smm *name = NULL; 419231200Smm return (ARCHIVE_EOF); /* End of ACL entries. */ 420231200Smm } 421231200Smm *type = acl->acl_p->type; 422231200Smm *permset = acl->acl_p->permset; 423231200Smm *tag = acl->acl_p->tag; 424231200Smm *id = acl->acl_p->id; 425238856Smm if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) { 426238856Smm if (errno == ENOMEM) 427238856Smm return (ARCHIVE_FATAL); 428231200Smm *name = NULL; 429238856Smm } 430231200Smm acl->acl_p = acl->acl_p->next; 431231200Smm return (ARCHIVE_OK); 432231200Smm} 433231200Smm 434231200Smm/* 435231200Smm * Generate a text version of the ACL. The flags parameter controls 436231200Smm * the style of the generated ACL. 437231200Smm */ 438231200Smmconst wchar_t * 439231200Smmarchive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags) 440231200Smm{ 441231200Smm int count; 442231200Smm size_t length; 443231200Smm const wchar_t *wname; 444231200Smm const wchar_t *prefix; 445231200Smm wchar_t separator; 446231200Smm struct archive_acl_entry *ap; 447238856Smm int id, r; 448231200Smm wchar_t *wp; 449231200Smm 450231200Smm if (acl->acl_text_w != NULL) { 451231200Smm free (acl->acl_text_w); 452231200Smm acl->acl_text_w = NULL; 453231200Smm } 454231200Smm 455231200Smm separator = L','; 456231200Smm count = 0; 457231200Smm length = 0; 458231200Smm ap = acl->acl_head; 459231200Smm while (ap != NULL) { 460231200Smm if ((ap->type & flags) != 0) { 461231200Smm count++; 462231200Smm if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && 463231200Smm (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) 464231200Smm length += 8; /* "default:" */ 465231200Smm length += 5; /* tag name */ 466231200Smm length += 1; /* colon */ 467238856Smm r = archive_mstring_get_wcs(a, &ap->name, &wname); 468238856Smm if (r == 0 && wname != NULL) 469231200Smm length += wcslen(wname); 470238856Smm else if (r < 0 && errno == ENOMEM) 471238856Smm return (NULL); 472231200Smm else 473231200Smm length += sizeof(uid_t) * 3 + 1; 474231200Smm length ++; /* colon */ 475231200Smm length += 3; /* rwx */ 476231200Smm length += 1; /* colon */ 477231200Smm length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; 478231200Smm length ++; /* newline */ 479231200Smm } 480231200Smm ap = ap->next; 481231200Smm } 482231200Smm 483231200Smm if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { 484231200Smm length += 10; /* "user::rwx\n" */ 485231200Smm length += 11; /* "group::rwx\n" */ 486231200Smm length += 11; /* "other::rwx\n" */ 487231200Smm } 488231200Smm 489231200Smm if (count == 0) 490231200Smm return (NULL); 491231200Smm 492231200Smm /* Now, allocate the string and actually populate it. */ 493231200Smm wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); 494231200Smm if (wp == NULL) 495238856Smm return (NULL); 496231200Smm count = 0; 497231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 498231200Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, 499231200Smm acl->mode & 0700, -1); 500231200Smm *wp++ = ','; 501231200Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, 502231200Smm acl->mode & 0070, -1); 503231200Smm *wp++ = ','; 504231200Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, 505231200Smm acl->mode & 0007, -1); 506231200Smm count += 3; 507231200Smm 508231200Smm ap = acl->acl_head; 509231200Smm while (ap != NULL) { 510238856Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 511238856Smm r = archive_mstring_get_wcs(a, &ap->name, &wname); 512238856Smm if (r == 0) { 513238856Smm *wp++ = separator; 514238856Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 515238856Smm id = ap->id; 516238856Smm else 517238856Smm id = -1; 518238856Smm append_entry_w(&wp, NULL, ap->tag, wname, 519238856Smm ap->permset, id); 520238856Smm count++; 521238856Smm } else if (r < 0 && errno == ENOMEM) 522238856Smm return (NULL); 523231200Smm } 524231200Smm ap = ap->next; 525231200Smm } 526231200Smm } 527231200Smm 528231200Smm 529231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 530231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) 531231200Smm prefix = L"default:"; 532231200Smm else 533231200Smm prefix = NULL; 534231200Smm ap = acl->acl_head; 535231200Smm count = 0; 536231200Smm while (ap != NULL) { 537238856Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 538238856Smm r = archive_mstring_get_wcs(a, &ap->name, &wname); 539238856Smm if (r == 0) { 540238856Smm if (count > 0) 541238856Smm *wp++ = separator; 542238856Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 543238856Smm id = ap->id; 544238856Smm else 545238856Smm id = -1; 546238856Smm append_entry_w(&wp, prefix, ap->tag, 547238856Smm wname, ap->permset, id); 548238856Smm count ++; 549238856Smm } else if (r < 0 && errno == ENOMEM) 550238856Smm return (NULL); 551231200Smm } 552231200Smm ap = ap->next; 553231200Smm } 554231200Smm } 555231200Smm 556231200Smm return (acl->acl_text_w); 557231200Smm} 558231200Smm 559231200Smm 560231200Smmstatic void 561231200Smmappend_id_w(wchar_t **wp, int id) 562231200Smm{ 563231200Smm if (id < 0) 564231200Smm id = 0; 565231200Smm if (id > 9) 566231200Smm append_id_w(wp, id / 10); 567231200Smm *(*wp)++ = L"0123456789"[id % 10]; 568231200Smm} 569231200Smm 570231200Smmstatic void 571231200Smmappend_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, 572231200Smm const wchar_t *wname, int perm, int id) 573231200Smm{ 574231200Smm if (prefix != NULL) { 575231200Smm wcscpy(*wp, prefix); 576231200Smm *wp += wcslen(*wp); 577231200Smm } 578231200Smm switch (tag) { 579231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 580231200Smm wname = NULL; 581231200Smm id = -1; 582231200Smm /* FALLTHROUGH */ 583231200Smm case ARCHIVE_ENTRY_ACL_USER: 584231200Smm wcscpy(*wp, L"user"); 585231200Smm break; 586231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 587231200Smm wname = NULL; 588231200Smm id = -1; 589231200Smm /* FALLTHROUGH */ 590231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 591231200Smm wcscpy(*wp, L"group"); 592231200Smm break; 593231200Smm case ARCHIVE_ENTRY_ACL_MASK: 594231200Smm wcscpy(*wp, L"mask"); 595231200Smm wname = NULL; 596231200Smm id = -1; 597231200Smm break; 598231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 599231200Smm wcscpy(*wp, L"other"); 600231200Smm wname = NULL; 601231200Smm id = -1; 602231200Smm break; 603231200Smm } 604231200Smm *wp += wcslen(*wp); 605231200Smm *(*wp)++ = L':'; 606231200Smm if (wname != NULL) { 607231200Smm wcscpy(*wp, wname); 608231200Smm *wp += wcslen(*wp); 609231200Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER 610231200Smm || tag == ARCHIVE_ENTRY_ACL_GROUP) { 611231200Smm append_id_w(wp, id); 612231200Smm id = -1; 613231200Smm } 614231200Smm *(*wp)++ = L':'; 615231200Smm *(*wp)++ = (perm & 0444) ? L'r' : L'-'; 616231200Smm *(*wp)++ = (perm & 0222) ? L'w' : L'-'; 617231200Smm *(*wp)++ = (perm & 0111) ? L'x' : L'-'; 618231200Smm if (id != -1) { 619231200Smm *(*wp)++ = L':'; 620231200Smm append_id_w(wp, id); 621231200Smm } 622231200Smm **wp = L'\0'; 623231200Smm} 624231200Smm 625231200Smmint 626231200Smmarchive_acl_text_l(struct archive_acl *acl, int flags, 627231200Smm const char **acl_text, size_t *acl_text_len, 628231200Smm struct archive_string_conv *sc) 629231200Smm{ 630231200Smm int count; 631231200Smm size_t length; 632231200Smm const char *name; 633231200Smm const char *prefix; 634231200Smm char separator; 635231200Smm struct archive_acl_entry *ap; 636231200Smm size_t len; 637231200Smm int id, r; 638231200Smm char *p; 639231200Smm 640231200Smm if (acl->acl_text != NULL) { 641231200Smm free (acl->acl_text); 642231200Smm acl->acl_text = NULL; 643231200Smm } 644231200Smm 645231200Smm *acl_text = NULL; 646231200Smm if (acl_text_len != NULL) 647231200Smm *acl_text_len = 0; 648231200Smm separator = ','; 649231200Smm count = 0; 650231200Smm length = 0; 651231200Smm ap = acl->acl_head; 652231200Smm while (ap != NULL) { 653231200Smm if ((ap->type & flags) != 0) { 654231200Smm count++; 655231200Smm if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && 656231200Smm (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) 657231200Smm length += 8; /* "default:" */ 658231200Smm length += 5; /* tag name */ 659231200Smm length += 1; /* colon */ 660231200Smm r = archive_mstring_get_mbs_l( 661231200Smm &ap->name, &name, &len, sc); 662231200Smm if (r != 0) 663231200Smm return (-1); 664231200Smm if (len > 0 && name != NULL) 665231200Smm length += len; 666231200Smm else 667231200Smm length += sizeof(uid_t) * 3 + 1; 668231200Smm length ++; /* colon */ 669231200Smm length += 3; /* rwx */ 670231200Smm length += 1; /* colon */ 671231200Smm length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; 672231200Smm length ++; /* newline */ 673231200Smm } 674231200Smm ap = ap->next; 675231200Smm } 676231200Smm 677231200Smm if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { 678231200Smm length += 10; /* "user::rwx\n" */ 679231200Smm length += 11; /* "group::rwx\n" */ 680231200Smm length += 11; /* "other::rwx\n" */ 681231200Smm } 682231200Smm 683231200Smm if (count == 0) 684231200Smm return (0); 685231200Smm 686231200Smm /* Now, allocate the string and actually populate it. */ 687231200Smm p = acl->acl_text = (char *)malloc(length); 688231200Smm if (p == NULL) 689238856Smm return (-1); 690231200Smm count = 0; 691231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 692231200Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, 693231200Smm acl->mode & 0700, -1); 694231200Smm *p++ = ','; 695231200Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, 696231200Smm acl->mode & 0070, -1); 697231200Smm *p++ = ','; 698231200Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, 699231200Smm acl->mode & 0007, -1); 700231200Smm count += 3; 701231200Smm 702231200Smm for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 703231200Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0) 704231200Smm continue; 705231200Smm r = archive_mstring_get_mbs_l( 706231200Smm &ap->name, &name, &len, sc); 707231200Smm if (r != 0) 708231200Smm return (-1); 709231200Smm *p++ = separator; 710231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 711231200Smm id = ap->id; 712231200Smm else 713231200Smm id = -1; 714231200Smm append_entry(&p, NULL, ap->tag, name, 715231200Smm ap->permset, id); 716231200Smm count++; 717231200Smm } 718231200Smm } 719231200Smm 720231200Smm 721231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 722231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) 723231200Smm prefix = "default:"; 724231200Smm else 725231200Smm prefix = NULL; 726231200Smm count = 0; 727231200Smm for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 728231200Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0) 729231200Smm continue; 730231200Smm r = archive_mstring_get_mbs_l( 731231200Smm &ap->name, &name, &len, sc); 732231200Smm if (r != 0) 733231200Smm return (-1); 734231200Smm if (count > 0) 735231200Smm *p++ = separator; 736231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 737231200Smm id = ap->id; 738231200Smm else 739231200Smm id = -1; 740231200Smm append_entry(&p, prefix, ap->tag, 741231200Smm name, ap->permset, id); 742231200Smm count ++; 743231200Smm } 744231200Smm } 745231200Smm 746231200Smm *acl_text = acl->acl_text; 747231200Smm if (acl_text_len != NULL) 748231200Smm *acl_text_len = strlen(acl->acl_text); 749231200Smm return (0); 750231200Smm} 751231200Smm 752231200Smmstatic void 753231200Smmappend_id(char **p, int id) 754231200Smm{ 755231200Smm if (id < 0) 756231200Smm id = 0; 757231200Smm if (id > 9) 758231200Smm append_id(p, id / 10); 759231200Smm *(*p)++ = "0123456789"[id % 10]; 760231200Smm} 761231200Smm 762231200Smmstatic void 763231200Smmappend_entry(char **p, const char *prefix, int tag, 764231200Smm const char *name, int perm, int id) 765231200Smm{ 766231200Smm if (prefix != NULL) { 767231200Smm strcpy(*p, prefix); 768231200Smm *p += strlen(*p); 769231200Smm } 770231200Smm switch (tag) { 771231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 772231200Smm name = NULL; 773231200Smm id = -1; 774231200Smm /* FALLTHROUGH */ 775231200Smm case ARCHIVE_ENTRY_ACL_USER: 776231200Smm strcpy(*p, "user"); 777231200Smm break; 778231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 779231200Smm name = NULL; 780231200Smm id = -1; 781231200Smm /* FALLTHROUGH */ 782231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 783231200Smm strcpy(*p, "group"); 784231200Smm break; 785231200Smm case ARCHIVE_ENTRY_ACL_MASK: 786231200Smm strcpy(*p, "mask"); 787231200Smm name = NULL; 788231200Smm id = -1; 789231200Smm break; 790231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 791231200Smm strcpy(*p, "other"); 792231200Smm name = NULL; 793231200Smm id = -1; 794231200Smm break; 795231200Smm } 796231200Smm *p += strlen(*p); 797231200Smm *(*p)++ = ':'; 798231200Smm if (name != NULL) { 799231200Smm strcpy(*p, name); 800231200Smm *p += strlen(*p); 801231200Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER 802231200Smm || tag == ARCHIVE_ENTRY_ACL_GROUP) { 803231200Smm append_id(p, id); 804231200Smm id = -1; 805231200Smm } 806231200Smm *(*p)++ = ':'; 807231200Smm *(*p)++ = (perm & 0444) ? 'r' : '-'; 808231200Smm *(*p)++ = (perm & 0222) ? 'w' : '-'; 809231200Smm *(*p)++ = (perm & 0111) ? 'x' : '-'; 810231200Smm if (id != -1) { 811231200Smm *(*p)++ = ':'; 812231200Smm append_id(p, id); 813231200Smm } 814231200Smm **p = '\0'; 815231200Smm} 816231200Smm 817231200Smm/* 818231200Smm * Parse a textual ACL. This automatically recognizes and supports 819231200Smm * extensions described above. The 'type' argument is used to 820231200Smm * indicate the type that should be used for any entries not 821231200Smm * explicitly marked as "default:". 822231200Smm */ 823231200Smmint 824231200Smmarchive_acl_parse_w(struct archive_acl *acl, 825231200Smm const wchar_t *text, int default_type) 826231200Smm{ 827231200Smm struct { 828231200Smm const wchar_t *start; 829231200Smm const wchar_t *end; 830231200Smm } field[4], name; 831231200Smm 832231200Smm int fields, n; 833231200Smm int type, tag, permset, id; 834231200Smm wchar_t sep; 835231200Smm 836231200Smm while (text != NULL && *text != L'\0') { 837231200Smm /* 838231200Smm * Parse the fields out of the next entry, 839231200Smm * advance 'text' to start of next entry. 840231200Smm */ 841231200Smm fields = 0; 842231200Smm do { 843231200Smm const wchar_t *start, *end; 844231200Smm next_field_w(&text, &start, &end, &sep); 845231200Smm if (fields < 4) { 846231200Smm field[fields].start = start; 847231200Smm field[fields].end = end; 848231200Smm } 849231200Smm ++fields; 850231200Smm } while (sep == L':'); 851231200Smm 852231200Smm /* Set remaining fields to blank. */ 853231200Smm for (n = fields; n < 4; ++n) 854231200Smm field[n].start = field[n].end = NULL; 855231200Smm 856231200Smm /* Check for a numeric ID in field 1 or 3. */ 857231200Smm id = -1; 858231200Smm isint_w(field[1].start, field[1].end, &id); 859231200Smm /* Field 3 is optional. */ 860231200Smm if (id == -1 && fields > 3) 861231200Smm isint_w(field[3].start, field[3].end, &id); 862231200Smm 863231200Smm /* 864231200Smm * Solaris extension: "defaultuser::rwx" is the 865231200Smm * default ACL corresponding to "user::rwx", etc. 866231200Smm */ 867231200Smm if (field[0].end - field[0].start > 7 868231200Smm && wmemcmp(field[0].start, L"default", 7) == 0) { 869231200Smm type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 870231200Smm field[0].start += 7; 871231200Smm } else 872231200Smm type = default_type; 873231200Smm 874231200Smm name.start = name.end = NULL; 875231200Smm if (prefix_w(field[0].start, field[0].end, L"user")) { 876231200Smm if (!ismode_w(field[2].start, field[2].end, &permset)) 877231200Smm return (ARCHIVE_WARN); 878231200Smm if (id != -1 || field[1].start < field[1].end) { 879231200Smm tag = ARCHIVE_ENTRY_ACL_USER; 880231200Smm name = field[1]; 881231200Smm } else 882231200Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 883231200Smm } else if (prefix_w(field[0].start, field[0].end, L"group")) { 884231200Smm if (!ismode_w(field[2].start, field[2].end, &permset)) 885231200Smm return (ARCHIVE_WARN); 886231200Smm if (id != -1 || field[1].start < field[1].end) { 887231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 888231200Smm name = field[1]; 889231200Smm } else 890231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 891231200Smm } else if (prefix_w(field[0].start, field[0].end, L"other")) { 892231200Smm if (fields == 2 893231200Smm && field[1].start < field[1].end 894231200Smm && ismode_w(field[1].start, field[1].end, &permset)) { 895231200Smm /* This is Solaris-style "other:rwx" */ 896231200Smm } else if (fields == 3 897231200Smm && field[1].start == field[1].end 898231200Smm && field[2].start < field[2].end 899231200Smm && ismode_w(field[2].start, field[2].end, &permset)) { 900231200Smm /* This is FreeBSD-style "other::rwx" */ 901231200Smm } else 902231200Smm return (ARCHIVE_WARN); 903231200Smm tag = ARCHIVE_ENTRY_ACL_OTHER; 904231200Smm } else if (prefix_w(field[0].start, field[0].end, L"mask")) { 905231200Smm if (fields == 2 906231200Smm && field[1].start < field[1].end 907231200Smm && ismode_w(field[1].start, field[1].end, &permset)) { 908231200Smm /* This is Solaris-style "mask:rwx" */ 909231200Smm } else if (fields == 3 910231200Smm && field[1].start == field[1].end 911231200Smm && field[2].start < field[2].end 912231200Smm && ismode_w(field[2].start, field[2].end, &permset)) { 913231200Smm /* This is FreeBSD-style "mask::rwx" */ 914231200Smm } else 915231200Smm return (ARCHIVE_WARN); 916231200Smm tag = ARCHIVE_ENTRY_ACL_MASK; 917231200Smm } else 918231200Smm return (ARCHIVE_WARN); 919231200Smm 920231200Smm /* Add entry to the internal list. */ 921231200Smm archive_acl_add_entry_w_len(acl, type, permset, 922231200Smm tag, id, name.start, name.end - name.start); 923231200Smm } 924231200Smm return (ARCHIVE_OK); 925231200Smm} 926231200Smm 927231200Smm/* 928231200Smm * Parse a string to a positive decimal integer. Returns true if 929231200Smm * the string is non-empty and consists only of decimal digits, 930231200Smm * false otherwise. 931231200Smm */ 932231200Smmstatic int 933231200Smmisint_w(const wchar_t *start, const wchar_t *end, int *result) 934231200Smm{ 935231200Smm int n = 0; 936231200Smm if (start >= end) 937231200Smm return (0); 938231200Smm while (start < end) { 939231200Smm if (*start < '0' || *start > '9') 940231200Smm return (0); 941231200Smm if (n > (INT_MAX / 10) || 942231200Smm (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 943231200Smm n = INT_MAX; 944231200Smm } else { 945231200Smm n *= 10; 946231200Smm n += *start - '0'; 947231200Smm } 948231200Smm start++; 949231200Smm } 950231200Smm *result = n; 951231200Smm return (1); 952231200Smm} 953231200Smm 954231200Smm/* 955231200Smm * Parse a string as a mode field. Returns true if 956231200Smm * the string is non-empty and consists only of mode characters, 957231200Smm * false otherwise. 958231200Smm */ 959231200Smmstatic int 960231200Smmismode_w(const wchar_t *start, const wchar_t *end, int *permset) 961231200Smm{ 962231200Smm const wchar_t *p; 963231200Smm 964231200Smm if (start >= end) 965231200Smm return (0); 966231200Smm p = start; 967231200Smm *permset = 0; 968231200Smm while (p < end) { 969231200Smm switch (*p++) { 970231200Smm case 'r': case 'R': 971231200Smm *permset |= ARCHIVE_ENTRY_ACL_READ; 972231200Smm break; 973231200Smm case 'w': case 'W': 974231200Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE; 975231200Smm break; 976231200Smm case 'x': case 'X': 977231200Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 978231200Smm break; 979231200Smm case '-': 980231200Smm break; 981231200Smm default: 982231200Smm return (0); 983231200Smm } 984231200Smm } 985231200Smm return (1); 986231200Smm} 987231200Smm 988231200Smm/* 989231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 990231200Smm * to point to just after the separator. *start points to the first 991231200Smm * character of the matched text and *end just after the last 992231200Smm * character of the matched identifier. In particular *end - *start 993231200Smm * is the length of the field body, not including leading or trailing 994231200Smm * whitespace. 995231200Smm */ 996231200Smmstatic void 997231200Smmnext_field_w(const wchar_t **wp, const wchar_t **start, 998231200Smm const wchar_t **end, wchar_t *sep) 999231200Smm{ 1000231200Smm /* Skip leading whitespace to find start of field. */ 1001231200Smm while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { 1002231200Smm (*wp)++; 1003231200Smm } 1004231200Smm *start = *wp; 1005231200Smm 1006231200Smm /* Scan for the separator. */ 1007231200Smm while (**wp != L'\0' && **wp != L',' && **wp != L':' && 1008231200Smm **wp != L'\n') { 1009231200Smm (*wp)++; 1010231200Smm } 1011231200Smm *sep = **wp; 1012231200Smm 1013231200Smm /* Trim trailing whitespace to locate end of field. */ 1014231200Smm *end = *wp - 1; 1015231200Smm while (**end == L' ' || **end == L'\t' || **end == L'\n') { 1016231200Smm (*end)--; 1017231200Smm } 1018231200Smm (*end)++; 1019231200Smm 1020231200Smm /* Adjust scanner location. */ 1021231200Smm if (**wp != L'\0') 1022231200Smm (*wp)++; 1023231200Smm} 1024231200Smm 1025231200Smm/* 1026231200Smm * Return true if the characters [start...end) are a prefix of 'test'. 1027231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1028231200Smm */ 1029231200Smmstatic int 1030231200Smmprefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) 1031231200Smm{ 1032231200Smm if (start == end) 1033231200Smm return (0); 1034231200Smm 1035231200Smm if (*start++ != *test++) 1036231200Smm return (0); 1037231200Smm 1038231200Smm while (start < end && *start++ == *test++) 1039231200Smm ; 1040231200Smm 1041231200Smm if (start < end) 1042231200Smm return (0); 1043231200Smm 1044231200Smm return (1); 1045231200Smm} 1046231200Smm 1047231200Smm/* 1048231200Smm * Parse a textual ACL. This automatically recognizes and supports 1049231200Smm * extensions described above. The 'type' argument is used to 1050231200Smm * indicate the type that should be used for any entries not 1051231200Smm * explicitly marked as "default:". 1052231200Smm */ 1053231200Smmint 1054231200Smmarchive_acl_parse_l(struct archive_acl *acl, 1055231200Smm const char *text, int default_type, struct archive_string_conv *sc) 1056231200Smm{ 1057231200Smm struct { 1058231200Smm const char *start; 1059231200Smm const char *end; 1060231200Smm } field[4], name; 1061231200Smm 1062231200Smm int fields, n, r, ret = ARCHIVE_OK; 1063231200Smm int type, tag, permset, id; 1064231200Smm char sep; 1065231200Smm 1066231200Smm while (text != NULL && *text != '\0') { 1067231200Smm /* 1068231200Smm * Parse the fields out of the next entry, 1069231200Smm * advance 'text' to start of next entry. 1070231200Smm */ 1071231200Smm fields = 0; 1072231200Smm do { 1073231200Smm const char *start, *end; 1074231200Smm next_field(&text, &start, &end, &sep); 1075231200Smm if (fields < 4) { 1076231200Smm field[fields].start = start; 1077231200Smm field[fields].end = end; 1078231200Smm } 1079231200Smm ++fields; 1080231200Smm } while (sep == ':'); 1081231200Smm 1082231200Smm /* Set remaining fields to blank. */ 1083231200Smm for (n = fields; n < 4; ++n) 1084231200Smm field[n].start = field[n].end = NULL; 1085231200Smm 1086231200Smm /* Check for a numeric ID in field 1 or 3. */ 1087231200Smm id = -1; 1088231200Smm isint(field[1].start, field[1].end, &id); 1089231200Smm /* Field 3 is optional. */ 1090231200Smm if (id == -1 && fields > 3) 1091231200Smm isint(field[3].start, field[3].end, &id); 1092231200Smm 1093231200Smm /* 1094231200Smm * Solaris extension: "defaultuser::rwx" is the 1095231200Smm * default ACL corresponding to "user::rwx", etc. 1096231200Smm */ 1097231200Smm if (field[0].end - field[0].start > 7 1098231200Smm && memcmp(field[0].start, "default", 7) == 0) { 1099231200Smm type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1100231200Smm field[0].start += 7; 1101231200Smm } else 1102231200Smm type = default_type; 1103231200Smm 1104231200Smm name.start = name.end = NULL; 1105232153Smm if (prefix_c(field[0].start, field[0].end, "user")) { 1106231200Smm if (!ismode(field[2].start, field[2].end, &permset)) 1107231200Smm return (ARCHIVE_WARN); 1108231200Smm if (id != -1 || field[1].start < field[1].end) { 1109231200Smm tag = ARCHIVE_ENTRY_ACL_USER; 1110231200Smm name = field[1]; 1111231200Smm } else 1112231200Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1113232153Smm } else if (prefix_c(field[0].start, field[0].end, "group")) { 1114231200Smm if (!ismode(field[2].start, field[2].end, &permset)) 1115231200Smm return (ARCHIVE_WARN); 1116231200Smm if (id != -1 || field[1].start < field[1].end) { 1117231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 1118231200Smm name = field[1]; 1119231200Smm } else 1120231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1121232153Smm } else if (prefix_c(field[0].start, field[0].end, "other")) { 1122231200Smm if (fields == 2 1123231200Smm && field[1].start < field[1].end 1124231200Smm && ismode(field[1].start, field[1].end, &permset)) { 1125231200Smm /* This is Solaris-style "other:rwx" */ 1126231200Smm } else if (fields == 3 1127231200Smm && field[1].start == field[1].end 1128231200Smm && field[2].start < field[2].end 1129231200Smm && ismode(field[2].start, field[2].end, &permset)) { 1130231200Smm /* This is FreeBSD-style "other::rwx" */ 1131231200Smm } else 1132231200Smm return (ARCHIVE_WARN); 1133231200Smm tag = ARCHIVE_ENTRY_ACL_OTHER; 1134232153Smm } else if (prefix_c(field[0].start, field[0].end, "mask")) { 1135231200Smm if (fields == 2 1136231200Smm && field[1].start < field[1].end 1137231200Smm && ismode(field[1].start, field[1].end, &permset)) { 1138231200Smm /* This is Solaris-style "mask:rwx" */ 1139231200Smm } else if (fields == 3 1140231200Smm && field[1].start == field[1].end 1141231200Smm && field[2].start < field[2].end 1142231200Smm && ismode(field[2].start, field[2].end, &permset)) { 1143231200Smm /* This is FreeBSD-style "mask::rwx" */ 1144231200Smm } else 1145231200Smm return (ARCHIVE_WARN); 1146231200Smm tag = ARCHIVE_ENTRY_ACL_MASK; 1147231200Smm } else 1148231200Smm return (ARCHIVE_WARN); 1149231200Smm 1150231200Smm /* Add entry to the internal list. */ 1151231200Smm r = archive_acl_add_entry_len_l(acl, type, permset, 1152231200Smm tag, id, name.start, name.end - name.start, sc); 1153231200Smm if (r < ARCHIVE_WARN) 1154231200Smm return (r); 1155231200Smm if (r != ARCHIVE_OK) 1156231200Smm ret = ARCHIVE_WARN; 1157231200Smm } 1158231200Smm return (ret); 1159231200Smm} 1160231200Smm 1161231200Smm/* 1162231200Smm * Parse a string to a positive decimal integer. Returns true if 1163231200Smm * the string is non-empty and consists only of decimal digits, 1164231200Smm * false otherwise. 1165231200Smm */ 1166231200Smmstatic int 1167231200Smmisint(const char *start, const char *end, int *result) 1168231200Smm{ 1169231200Smm int n = 0; 1170231200Smm if (start >= end) 1171231200Smm return (0); 1172231200Smm while (start < end) { 1173231200Smm if (*start < '0' || *start > '9') 1174231200Smm return (0); 1175231200Smm if (n > (INT_MAX / 10) || 1176231200Smm (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1177231200Smm n = INT_MAX; 1178231200Smm } else { 1179231200Smm n *= 10; 1180231200Smm n += *start - '0'; 1181231200Smm } 1182231200Smm start++; 1183231200Smm } 1184231200Smm *result = n; 1185231200Smm return (1); 1186231200Smm} 1187231200Smm 1188231200Smm/* 1189231200Smm * Parse a string as a mode field. Returns true if 1190231200Smm * the string is non-empty and consists only of mode characters, 1191231200Smm * false otherwise. 1192231200Smm */ 1193231200Smmstatic int 1194231200Smmismode(const char *start, const char *end, int *permset) 1195231200Smm{ 1196231200Smm const char *p; 1197231200Smm 1198231200Smm if (start >= end) 1199231200Smm return (0); 1200231200Smm p = start; 1201231200Smm *permset = 0; 1202231200Smm while (p < end) { 1203231200Smm switch (*p++) { 1204231200Smm case 'r': case 'R': 1205231200Smm *permset |= ARCHIVE_ENTRY_ACL_READ; 1206231200Smm break; 1207231200Smm case 'w': case 'W': 1208231200Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1209231200Smm break; 1210231200Smm case 'x': case 'X': 1211231200Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1212231200Smm break; 1213231200Smm case '-': 1214231200Smm break; 1215231200Smm default: 1216231200Smm return (0); 1217231200Smm } 1218231200Smm } 1219231200Smm return (1); 1220231200Smm} 1221231200Smm 1222231200Smm/* 1223231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1224231200Smm * to point to just after the separator. *start points to the first 1225231200Smm * character of the matched text and *end just after the last 1226231200Smm * character of the matched identifier. In particular *end - *start 1227231200Smm * is the length of the field body, not including leading or trailing 1228231200Smm * whitespace. 1229231200Smm */ 1230231200Smmstatic void 1231231200Smmnext_field(const char **p, const char **start, 1232231200Smm const char **end, char *sep) 1233231200Smm{ 1234231200Smm /* Skip leading whitespace to find start of field. */ 1235231200Smm while (**p == ' ' || **p == '\t' || **p == '\n') { 1236231200Smm (*p)++; 1237231200Smm } 1238231200Smm *start = *p; 1239231200Smm 1240231200Smm /* Scan for the separator. */ 1241231200Smm while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { 1242231200Smm (*p)++; 1243231200Smm } 1244231200Smm *sep = **p; 1245231200Smm 1246231200Smm /* Trim trailing whitespace to locate end of field. */ 1247231200Smm *end = *p - 1; 1248231200Smm while (**end == ' ' || **end == '\t' || **end == '\n') { 1249231200Smm (*end)--; 1250231200Smm } 1251231200Smm (*end)++; 1252231200Smm 1253231200Smm /* Adjust scanner location. */ 1254231200Smm if (**p != '\0') 1255231200Smm (*p)++; 1256231200Smm} 1257231200Smm 1258231200Smm/* 1259231200Smm * Return true if the characters [start...end) are a prefix of 'test'. 1260231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1261231200Smm */ 1262231200Smmstatic int 1263232153Smmprefix_c(const char *start, const char *end, const char *test) 1264231200Smm{ 1265231200Smm if (start == end) 1266231200Smm return (0); 1267231200Smm 1268231200Smm if (*start++ != *test++) 1269231200Smm return (0); 1270231200Smm 1271231200Smm while (start < end && *start++ == *test++) 1272231200Smm ; 1273231200Smm 1274231200Smm if (start < end) 1275231200Smm return (0); 1276231200Smm 1277231200Smm return (1); 1278231200Smm} 1279