Path: blob/master/Utilities/cmlibarchive/libarchive/archive_acl.c
5053 views
/*-1* Copyright (c) 2003-2010 Tim Kientzle2* Copyright (c) 2016 Martin Matuska3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT19* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,20* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY21* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT22* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF23* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.24*/2526#include "archive_platform.h"2728#ifdef HAVE_ERRNO_H29#include <errno.h>30#endif31#ifdef HAVE_LIMITS_H32#include <limits.h>33#endif34#ifdef HAVE_WCHAR_H35#include <wchar.h>36#endif3738#ifdef __clang_analyzer__39#include <assert.h>40#endif4142#include "archive_acl_private.h"43#include "archive_entry.h"44#include "archive_private.h"4546#undef max47#define max(a, b) ((a)>(b)?(a):(b))4849#ifndef HAVE_WMEMCMP50/* Good enough for simple equality testing, but not for sorting. */51#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))52#endif5354static int acl_special(struct archive_acl *acl,55int type, int permset, int tag);56static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,57int type, int permset, int tag, int id);58static int archive_acl_add_entry_len_l(struct archive_acl *acl,59int type, int permset, int tag, int id, const char *name,60size_t len, struct archive_string_conv *sc);61static int archive_acl_text_want_type(struct archive_acl *acl, int flags);62static size_t archive_acl_text_len(struct archive_acl *acl, int want_type,63int flags, int wide, struct archive *a,64struct archive_string_conv *sc);65static int isint_w(const wchar_t *start, const wchar_t *end, int *result);66static int ismode_w(const wchar_t *start, const wchar_t *end, int *result);67static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,68int *result);69static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,70int *result);71static void next_field_w(const wchar_t **wp, const wchar_t **start,72const wchar_t **end, wchar_t *sep);73static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,74int tag, int flags, const wchar_t *wname, int perm, int id);75static void append_id_w(wchar_t **wp, int id);76static int isint(const char *start, const char *end, int *result);77static int ismode(const char *start, const char *end, int *result);78static int is_nfs4_flags(const char *start, const char *end,79int *result);80static int is_nfs4_perms(const char *start, const char *end,81int *result);82static void next_field(const char **p, size_t *l, const char **start,83const char **end, char *sep);84static void append_entry(char **p, const char *prefix, int type,85int tag, int flags, const char *name, int perm, int id);86static void append_id(char **p, int id);8788static const struct {89const int perm;90const char c;91const wchar_t wc;92} nfsv4_acl_perm_map[] = {93{ ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',94L'r' },95{ ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',96L'w' },97{ ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },98{ ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,99'p', L'p' },100{ ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },101{ ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },102{ ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },103{ ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },104{ ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },105{ ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },106{ ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },107{ ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },108{ ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },109{ ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }110};111112static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /113sizeof(nfsv4_acl_perm_map[0]));114115static const struct {116const int perm;117const char c;118const wchar_t wc;119} nfsv4_acl_flag_map[] = {120{ ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },121{ ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },122{ ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },123{ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },124{ ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },125{ ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },126{ ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }127};128129static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /130sizeof(nfsv4_acl_flag_map[0]));131132void133archive_acl_clear(struct archive_acl *acl)134{135struct archive_acl_entry *ap;136137while (acl->acl_head != NULL) {138ap = acl->acl_head->next;139archive_mstring_clean(&acl->acl_head->name);140free(acl->acl_head);141acl->acl_head = ap;142}143free(acl->acl_text_w);144acl->acl_text_w = NULL;145free(acl->acl_text);146acl->acl_text = NULL;147acl->acl_p = NULL;148acl->acl_types = 0;149acl->acl_state = 0; /* Not counting. */150}151152void153archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)154{155struct archive_acl_entry *ap, *ap2;156157archive_acl_clear(dest);158159dest->mode = src->mode;160ap = src->acl_head;161while (ap != NULL) {162ap2 = acl_new_entry(dest,163ap->type, ap->permset, ap->tag, ap->id);164if (ap2 != NULL)165archive_mstring_copy(&ap2->name, &ap->name);166ap = ap->next;167}168}169170int171archive_acl_add_entry(struct archive_acl *acl,172int type, int permset, int tag, int id, const char *name)173{174struct archive_acl_entry *ap;175176if (acl_special(acl, type, permset, tag) == 0)177return ARCHIVE_OK;178ap = acl_new_entry(acl, type, permset, tag, id);179if (ap == NULL) {180/* XXX Error XXX */181return ARCHIVE_FAILED;182}183if (name != NULL && *name != '\0')184archive_mstring_copy_mbs(&ap->name, name);185else186archive_mstring_clean(&ap->name);187return ARCHIVE_OK;188}189190int191archive_acl_add_entry_w_len(struct archive_acl *acl,192int type, int permset, int tag, int id, const wchar_t *name, size_t len)193{194struct archive_acl_entry *ap;195196if (acl_special(acl, type, permset, tag) == 0)197return ARCHIVE_OK;198ap = acl_new_entry(acl, type, permset, tag, id);199if (ap == NULL) {200/* XXX Error XXX */201return ARCHIVE_FAILED;202}203if (name != NULL && *name != L'\0' && len > 0)204archive_mstring_copy_wcs_len(&ap->name, name, len);205else206archive_mstring_clean(&ap->name);207return ARCHIVE_OK;208}209210static int211archive_acl_add_entry_len_l(struct archive_acl *acl,212int type, int permset, int tag, int id, const char *name, size_t len,213struct archive_string_conv *sc)214{215struct archive_acl_entry *ap;216int r;217218if (acl_special(acl, type, permset, tag) == 0)219return ARCHIVE_OK;220ap = acl_new_entry(acl, type, permset, tag, id);221if (ap == NULL) {222/* XXX Error XXX */223return ARCHIVE_FAILED;224}225if (name != NULL && *name != '\0' && len > 0) {226r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);227} else {228r = 0;229archive_mstring_clean(&ap->name);230}231if (r == 0)232return (ARCHIVE_OK);233else if (errno == ENOMEM)234return (ARCHIVE_FATAL);235else236return (ARCHIVE_WARN);237}238239/*240* If this ACL entry is part of the standard POSIX permissions set,241* store the permissions in the stat structure and return zero.242*/243static int244acl_special(struct archive_acl *acl, int type, int permset, int tag)245{246if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS247&& ((permset & ~007) == 0)) {248switch (tag) {249case ARCHIVE_ENTRY_ACL_USER_OBJ:250acl->mode &= ~0700;251acl->mode |= (permset & 7) << 6;252return (0);253case ARCHIVE_ENTRY_ACL_GROUP_OBJ:254acl->mode &= ~0070;255acl->mode |= (permset & 7) << 3;256return (0);257case ARCHIVE_ENTRY_ACL_OTHER:258acl->mode &= ~0007;259acl->mode |= permset & 7;260return (0);261}262}263return (1);264}265266/*267* Allocate and populate a new ACL entry with everything but the268* name.269*/270static struct archive_acl_entry *271acl_new_entry(struct archive_acl *acl,272int type, int permset, int tag, int id)273{274struct archive_acl_entry *ap, *aq;275276/* Reject an invalid type */277switch (type) {278case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:279case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:280case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:281case ARCHIVE_ENTRY_ACL_TYPE_DENY:282case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:283case ARCHIVE_ENTRY_ACL_TYPE_ALARM:284break;285default:286return (NULL);287}288289/* Type argument must be a valid NFS4 or POSIX.1e type.290* The type must agree with anything already set and291* the permset must be compatible. */292if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {293if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {294return (NULL);295}296if (permset &297~(ARCHIVE_ENTRY_ACL_PERMS_NFS4298| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {299return (NULL);300}301} else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {302if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {303return (NULL);304}305if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {306return (NULL);307}308} else {309return (NULL);310}311312/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */313switch (tag) {314case ARCHIVE_ENTRY_ACL_USER:315case ARCHIVE_ENTRY_ACL_USER_OBJ:316case ARCHIVE_ENTRY_ACL_GROUP:317case ARCHIVE_ENTRY_ACL_GROUP_OBJ:318/* Tags valid in both NFS4 and POSIX.1e */319break;320case ARCHIVE_ENTRY_ACL_MASK:321case ARCHIVE_ENTRY_ACL_OTHER:322/* Tags valid only in POSIX.1e. */323if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {324return (NULL);325}326break;327case ARCHIVE_ENTRY_ACL_EVERYONE:328/* Tags valid only in NFS4. */329if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {330return (NULL);331}332break;333default:334/* No other values are valid. */335return (NULL);336}337338free(acl->acl_text_w);339acl->acl_text_w = NULL;340free(acl->acl_text);341acl->acl_text = NULL;342343/*344* If there's a matching entry already in the list, overwrite it.345* NFSv4 entries may be repeated and are not overwritten.346*347* TODO: compare names of no id is provided (needs more rework)348*/349ap = acl->acl_head;350aq = NULL;351while (ap != NULL) {352if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&353ap->type == type && ap->tag == tag && ap->id == id) {354if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&355tag != ARCHIVE_ENTRY_ACL_GROUP)) {356ap->permset = permset;357return (ap);358}359}360aq = ap;361ap = ap->next;362}363364/* Add a new entry to the end of the list. */365ap = calloc(1, sizeof(*ap));366if (ap == NULL)367return (NULL);368if (aq == NULL)369acl->acl_head = ap;370else371aq->next = ap;372ap->type = type;373ap->tag = tag;374ap->id = id;375ap->permset = permset;376acl->acl_types |= type;377return (ap);378}379380/*381* Return a count of entries matching "want_type".382*/383int384archive_acl_count(struct archive_acl *acl, int want_type)385{386int count;387struct archive_acl_entry *ap;388389count = 0;390ap = acl->acl_head;391while (ap != NULL) {392if ((ap->type & want_type) != 0)393count++;394ap = ap->next;395}396397if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))398count += 3;399return (count);400}401402/*403* Return a bitmask of stored ACL types in an ACL list404*/405int406archive_acl_types(struct archive_acl *acl)407{408return (acl->acl_types);409}410411/*412* Prepare for reading entries from the ACL data. Returns a count413* of entries matching "want_type", or zero if there are no414* non-extended ACL entries of that type.415*/416int417archive_acl_reset(struct archive_acl *acl, int want_type)418{419int count, cutoff;420421count = archive_acl_count(acl, want_type);422423/*424* If the only entries are the three standard ones,425* then don't return any ACL data. (In this case,426* client can just use chmod(2) to set permissions.)427*/428if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)429cutoff = 3;430else431cutoff = 0;432433if (count > cutoff)434acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;435else436acl->acl_state = 0;437acl->acl_p = acl->acl_head;438return (count);439}440441442/*443* Return the next ACL entry in the list. Fake entries for the444* standard permissions and include them in the returned list.445*/446int447archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,448int *type, int *permset, int *tag, int *id, const char **name)449{450*name = NULL;451*id = -1;452453/*454* The acl_state is either zero (no entries available), -1455* (reading from list), or an entry type (retrieve that type456* from ae_stat.aest_mode).457*/458if (acl->acl_state == 0)459return (ARCHIVE_WARN);460461/* The first three access entries are special. */462if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {463switch (acl->acl_state) {464case ARCHIVE_ENTRY_ACL_USER_OBJ:465*permset = (acl->mode >> 6) & 7;466*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;467*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;468acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;469return (ARCHIVE_OK);470case ARCHIVE_ENTRY_ACL_GROUP_OBJ:471*permset = (acl->mode >> 3) & 7;472*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;473*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;474acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;475return (ARCHIVE_OK);476case ARCHIVE_ENTRY_ACL_OTHER:477*permset = acl->mode & 7;478*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;479*tag = ARCHIVE_ENTRY_ACL_OTHER;480acl->acl_state = -1;481acl->acl_p = acl->acl_head;482return (ARCHIVE_OK);483default:484break;485}486}487488while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)489acl->acl_p = acl->acl_p->next;490if (acl->acl_p == NULL) {491acl->acl_state = 0;492*type = 0;493*permset = 0;494*tag = 0;495*id = -1;496*name = NULL;497return (ARCHIVE_EOF); /* End of ACL entries. */498}499*type = acl->acl_p->type;500*permset = acl->acl_p->permset;501*tag = acl->acl_p->tag;502*id = acl->acl_p->id;503if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {504if (errno == ENOMEM)505return (ARCHIVE_FATAL);506*name = NULL;507}508acl->acl_p = acl->acl_p->next;509return (ARCHIVE_OK);510}511512/*513* Determine what type of ACL do we want514*/515static int516archive_acl_text_want_type(struct archive_acl *acl, int flags)517{518int want_type;519520/* Check if ACL is NFSv4 */521if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {522/* NFSv4 should never mix with POSIX.1e */523if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)524return (0);525else526return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);527}528529/* Now deal with POSIX.1e ACLs */530531want_type = 0;532if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)533want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;534if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)535want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;536537/* By default we want both access and default ACLs */538if (want_type == 0)539return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);540541return (want_type);542}543544/*545* Calculate ACL text string length546*/547static size_t548archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,549int wide, struct archive *a, struct archive_string_conv *sc) {550struct archive_acl_entry *ap;551const char *name;552const wchar_t *wname;553int count, idlen, tmp, r;554size_t length;555size_t len;556557count = 0;558length = 0;559for (ap = acl->acl_head; ap != NULL; ap = ap->next) {560if ((ap->type & want_type) == 0)561continue;562/*563* Filemode-mapping ACL entries are stored exclusively in564* ap->mode so they should not be in the list565*/566if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)567&& (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ568|| ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ569|| ap->tag == ARCHIVE_ENTRY_ACL_OTHER))570continue;571count++;572if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0573&& (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)574length += 8; /* "default:" */575switch (ap->tag) {576case ARCHIVE_ENTRY_ACL_USER_OBJ:577if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {578length += 6; /* "owner@" */579break;580}581/* FALLTHROUGH */582case ARCHIVE_ENTRY_ACL_USER:583case ARCHIVE_ENTRY_ACL_MASK:584length += 4; /* "user", "mask" */585break;586case ARCHIVE_ENTRY_ACL_GROUP_OBJ:587if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {588length += 6; /* "group@" */589break;590}591/* FALLTHROUGH */592case ARCHIVE_ENTRY_ACL_GROUP:593case ARCHIVE_ENTRY_ACL_OTHER:594length += 5; /* "group", "other" */595break;596case ARCHIVE_ENTRY_ACL_EVERYONE:597length += 9; /* "everyone@" */598break;599}600length += 1; /* colon after tag */601if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||602ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {603if (wide) {604r = archive_mstring_get_wcs(a, &ap->name,605&wname);606if (r == 0 && wname != NULL)607length += wcslen(wname);608else if (r < 0 && errno == ENOMEM)609return (0);610else611length += sizeof(uid_t) * 3 + 1;612} else {613r = archive_mstring_get_mbs_l(a, &ap->name, &name,614&len, sc);615if (r != 0)616return (0);617if (len > 0 && name != NULL)618length += len;619else620length += sizeof(uid_t) * 3 + 1;621}622length += 1; /* colon after user or group name */623} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)624length += 1; /* 2nd colon empty user,group or other */625626if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)627&& ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)628&& (ap->tag == ARCHIVE_ENTRY_ACL_OTHER629|| ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {630/* Solaris has no colon after other: and mask: */631length = length - 1;632}633634if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {635/* rwxpdDaARWcCos:fdinSFI:deny */636length += 27;637if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)638length += 1; /* allow, alarm, audit */639} else640length += 3; /* rwx */641642if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||643ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&644(flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {645length += 1; /* colon */646/* ID digit count */647idlen = 1;648tmp = ap->id;649while (tmp > 9) {650tmp = tmp / 10;651idlen++;652}653length += idlen;654}655length ++; /* entry separator */656}657658/* Add filemode-mapping access entries to the length */659if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {660if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {661/* "user::rwx\ngroup::rwx\nother:rwx\n" */662length += 31;663} else {664/* "user::rwx\ngroup::rwx\nother::rwx\n" */665length += 32;666}667} else if (count == 0)668return (0);669670/* The terminating character is included in count */671return (length);672}673674/*675* Generate a wide text version of the ACL. The flags parameter controls676* the type and style of the generated ACL.677*/678wchar_t *679archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,680struct archive *a)681{682int count;683size_t length;684size_t len;685const wchar_t *wname;686const wchar_t *prefix;687wchar_t separator;688struct archive_acl_entry *ap;689int id, r, want_type;690wchar_t *wp, *ws;691692want_type = archive_acl_text_want_type(acl, flags);693694/* Both NFSv4 and POSIX.1 types found */695if (want_type == 0)696return (NULL);697698if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)699flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;700701length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);702703if (length == 0)704return (NULL);705706if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)707separator = L',';708else709separator = L'\n';710711/* Now, allocate the string and actually populate it. */712wp = ws = malloc(length * sizeof(*wp));713if (wp == NULL) {714if (errno == ENOMEM)715__archive_errx(1, "No memory");716return (NULL);717}718count = 0;719720if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {721append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,722ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,723acl->mode & 0700, -1);724*wp++ = separator;725append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,726ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,727acl->mode & 0070, -1);728*wp++ = separator;729append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,730ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,731acl->mode & 0007, -1);732count += 3;733}734735for (ap = acl->acl_head; ap != NULL; ap = ap->next) {736if ((ap->type & want_type) == 0)737continue;738/*739* Filemode-mapping ACL entries are stored exclusively in740* ap->mode so they should not be in the list741*/742if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)743&& (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ744|| ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ745|| ap->tag == ARCHIVE_ENTRY_ACL_OTHER))746continue;747if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&748(flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)749prefix = L"default:";750else751prefix = NULL;752r = archive_mstring_get_wcs(a, &ap->name, &wname);753if (r == 0) {754if (count > 0)755*wp++ = separator;756if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)757id = ap->id;758else759id = -1;760append_entry_w(&wp, prefix, ap->type, ap->tag, flags,761wname, ap->permset, id);762count++;763} else if (r < 0 && errno == ENOMEM) {764free(ws);765return (NULL);766}767}768769/* Add terminating character */770*wp++ = L'\0';771772len = wcslen(ws);773774if (len > length - 1)775__archive_errx(1, "Buffer overrun");776777if (text_len != NULL)778*text_len = len;779780return (ws);781}782783static void784append_id_w(wchar_t **wp, int id)785{786if (id < 0)787id = 0;788if (id > 9)789append_id_w(wp, id / 10);790*(*wp)++ = L"0123456789"[id % 10];791}792793static void794append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,795int tag, int flags, const wchar_t *wname, int perm, int id)796{797int i;798799if (prefix != NULL) {800wcscpy(*wp, prefix);801*wp += wcslen(*wp);802}803switch (tag) {804case ARCHIVE_ENTRY_ACL_USER_OBJ:805wname = NULL;806id = -1;807if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {808wcscpy(*wp, L"owner@");809break;810}811/* FALLTHROUGH */812case ARCHIVE_ENTRY_ACL_USER:813wcscpy(*wp, L"user");814break;815case ARCHIVE_ENTRY_ACL_GROUP_OBJ:816wname = NULL;817id = -1;818if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {819wcscpy(*wp, L"group@");820break;821}822/* FALLTHROUGH */823case ARCHIVE_ENTRY_ACL_GROUP:824wcscpy(*wp, L"group");825break;826case ARCHIVE_ENTRY_ACL_MASK:827wcscpy(*wp, L"mask");828wname = NULL;829id = -1;830break;831case ARCHIVE_ENTRY_ACL_OTHER:832wcscpy(*wp, L"other");833wname = NULL;834id = -1;835break;836case ARCHIVE_ENTRY_ACL_EVERYONE:837wcscpy(*wp, L"everyone@");838wname = NULL;839id = -1;840break;841default:842**wp = '\0';843break;844}845*wp += wcslen(*wp);846*(*wp)++ = L':';847if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||848tag == ARCHIVE_ENTRY_ACL_USER ||849tag == ARCHIVE_ENTRY_ACL_GROUP) {850if (wname != NULL) {851wcscpy(*wp, wname);852*wp += wcslen(*wp);853} else if (tag == ARCHIVE_ENTRY_ACL_USER854|| tag == ARCHIVE_ENTRY_ACL_GROUP) {855append_id_w(wp, id);856if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)857id = -1;858}859/* Solaris style has no second colon after other and mask */860if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)861|| (tag != ARCHIVE_ENTRY_ACL_OTHER862&& tag != ARCHIVE_ENTRY_ACL_MASK))863*(*wp)++ = L':';864}865if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {866/* POSIX.1e ACL perms */867*(*wp)++ = (perm & 0444) ? L'r' : L'-';868*(*wp)++ = (perm & 0222) ? L'w' : L'-';869*(*wp)++ = (perm & 0111) ? L'x' : L'-';870} else {871/* NFSv4 ACL perms */872for (i = 0; i < nfsv4_acl_perm_map_size; i++) {873if (perm & nfsv4_acl_perm_map[i].perm)874*(*wp)++ = nfsv4_acl_perm_map[i].wc;875else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)876*(*wp)++ = L'-';877}878*(*wp)++ = L':';879for (i = 0; i < nfsv4_acl_flag_map_size; i++) {880if (perm & nfsv4_acl_flag_map[i].perm)881*(*wp)++ = nfsv4_acl_flag_map[i].wc;882else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)883*(*wp)++ = L'-';884}885*(*wp)++ = L':';886switch (type) {887case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:888wcscpy(*wp, L"allow");889break;890case ARCHIVE_ENTRY_ACL_TYPE_DENY:891wcscpy(*wp, L"deny");892break;893case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:894wcscpy(*wp, L"audit");895break;896case ARCHIVE_ENTRY_ACL_TYPE_ALARM:897wcscpy(*wp, L"alarm");898break;899default:900*(*wp) = L'\0';901break;902}903*wp += wcslen(*wp);904}905if (id != -1) {906*(*wp)++ = L':';907append_id_w(wp, id);908}909}910911/*912* Generate a text version of the ACL. The flags parameter controls913* the type and style of the generated ACL.914*/915char *916archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,917struct archive_string_conv *sc)918{919int count;920size_t length;921size_t len;922const char *name;923const char *prefix;924char separator;925struct archive_acl_entry *ap;926int id, r, want_type;927char *p, *s;928929want_type = archive_acl_text_want_type(acl, flags);930931/* Both NFSv4 and POSIX.1 types found */932if (want_type == 0)933return (NULL);934935if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)936flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;937938length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);939940if (length == 0)941return (NULL);942943if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)944separator = ',';945else946separator = '\n';947948/* Now, allocate the string and actually populate it. */949p = s = malloc(length * sizeof(*p));950if (p == NULL) {951if (errno == ENOMEM)952__archive_errx(1, "No memory");953return (NULL);954}955count = 0;956957if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {958append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,959ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,960acl->mode & 0700, -1);961*p++ = separator;962append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,963ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,964acl->mode & 0070, -1);965*p++ = separator;966append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,967ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,968acl->mode & 0007, -1);969count += 3;970}971972for (ap = acl->acl_head; ap != NULL; ap = ap->next) {973if ((ap->type & want_type) == 0)974continue;975/*976* Filemode-mapping ACL entries are stored exclusively in977* ap->mode so they should not be in the list978*/979if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)980&& (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ981|| ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ982|| ap->tag == ARCHIVE_ENTRY_ACL_OTHER))983continue;984if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&985(flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)986prefix = "default:";987else988prefix = NULL;989r = archive_mstring_get_mbs_l(990NULL, &ap->name, &name, &len, sc);991if (r != 0) {992free(s);993return (NULL);994}995if (count > 0)996*p++ = separator;997if (name == NULL ||998(flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {999id = ap->id;1000} else {1001id = -1;1002}1003append_entry(&p, prefix, ap->type, ap->tag, flags, name,1004ap->permset, id);1005count++;1006}10071008/* Add terminating character */1009*p++ = '\0';10101011len = strlen(s);10121013if (len > length - 1)1014__archive_errx(1, "Buffer overrun");10151016if (text_len != NULL)1017*text_len = len;10181019return (s);1020}10211022static void1023append_id(char **p, int id)1024{1025if (id < 0)1026id = 0;1027if (id > 9)1028append_id(p, id / 10);1029*(*p)++ = "0123456789"[id % 10];1030}10311032static void1033append_entry(char **p, const char *prefix, int type,1034int tag, int flags, const char *name, int perm, int id)1035{1036int i;10371038if (prefix != NULL) {1039strcpy(*p, prefix);1040*p += strlen(*p);1041}1042switch (tag) {1043case ARCHIVE_ENTRY_ACL_USER_OBJ:1044name = NULL;1045id = -1;1046if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {1047strcpy(*p, "owner@");1048break;1049}1050/* FALLTHROUGH */1051case ARCHIVE_ENTRY_ACL_USER:1052strcpy(*p, "user");1053break;1054case ARCHIVE_ENTRY_ACL_GROUP_OBJ:1055name = NULL;1056id = -1;1057if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {1058strcpy(*p, "group@");1059break;1060}1061/* FALLTHROUGH */1062case ARCHIVE_ENTRY_ACL_GROUP:1063strcpy(*p, "group");1064break;1065case ARCHIVE_ENTRY_ACL_MASK:1066strcpy(*p, "mask");1067name = NULL;1068id = -1;1069break;1070case ARCHIVE_ENTRY_ACL_OTHER:1071strcpy(*p, "other");1072name = NULL;1073id = -1;1074break;1075case ARCHIVE_ENTRY_ACL_EVERYONE:1076strcpy(*p, "everyone@");1077name = NULL;1078id = -1;1079break;1080default:1081**p = '\0';1082break;1083}1084*p += strlen(*p);1085*(*p)++ = ':';1086if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||1087tag == ARCHIVE_ENTRY_ACL_USER ||1088tag == ARCHIVE_ENTRY_ACL_GROUP) {1089if (name != NULL) {1090strcpy(*p, name);1091*p += strlen(*p);1092} else if (tag == ARCHIVE_ENTRY_ACL_USER1093|| tag == ARCHIVE_ENTRY_ACL_GROUP) {1094append_id(p, id);1095if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)1096id = -1;1097}1098/* Solaris style has no second colon after other and mask */1099if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)1100|| (tag != ARCHIVE_ENTRY_ACL_OTHER1101&& tag != ARCHIVE_ENTRY_ACL_MASK))1102*(*p)++ = ':';1103}1104if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {1105/* POSIX.1e ACL perms */1106*(*p)++ = (perm & 0444) ? 'r' : '-';1107*(*p)++ = (perm & 0222) ? 'w' : '-';1108*(*p)++ = (perm & 0111) ? 'x' : '-';1109} else {1110/* NFSv4 ACL perms */1111for (i = 0; i < nfsv4_acl_perm_map_size; i++) {1112if (perm & nfsv4_acl_perm_map[i].perm)1113*(*p)++ = nfsv4_acl_perm_map[i].c;1114else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)1115*(*p)++ = '-';1116}1117*(*p)++ = ':';1118for (i = 0; i < nfsv4_acl_flag_map_size; i++) {1119if (perm & nfsv4_acl_flag_map[i].perm)1120*(*p)++ = nfsv4_acl_flag_map[i].c;1121else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)1122*(*p)++ = '-';1123}1124*(*p)++ = ':';1125switch (type) {1126case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:1127strcpy(*p, "allow");1128break;1129case ARCHIVE_ENTRY_ACL_TYPE_DENY:1130strcpy(*p, "deny");1131break;1132case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:1133strcpy(*p, "audit");1134break;1135case ARCHIVE_ENTRY_ACL_TYPE_ALARM:1136strcpy(*p, "alarm");1137break;1138default:1139*(*p) = '\0';1140break;1141}1142*p += strlen(*p);1143}1144if (id != -1) {1145*(*p)++ = ':';1146append_id(p, id);1147}1148}11491150/*1151* Parse a wide ACL text string.1152*1153* The want_type argument may be one of the following:1154* ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS1155* ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT1156* ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL1157*1158* POSIX.1e ACL entries prefixed with "default:" are treated as1159* ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS41160*/1161int1162archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,1163int want_type)1164{1165struct {1166const wchar_t *start;1167const wchar_t *end;1168} field[6], name;11691170const wchar_t *s, *st;11711172int numfields, fields, n, r, sol, ret;1173int type, types, tag, permset, id;1174size_t len;1175wchar_t sep;11761177ret = ARCHIVE_OK;1178types = 0;11791180switch (want_type) {1181case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:1182want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;1183__LA_FALLTHROUGH;1184case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:1185case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:1186numfields = 5;1187break;1188case ARCHIVE_ENTRY_ACL_TYPE_NFS4:1189numfields = 6;1190break;1191default:1192return (ARCHIVE_FATAL);1193}11941195while (text != NULL && *text != L'\0') {1196/*1197* Parse the fields out of the next entry,1198* advance 'text' to start of next entry.1199*/1200fields = 0;1201do {1202const wchar_t *start, *end;1203next_field_w(&text, &start, &end, &sep);1204if (fields < numfields) {1205field[fields].start = start;1206field[fields].end = end;1207}1208++fields;1209} while (sep == L':');12101211/* Set remaining fields to blank. */1212for (n = fields; n < numfields; ++n)1213field[n].start = field[n].end = NULL;12141215if (field[0].start == NULL || field[0].end == NULL) {1216/* This should never happen */1217return (ARCHIVE_FATAL);1218}12191220if (*(field[0].start) == L'#') {1221/* Comment, skip entry */1222continue;1223}12241225n = 0;1226sol = 0;1227id = -1;1228permset = 0;1229name.start = name.end = NULL;12301231if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {1232/* POSIX.1e ACLs */1233/*1234* Default keyword "default:user::rwx"1235* if found, we have one more field1236*1237* We also support old Solaris extension:1238* "defaultuser::rwx" is the default ACL corresponding1239* to "user::rwx", etc. valid only for first field1240*/1241s = field[0].start;1242#ifdef __clang_analyzer__1243assert(s);1244#endif1245len = field[0].end - field[0].start;1246if (*s == L'd' && (len == 1 || (len >= 71247&& wmemcmp((s + 1), L"efault", 6) == 0))) {1248type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;1249if (len > 7)1250field[0].start += 7;1251else1252n = 1;1253} else1254type = want_type;12551256/* Check for a numeric ID in field n+1 or n+3. */1257isint_w(field[n + 1].start, field[n + 1].end, &id);1258/* Field n+3 is optional. */1259if (id == -1 && fields > n+3)1260isint_w(field[n + 3].start, field[n + 3].end,1261&id);12621263tag = 0;1264s = field[n].start;1265st = field[n].start + 1;1266len = field[n].end - field[n].start;12671268switch (*s) {1269case L'u':1270if (len == 1 || (len == 41271&& wmemcmp(st, L"ser", 3) == 0))1272tag = ARCHIVE_ENTRY_ACL_USER_OBJ;1273break;1274case L'g':1275if (len == 1 || (len == 51276&& wmemcmp(st, L"roup", 4) == 0))1277tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;1278break;1279case L'o':1280if (len == 1 || (len == 51281&& wmemcmp(st, L"ther", 4) == 0))1282tag = ARCHIVE_ENTRY_ACL_OTHER;1283break;1284case L'm':1285if (len == 1 || (len == 41286&& wmemcmp(st, L"ask", 3) == 0))1287tag = ARCHIVE_ENTRY_ACL_MASK;1288break;1289default:1290break;1291}12921293switch (tag) {1294case ARCHIVE_ENTRY_ACL_OTHER:1295case ARCHIVE_ENTRY_ACL_MASK:1296if (fields == (n + 2)1297&& field[n + 1].start < field[n + 1].end1298&& ismode_w(field[n + 1].start,1299field[n + 1].end, &permset)) {1300/* This is Solaris-style "other:rwx" */1301sol = 1;1302} else if (fields == (n + 3) &&1303field[n + 1].start < field[n + 1].end) {1304/* Invalid mask or other field */1305ret = ARCHIVE_WARN;1306continue;1307}1308break;1309case ARCHIVE_ENTRY_ACL_USER_OBJ:1310case ARCHIVE_ENTRY_ACL_GROUP_OBJ:1311if (id != -1 ||1312field[n + 1].start < field[n + 1].end) {1313name = field[n + 1];1314if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)1315tag = ARCHIVE_ENTRY_ACL_USER;1316else1317tag = ARCHIVE_ENTRY_ACL_GROUP;1318}1319break;1320default:1321/* Invalid tag, skip entry */1322ret = ARCHIVE_WARN;1323continue;1324}13251326/*1327* Without "default:" we expect mode in field 21328* Exception: Solaris other and mask fields1329*/1330if (permset == 0 && !ismode_w(field[n + 2 - sol].start,1331field[n + 2 - sol].end, &permset)) {1332/* Invalid mode, skip entry */1333ret = ARCHIVE_WARN;1334continue;1335}1336} else {1337/* NFS4 ACLs */1338s = field[0].start;1339len = field[0].end - field[0].start;1340tag = 0;13411342switch (len) {1343case 4:1344if (wmemcmp(s, L"user", 4) == 0)1345tag = ARCHIVE_ENTRY_ACL_USER;1346break;1347case 5:1348if (wmemcmp(s, L"group", 5) == 0)1349tag = ARCHIVE_ENTRY_ACL_GROUP;1350break;1351case 6:1352if (wmemcmp(s, L"owner@", 6) == 0)1353tag = ARCHIVE_ENTRY_ACL_USER_OBJ;1354else if (wmemcmp(s, L"group@", len) == 0)1355tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;1356break;1357case 9:1358if (wmemcmp(s, L"everyone@", 9) == 0)1359tag = ARCHIVE_ENTRY_ACL_EVERYONE;1360default:1361break;1362}13631364if (tag == 0) {1365/* Invalid tag, skip entry */1366ret = ARCHIVE_WARN;1367continue;1368} else if (tag == ARCHIVE_ENTRY_ACL_USER ||1369tag == ARCHIVE_ENTRY_ACL_GROUP) {1370n = 1;1371name = field[1];1372isint_w(name.start, name.end, &id);1373} else1374n = 0;13751376if (!is_nfs4_perms_w(field[1 + n].start,1377field[1 + n].end, &permset)) {1378/* Invalid NFSv4 perms, skip entry */1379ret = ARCHIVE_WARN;1380continue;1381}1382if (!is_nfs4_flags_w(field[2 + n].start,1383field[2 + n].end, &permset)) {1384/* Invalid NFSv4 flags, skip entry */1385ret = ARCHIVE_WARN;1386continue;1387}1388s = field[3 + n].start;1389len = field[3 + n].end - field[3 + n].start;1390type = 0;1391if (len == 4) {1392if (wmemcmp(s, L"deny", 4) == 0)1393type = ARCHIVE_ENTRY_ACL_TYPE_DENY;1394} else if (len == 5) {1395if (wmemcmp(s, L"allow", 5) == 0)1396type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;1397else if (wmemcmp(s, L"audit", 5) == 0)1398type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;1399else if (wmemcmp(s, L"alarm", 5) == 0)1400type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;1401}1402if (type == 0) {1403/* Invalid entry type, skip entry */1404ret = ARCHIVE_WARN;1405continue;1406}1407isint_w(field[4 + n].start, field[4 + n].end, &id);1408}14091410/* Add entry to the internal list. */1411r = archive_acl_add_entry_w_len(acl, type, permset,1412tag, id, name.start, name.end - name.start);1413if (r < ARCHIVE_WARN)1414return (r);1415if (r != ARCHIVE_OK)1416ret = ARCHIVE_WARN;1417types |= type;1418}14191420/* Reset ACL */1421archive_acl_reset(acl, types);14221423return (ret);1424}14251426/*1427* Parse a string to a positive decimal integer. Returns true if1428* the string is non-empty and consists only of decimal digits,1429* false otherwise.1430*/1431static int1432isint_w(const wchar_t *start, const wchar_t *end, int *result)1433{1434int n = 0;1435if (start >= end)1436return (0);1437while (start < end) {1438if (*start < L'0' || *start > L'9')1439return (0);1440if (n > (INT_MAX / 10) ||1441(n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {1442n = INT_MAX;1443} else {1444n *= 10;1445n += *start - L'0';1446}1447start++;1448}1449*result = n;1450return (1);1451}14521453/*1454* Parse a string as a mode field. Returns true if1455* the string is non-empty and consists only of mode characters,1456* false otherwise.1457*/1458static int1459ismode_w(const wchar_t *start, const wchar_t *end, int *permset)1460{1461const wchar_t *p;14621463if (start >= end)1464return (0);1465p = start;1466*permset = 0;1467while (p < end) {1468switch (*p++) {1469case L'r': case L'R':1470*permset |= ARCHIVE_ENTRY_ACL_READ;1471break;1472case L'w': case L'W':1473*permset |= ARCHIVE_ENTRY_ACL_WRITE;1474break;1475case L'x': case L'X':1476*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;1477break;1478case L'-':1479break;1480default:1481return (0);1482}1483}1484return (1);1485}14861487/*1488* Parse a string as a NFS4 ACL permission field.1489* Returns true if the string is non-empty and consists only of NFS4 ACL1490* permission characters, false otherwise1491*/1492static int1493is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)1494{1495const wchar_t *p = start;14961497while (p < end) {1498switch (*p++) {1499case L'r':1500*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;1501break;1502case L'w':1503*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;1504break;1505case L'x':1506*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;1507break;1508case L'p':1509*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;1510break;1511case L'D':1512*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;1513break;1514case L'd':1515*permset |= ARCHIVE_ENTRY_ACL_DELETE;1516break;1517case L'a':1518*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;1519break;1520case L'A':1521*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;1522break;1523case L'R':1524*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;1525break;1526case L'W':1527*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;1528break;1529case L'c':1530*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;1531break;1532case L'C':1533*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;1534break;1535case L'o':1536*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;1537break;1538case L's':1539*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;1540break;1541case L'-':1542break;1543default:1544return(0);1545}1546}1547return (1);1548}15491550/*1551* Parse a string as a NFS4 ACL flags field.1552* Returns true if the string is non-empty and consists only of NFS4 ACL1553* flag characters, false otherwise1554*/1555static int1556is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)1557{1558const wchar_t *p = start;15591560while (p < end) {1561switch(*p++) {1562case L'f':1563*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;1564break;1565case L'd':1566*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;1567break;1568case L'i':1569*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;1570break;1571case L'n':1572*permset |=1573ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;1574break;1575case L'S':1576*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;1577break;1578case L'F':1579*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;1580break;1581case L'I':1582*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;1583break;1584case L'-':1585break;1586default:1587return (0);1588}1589}1590return (1);1591}15921593/*1594* Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated1595* to point to just after the separator. *start points to the first1596* character of the matched text and *end just after the last1597* character of the matched identifier. In particular *end - *start1598* is the length of the field body, not including leading or trailing1599* whitespace.1600*/1601static void1602next_field_w(const wchar_t **wp, const wchar_t **start,1603const wchar_t **end, wchar_t *sep)1604{1605/* Skip leading whitespace to find start of field. */1606while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {1607(*wp)++;1608}1609*start = *wp;16101611/* Scan for the separator. */1612while (**wp != L'\0' && **wp != L',' && **wp != L':' &&1613**wp != L'\n' && **wp != L'#') {1614(*wp)++;1615}1616*sep = **wp;16171618/* Locate end of field, trim trailing whitespace if necessary */1619if (*wp == *start) {1620*end = *wp;1621} else {1622*end = *wp - 1;1623while (**end == L' ' || **end == L'\t' || **end == L'\n') {1624(*end)--;1625}1626(*end)++;1627}16281629/* Handle in-field comments */1630if (*sep == L'#') {1631while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {1632(*wp)++;1633}1634*sep = **wp;1635}16361637/* Adjust scanner location. */1638if (**wp != L'\0')1639(*wp)++;1640}16411642/*1643* Parse an ACL text string.1644*1645* The want_type argument may be one of the following:1646* ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS1647* ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT1648* ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL1649*1650* POSIX.1e ACL entries prefixed with "default:" are treated as1651* ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS41652*/1653int1654archive_acl_from_text_l(struct archive_acl *acl, const char *text,1655int want_type, struct archive_string_conv *sc)1656{1657return archive_acl_from_text_nl(acl, text, strlen(text), want_type, sc);1658}16591660int1661archive_acl_from_text_nl(struct archive_acl *acl, const char *text,1662size_t length, int want_type, struct archive_string_conv *sc)1663{1664struct {1665const char *start;1666const char *end;1667} field[6], name;16681669const char *s, *st;1670int numfields, fields, n, r, sol, ret;1671int type, types, tag, permset, id;1672size_t len;1673char sep;16741675switch (want_type) {1676case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:1677want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;1678__LA_FALLTHROUGH;1679case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:1680case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:1681numfields = 5;1682break;1683case ARCHIVE_ENTRY_ACL_TYPE_NFS4:1684numfields = 6;1685break;1686default:1687return (ARCHIVE_FATAL);1688}16891690ret = ARCHIVE_OK;1691types = 0;16921693while (text != NULL && length > 0 && *text != '\0') {1694/*1695* Parse the fields out of the next entry,1696* advance 'text' to start of next entry.1697*/1698fields = 0;1699do {1700const char *start, *end;1701next_field(&text, &length, &start, &end, &sep);1702if (fields < numfields) {1703field[fields].start = start;1704field[fields].end = end;1705}1706++fields;1707} while (sep == ':');17081709/* Set remaining fields to blank. */1710for (n = fields; n < numfields; ++n)1711field[n].start = field[n].end = NULL;17121713if (field[0].start == NULL || field[0].end == NULL) {1714/* This should never happen */1715return (ARCHIVE_FATAL);1716}17171718if (*(field[0].start) == '#') {1719/* Comment, skip entry */1720continue;1721}17221723n = 0;1724sol = 0;1725id = -1;1726permset = 0;1727name.start = name.end = NULL;17281729if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {1730/* POSIX.1e ACLs */1731/*1732* Default keyword "default:user::rwx"1733* if found, we have one more field1734*1735* We also support old Solaris extension:1736* "defaultuser::rwx" is the default ACL corresponding1737* to "user::rwx", etc. valid only for first field1738*/1739s = field[0].start;1740#ifdef __clang_analyzer__1741assert(s);1742#endif1743len = field[0].end - field[0].start;1744if (*s == 'd' && (len == 1 || (len >= 71745&& memcmp((s + 1), "efault", 6) == 0))) {1746type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;1747if (len > 7)1748field[0].start += 7;1749else1750n = 1;1751} else1752type = want_type;17531754/* Check for a numeric ID in field n+1 or n+3. */1755isint(field[n + 1].start, field[n + 1].end, &id);1756/* Field n+3 is optional. */1757if (id == -1 && fields > (n + 3))1758isint(field[n + 3].start, field[n + 3].end,1759&id);17601761tag = 0;1762s = field[n].start;1763st = field[n].start + 1;1764len = field[n].end - field[n].start;17651766if (len == 0) {1767ret = ARCHIVE_WARN;1768continue;1769}17701771switch (*s) {1772case 'u':1773if (len == 1 || (len == 41774&& memcmp(st, "ser", 3) == 0))1775tag = ARCHIVE_ENTRY_ACL_USER_OBJ;1776break;1777case 'g':1778if (len == 1 || (len == 51779&& memcmp(st, "roup", 4) == 0))1780tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;1781break;1782case 'o':1783if (len == 1 || (len == 51784&& memcmp(st, "ther", 4) == 0))1785tag = ARCHIVE_ENTRY_ACL_OTHER;1786break;1787case 'm':1788if (len == 1 || (len == 41789&& memcmp(st, "ask", 3) == 0))1790tag = ARCHIVE_ENTRY_ACL_MASK;1791break;1792default:1793break;1794}17951796switch (tag) {1797case ARCHIVE_ENTRY_ACL_OTHER:1798case ARCHIVE_ENTRY_ACL_MASK:1799if (fields == (n + 2)1800&& field[n + 1].start < field[n + 1].end1801&& ismode(field[n + 1].start,1802field[n + 1].end, &permset)) {1803/* This is Solaris-style "other:rwx" */1804sol = 1;1805} else if (fields == (n + 3) &&1806field[n + 1].start < field[n + 1].end) {1807/* Invalid mask or other field */1808ret = ARCHIVE_WARN;1809continue;1810}1811break;1812case ARCHIVE_ENTRY_ACL_USER_OBJ:1813case ARCHIVE_ENTRY_ACL_GROUP_OBJ:1814if (id != -1 ||1815field[n + 1].start < field[n + 1].end) {1816name = field[n + 1];1817if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)1818tag = ARCHIVE_ENTRY_ACL_USER;1819else1820tag = ARCHIVE_ENTRY_ACL_GROUP;1821}1822break;1823default:1824/* Invalid tag, skip entry */1825ret = ARCHIVE_WARN;1826continue;1827}18281829/*1830* Without "default:" we expect mode in field 31831* Exception: Solaris other and mask fields1832*/1833if (permset == 0 && !ismode(field[n + 2 - sol].start,1834field[n + 2 - sol].end, &permset)) {1835/* Invalid mode, skip entry */1836ret = ARCHIVE_WARN;1837continue;1838}1839} else {1840/* NFS4 ACLs */1841s = field[0].start;1842len = field[0].end - field[0].start;1843tag = 0;18441845switch (len) {1846case 4:1847if (memcmp(s, "user", 4) == 0)1848tag = ARCHIVE_ENTRY_ACL_USER;1849break;1850case 5:1851if (memcmp(s, "group", 5) == 0)1852tag = ARCHIVE_ENTRY_ACL_GROUP;1853break;1854case 6:1855if (memcmp(s, "owner@", 6) == 0)1856tag = ARCHIVE_ENTRY_ACL_USER_OBJ;1857else if (memcmp(s, "group@", 6) == 0)1858tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;1859break;1860case 9:1861if (memcmp(s, "everyone@", 9) == 0)1862tag = ARCHIVE_ENTRY_ACL_EVERYONE;1863break;1864default:1865break;1866}18671868if (tag == 0) {1869/* Invalid tag, skip entry */1870ret = ARCHIVE_WARN;1871continue;1872} else if (tag == ARCHIVE_ENTRY_ACL_USER ||1873tag == ARCHIVE_ENTRY_ACL_GROUP) {1874n = 1;1875name = field[1];1876isint(name.start, name.end, &id);1877} else1878n = 0;18791880if (!is_nfs4_perms(field[1 + n].start,1881field[1 + n].end, &permset)) {1882/* Invalid NFSv4 perms, skip entry */1883ret = ARCHIVE_WARN;1884continue;1885}1886if (!is_nfs4_flags(field[2 + n].start,1887field[2 + n].end, &permset)) {1888/* Invalid NFSv4 flags, skip entry */1889ret = ARCHIVE_WARN;1890continue;1891}1892s = field[3 + n].start;1893len = field[3 + n].end - field[3 + n].start;1894type = 0;1895if (len == 4) {1896if (memcmp(s, "deny", 4) == 0)1897type = ARCHIVE_ENTRY_ACL_TYPE_DENY;1898} else if (len == 5) {1899if (memcmp(s, "allow", 5) == 0)1900type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;1901else if (memcmp(s, "audit", 5) == 0)1902type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;1903else if (memcmp(s, "alarm", 5) == 0)1904type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;1905}1906if (type == 0) {1907/* Invalid entry type, skip entry */1908ret = ARCHIVE_WARN;1909continue;1910}1911isint(field[4 + n].start, field[4 + n].end,1912&id);1913}19141915/* Add entry to the internal list. */1916r = archive_acl_add_entry_len_l(acl, type, permset,1917tag, id, name.start, name.end - name.start, sc);1918if (r < ARCHIVE_WARN)1919return (r);1920if (r != ARCHIVE_OK)1921ret = ARCHIVE_WARN;1922types |= type;1923}19241925/* Reset ACL */1926archive_acl_reset(acl, types);19271928return (ret);1929}19301931/*1932* Parse a string to a positive decimal integer. Returns true if1933* the string is non-empty and consists only of decimal digits,1934* false otherwise.1935*/1936static int1937isint(const char *start, const char *end, int *result)1938{1939int n = 0;1940if (start >= end)1941return (0);1942while (start < end) {1943if (*start < '0' || *start > '9')1944return (0);1945if (n > (INT_MAX / 10) ||1946(n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {1947n = INT_MAX;1948} else {1949n *= 10;1950n += *start - '0';1951}1952start++;1953}1954*result = n;1955return (1);1956}19571958/*1959* Parse a string as a mode field. Returns true if1960* the string is non-empty and consists only of mode characters,1961* false otherwise.1962*/1963static int1964ismode(const char *start, const char *end, int *permset)1965{1966const char *p;19671968if (start >= end)1969return (0);1970p = start;1971*permset = 0;1972while (p < end) {1973switch (*p++) {1974case 'r': case 'R':1975*permset |= ARCHIVE_ENTRY_ACL_READ;1976break;1977case 'w': case 'W':1978*permset |= ARCHIVE_ENTRY_ACL_WRITE;1979break;1980case 'x': case 'X':1981*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;1982break;1983case '-':1984break;1985default:1986return (0);1987}1988}1989return (1);1990}19911992/*1993* Parse a string as a NFS4 ACL permission field.1994* Returns true if the string is non-empty and consists only of NFS4 ACL1995* permission characters, false otherwise1996*/1997static int1998is_nfs4_perms(const char *start, const char *end, int *permset)1999{2000const char *p = start;20012002while (p < end) {2003switch (*p++) {2004case 'r':2005*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;2006break;2007case 'w':2008*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;2009break;2010case 'x':2011*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;2012break;2013case 'p':2014*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;2015break;2016case 'D':2017*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;2018break;2019case 'd':2020*permset |= ARCHIVE_ENTRY_ACL_DELETE;2021break;2022case 'a':2023*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;2024break;2025case 'A':2026*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;2027break;2028case 'R':2029*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;2030break;2031case 'W':2032*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;2033break;2034case 'c':2035*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;2036break;2037case 'C':2038*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;2039break;2040case 'o':2041*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;2042break;2043case 's':2044*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;2045break;2046case '-':2047break;2048default:2049return(0);2050}2051}2052return (1);2053}20542055/*2056* Parse a string as a NFS4 ACL flags field.2057* Returns true if the string is non-empty and consists only of NFS4 ACL2058* flag characters, false otherwise2059*/2060static int2061is_nfs4_flags(const char *start, const char *end, int *permset)2062{2063const char *p = start;20642065while (p < end) {2066switch(*p++) {2067case 'f':2068*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;2069break;2070case 'd':2071*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;2072break;2073case 'i':2074*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;2075break;2076case 'n':2077*permset |=2078ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;2079break;2080case 'S':2081*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;2082break;2083case 'F':2084*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;2085break;2086case 'I':2087*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;2088break;2089case '-':2090break;2091default:2092return (0);2093}2094}2095return (1);2096}20972098/*2099* Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *p is updated2100* to point to just after the separator. *start points to the first2101* character of the matched text and *end just after the last2102* character of the matched identifier. In particular *end - *start2103* is the length of the field body, not including leading or trailing2104* whitespace.2105*/2106static void2107next_field(const char **p, size_t *l, const char **start,2108const char **end, char *sep)2109{2110/* Skip leading whitespace to find start of field. */2111while (*l > 0 && (**p == ' ' || **p == '\t' || **p == '\n')) {2112(*p)++;2113(*l)--;2114}2115*start = *p;21162117/* Locate end of field, trim trailing whitespace if necessary */2118while (*l > 0 && **p != ' ' && **p != '\t' && **p != '\n' && **p != ',' && **p != ':' && **p != '#') {2119(*p)++;2120(*l)--;2121}2122*end = *p;21232124/* Scan for the separator. */2125while (*l > 0 && **p != ',' && **p != ':' && **p != '\n' && **p != '#') {2126(*p)++;2127(*l)--;2128}2129*sep = **p;21302131/* Handle in-field comments */2132if (*sep == '#') {2133while (*l > 0 && **p != ',' && **p != '\n') {2134(*p)++;2135(*l)--;2136}2137*sep = **p;2138}21392140/* Skip separator. */2141if (*l > 0) {2142(*p)++;2143(*l)--;2144}2145}214621472148