archive_acl.c revision 232153
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; 425231200Smm if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) 426231200Smm *name = NULL; 427231200Smm acl->acl_p = acl->acl_p->next; 428231200Smm return (ARCHIVE_OK); 429231200Smm} 430231200Smm 431231200Smm/* 432231200Smm * Generate a text version of the ACL. The flags parameter controls 433231200Smm * the style of the generated ACL. 434231200Smm */ 435231200Smmconst wchar_t * 436231200Smmarchive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags) 437231200Smm{ 438231200Smm int count; 439231200Smm size_t length; 440231200Smm const wchar_t *wname; 441231200Smm const wchar_t *prefix; 442231200Smm wchar_t separator; 443231200Smm struct archive_acl_entry *ap; 444231200Smm int id; 445231200Smm wchar_t *wp; 446231200Smm 447231200Smm if (acl->acl_text_w != NULL) { 448231200Smm free (acl->acl_text_w); 449231200Smm acl->acl_text_w = NULL; 450231200Smm } 451231200Smm 452231200Smm separator = L','; 453231200Smm count = 0; 454231200Smm length = 0; 455231200Smm ap = acl->acl_head; 456231200Smm while (ap != NULL) { 457231200Smm if ((ap->type & flags) != 0) { 458231200Smm count++; 459231200Smm if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && 460231200Smm (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) 461231200Smm length += 8; /* "default:" */ 462231200Smm length += 5; /* tag name */ 463231200Smm length += 1; /* colon */ 464231200Smm if (archive_mstring_get_wcs(a, &ap->name, &wname) == 0 && 465231200Smm wname != NULL) 466231200Smm length += wcslen(wname); 467231200Smm else 468231200Smm length += sizeof(uid_t) * 3 + 1; 469231200Smm length ++; /* colon */ 470231200Smm length += 3; /* rwx */ 471231200Smm length += 1; /* colon */ 472231200Smm length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; 473231200Smm length ++; /* newline */ 474231200Smm } 475231200Smm ap = ap->next; 476231200Smm } 477231200Smm 478231200Smm if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { 479231200Smm length += 10; /* "user::rwx\n" */ 480231200Smm length += 11; /* "group::rwx\n" */ 481231200Smm length += 11; /* "other::rwx\n" */ 482231200Smm } 483231200Smm 484231200Smm if (count == 0) 485231200Smm return (NULL); 486231200Smm 487231200Smm /* Now, allocate the string and actually populate it. */ 488231200Smm wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); 489231200Smm if (wp == NULL) 490231200Smm __archive_errx(1, "No memory to generate the text version of the ACL"); 491231200Smm count = 0; 492231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 493231200Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, 494231200Smm acl->mode & 0700, -1); 495231200Smm *wp++ = ','; 496231200Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, 497231200Smm acl->mode & 0070, -1); 498231200Smm *wp++ = ','; 499231200Smm append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, 500231200Smm acl->mode & 0007, -1); 501231200Smm count += 3; 502231200Smm 503231200Smm ap = acl->acl_head; 504231200Smm while (ap != NULL) { 505231200Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 && 506231200Smm archive_mstring_get_wcs(a, &ap->name, &wname) == 0) { 507231200Smm *wp++ = separator; 508231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 509231200Smm id = ap->id; 510231200Smm else 511231200Smm id = -1; 512231200Smm append_entry_w(&wp, NULL, ap->tag, wname, 513231200Smm ap->permset, id); 514231200Smm count++; 515231200Smm } 516231200Smm ap = ap->next; 517231200Smm } 518231200Smm } 519231200Smm 520231200Smm 521231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 522231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) 523231200Smm prefix = L"default:"; 524231200Smm else 525231200Smm prefix = NULL; 526231200Smm ap = acl->acl_head; 527231200Smm count = 0; 528231200Smm while (ap != NULL) { 529231200Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 && 530231200Smm archive_mstring_get_wcs(a, &ap->name, &wname) == 0) { 531231200Smm if (count > 0) 532231200Smm *wp++ = separator; 533231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 534231200Smm id = ap->id; 535231200Smm else 536231200Smm id = -1; 537231200Smm append_entry_w(&wp, prefix, ap->tag, 538231200Smm wname, ap->permset, id); 539231200Smm count ++; 540231200Smm } 541231200Smm ap = ap->next; 542231200Smm } 543231200Smm } 544231200Smm 545231200Smm return (acl->acl_text_w); 546231200Smm} 547231200Smm 548231200Smm 549231200Smmstatic void 550231200Smmappend_id_w(wchar_t **wp, int id) 551231200Smm{ 552231200Smm if (id < 0) 553231200Smm id = 0; 554231200Smm if (id > 9) 555231200Smm append_id_w(wp, id / 10); 556231200Smm *(*wp)++ = L"0123456789"[id % 10]; 557231200Smm} 558231200Smm 559231200Smmstatic void 560231200Smmappend_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, 561231200Smm const wchar_t *wname, int perm, int id) 562231200Smm{ 563231200Smm if (prefix != NULL) { 564231200Smm wcscpy(*wp, prefix); 565231200Smm *wp += wcslen(*wp); 566231200Smm } 567231200Smm switch (tag) { 568231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 569231200Smm wname = NULL; 570231200Smm id = -1; 571231200Smm /* FALLTHROUGH */ 572231200Smm case ARCHIVE_ENTRY_ACL_USER: 573231200Smm wcscpy(*wp, L"user"); 574231200Smm break; 575231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 576231200Smm wname = NULL; 577231200Smm id = -1; 578231200Smm /* FALLTHROUGH */ 579231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 580231200Smm wcscpy(*wp, L"group"); 581231200Smm break; 582231200Smm case ARCHIVE_ENTRY_ACL_MASK: 583231200Smm wcscpy(*wp, L"mask"); 584231200Smm wname = NULL; 585231200Smm id = -1; 586231200Smm break; 587231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 588231200Smm wcscpy(*wp, L"other"); 589231200Smm wname = NULL; 590231200Smm id = -1; 591231200Smm break; 592231200Smm } 593231200Smm *wp += wcslen(*wp); 594231200Smm *(*wp)++ = L':'; 595231200Smm if (wname != NULL) { 596231200Smm wcscpy(*wp, wname); 597231200Smm *wp += wcslen(*wp); 598231200Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER 599231200Smm || tag == ARCHIVE_ENTRY_ACL_GROUP) { 600231200Smm append_id_w(wp, id); 601231200Smm id = -1; 602231200Smm } 603231200Smm *(*wp)++ = L':'; 604231200Smm *(*wp)++ = (perm & 0444) ? L'r' : L'-'; 605231200Smm *(*wp)++ = (perm & 0222) ? L'w' : L'-'; 606231200Smm *(*wp)++ = (perm & 0111) ? L'x' : L'-'; 607231200Smm if (id != -1) { 608231200Smm *(*wp)++ = L':'; 609231200Smm append_id_w(wp, id); 610231200Smm } 611231200Smm **wp = L'\0'; 612231200Smm} 613231200Smm 614231200Smmint 615231200Smmarchive_acl_text_l(struct archive_acl *acl, int flags, 616231200Smm const char **acl_text, size_t *acl_text_len, 617231200Smm struct archive_string_conv *sc) 618231200Smm{ 619231200Smm int count; 620231200Smm size_t length; 621231200Smm const char *name; 622231200Smm const char *prefix; 623231200Smm char separator; 624231200Smm struct archive_acl_entry *ap; 625231200Smm size_t len; 626231200Smm int id, r; 627231200Smm char *p; 628231200Smm 629231200Smm if (acl->acl_text != NULL) { 630231200Smm free (acl->acl_text); 631231200Smm acl->acl_text = NULL; 632231200Smm } 633231200Smm 634231200Smm *acl_text = NULL; 635231200Smm if (acl_text_len != NULL) 636231200Smm *acl_text_len = 0; 637231200Smm separator = ','; 638231200Smm count = 0; 639231200Smm length = 0; 640231200Smm ap = acl->acl_head; 641231200Smm while (ap != NULL) { 642231200Smm if ((ap->type & flags) != 0) { 643231200Smm count++; 644231200Smm if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && 645231200Smm (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) 646231200Smm length += 8; /* "default:" */ 647231200Smm length += 5; /* tag name */ 648231200Smm length += 1; /* colon */ 649231200Smm r = archive_mstring_get_mbs_l( 650231200Smm &ap->name, &name, &len, sc); 651231200Smm if (r != 0) 652231200Smm return (-1); 653231200Smm if (len > 0 && name != NULL) 654231200Smm length += len; 655231200Smm else 656231200Smm length += sizeof(uid_t) * 3 + 1; 657231200Smm length ++; /* colon */ 658231200Smm length += 3; /* rwx */ 659231200Smm length += 1; /* colon */ 660231200Smm length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; 661231200Smm length ++; /* newline */ 662231200Smm } 663231200Smm ap = ap->next; 664231200Smm } 665231200Smm 666231200Smm if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { 667231200Smm length += 10; /* "user::rwx\n" */ 668231200Smm length += 11; /* "group::rwx\n" */ 669231200Smm length += 11; /* "other::rwx\n" */ 670231200Smm } 671231200Smm 672231200Smm if (count == 0) 673231200Smm return (0); 674231200Smm 675231200Smm /* Now, allocate the string and actually populate it. */ 676231200Smm p = acl->acl_text = (char *)malloc(length); 677231200Smm if (p == NULL) 678231200Smm __archive_errx(1, "No memory to generate the text version of the ACL"); 679231200Smm count = 0; 680231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { 681231200Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, 682231200Smm acl->mode & 0700, -1); 683231200Smm *p++ = ','; 684231200Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, 685231200Smm acl->mode & 0070, -1); 686231200Smm *p++ = ','; 687231200Smm append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, 688231200Smm acl->mode & 0007, -1); 689231200Smm count += 3; 690231200Smm 691231200Smm for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 692231200Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0) 693231200Smm continue; 694231200Smm r = archive_mstring_get_mbs_l( 695231200Smm &ap->name, &name, &len, sc); 696231200Smm if (r != 0) 697231200Smm return (-1); 698231200Smm *p++ = separator; 699231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 700231200Smm id = ap->id; 701231200Smm else 702231200Smm id = -1; 703231200Smm append_entry(&p, NULL, ap->tag, name, 704231200Smm ap->permset, id); 705231200Smm count++; 706231200Smm } 707231200Smm } 708231200Smm 709231200Smm 710231200Smm if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { 711231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) 712231200Smm prefix = "default:"; 713231200Smm else 714231200Smm prefix = NULL; 715231200Smm count = 0; 716231200Smm for (ap = acl->acl_head; ap != NULL; ap = ap->next) { 717231200Smm if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0) 718231200Smm continue; 719231200Smm r = archive_mstring_get_mbs_l( 720231200Smm &ap->name, &name, &len, sc); 721231200Smm if (r != 0) 722231200Smm return (-1); 723231200Smm if (count > 0) 724231200Smm *p++ = separator; 725231200Smm if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) 726231200Smm id = ap->id; 727231200Smm else 728231200Smm id = -1; 729231200Smm append_entry(&p, prefix, ap->tag, 730231200Smm name, ap->permset, id); 731231200Smm count ++; 732231200Smm } 733231200Smm } 734231200Smm 735231200Smm *acl_text = acl->acl_text; 736231200Smm if (acl_text_len != NULL) 737231200Smm *acl_text_len = strlen(acl->acl_text); 738231200Smm return (0); 739231200Smm} 740231200Smm 741231200Smmstatic void 742231200Smmappend_id(char **p, int id) 743231200Smm{ 744231200Smm if (id < 0) 745231200Smm id = 0; 746231200Smm if (id > 9) 747231200Smm append_id(p, id / 10); 748231200Smm *(*p)++ = "0123456789"[id % 10]; 749231200Smm} 750231200Smm 751231200Smmstatic void 752231200Smmappend_entry(char **p, const char *prefix, int tag, 753231200Smm const char *name, int perm, int id) 754231200Smm{ 755231200Smm if (prefix != NULL) { 756231200Smm strcpy(*p, prefix); 757231200Smm *p += strlen(*p); 758231200Smm } 759231200Smm switch (tag) { 760231200Smm case ARCHIVE_ENTRY_ACL_USER_OBJ: 761231200Smm name = NULL; 762231200Smm id = -1; 763231200Smm /* FALLTHROUGH */ 764231200Smm case ARCHIVE_ENTRY_ACL_USER: 765231200Smm strcpy(*p, "user"); 766231200Smm break; 767231200Smm case ARCHIVE_ENTRY_ACL_GROUP_OBJ: 768231200Smm name = NULL; 769231200Smm id = -1; 770231200Smm /* FALLTHROUGH */ 771231200Smm case ARCHIVE_ENTRY_ACL_GROUP: 772231200Smm strcpy(*p, "group"); 773231200Smm break; 774231200Smm case ARCHIVE_ENTRY_ACL_MASK: 775231200Smm strcpy(*p, "mask"); 776231200Smm name = NULL; 777231200Smm id = -1; 778231200Smm break; 779231200Smm case ARCHIVE_ENTRY_ACL_OTHER: 780231200Smm strcpy(*p, "other"); 781231200Smm name = NULL; 782231200Smm id = -1; 783231200Smm break; 784231200Smm } 785231200Smm *p += strlen(*p); 786231200Smm *(*p)++ = ':'; 787231200Smm if (name != NULL) { 788231200Smm strcpy(*p, name); 789231200Smm *p += strlen(*p); 790231200Smm } else if (tag == ARCHIVE_ENTRY_ACL_USER 791231200Smm || tag == ARCHIVE_ENTRY_ACL_GROUP) { 792231200Smm append_id(p, id); 793231200Smm id = -1; 794231200Smm } 795231200Smm *(*p)++ = ':'; 796231200Smm *(*p)++ = (perm & 0444) ? 'r' : '-'; 797231200Smm *(*p)++ = (perm & 0222) ? 'w' : '-'; 798231200Smm *(*p)++ = (perm & 0111) ? 'x' : '-'; 799231200Smm if (id != -1) { 800231200Smm *(*p)++ = ':'; 801231200Smm append_id(p, id); 802231200Smm } 803231200Smm **p = '\0'; 804231200Smm} 805231200Smm 806231200Smm/* 807231200Smm * Parse a textual ACL. This automatically recognizes and supports 808231200Smm * extensions described above. The 'type' argument is used to 809231200Smm * indicate the type that should be used for any entries not 810231200Smm * explicitly marked as "default:". 811231200Smm */ 812231200Smmint 813231200Smmarchive_acl_parse_w(struct archive_acl *acl, 814231200Smm const wchar_t *text, int default_type) 815231200Smm{ 816231200Smm struct { 817231200Smm const wchar_t *start; 818231200Smm const wchar_t *end; 819231200Smm } field[4], name; 820231200Smm 821231200Smm int fields, n; 822231200Smm int type, tag, permset, id; 823231200Smm wchar_t sep; 824231200Smm 825231200Smm while (text != NULL && *text != L'\0') { 826231200Smm /* 827231200Smm * Parse the fields out of the next entry, 828231200Smm * advance 'text' to start of next entry. 829231200Smm */ 830231200Smm fields = 0; 831231200Smm do { 832231200Smm const wchar_t *start, *end; 833231200Smm next_field_w(&text, &start, &end, &sep); 834231200Smm if (fields < 4) { 835231200Smm field[fields].start = start; 836231200Smm field[fields].end = end; 837231200Smm } 838231200Smm ++fields; 839231200Smm } while (sep == L':'); 840231200Smm 841231200Smm /* Set remaining fields to blank. */ 842231200Smm for (n = fields; n < 4; ++n) 843231200Smm field[n].start = field[n].end = NULL; 844231200Smm 845231200Smm /* Check for a numeric ID in field 1 or 3. */ 846231200Smm id = -1; 847231200Smm isint_w(field[1].start, field[1].end, &id); 848231200Smm /* Field 3 is optional. */ 849231200Smm if (id == -1 && fields > 3) 850231200Smm isint_w(field[3].start, field[3].end, &id); 851231200Smm 852231200Smm /* 853231200Smm * Solaris extension: "defaultuser::rwx" is the 854231200Smm * default ACL corresponding to "user::rwx", etc. 855231200Smm */ 856231200Smm if (field[0].end - field[0].start > 7 857231200Smm && wmemcmp(field[0].start, L"default", 7) == 0) { 858231200Smm type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 859231200Smm field[0].start += 7; 860231200Smm } else 861231200Smm type = default_type; 862231200Smm 863231200Smm name.start = name.end = NULL; 864231200Smm if (prefix_w(field[0].start, field[0].end, L"user")) { 865231200Smm if (!ismode_w(field[2].start, field[2].end, &permset)) 866231200Smm return (ARCHIVE_WARN); 867231200Smm if (id != -1 || field[1].start < field[1].end) { 868231200Smm tag = ARCHIVE_ENTRY_ACL_USER; 869231200Smm name = field[1]; 870231200Smm } else 871231200Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 872231200Smm } else if (prefix_w(field[0].start, field[0].end, L"group")) { 873231200Smm if (!ismode_w(field[2].start, field[2].end, &permset)) 874231200Smm return (ARCHIVE_WARN); 875231200Smm if (id != -1 || field[1].start < field[1].end) { 876231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 877231200Smm name = field[1]; 878231200Smm } else 879231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 880231200Smm } else if (prefix_w(field[0].start, field[0].end, L"other")) { 881231200Smm if (fields == 2 882231200Smm && field[1].start < field[1].end 883231200Smm && ismode_w(field[1].start, field[1].end, &permset)) { 884231200Smm /* This is Solaris-style "other:rwx" */ 885231200Smm } else if (fields == 3 886231200Smm && field[1].start == field[1].end 887231200Smm && field[2].start < field[2].end 888231200Smm && ismode_w(field[2].start, field[2].end, &permset)) { 889231200Smm /* This is FreeBSD-style "other::rwx" */ 890231200Smm } else 891231200Smm return (ARCHIVE_WARN); 892231200Smm tag = ARCHIVE_ENTRY_ACL_OTHER; 893231200Smm } else if (prefix_w(field[0].start, field[0].end, L"mask")) { 894231200Smm if (fields == 2 895231200Smm && field[1].start < field[1].end 896231200Smm && ismode_w(field[1].start, field[1].end, &permset)) { 897231200Smm /* This is Solaris-style "mask:rwx" */ 898231200Smm } else if (fields == 3 899231200Smm && field[1].start == field[1].end 900231200Smm && field[2].start < field[2].end 901231200Smm && ismode_w(field[2].start, field[2].end, &permset)) { 902231200Smm /* This is FreeBSD-style "mask::rwx" */ 903231200Smm } else 904231200Smm return (ARCHIVE_WARN); 905231200Smm tag = ARCHIVE_ENTRY_ACL_MASK; 906231200Smm } else 907231200Smm return (ARCHIVE_WARN); 908231200Smm 909231200Smm /* Add entry to the internal list. */ 910231200Smm archive_acl_add_entry_w_len(acl, type, permset, 911231200Smm tag, id, name.start, name.end - name.start); 912231200Smm } 913231200Smm return (ARCHIVE_OK); 914231200Smm} 915231200Smm 916231200Smm/* 917231200Smm * Parse a string to a positive decimal integer. Returns true if 918231200Smm * the string is non-empty and consists only of decimal digits, 919231200Smm * false otherwise. 920231200Smm */ 921231200Smmstatic int 922231200Smmisint_w(const wchar_t *start, const wchar_t *end, int *result) 923231200Smm{ 924231200Smm int n = 0; 925231200Smm if (start >= end) 926231200Smm return (0); 927231200Smm while (start < end) { 928231200Smm if (*start < '0' || *start > '9') 929231200Smm return (0); 930231200Smm if (n > (INT_MAX / 10) || 931231200Smm (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 932231200Smm n = INT_MAX; 933231200Smm } else { 934231200Smm n *= 10; 935231200Smm n += *start - '0'; 936231200Smm } 937231200Smm start++; 938231200Smm } 939231200Smm *result = n; 940231200Smm return (1); 941231200Smm} 942231200Smm 943231200Smm/* 944231200Smm * Parse a string as a mode field. Returns true if 945231200Smm * the string is non-empty and consists only of mode characters, 946231200Smm * false otherwise. 947231200Smm */ 948231200Smmstatic int 949231200Smmismode_w(const wchar_t *start, const wchar_t *end, int *permset) 950231200Smm{ 951231200Smm const wchar_t *p; 952231200Smm 953231200Smm if (start >= end) 954231200Smm return (0); 955231200Smm p = start; 956231200Smm *permset = 0; 957231200Smm while (p < end) { 958231200Smm switch (*p++) { 959231200Smm case 'r': case 'R': 960231200Smm *permset |= ARCHIVE_ENTRY_ACL_READ; 961231200Smm break; 962231200Smm case 'w': case 'W': 963231200Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE; 964231200Smm break; 965231200Smm case 'x': case 'X': 966231200Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 967231200Smm break; 968231200Smm case '-': 969231200Smm break; 970231200Smm default: 971231200Smm return (0); 972231200Smm } 973231200Smm } 974231200Smm return (1); 975231200Smm} 976231200Smm 977231200Smm/* 978231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 979231200Smm * to point to just after the separator. *start points to the first 980231200Smm * character of the matched text and *end just after the last 981231200Smm * character of the matched identifier. In particular *end - *start 982231200Smm * is the length of the field body, not including leading or trailing 983231200Smm * whitespace. 984231200Smm */ 985231200Smmstatic void 986231200Smmnext_field_w(const wchar_t **wp, const wchar_t **start, 987231200Smm const wchar_t **end, wchar_t *sep) 988231200Smm{ 989231200Smm /* Skip leading whitespace to find start of field. */ 990231200Smm while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { 991231200Smm (*wp)++; 992231200Smm } 993231200Smm *start = *wp; 994231200Smm 995231200Smm /* Scan for the separator. */ 996231200Smm while (**wp != L'\0' && **wp != L',' && **wp != L':' && 997231200Smm **wp != L'\n') { 998231200Smm (*wp)++; 999231200Smm } 1000231200Smm *sep = **wp; 1001231200Smm 1002231200Smm /* Trim trailing whitespace to locate end of field. */ 1003231200Smm *end = *wp - 1; 1004231200Smm while (**end == L' ' || **end == L'\t' || **end == L'\n') { 1005231200Smm (*end)--; 1006231200Smm } 1007231200Smm (*end)++; 1008231200Smm 1009231200Smm /* Adjust scanner location. */ 1010231200Smm if (**wp != L'\0') 1011231200Smm (*wp)++; 1012231200Smm} 1013231200Smm 1014231200Smm/* 1015231200Smm * Return true if the characters [start...end) are a prefix of 'test'. 1016231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1017231200Smm */ 1018231200Smmstatic int 1019231200Smmprefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) 1020231200Smm{ 1021231200Smm if (start == end) 1022231200Smm return (0); 1023231200Smm 1024231200Smm if (*start++ != *test++) 1025231200Smm return (0); 1026231200Smm 1027231200Smm while (start < end && *start++ == *test++) 1028231200Smm ; 1029231200Smm 1030231200Smm if (start < end) 1031231200Smm return (0); 1032231200Smm 1033231200Smm return (1); 1034231200Smm} 1035231200Smm 1036231200Smm/* 1037231200Smm * Parse a textual ACL. This automatically recognizes and supports 1038231200Smm * extensions described above. The 'type' argument is used to 1039231200Smm * indicate the type that should be used for any entries not 1040231200Smm * explicitly marked as "default:". 1041231200Smm */ 1042231200Smmint 1043231200Smmarchive_acl_parse_l(struct archive_acl *acl, 1044231200Smm const char *text, int default_type, struct archive_string_conv *sc) 1045231200Smm{ 1046231200Smm struct { 1047231200Smm const char *start; 1048231200Smm const char *end; 1049231200Smm } field[4], name; 1050231200Smm 1051231200Smm int fields, n, r, ret = ARCHIVE_OK; 1052231200Smm int type, tag, permset, id; 1053231200Smm char sep; 1054231200Smm 1055231200Smm while (text != NULL && *text != '\0') { 1056231200Smm /* 1057231200Smm * Parse the fields out of the next entry, 1058231200Smm * advance 'text' to start of next entry. 1059231200Smm */ 1060231200Smm fields = 0; 1061231200Smm do { 1062231200Smm const char *start, *end; 1063231200Smm next_field(&text, &start, &end, &sep); 1064231200Smm if (fields < 4) { 1065231200Smm field[fields].start = start; 1066231200Smm field[fields].end = end; 1067231200Smm } 1068231200Smm ++fields; 1069231200Smm } while (sep == ':'); 1070231200Smm 1071231200Smm /* Set remaining fields to blank. */ 1072231200Smm for (n = fields; n < 4; ++n) 1073231200Smm field[n].start = field[n].end = NULL; 1074231200Smm 1075231200Smm /* Check for a numeric ID in field 1 or 3. */ 1076231200Smm id = -1; 1077231200Smm isint(field[1].start, field[1].end, &id); 1078231200Smm /* Field 3 is optional. */ 1079231200Smm if (id == -1 && fields > 3) 1080231200Smm isint(field[3].start, field[3].end, &id); 1081231200Smm 1082231200Smm /* 1083231200Smm * Solaris extension: "defaultuser::rwx" is the 1084231200Smm * default ACL corresponding to "user::rwx", etc. 1085231200Smm */ 1086231200Smm if (field[0].end - field[0].start > 7 1087231200Smm && memcmp(field[0].start, "default", 7) == 0) { 1088231200Smm type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 1089231200Smm field[0].start += 7; 1090231200Smm } else 1091231200Smm type = default_type; 1092231200Smm 1093231200Smm name.start = name.end = NULL; 1094232153Smm if (prefix_c(field[0].start, field[0].end, "user")) { 1095231200Smm if (!ismode(field[2].start, field[2].end, &permset)) 1096231200Smm return (ARCHIVE_WARN); 1097231200Smm if (id != -1 || field[1].start < field[1].end) { 1098231200Smm tag = ARCHIVE_ENTRY_ACL_USER; 1099231200Smm name = field[1]; 1100231200Smm } else 1101231200Smm tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 1102232153Smm } else if (prefix_c(field[0].start, field[0].end, "group")) { 1103231200Smm if (!ismode(field[2].start, field[2].end, &permset)) 1104231200Smm return (ARCHIVE_WARN); 1105231200Smm if (id != -1 || field[1].start < field[1].end) { 1106231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP; 1107231200Smm name = field[1]; 1108231200Smm } else 1109231200Smm tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 1110232153Smm } else if (prefix_c(field[0].start, field[0].end, "other")) { 1111231200Smm if (fields == 2 1112231200Smm && field[1].start < field[1].end 1113231200Smm && ismode(field[1].start, field[1].end, &permset)) { 1114231200Smm /* This is Solaris-style "other:rwx" */ 1115231200Smm } else if (fields == 3 1116231200Smm && field[1].start == field[1].end 1117231200Smm && field[2].start < field[2].end 1118231200Smm && ismode(field[2].start, field[2].end, &permset)) { 1119231200Smm /* This is FreeBSD-style "other::rwx" */ 1120231200Smm } else 1121231200Smm return (ARCHIVE_WARN); 1122231200Smm tag = ARCHIVE_ENTRY_ACL_OTHER; 1123232153Smm } else if (prefix_c(field[0].start, field[0].end, "mask")) { 1124231200Smm if (fields == 2 1125231200Smm && field[1].start < field[1].end 1126231200Smm && ismode(field[1].start, field[1].end, &permset)) { 1127231200Smm /* This is Solaris-style "mask:rwx" */ 1128231200Smm } else if (fields == 3 1129231200Smm && field[1].start == field[1].end 1130231200Smm && field[2].start < field[2].end 1131231200Smm && ismode(field[2].start, field[2].end, &permset)) { 1132231200Smm /* This is FreeBSD-style "mask::rwx" */ 1133231200Smm } else 1134231200Smm return (ARCHIVE_WARN); 1135231200Smm tag = ARCHIVE_ENTRY_ACL_MASK; 1136231200Smm } else 1137231200Smm return (ARCHIVE_WARN); 1138231200Smm 1139231200Smm /* Add entry to the internal list. */ 1140231200Smm r = archive_acl_add_entry_len_l(acl, type, permset, 1141231200Smm tag, id, name.start, name.end - name.start, sc); 1142231200Smm if (r < ARCHIVE_WARN) 1143231200Smm return (r); 1144231200Smm if (r != ARCHIVE_OK) 1145231200Smm ret = ARCHIVE_WARN; 1146231200Smm } 1147231200Smm return (ret); 1148231200Smm} 1149231200Smm 1150231200Smm/* 1151231200Smm * Parse a string to a positive decimal integer. Returns true if 1152231200Smm * the string is non-empty and consists only of decimal digits, 1153231200Smm * false otherwise. 1154231200Smm */ 1155231200Smmstatic int 1156231200Smmisint(const char *start, const char *end, int *result) 1157231200Smm{ 1158231200Smm int n = 0; 1159231200Smm if (start >= end) 1160231200Smm return (0); 1161231200Smm while (start < end) { 1162231200Smm if (*start < '0' || *start > '9') 1163231200Smm return (0); 1164231200Smm if (n > (INT_MAX / 10) || 1165231200Smm (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { 1166231200Smm n = INT_MAX; 1167231200Smm } else { 1168231200Smm n *= 10; 1169231200Smm n += *start - '0'; 1170231200Smm } 1171231200Smm start++; 1172231200Smm } 1173231200Smm *result = n; 1174231200Smm return (1); 1175231200Smm} 1176231200Smm 1177231200Smm/* 1178231200Smm * Parse a string as a mode field. Returns true if 1179231200Smm * the string is non-empty and consists only of mode characters, 1180231200Smm * false otherwise. 1181231200Smm */ 1182231200Smmstatic int 1183231200Smmismode(const char *start, const char *end, int *permset) 1184231200Smm{ 1185231200Smm const char *p; 1186231200Smm 1187231200Smm if (start >= end) 1188231200Smm return (0); 1189231200Smm p = start; 1190231200Smm *permset = 0; 1191231200Smm while (p < end) { 1192231200Smm switch (*p++) { 1193231200Smm case 'r': case 'R': 1194231200Smm *permset |= ARCHIVE_ENTRY_ACL_READ; 1195231200Smm break; 1196231200Smm case 'w': case 'W': 1197231200Smm *permset |= ARCHIVE_ENTRY_ACL_WRITE; 1198231200Smm break; 1199231200Smm case 'x': case 'X': 1200231200Smm *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 1201231200Smm break; 1202231200Smm case '-': 1203231200Smm break; 1204231200Smm default: 1205231200Smm return (0); 1206231200Smm } 1207231200Smm } 1208231200Smm return (1); 1209231200Smm} 1210231200Smm 1211231200Smm/* 1212231200Smm * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated 1213231200Smm * to point to just after the separator. *start points to the first 1214231200Smm * character of the matched text and *end just after the last 1215231200Smm * character of the matched identifier. In particular *end - *start 1216231200Smm * is the length of the field body, not including leading or trailing 1217231200Smm * whitespace. 1218231200Smm */ 1219231200Smmstatic void 1220231200Smmnext_field(const char **p, const char **start, 1221231200Smm const char **end, char *sep) 1222231200Smm{ 1223231200Smm /* Skip leading whitespace to find start of field. */ 1224231200Smm while (**p == ' ' || **p == '\t' || **p == '\n') { 1225231200Smm (*p)++; 1226231200Smm } 1227231200Smm *start = *p; 1228231200Smm 1229231200Smm /* Scan for the separator. */ 1230231200Smm while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { 1231231200Smm (*p)++; 1232231200Smm } 1233231200Smm *sep = **p; 1234231200Smm 1235231200Smm /* Trim trailing whitespace to locate end of field. */ 1236231200Smm *end = *p - 1; 1237231200Smm while (**end == ' ' || **end == '\t' || **end == '\n') { 1238231200Smm (*end)--; 1239231200Smm } 1240231200Smm (*end)++; 1241231200Smm 1242231200Smm /* Adjust scanner location. */ 1243231200Smm if (**p != '\0') 1244231200Smm (*p)++; 1245231200Smm} 1246231200Smm 1247231200Smm/* 1248231200Smm * Return true if the characters [start...end) are a prefix of 'test'. 1249231200Smm * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. 1250231200Smm */ 1251231200Smmstatic int 1252232153Smmprefix_c(const char *start, const char *end, const char *test) 1253231200Smm{ 1254231200Smm if (start == end) 1255231200Smm return (0); 1256231200Smm 1257231200Smm if (*start++ != *test++) 1258231200Smm return (0); 1259231200Smm 1260231200Smm while (start < end && *start++ == *test++) 1261231200Smm ; 1262231200Smm 1263231200Smm if (start < end) 1264231200Smm return (0); 1265231200Smm 1266231200Smm return (1); 1267231200Smm} 1268