archive_acl.c revision 311041
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; 97311041Smm acl->acl_types = 0; 98231200Smm acl->acl_state = 0; /* Not counting. */ 99231200Smm} 100231200Smm 101231200Smmvoid 102231200Smmarchive_acl_copy(struct archive_acl *dest, struct archive_acl *src) 103231200Smm{ 104231200Smm struct archive_acl_entry *ap, *ap2; 105231200Smm 106231200Smm archive_acl_clear(dest); 107231200Smm 108231200Smm dest->mode = src->mode; 109231200Smm ap = src->acl_head; 110231200Smm while (ap != NULL) { 111231200Smm ap2 = acl_new_entry(dest, 112231200Smm ap->type, ap->permset, ap->tag, ap->id); 113231200Smm if (ap2 != NULL) 114231200Smm archive_mstring_copy(&ap2->name, &ap->name); 115231200Smm ap = ap->next; 116231200Smm } 117231200Smm} 118231200Smm 119231200Smmint 120231200Smmarchive_acl_add_entry(struct archive_acl *acl, 121231200Smm int type, int permset, int tag, int id, const char *name) 122231200Smm{ 123231200Smm struct archive_acl_entry *ap; 124231200Smm 125231200Smm if (acl_special(acl, type, permset, tag) == 0) 126231200Smm return ARCHIVE_OK; 127231200Smm ap = acl_new_entry(acl, type, permset, tag, id); 128231200Smm if (ap == NULL) { 129231200Smm /* XXX Error XXX */ 130231200Smm return ARCHIVE_FAILED; 131231200Smm } 132231200Smm if (name != NULL && *name != '\0') 133231200Smm archive_mstring_copy_mbs(&ap->name, name); 134231200Smm else 135231200Smm archive_mstring_clean(&ap->name); 136231200Smm return ARCHIVE_OK; 137231200Smm} 138231200Smm 139231200Smmint 140231200Smmarchive_acl_add_entry_w_len(struct archive_acl *acl, 141231200Smm int type, int permset, int tag, int id, const wchar_t *name, size_t len) 142231200Smm{ 143231200Smm struct archive_acl_entry *ap; 144231200Smm 145231200Smm if (acl_special(acl, type, permset, tag) == 0) 146231200Smm return ARCHIVE_OK; 147231200Smm ap = acl_new_entry(acl, type, permset, tag, id); 148231200Smm if (ap == NULL) { 149231200Smm /* XXX Error XXX */ 150231200Smm return ARCHIVE_FAILED; 151231200Smm } 152231200Smm if (name != NULL && *name != L'\0' && len > 0) 153231200Smm archive_mstring_copy_wcs_len(&ap->name, name, len); 154231200Smm else 155231200Smm archive_mstring_clean(&ap->name); 156231200Smm return ARCHIVE_OK; 157231200Smm} 158231200Smm 159232153Smmstatic int 160231200Smmarchive_acl_add_entry_len_l(struct archive_acl *acl, 161231200Smm int type, int permset, int tag, int id, const char *name, size_t len, 162231200Smm struct archive_string_conv *sc) 163231200Smm{ 164231200Smm struct archive_acl_entry *ap; 165231200Smm int r; 166231200Smm 167231200Smm if (acl_special(acl, type, permset, tag) == 0) 168231200Smm return ARCHIVE_OK; 169231200Smm ap = acl_new_entry(acl, type, permset, tag, id); 170231200Smm if (ap == NULL) { 171231200Smm /* XXX Error XXX */ 172231200Smm return ARCHIVE_FAILED; 173231200Smm } 174231200Smm if (name != NULL && *name != '\0' && len > 0) { 175231200Smm r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc); 176231200Smm } else { 177231200Smm r = 0; 178231200Smm archive_mstring_clean(&ap->name); 179231200Smm } 180231200Smm if (r == 0) 181231200Smm return (ARCHIVE_OK); 182231200Smm else if (errno == ENOMEM) 183231200Smm return (ARCHIVE_FATAL); 184231200Smm else 185231200Smm return (ARCHIVE_WARN); 186231200Smm} 187231200Smm 188231200Smm/* 189231200Smm * If this ACL entry is part of the standard POSIX permissions set, 190231200Smm * store the permissions in the stat structure and return zero. 191231200Smm */ 192231200Smmstatic int 193231200Smmacl_special(struct archive_acl *acl, int type, int permset, int tag) 194231200Smm{ 195231200Smm if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS 196231200Smm && ((permset & ~007) == 0)) { 197231200Smm switch (tag) { 198231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 199231200Smm acl->mode &= ~0700; 200231200Smm acl->mode |= (permset & 7) << 6; 201231200Smm return (0); 202231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 203231200Smm acl->mode &= ~0070; 204231200Smm acl->mode |= (permset & 7) << 3; 205231200Smm return (0); 206231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 207231200Smm acl->mode &= ~0007; 208231200Smm acl->mode |= permset & 7; 209231200Smm return (0); 210231200Smm } 211231200Smm } 212231200Smm return (1); 213231200Smm} 214231200Smm 215231200Smm/* 216231200Smm * Allocate and populate a new ACL entry with everything but the 217231200Smm * name. 218231200Smm */ 219231200Smmstatic struct archive_acl_entry * 220231200Smmacl_new_entry(struct archive_acl *acl, 221231200Smm int type, int permset, int tag, int id) 222231200Smm{ 223231200Smm struct archive_acl_entry *ap, *aq; 224231200Smm 225231200Smm /* Type argument must be a valid NFS4 or POSIX.1e type. 226231200Smm * The type must agree with anything already set and 227231200Smm * the permset must be compatible. */ 228231200Smm if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 229231200Smm if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 230231200Smm return (NULL); 231231200Smm } 232231200Smm if (permset & 233231200Smm ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4 234231200Smm | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) { 235231200Smm return (NULL); 236231200Smm } 237231200Smm } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 238231200Smm if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 239231200Smm return (NULL); 240231200Smm } 241231200Smm if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) { 242231200Smm return (NULL); 243231200Smm } 244231200Smm } else { 245231200Smm return (NULL); 246231200Smm } 247231200Smm 248231200Smm /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */ 249231200Smm switch (tag) { 250231200Smm case ARCHIVE_ENTRY_ACL_USER: 251231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 252231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 253231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 254231200Smm /* Tags valid in both NFS4 and POSIX.1e */ 255231200Smm break; 256231200Smm case ARCHIVE_ENTRY_ACL_MASK: 257231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 258231200Smm /* Tags valid only in POSIX.1e. */ 259231200Smm if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { 260231200Smm return (NULL); 261231200Smm } 262231200Smm break; 263231200Smm case ARCHIVE_ENTRY_ACL_EVERYONE: 264231200Smm /* Tags valid only in NFS4. */ 265231200Smm if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 266231200Smm return (NULL); 267231200Smm } 268231200Smm break; 269231200Smm default: 270231200Smm /* No other values are valid. */ 271231200Smm return (NULL); 272231200Smm } 273231200Smm 274231200Smm if (acl->acl_text_w != NULL) { 275231200Smm free(acl->acl_text_w); 276231200Smm acl->acl_text_w = NULL; 277231200Smm } 278231200Smm if (acl->acl_text != NULL) { 279231200Smm free(acl->acl_text); 280231200Smm acl->acl_text = NULL; 281231200Smm } 282231200Smm 283311041Smm /* 284311041Smm * If there's a matching entry already in the list, overwrite it. 285311041Smm * NFSv4 entries may be repeated and are not overwritten. 286311041Smm * 287311041Smm * TODO: compare names of no id is provided (needs more rework) 288311041Smm */ 289231200Smm ap = acl->acl_head; 290231200Smm aq = NULL; 291231200Smm while (ap != NULL) { 292311041Smm if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) && 293311041Smm ap->type == type && ap->tag == tag && ap->id == id) { 294311041Smm if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER && 295311041Smm tag != ARCHIVE_ENTRY_ACL_GROUP)) { 296311041Smm ap->permset = permset; 297311041Smm return (ap); 298311041Smm } 299231200Smm } 300231200Smm aq = ap; 301231200Smm ap = ap->next; 302231200Smm } 303231200Smm 304231200Smm /* Add a new entry to the end of the list. */ 305311041Smm ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap)); 306231200Smm if (ap == NULL) 307231200Smm return (NULL); 308231200Smm if (aq == NULL) 309231200Smm acl->acl_head = ap; 310231200Smm else 311231200Smm aq->next = ap; 312231200Smm ap->type = type; 313231200Smm ap->tag = tag; 314231200Smm ap->id = id; 315231200Smm ap->permset = permset; 316231200Smm acl->acl_types |= type; 317231200Smm return (ap); 318231200Smm} 319231200Smm 320231200Smm/* 321231200Smm * Return a count of entries matching "want_type". 322231200Smm */ 323231200Smmint 324231200Smmarchive_acl_count(struct archive_acl *acl, int want_type) 325231200Smm{ 326231200Smm int count; 327231200Smm struct archive_acl_entry *ap; 328231200Smm 329231200Smm count = 0; 330231200Smm ap = acl->acl_head; 331231200Smm while (ap != NULL) { 332231200Smm if ((ap->type & want_type) != 0) 333231200Smm count++; 334231200Smm ap = ap->next; 335231200Smm } 336231200Smm 337231200Smm if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) 338231200Smm count += 3; 339231200Smm return (count); 340231200Smm} 341231200Smm 342231200Smm/* 343231200Smm * Prepare for reading entries from the ACL data. Returns a count 344231200Smm * of entries matching "want_type", or zero if there are no 345231200Smm * non-extended ACL entries of that type. 346231200Smm */ 347231200Smmint 348231200Smmarchive_acl_reset(struct archive_acl *acl, int want_type) 349231200Smm{ 350231200Smm int count, cutoff; 351231200Smm 352231200Smm count = archive_acl_count(acl, want_type); 353231200Smm 354231200Smm /* 355231200Smm * If the only entries are the three standard ones, 356231200Smm * then don't return any ACL data. (In this case, 357231200Smm * client can just use chmod(2) to set permissions.) 358231200Smm */ 359231200Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) 360231200Smm cutoff = 3; 361231200Smm else 362231200Smm cutoff = 0; 363231200Smm 364231200Smm if (count > cutoff) 365231200Smm acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; 366231200Smm else 367231200Smm acl->acl_state = 0; 368231200Smm acl->acl_p = acl->acl_head; 369231200Smm return (count); 370231200Smm} 371231200Smm 372231200Smm 373231200Smm/* 374231200Smm * Return the next ACL entry in the list. Fake entries for the 375231200Smm * standard permissions and include them in the returned list. 376231200Smm */ 377231200Smmint 378231200Smmarchive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type, 379231200Smm int *permset, int *tag, int *id, const char **name) 380231200Smm{ 381231200Smm *name = NULL; 382231200Smm *id = -1; 383231200Smm 384231200Smm /* 385231200Smm * The acl_state is either zero (no entries available), -1 386231200Smm * (reading from list), or an entry type (retrieve that type 387231200Smm * from ae_stat.aest_mode). 388231200Smm */ 389231200Smm if (acl->acl_state == 0) 390231200Smm return (ARCHIVE_WARN); 391231200Smm 392231200Smm /* The first three access entries are special. */ 393231200Smm if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 394231200Smm switch (acl->acl_state) { 395231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 396231200Smm *permset = (acl->mode >> 6) & 7; 397231200Smm *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 398231200Smm *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 399231200Smm acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 400231200Smm return (ARCHIVE_OK); 401231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 402231200Smm *permset = (acl->mode >> 3) & 7; 403231200Smm *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 404231200Smm *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 405231200Smm acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER; 406231200Smm return (ARCHIVE_OK); 407231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 408231200Smm *permset = acl->mode & 7; 409231200Smm *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 410231200Smm *tag = ARCHIVE_ENTRY_ACL_OTHER; 411231200Smm acl->acl_state = -1; 412231200Smm acl->acl_p = acl->acl_head; 413231200Smm return (ARCHIVE_OK); 414231200Smm default: 415231200Smm break; 416231200Smm } 417231200Smm } 418231200Smm 419231200Smm while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0) 420231200Smm acl->acl_p = acl->acl_p->next; 421231200Smm if (acl->acl_p == NULL) { 422231200Smm acl->acl_state = 0; 423231200Smm *type = 0; 424231200Smm *permset = 0; 425231200Smm *tag = 0; 426231200Smm *id = -1; 427231200Smm *name = NULL; 428231200Smm return (ARCHIVE_EOF); /* End of ACL entries. */ 429231200Smm } 430231200Smm *type = acl->acl_p->type; 431231200Smm *permset = acl->acl_p->permset; 432231200Smm *tag = acl->acl_p->tag; 433231200Smm *id = acl->acl_p->id; 434238856Smm if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) { 435238856Smm if (errno == ENOMEM) 436238856Smm return (ARCHIVE_FATAL); 437231200Smm *name = NULL; 438238856Smm } 439231200Smm acl->acl_p = acl->acl_p->next; 440231200Smm return (ARCHIVE_OK); 441231200Smm} 442231200Smm 443231200Smm/* 444231200Smm * Generate a text version of the ACL. The flags parameter controls 445231200Smm * the style of the generated ACL. 446231200Smm */ 447231200Smmconst wchar_t * 448231200Smmarchive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags) 449231200Smm{ 450231200Smm int count; 451231200Smm size_t length; 452231200Smm const wchar_t *wname; 453231200Smm const wchar_t *prefix; 454231200Smm wchar_t separator; 455231200Smm struct archive_acl_entry *ap; 456238856Smm int id, r; 457231200Smm wchar_t *wp; 458231200Smm 459231200Smm if (acl->acl_text_w != NULL) { 460231200Smm free (acl->acl_text_w); 461231200Smm acl->acl_text_w = NULL; 462231200Smm } 463231200Smm 464231200Smm separator = L','; 465231200Smm count = 0; 466231200Smm length = 0; 467231200Smm ap = acl->acl_head; 468231200Smm while (ap != NULL) { 469231200Smm if ((ap->type & flags) != 0) { 470231200Smm count++; 471231200Smm if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && 472231200Smm (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) 473231200Smm length += 8; /* "default:" */ 474231200Smm length += 5; /* tag name */ 475231200Smm length += 1; /* colon */ 476238856Smm r = archive_mstring_get_wcs(a, &ap->name, &wname); 477238856Smm if (r == 0 && wname != NULL) 478231200Smm length += wcslen(wname); 479238856Smm else if (r < 0 && errno == ENOMEM) 480238856Smm return (NULL); 481231200Smm else 482231200Smm length += sizeof(uid_t) * 3 + 1; 483231200Smm length ++; /* colon */ 484231200Smm length += 3; /* rwx */ 485231200Smm length += 1; /* colon */ 486231200Smm length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; 487231200Smm length ++; /* newline */ 488231200Smm } 489231200Smm ap = ap->next; 490231200Smm } 491231200Smm 492231200Smm if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { 493231200Smm length += 10; /* "user::rwx\n" */ 494231200Smm length += 11; /* "group::rwx\n" */ 495231200Smm length += 11; /* "other::rwx\n" */ 496231200Smm } 497231200Smm 498231200Smm if (count == 0) 499231200Smm return (NULL); 500231200Smm 501231200Smm /* Now, allocate the string and actually populate it. */ 502231200Smm wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); 503231200Smm if (wp == NULL) 504238856Smm return (NULL); 505231200Smm count = 0; 506231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 507231200Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, 508231200Smm acl->mode & 0700, -1); 509231200Smm *wp++ = ','; 510231200Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, 511231200Smm acl->mode & 0070, -1); 512231200Smm *wp++ = ','; 513231200Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, 514231200Smm acl->mode & 0007, -1); 515231200Smm count += 3; 516231200Smm 517231200Smm ap = acl->acl_head; 518231200Smm while (ap != NULL) { 519238856Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 520238856Smm r = archive_mstring_get_wcs(a, &ap->name, &wname); 521238856Smm if (r == 0) { 522238856Smm *wp++ = separator; 523238856Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 524238856Smm id = ap->id; 525238856Smm else 526238856Smm id = -1; 527238856Smm append_entry_w(&wp, NULL, ap->tag, wname, 528238856Smm ap->permset, id); 529238856Smm count++; 530238856Smm } else if (r < 0 && errno == ENOMEM) 531238856Smm return (NULL); 532231200Smm } 533231200Smm ap = ap->next; 534231200Smm } 535231200Smm } 536231200Smm 537231200Smm 538231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 539231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) 540231200Smm prefix = L"default:"; 541231200Smm else 542231200Smm prefix = NULL; 543231200Smm ap = acl->acl_head; 544231200Smm count = 0; 545231200Smm while (ap != NULL) { 546238856Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 547238856Smm r = archive_mstring_get_wcs(a, &ap->name, &wname); 548238856Smm if (r == 0) { 549238856Smm if (count > 0) 550238856Smm *wp++ = separator; 551238856Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 552238856Smm id = ap->id; 553238856Smm else 554238856Smm id = -1; 555238856Smm append_entry_w(&wp, prefix, ap->tag, 556238856Smm wname, ap->permset, id); 557238856Smm count ++; 558238856Smm } else if (r < 0 && errno == ENOMEM) 559238856Smm return (NULL); 560231200Smm } 561231200Smm ap = ap->next; 562231200Smm } 563231200Smm } 564231200Smm 565231200Smm return (acl->acl_text_w); 566231200Smm} 567231200Smm 568231200Smm 569231200Smmstatic void 570231200Smmappend_id_w(wchar_t **wp, int id) 571231200Smm{ 572231200Smm if (id < 0) 573231200Smm id = 0; 574231200Smm if (id > 9) 575231200Smm append_id_w(wp, id / 10); 576231200Smm *(*wp)++ = L"0123456789"[id % 10]; 577231200Smm} 578231200Smm 579231200Smmstatic void 580231200Smmappend_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, 581231200Smm const wchar_t *wname, int perm, int id) 582231200Smm{ 583231200Smm if (prefix != NULL) { 584231200Smm wcscpy(*wp, prefix); 585231200Smm *wp += wcslen(*wp); 586231200Smm } 587231200Smm switch (tag) { 588231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 589231200Smm wname = NULL; 590231200Smm id = -1; 591231200Smm /* FALLTHROUGH */ 592231200Smm case ARCHIVE_ENTRY_ACL_USER: 593231200Smm wcscpy(*wp, L"user"); 594231200Smm break; 595231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 596231200Smm wname = NULL; 597231200Smm id = -1; 598231200Smm /* FALLTHROUGH */ 599231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 600231200Smm wcscpy(*wp, L"group"); 601231200Smm break; 602231200Smm case ARCHIVE_ENTRY_ACL_MASK: 603231200Smm wcscpy(*wp, L"mask"); 604231200Smm wname = NULL; 605231200Smm id = -1; 606231200Smm break; 607231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 608231200Smm wcscpy(*wp, L"other"); 609231200Smm wname = NULL; 610231200Smm id = -1; 611231200Smm break; 612231200Smm } 613231200Smm *wp += wcslen(*wp); 614231200Smm *(*wp)++ = L':'; 615231200Smm if (wname != NULL) { 616231200Smm wcscpy(*wp, wname); 617231200Smm *wp += wcslen(*wp); 618231200Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER 619231200Smm || tag == ARCHIVE_ENTRY_ACL_GROUP) { 620231200Smm append_id_w(wp, id); 621231200Smm id = -1; 622231200Smm } 623231200Smm *(*wp)++ = L':'; 624231200Smm *(*wp)++ = (perm & 0444) ? L'r' : L'-'; 625231200Smm *(*wp)++ = (perm & 0222) ? L'w' : L'-'; 626231200Smm *(*wp)++ = (perm & 0111) ? L'x' : L'-'; 627231200Smm if (id != -1) { 628231200Smm *(*wp)++ = L':'; 629231200Smm append_id_w(wp, id); 630231200Smm } 631231200Smm **wp = L'\0'; 632231200Smm} 633231200Smm 634231200Smmint 635231200Smmarchive_acl_text_l(struct archive_acl *acl, int flags, 636231200Smm const char **acl_text, size_t *acl_text_len, 637231200Smm struct archive_string_conv *sc) 638231200Smm{ 639231200Smm int count; 640231200Smm size_t length; 641231200Smm const char *name; 642231200Smm const char *prefix; 643231200Smm char separator; 644231200Smm struct archive_acl_entry *ap; 645231200Smm size_t len; 646231200Smm int id, r; 647231200Smm char *p; 648231200Smm 649231200Smm if (acl->acl_text != NULL) { 650231200Smm free (acl->acl_text); 651231200Smm acl->acl_text = NULL; 652231200Smm } 653231200Smm 654231200Smm *acl_text = NULL; 655231200Smm if (acl_text_len != NULL) 656231200Smm *acl_text_len = 0; 657231200Smm separator = ','; 658231200Smm count = 0; 659231200Smm length = 0; 660231200Smm ap = acl->acl_head; 661231200Smm while (ap != NULL) { 662231200Smm if ((ap->type & flags) != 0) { 663231200Smm count++; 664231200Smm if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && 665231200Smm (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) 666231200Smm length += 8; /* "default:" */ 667231200Smm length += 5; /* tag name */ 668231200Smm length += 1; /* colon */ 669231200Smm r = archive_mstring_get_mbs_l( 670231200Smm &ap->name, &name, &len, sc); 671231200Smm if (r != 0) 672231200Smm return (-1); 673231200Smm if (len > 0 && name != NULL) 674231200Smm length += len; 675231200Smm else 676231200Smm length += sizeof(uid_t) * 3 + 1; 677231200Smm length ++; /* colon */ 678231200Smm length += 3; /* rwx */ 679231200Smm length += 1; /* colon */ 680231200Smm length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; 681231200Smm length ++; /* newline */ 682231200Smm } 683231200Smm ap = ap->next; 684231200Smm } 685231200Smm 686231200Smm if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { 687231200Smm length += 10; /* "user::rwx\n" */ 688231200Smm length += 11; /* "group::rwx\n" */ 689231200Smm length += 11; /* "other::rwx\n" */ 690231200Smm } 691231200Smm 692231200Smm if (count == 0) 693231200Smm return (0); 694231200Smm 695231200Smm /* Now, allocate the string and actually populate it. */ 696231200Smm p = acl->acl_text = (char *)malloc(length); 697231200Smm if (p == NULL) 698238856Smm return (-1); 699231200Smm count = 0; 700231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 701231200Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, 702231200Smm acl->mode & 0700, -1); 703231200Smm *p++ = ','; 704231200Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, 705231200Smm acl->mode & 0070, -1); 706231200Smm *p++ = ','; 707231200Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, 708231200Smm acl->mode & 0007, -1); 709231200Smm count += 3; 710231200Smm 711231200Smm for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 712231200Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0) 713231200Smm continue; 714231200Smm r = archive_mstring_get_mbs_l( 715231200Smm &ap->name, &name, &len, sc); 716231200Smm if (r != 0) 717231200Smm return (-1); 718231200Smm *p++ = separator; 719305754Smm if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { 720231200Smm id = ap->id; 721305754Smm } else { 722231200Smm id = -1; 723305754Smm } 724231200Smm append_entry(&p, NULL, ap->tag, name, 725231200Smm ap->permset, id); 726231200Smm count++; 727231200Smm } 728231200Smm } 729231200Smm 730231200Smm 731231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 732231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) 733231200Smm prefix = "default:"; 734231200Smm else 735231200Smm prefix = NULL; 736231200Smm count = 0; 737231200Smm for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 738231200Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0) 739231200Smm continue; 740231200Smm r = archive_mstring_get_mbs_l( 741231200Smm &ap->name, &name, &len, sc); 742231200Smm if (r != 0) 743231200Smm return (-1); 744231200Smm if (count > 0) 745231200Smm *p++ = separator; 746231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 747231200Smm id = ap->id; 748231200Smm else 749231200Smm id = -1; 750231200Smm append_entry(&p, prefix, ap->tag, 751231200Smm name, ap->permset, id); 752231200Smm count ++; 753231200Smm } 754231200Smm } 755231200Smm 756231200Smm *acl_text = acl->acl_text; 757231200Smm if (acl_text_len != NULL) 758231200Smm *acl_text_len = strlen(acl->acl_text); 759231200Smm return (0); 760231200Smm} 761231200Smm 762231200Smmstatic void 763231200Smmappend_id(char **p, int id) 764231200Smm{ 765231200Smm if (id < 0) 766231200Smm id = 0; 767231200Smm if (id > 9) 768231200Smm append_id(p, id / 10); 769231200Smm *(*p)++ = "0123456789"[id % 10]; 770231200Smm} 771231200Smm 772231200Smmstatic void 773231200Smmappend_entry(char **p, const char *prefix, int tag, 774231200Smm const char *name, int perm, int id) 775231200Smm{ 776231200Smm if (prefix != NULL) { 777231200Smm strcpy(*p, prefix); 778231200Smm *p += strlen(*p); 779231200Smm } 780231200Smm switch (tag) { 781231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 782231200Smm name = NULL; 783231200Smm id = -1; 784231200Smm /* FALLTHROUGH */ 785231200Smm case ARCHIVE_ENTRY_ACL_USER: 786231200Smm strcpy(*p, "user"); 787231200Smm break; 788231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 789231200Smm name = NULL; 790231200Smm id = -1; 791231200Smm /* FALLTHROUGH */ 792231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 793231200Smm strcpy(*p, "group"); 794231200Smm break; 795231200Smm case ARCHIVE_ENTRY_ACL_MASK: 796231200Smm strcpy(*p, "mask"); 797231200Smm name = NULL; 798231200Smm id = -1; 799231200Smm break; 800231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 801231200Smm strcpy(*p, "other"); 802231200Smm name = NULL; 803231200Smm id = -1; 804231200Smm break; 805231200Smm } 806231200Smm *p += strlen(*p); 807231200Smm *(*p)++ = ':'; 808231200Smm if (name != NULL) { 809231200Smm strcpy(*p, name); 810231200Smm *p += strlen(*p); 811231200Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER 812231200Smm || tag == ARCHIVE_ENTRY_ACL_GROUP) { 813231200Smm append_id(p, id); 814231200Smm id = -1; 815231200Smm } 816231200Smm *(*p)++ = ':'; 817231200Smm *(*p)++ = (perm & 0444) ? 'r' : '-'; 818231200Smm *(*p)++ = (perm & 0222) ? 'w' : '-'; 819231200Smm *(*p)++ = (perm & 0111) ? 'x' : '-'; 820231200Smm if (id != -1) { 821231200Smm *(*p)++ = ':'; 822231200Smm append_id(p, id); 823231200Smm } 824231200Smm **p = '\0'; 825231200Smm} 826231200Smm 827231200Smm/* 828231200Smm * Parse a textual ACL. This automatically recognizes and supports 829231200Smm * extensions described above. The 'type' argument is used to 830231200Smm * indicate the type that should be used for any entries not 831231200Smm * explicitly marked as "default:". 832231200Smm */ 833231200Smmint 834231200Smmarchive_acl_parse_w(struct archive_acl *acl, 835231200Smm const wchar_t *text, int default_type) 836231200Smm{ 837231200Smm struct { 838231200Smm const wchar_t *start; 839231200Smm const wchar_t *end; 840231200Smm } field[4], name; 841231200Smm 842231200Smm int fields, n; 843231200Smm int type, tag, permset, id; 844231200Smm wchar_t sep; 845231200Smm 846231200Smm while (text != NULL && *text != L'\0') { 847231200Smm /* 848231200Smm * Parse the fields out of the next entry, 849231200Smm * advance 'text' to start of next entry. 850231200Smm */ 851231200Smm fields = 0; 852231200Smm do { 853231200Smm const wchar_t *start, *end; 854231200Smm next_field_w(&text, &start, &end, &sep); 855231200Smm if (fields < 4) { 856231200Smm field[fields].start = start; 857231200Smm field[fields].end = end; 858231200Smm } 859231200Smm ++fields; 860231200Smm } while (sep == L':'); 861231200Smm 862231200Smm /* Set remaining fields to blank. */ 863231200Smm for (n = fields; n < 4; ++n) 864231200Smm field[n].start = field[n].end = NULL; 865231200Smm 866231200Smm /* Check for a numeric ID in field 1 or 3. */ 867231200Smm id = -1; 868231200Smm isint_w(field[1].start, field[1].end, &id); 869231200Smm /* Field 3 is optional. */ 870231200Smm if (id == -1 && fields > 3) 871231200Smm isint_w(field[3].start, field[3].end, &id); 872231200Smm 873231200Smm /* 874231200Smm * Solaris extension: "defaultuser::rwx" is the 875231200Smm * default ACL corresponding to "user::rwx", etc. 876231200Smm */ 877231200Smm if (field[0].end - field[0].start > 7 878231200Smm && wmemcmp(field[0].start, L"default", 7) == 0) { 879231200Smm type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 880231200Smm field[0].start += 7; 881231200Smm } else 882231200Smm type = default_type; 883231200Smm 884231200Smm name.start = name.end = NULL; 885231200Smm if (prefix_w(field[0].start, field[0].end, L"user")) { 886231200Smm if (!ismode_w(field[2].start, field[2].end, &permset)) 887231200Smm return (ARCHIVE_WARN); 888231200Smm if (id != -1 || field[1].start < field[1].end) { 889231200Smm tag = ARCHIVE_ENTRY_ACL_USER; 890231200Smm name = field[1]; 891231200Smm } else 892231200Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 893231200Smm } else if (prefix_w(field[0].start, field[0].end, L"group")) { 894231200Smm if (!ismode_w(field[2].start, field[2].end, &permset)) 895231200Smm return (ARCHIVE_WARN); 896231200Smm if (id != -1 || field[1].start < field[1].end) { 897231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 898231200Smm name = field[1]; 899231200Smm } else 900231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 901231200Smm } else if (prefix_w(field[0].start, field[0].end, L"other")) { 902231200Smm if (fields == 2 903231200Smm && field[1].start < field[1].end 904231200Smm && ismode_w(field[1].start, field[1].end, &permset)) { 905231200Smm /* This is Solaris-style "other:rwx" */ 906231200Smm } else if (fields == 3 907231200Smm && field[1].start == field[1].end 908231200Smm && field[2].start < field[2].end 909231200Smm && ismode_w(field[2].start, field[2].end, &permset)) { 910231200Smm /* This is FreeBSD-style "other::rwx" */ 911231200Smm } else 912231200Smm return (ARCHIVE_WARN); 913231200Smm tag = ARCHIVE_ENTRY_ACL_OTHER; 914231200Smm } else if (prefix_w(field[0].start, field[0].end, L"mask")) { 915231200Smm if (fields == 2 916231200Smm && field[1].start < field[1].end 917231200Smm && ismode_w(field[1].start, field[1].end, &permset)) { 918231200Smm /* This is Solaris-style "mask:rwx" */ 919231200Smm } else if (fields == 3 920231200Smm && field[1].start == field[1].end 921231200Smm && field[2].start < field[2].end 922231200Smm && ismode_w(field[2].start, field[2].end, &permset)) { 923231200Smm /* This is FreeBSD-style "mask::rwx" */ 924231200Smm } else 925231200Smm return (ARCHIVE_WARN); 926231200Smm tag = ARCHIVE_ENTRY_ACL_MASK; 927231200Smm } else 928231200Smm return (ARCHIVE_WARN); 929231200Smm 930231200Smm /* Add entry to the internal list. */ 931231200Smm archive_acl_add_entry_w_len(acl, type, permset, 932231200Smm tag, id, name.start, name.end - name.start); 933231200Smm } 934231200Smm return (ARCHIVE_OK); 935231200Smm} 936231200Smm 937231200Smm/* 938231200Smm * Parse a string to a positive decimal integer. Returns true if 939231200Smm * the string is non-empty and consists only of decimal digits, 940231200Smm * false otherwise. 941231200Smm */ 942231200Smmstatic int 943231200Smmisint_w(const wchar_t *start, const wchar_t *end, int *result) 944231200Smm{ 945231200Smm int n = 0; 946231200Smm if (start >= end) 947231200Smm return (0); 948231200Smm while (start < end) { 949231200Smm if (*start < '0' || *start > '9') 950231200Smm return (0); 951231200Smm if (n > (INT_MAX / 10) || 952231200Smm (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 953231200Smm n = INT_MAX; 954231200Smm } else { 955231200Smm n *= 10; 956231200Smm n += *start - '0'; 957231200Smm } 958231200Smm start++; 959231200Smm } 960231200Smm *result = n; 961231200Smm return (1); 962231200Smm} 963231200Smm 964231200Smm/* 965231200Smm * Parse a string as a mode field. Returns true if 966231200Smm * the string is non-empty and consists only of mode characters, 967231200Smm * false otherwise. 968231200Smm */ 969231200Smmstatic int 970231200Smmismode_w(const wchar_t *start, const wchar_t *end, int *permset) 971231200Smm{ 972231200Smm const wchar_t *p; 973231200Smm 974231200Smm if (start >= end) 975231200Smm return (0); 976231200Smm p = start; 977231200Smm *permset = 0; 978231200Smm while (p < end) { 979231200Smm switch (*p++) { 980231200Smm case 'r': case 'R': 981231200Smm *permset |= ARCHIVE_ENTRY_ACL_READ; 982231200Smm break; 983231200Smm case 'w': case 'W': 984231200Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE; 985231200Smm break; 986231200Smm case 'x': case 'X': 987231200Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 988231200Smm break; 989231200Smm case '-': 990231200Smm break; 991231200Smm default: 992231200Smm return (0); 993231200Smm } 994231200Smm } 995231200Smm return (1); 996231200Smm} 997231200Smm 998231200Smm/* 999231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1000231200Smm * to point to just after the separator. *start points to the first 1001231200Smm * character of the matched text and *end just after the last 1002231200Smm * character of the matched identifier. In particular *end - *start 1003231200Smm * is the length of the field body, not including leading or trailing 1004231200Smm * whitespace. 1005231200Smm */ 1006231200Smmstatic void 1007231200Smmnext_field_w(const wchar_t **wp, const wchar_t **start, 1008231200Smm const wchar_t **end, wchar_t *sep) 1009231200Smm{ 1010231200Smm /* Skip leading whitespace to find start of field. */ 1011231200Smm while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { 1012231200Smm (*wp)++; 1013231200Smm } 1014231200Smm *start = *wp; 1015231200Smm 1016231200Smm /* Scan for the separator. */ 1017231200Smm while (**wp != L'\0' && **wp != L',' && **wp != L':' && 1018231200Smm **wp != L'\n') { 1019231200Smm (*wp)++; 1020231200Smm } 1021231200Smm *sep = **wp; 1022231200Smm 1023231200Smm /* Trim trailing whitespace to locate end of field. */ 1024231200Smm *end = *wp - 1; 1025231200Smm while (**end == L' ' || **end == L'\t' || **end == L'\n') { 1026231200Smm (*end)--; 1027231200Smm } 1028231200Smm (*end)++; 1029231200Smm 1030231200Smm /* Adjust scanner location. */ 1031231200Smm if (**wp != L'\0') 1032231200Smm (*wp)++; 1033231200Smm} 1034231200Smm 1035231200Smm/* 1036231200Smm * Return true if the characters [start...end) are a prefix of 'test'. 1037231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1038231200Smm */ 1039231200Smmstatic int 1040231200Smmprefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) 1041231200Smm{ 1042231200Smm if (start == end) 1043231200Smm return (0); 1044231200Smm 1045231200Smm if (*start++ != *test++) 1046231200Smm return (0); 1047231200Smm 1048231200Smm while (start < end && *start++ == *test++) 1049231200Smm ; 1050231200Smm 1051231200Smm if (start < end) 1052231200Smm return (0); 1053231200Smm 1054231200Smm return (1); 1055231200Smm} 1056231200Smm 1057231200Smm/* 1058231200Smm * Parse a textual ACL. This automatically recognizes and supports 1059231200Smm * extensions described above. The 'type' argument is used to 1060231200Smm * indicate the type that should be used for any entries not 1061231200Smm * explicitly marked as "default:". 1062231200Smm */ 1063231200Smmint 1064231200Smmarchive_acl_parse_l(struct archive_acl *acl, 1065231200Smm const char *text, int default_type, struct archive_string_conv *sc) 1066231200Smm{ 1067231200Smm struct { 1068231200Smm const char *start; 1069231200Smm const char *end; 1070231200Smm } field[4], name; 1071231200Smm 1072231200Smm int fields, n, r, ret = ARCHIVE_OK; 1073231200Smm int type, tag, permset, id; 1074231200Smm char sep; 1075231200Smm 1076231200Smm while (text != NULL && *text != '\0') { 1077231200Smm /* 1078231200Smm * Parse the fields out of the next entry, 1079231200Smm * advance 'text' to start of next entry. 1080231200Smm */ 1081231200Smm fields = 0; 1082231200Smm do { 1083231200Smm const char *start, *end; 1084231200Smm next_field(&text, &start, &end, &sep); 1085231200Smm if (fields < 4) { 1086231200Smm field[fields].start = start; 1087231200Smm field[fields].end = end; 1088231200Smm } 1089231200Smm ++fields; 1090231200Smm } while (sep == ':'); 1091231200Smm 1092231200Smm /* Set remaining fields to blank. */ 1093231200Smm for (n = fields; n < 4; ++n) 1094231200Smm field[n].start = field[n].end = NULL; 1095231200Smm 1096231200Smm /* Check for a numeric ID in field 1 or 3. */ 1097231200Smm id = -1; 1098231200Smm isint(field[1].start, field[1].end, &id); 1099231200Smm /* Field 3 is optional. */ 1100231200Smm if (id == -1 && fields > 3) 1101231200Smm isint(field[3].start, field[3].end, &id); 1102231200Smm 1103231200Smm /* 1104231200Smm * Solaris extension: "defaultuser::rwx" is the 1105231200Smm * default ACL corresponding to "user::rwx", etc. 1106231200Smm */ 1107231200Smm if (field[0].end - field[0].start > 7 1108231200Smm && memcmp(field[0].start, "default", 7) == 0) { 1109231200Smm type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1110231200Smm field[0].start += 7; 1111231200Smm } else 1112231200Smm type = default_type; 1113231200Smm 1114231200Smm name.start = name.end = NULL; 1115232153Smm if (prefix_c(field[0].start, field[0].end, "user")) { 1116231200Smm if (!ismode(field[2].start, field[2].end, &permset)) 1117231200Smm return (ARCHIVE_WARN); 1118231200Smm if (id != -1 || field[1].start < field[1].end) { 1119231200Smm tag = ARCHIVE_ENTRY_ACL_USER; 1120231200Smm name = field[1]; 1121231200Smm } else 1122231200Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1123232153Smm } else if (prefix_c(field[0].start, field[0].end, "group")) { 1124231200Smm if (!ismode(field[2].start, field[2].end, &permset)) 1125231200Smm return (ARCHIVE_WARN); 1126231200Smm if (id != -1 || field[1].start < field[1].end) { 1127231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 1128231200Smm name = field[1]; 1129231200Smm } else 1130231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1131232153Smm } else if (prefix_c(field[0].start, field[0].end, "other")) { 1132231200Smm if (fields == 2 1133231200Smm && field[1].start < field[1].end 1134231200Smm && ismode(field[1].start, field[1].end, &permset)) { 1135231200Smm /* This is Solaris-style "other:rwx" */ 1136231200Smm } else if (fields == 3 1137231200Smm && field[1].start == field[1].end 1138231200Smm && field[2].start < field[2].end 1139231200Smm && ismode(field[2].start, field[2].end, &permset)) { 1140231200Smm /* This is FreeBSD-style "other::rwx" */ 1141231200Smm } else 1142231200Smm return (ARCHIVE_WARN); 1143231200Smm tag = ARCHIVE_ENTRY_ACL_OTHER; 1144232153Smm } else if (prefix_c(field[0].start, field[0].end, "mask")) { 1145231200Smm if (fields == 2 1146231200Smm && field[1].start < field[1].end 1147231200Smm && ismode(field[1].start, field[1].end, &permset)) { 1148231200Smm /* This is Solaris-style "mask:rwx" */ 1149231200Smm } else if (fields == 3 1150231200Smm && field[1].start == field[1].end 1151231200Smm && field[2].start < field[2].end 1152231200Smm && ismode(field[2].start, field[2].end, &permset)) { 1153231200Smm /* This is FreeBSD-style "mask::rwx" */ 1154231200Smm } else 1155231200Smm return (ARCHIVE_WARN); 1156231200Smm tag = ARCHIVE_ENTRY_ACL_MASK; 1157231200Smm } else 1158231200Smm return (ARCHIVE_WARN); 1159231200Smm 1160231200Smm /* Add entry to the internal list. */ 1161231200Smm r = archive_acl_add_entry_len_l(acl, type, permset, 1162231200Smm tag, id, name.start, name.end - name.start, sc); 1163231200Smm if (r < ARCHIVE_WARN) 1164231200Smm return (r); 1165231200Smm if (r != ARCHIVE_OK) 1166231200Smm ret = ARCHIVE_WARN; 1167231200Smm } 1168231200Smm return (ret); 1169231200Smm} 1170231200Smm 1171231200Smm/* 1172231200Smm * Parse a string to a positive decimal integer. Returns true if 1173231200Smm * the string is non-empty and consists only of decimal digits, 1174231200Smm * false otherwise. 1175231200Smm */ 1176231200Smmstatic int 1177231200Smmisint(const char *start, const char *end, int *result) 1178231200Smm{ 1179231200Smm int n = 0; 1180231200Smm if (start >= end) 1181231200Smm return (0); 1182231200Smm while (start < end) { 1183231200Smm if (*start < '0' || *start > '9') 1184231200Smm return (0); 1185231200Smm if (n > (INT_MAX / 10) || 1186231200Smm (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1187231200Smm n = INT_MAX; 1188231200Smm } else { 1189231200Smm n *= 10; 1190231200Smm n += *start - '0'; 1191231200Smm } 1192231200Smm start++; 1193231200Smm } 1194231200Smm *result = n; 1195231200Smm return (1); 1196231200Smm} 1197231200Smm 1198231200Smm/* 1199231200Smm * Parse a string as a mode field. Returns true if 1200231200Smm * the string is non-empty and consists only of mode characters, 1201231200Smm * false otherwise. 1202231200Smm */ 1203231200Smmstatic int 1204231200Smmismode(const char *start, const char *end, int *permset) 1205231200Smm{ 1206231200Smm const char *p; 1207231200Smm 1208231200Smm if (start >= end) 1209231200Smm return (0); 1210231200Smm p = start; 1211231200Smm *permset = 0; 1212231200Smm while (p < end) { 1213231200Smm switch (*p++) { 1214231200Smm case 'r': case 'R': 1215231200Smm *permset |= ARCHIVE_ENTRY_ACL_READ; 1216231200Smm break; 1217231200Smm case 'w': case 'W': 1218231200Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1219231200Smm break; 1220231200Smm case 'x': case 'X': 1221231200Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1222231200Smm break; 1223231200Smm case '-': 1224231200Smm break; 1225231200Smm default: 1226231200Smm return (0); 1227231200Smm } 1228231200Smm } 1229231200Smm return (1); 1230231200Smm} 1231231200Smm 1232231200Smm/* 1233231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1234231200Smm * to point to just after the separator. *start points to the first 1235231200Smm * character of the matched text and *end just after the last 1236231200Smm * character of the matched identifier. In particular *end - *start 1237231200Smm * is the length of the field body, not including leading or trailing 1238231200Smm * whitespace. 1239231200Smm */ 1240231200Smmstatic void 1241231200Smmnext_field(const char **p, const char **start, 1242231200Smm const char **end, char *sep) 1243231200Smm{ 1244231200Smm /* Skip leading whitespace to find start of field. */ 1245231200Smm while (**p == ' ' || **p == '\t' || **p == '\n') { 1246231200Smm (*p)++; 1247231200Smm } 1248231200Smm *start = *p; 1249231200Smm 1250231200Smm /* Scan for the separator. */ 1251231200Smm while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { 1252231200Smm (*p)++; 1253231200Smm } 1254231200Smm *sep = **p; 1255231200Smm 1256231200Smm /* Trim trailing whitespace to locate end of field. */ 1257231200Smm *end = *p - 1; 1258231200Smm while (**end == ' ' || **end == '\t' || **end == '\n') { 1259231200Smm (*end)--; 1260231200Smm } 1261231200Smm (*end)++; 1262231200Smm 1263231200Smm /* Adjust scanner location. */ 1264231200Smm if (**p != '\0') 1265231200Smm (*p)++; 1266231200Smm} 1267231200Smm 1268231200Smm/* 1269231200Smm * Return true if the characters [start...end) are a prefix of 'test'. 1270231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1271231200Smm */ 1272231200Smmstatic int 1273232153Smmprefix_c(const char *start, const char *end, const char *test) 1274231200Smm{ 1275231200Smm if (start == end) 1276231200Smm return (0); 1277231200Smm 1278231200Smm if (*start++ != *test++) 1279231200Smm return (0); 1280231200Smm 1281231200Smm while (start < end && *start++ == *test++) 1282231200Smm ; 1283231200Smm 1284231200Smm if (start < end) 1285231200Smm return (0); 1286231200Smm 1287231200Smm return (1); 1288231200Smm} 1289