Path: blob/master/Utilities/cmlibarchive/libarchive/archive_acl.c
3153 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/* Type argument must be a valid NFS4 or POSIX.1e type.277* The type must agree with anything already set and278* the permset must be compatible. */279if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {280if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {281return (NULL);282}283if (permset &284~(ARCHIVE_ENTRY_ACL_PERMS_NFS4285| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {286return (NULL);287}288} else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {289if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {290return (NULL);291}292if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {293return (NULL);294}295} else {296return (NULL);297}298299/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */300switch (tag) {301case ARCHIVE_ENTRY_ACL_USER:302case ARCHIVE_ENTRY_ACL_USER_OBJ:303case ARCHIVE_ENTRY_ACL_GROUP:304case ARCHIVE_ENTRY_ACL_GROUP_OBJ:305/* Tags valid in both NFS4 and POSIX.1e */306break;307case ARCHIVE_ENTRY_ACL_MASK:308case ARCHIVE_ENTRY_ACL_OTHER:309/* Tags valid only in POSIX.1e. */310if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {311return (NULL);312}313break;314case ARCHIVE_ENTRY_ACL_EVERYONE:315/* Tags valid only in NFS4. */316if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {317return (NULL);318}319break;320default:321/* No other values are valid. */322return (NULL);323}324325free(acl->acl_text_w);326acl->acl_text_w = NULL;327free(acl->acl_text);328acl->acl_text = NULL;329330/*331* If there's a matching entry already in the list, overwrite it.332* NFSv4 entries may be repeated and are not overwritten.333*334* TODO: compare names of no id is provided (needs more rework)335*/336ap = acl->acl_head;337aq = NULL;338while (ap != NULL) {339if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&340ap->type == type && ap->tag == tag && ap->id == id) {341if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&342tag != ARCHIVE_ENTRY_ACL_GROUP)) {343ap->permset = permset;344return (ap);345}346}347aq = ap;348ap = ap->next;349}350351/* Add a new entry to the end of the list. */352ap = calloc(1, sizeof(*ap));353if (ap == NULL)354return (NULL);355if (aq == NULL)356acl->acl_head = ap;357else358aq->next = ap;359ap->type = type;360ap->tag = tag;361ap->id = id;362ap->permset = permset;363acl->acl_types |= type;364return (ap);365}366367/*368* Return a count of entries matching "want_type".369*/370int371archive_acl_count(struct archive_acl *acl, int want_type)372{373int count;374struct archive_acl_entry *ap;375376count = 0;377ap = acl->acl_head;378while (ap != NULL) {379if ((ap->type & want_type) != 0)380count++;381ap = ap->next;382}383384if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))385count += 3;386return (count);387}388389/*390* Return a bitmask of stored ACL types in an ACL list391*/392int393archive_acl_types(struct archive_acl *acl)394{395return (acl->acl_types);396}397398/*399* Prepare for reading entries from the ACL data. Returns a count400* of entries matching "want_type", or zero if there are no401* non-extended ACL entries of that type.402*/403int404archive_acl_reset(struct archive_acl *acl, int want_type)405{406int count, cutoff;407408count = archive_acl_count(acl, want_type);409410/*411* If the only entries are the three standard ones,412* then don't return any ACL data. (In this case,413* client can just use chmod(2) to set permissions.)414*/415if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)416cutoff = 3;417else418cutoff = 0;419420if (count > cutoff)421acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;422else423acl->acl_state = 0;424acl->acl_p = acl->acl_head;425return (count);426}427428429/*430* Return the next ACL entry in the list. Fake entries for the431* standard permissions and include them in the returned list.432*/433int434archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,435int *type, int *permset, int *tag, int *id, const char **name)436{437*name = NULL;438*id = -1;439440/*441* The acl_state is either zero (no entries available), -1442* (reading from list), or an entry type (retrieve that type443* from ae_stat.aest_mode).444*/445if (acl->acl_state == 0)446return (ARCHIVE_WARN);447448/* The first three access entries are special. */449if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {450switch (acl->acl_state) {451case ARCHIVE_ENTRY_ACL_USER_OBJ:452*permset = (acl->mode >> 6) & 7;453*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;454*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;455acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;456return (ARCHIVE_OK);457case ARCHIVE_ENTRY_ACL_GROUP_OBJ:458*permset = (acl->mode >> 3) & 7;459*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;460*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;461acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;462return (ARCHIVE_OK);463case ARCHIVE_ENTRY_ACL_OTHER:464*permset = acl->mode & 7;465*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;466*tag = ARCHIVE_ENTRY_ACL_OTHER;467acl->acl_state = -1;468acl->acl_p = acl->acl_head;469return (ARCHIVE_OK);470default:471break;472}473}474475while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)476acl->acl_p = acl->acl_p->next;477if (acl->acl_p == NULL) {478acl->acl_state = 0;479*type = 0;480*permset = 0;481*tag = 0;482*id = -1;483*name = NULL;484return (ARCHIVE_EOF); /* End of ACL entries. */485}486*type = acl->acl_p->type;487*permset = acl->acl_p->permset;488*tag = acl->acl_p->tag;489*id = acl->acl_p->id;490if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {491if (errno == ENOMEM)492return (ARCHIVE_FATAL);493*name = NULL;494}495acl->acl_p = acl->acl_p->next;496return (ARCHIVE_OK);497}498499/*500* Determine what type of ACL do we want501*/502static int503archive_acl_text_want_type(struct archive_acl *acl, int flags)504{505int want_type;506507/* Check if ACL is NFSv4 */508if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {509/* NFSv4 should never mix with POSIX.1e */510if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)511return (0);512else513return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);514}515516/* Now deal with POSIX.1e ACLs */517518want_type = 0;519if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)520want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;521if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)522want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;523524/* By default we want both access and default ACLs */525if (want_type == 0)526return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);527528return (want_type);529}530531/*532* Calculate ACL text string length533*/534static size_t535archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,536int wide, struct archive *a, struct archive_string_conv *sc) {537struct archive_acl_entry *ap;538const char *name;539const wchar_t *wname;540int count, idlen, tmp, r;541size_t length;542size_t len;543544count = 0;545length = 0;546for (ap = acl->acl_head; ap != NULL; ap = ap->next) {547if ((ap->type & want_type) == 0)548continue;549/*550* Filemode-mapping ACL entries are stored exclusively in551* ap->mode so they should not be in the list552*/553if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)554&& (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ555|| ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ556|| ap->tag == ARCHIVE_ENTRY_ACL_OTHER))557continue;558count++;559if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0560&& (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)561length += 8; /* "default:" */562switch (ap->tag) {563case ARCHIVE_ENTRY_ACL_USER_OBJ:564if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {565length += 6; /* "owner@" */566break;567}568/* FALLTHROUGH */569case ARCHIVE_ENTRY_ACL_USER:570case ARCHIVE_ENTRY_ACL_MASK:571length += 4; /* "user", "mask" */572break;573case ARCHIVE_ENTRY_ACL_GROUP_OBJ:574if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {575length += 6; /* "group@" */576break;577}578/* FALLTHROUGH */579case ARCHIVE_ENTRY_ACL_GROUP:580case ARCHIVE_ENTRY_ACL_OTHER:581length += 5; /* "group", "other" */582break;583case ARCHIVE_ENTRY_ACL_EVERYONE:584length += 9; /* "everyone@" */585break;586}587length += 1; /* colon after tag */588if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||589ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {590if (wide) {591r = archive_mstring_get_wcs(a, &ap->name,592&wname);593if (r == 0 && wname != NULL)594length += wcslen(wname);595else if (r < 0 && errno == ENOMEM)596return (0);597else598length += sizeof(uid_t) * 3 + 1;599} else {600r = archive_mstring_get_mbs_l(a, &ap->name, &name,601&len, sc);602if (r != 0)603return (0);604if (len > 0 && name != NULL)605length += len;606else607length += sizeof(uid_t) * 3 + 1;608}609length += 1; /* colon after user or group name */610} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)611length += 1; /* 2nd colon empty user,group or other */612613if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)614&& ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)615&& (ap->tag == ARCHIVE_ENTRY_ACL_OTHER616|| ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {617/* Solaris has no colon after other: and mask: */618length = length - 1;619}620621if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {622/* rwxpdDaARWcCos:fdinSFI:deny */623length += 27;624if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)625length += 1; /* allow, alarm, audit */626} else627length += 3; /* rwx */628629if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||630ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&631(flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {632length += 1; /* colon */633/* ID digit count */634idlen = 1;635tmp = ap->id;636while (tmp > 9) {637tmp = tmp / 10;638idlen++;639}640length += idlen;641}642length ++; /* entry separator */643}644645/* Add filemode-mapping access entries to the length */646if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {647if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {648/* "user::rwx\ngroup::rwx\nother:rwx\n" */649length += 31;650} else {651/* "user::rwx\ngroup::rwx\nother::rwx\n" */652length += 32;653}654} else if (count == 0)655return (0);656657/* The terminating character is included in count */658return (length);659}660661/*662* Generate a wide text version of the ACL. The flags parameter controls663* the type and style of the generated ACL.664*/665wchar_t *666archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,667struct archive *a)668{669int count;670size_t length;671size_t len;672const wchar_t *wname;673const wchar_t *prefix;674wchar_t separator;675struct archive_acl_entry *ap;676int id, r, want_type;677wchar_t *wp, *ws;678679want_type = archive_acl_text_want_type(acl, flags);680681/* Both NFSv4 and POSIX.1 types found */682if (want_type == 0)683return (NULL);684685if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)686flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;687688length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);689690if (length == 0)691return (NULL);692693if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)694separator = L',';695else696separator = L'\n';697698/* Now, allocate the string and actually populate it. */699wp = ws = malloc(length * sizeof(*wp));700if (wp == NULL) {701if (errno == ENOMEM)702__archive_errx(1, "No memory");703return (NULL);704}705count = 0;706707if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {708append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,709ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,710acl->mode & 0700, -1);711*wp++ = separator;712append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,713ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,714acl->mode & 0070, -1);715*wp++ = separator;716append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,717ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,718acl->mode & 0007, -1);719count += 3;720}721722for (ap = acl->acl_head; ap != NULL; ap = ap->next) {723if ((ap->type & want_type) == 0)724continue;725/*726* Filemode-mapping ACL entries are stored exclusively in727* ap->mode so they should not be in the list728*/729if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)730&& (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ731|| ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ732|| ap->tag == ARCHIVE_ENTRY_ACL_OTHER))733continue;734if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&735(flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)736prefix = L"default:";737else738prefix = NULL;739r = archive_mstring_get_wcs(a, &ap->name, &wname);740if (r == 0) {741if (count > 0)742*wp++ = separator;743if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)744id = ap->id;745else746id = -1;747append_entry_w(&wp, prefix, ap->type, ap->tag, flags,748wname, ap->permset, id);749count++;750} else if (r < 0 && errno == ENOMEM) {751free(ws);752return (NULL);753}754}755756/* Add terminating character */757*wp++ = L'\0';758759len = wcslen(ws);760761if (len > length - 1)762__archive_errx(1, "Buffer overrun");763764if (text_len != NULL)765*text_len = len;766767return (ws);768}769770static void771append_id_w(wchar_t **wp, int id)772{773if (id < 0)774id = 0;775if (id > 9)776append_id_w(wp, id / 10);777*(*wp)++ = L"0123456789"[id % 10];778}779780static void781append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,782int tag, int flags, const wchar_t *wname, int perm, int id)783{784int i;785786if (prefix != NULL) {787wcscpy(*wp, prefix);788*wp += wcslen(*wp);789}790switch (tag) {791case ARCHIVE_ENTRY_ACL_USER_OBJ:792wname = NULL;793id = -1;794if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {795wcscpy(*wp, L"owner@");796break;797}798/* FALLTHROUGH */799case ARCHIVE_ENTRY_ACL_USER:800wcscpy(*wp, L"user");801break;802case ARCHIVE_ENTRY_ACL_GROUP_OBJ:803wname = NULL;804id = -1;805if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {806wcscpy(*wp, L"group@");807break;808}809/* FALLTHROUGH */810case ARCHIVE_ENTRY_ACL_GROUP:811wcscpy(*wp, L"group");812break;813case ARCHIVE_ENTRY_ACL_MASK:814wcscpy(*wp, L"mask");815wname = NULL;816id = -1;817break;818case ARCHIVE_ENTRY_ACL_OTHER:819wcscpy(*wp, L"other");820wname = NULL;821id = -1;822break;823case ARCHIVE_ENTRY_ACL_EVERYONE:824wcscpy(*wp, L"everyone@");825wname = NULL;826id = -1;827break;828}829*wp += wcslen(*wp);830*(*wp)++ = L':';831if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||832tag == ARCHIVE_ENTRY_ACL_USER ||833tag == ARCHIVE_ENTRY_ACL_GROUP) {834if (wname != NULL) {835wcscpy(*wp, wname);836*wp += wcslen(*wp);837} else if (tag == ARCHIVE_ENTRY_ACL_USER838|| tag == ARCHIVE_ENTRY_ACL_GROUP) {839append_id_w(wp, id);840if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)841id = -1;842}843/* Solaris style has no second colon after other and mask */844if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)845|| (tag != ARCHIVE_ENTRY_ACL_OTHER846&& tag != ARCHIVE_ENTRY_ACL_MASK))847*(*wp)++ = L':';848}849if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {850/* POSIX.1e ACL perms */851*(*wp)++ = (perm & 0444) ? L'r' : L'-';852*(*wp)++ = (perm & 0222) ? L'w' : L'-';853*(*wp)++ = (perm & 0111) ? L'x' : L'-';854} else {855/* NFSv4 ACL perms */856for (i = 0; i < nfsv4_acl_perm_map_size; i++) {857if (perm & nfsv4_acl_perm_map[i].perm)858*(*wp)++ = nfsv4_acl_perm_map[i].wc;859else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)860*(*wp)++ = L'-';861}862*(*wp)++ = L':';863for (i = 0; i < nfsv4_acl_flag_map_size; i++) {864if (perm & nfsv4_acl_flag_map[i].perm)865*(*wp)++ = nfsv4_acl_flag_map[i].wc;866else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)867*(*wp)++ = L'-';868}869*(*wp)++ = L':';870switch (type) {871case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:872wcscpy(*wp, L"allow");873break;874case ARCHIVE_ENTRY_ACL_TYPE_DENY:875wcscpy(*wp, L"deny");876break;877case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:878wcscpy(*wp, L"audit");879break;880case ARCHIVE_ENTRY_ACL_TYPE_ALARM:881wcscpy(*wp, L"alarm");882break;883default:884break;885}886*wp += wcslen(*wp);887}888if (id != -1) {889*(*wp)++ = L':';890append_id_w(wp, id);891}892}893894/*895* Generate a text version of the ACL. The flags parameter controls896* the type and style of the generated ACL.897*/898char *899archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,900struct archive_string_conv *sc)901{902int count;903size_t length;904size_t len;905const char *name;906const char *prefix;907char separator;908struct archive_acl_entry *ap;909int id, r, want_type;910char *p, *s;911912want_type = archive_acl_text_want_type(acl, flags);913914/* Both NFSv4 and POSIX.1 types found */915if (want_type == 0)916return (NULL);917918if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)919flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;920921length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);922923if (length == 0)924return (NULL);925926if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)927separator = ',';928else929separator = '\n';930931/* Now, allocate the string and actually populate it. */932p = s = malloc(length * sizeof(*p));933if (p == NULL) {934if (errno == ENOMEM)935__archive_errx(1, "No memory");936return (NULL);937}938count = 0;939940if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {941append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,942ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,943acl->mode & 0700, -1);944*p++ = separator;945append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,946ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,947acl->mode & 0070, -1);948*p++ = separator;949append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,950ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,951acl->mode & 0007, -1);952count += 3;953}954955for (ap = acl->acl_head; ap != NULL; ap = ap->next) {956if ((ap->type & want_type) == 0)957continue;958/*959* Filemode-mapping ACL entries are stored exclusively in960* ap->mode so they should not be in the list961*/962if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)963&& (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ964|| ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ965|| ap->tag == ARCHIVE_ENTRY_ACL_OTHER))966continue;967if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&968(flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)969prefix = "default:";970else971prefix = NULL;972r = archive_mstring_get_mbs_l(973NULL, &ap->name, &name, &len, sc);974if (r != 0) {975free(s);976return (NULL);977}978if (count > 0)979*p++ = separator;980if (name == NULL ||981(flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {982id = ap->id;983} else {984id = -1;985}986append_entry(&p, prefix, ap->type, ap->tag, flags, name,987ap->permset, id);988count++;989}990991/* Add terminating character */992*p++ = '\0';993994len = strlen(s);995996if (len > length - 1)997__archive_errx(1, "Buffer overrun");998999if (text_len != NULL)1000*text_len = len;10011002return (s);1003}10041005static void1006append_id(char **p, int id)1007{1008if (id < 0)1009id = 0;1010if (id > 9)1011append_id(p, id / 10);1012*(*p)++ = "0123456789"[id % 10];1013}10141015static void1016append_entry(char **p, const char *prefix, int type,1017int tag, int flags, const char *name, int perm, int id)1018{1019int i;10201021if (prefix != NULL) {1022strcpy(*p, prefix);1023*p += strlen(*p);1024}1025switch (tag) {1026case ARCHIVE_ENTRY_ACL_USER_OBJ:1027name = NULL;1028id = -1;1029if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {1030strcpy(*p, "owner@");1031break;1032}1033/* FALLTHROUGH */1034case ARCHIVE_ENTRY_ACL_USER:1035strcpy(*p, "user");1036break;1037case ARCHIVE_ENTRY_ACL_GROUP_OBJ:1038name = NULL;1039id = -1;1040if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {1041strcpy(*p, "group@");1042break;1043}1044/* FALLTHROUGH */1045case ARCHIVE_ENTRY_ACL_GROUP:1046strcpy(*p, "group");1047break;1048case ARCHIVE_ENTRY_ACL_MASK:1049strcpy(*p, "mask");1050name = NULL;1051id = -1;1052break;1053case ARCHIVE_ENTRY_ACL_OTHER:1054strcpy(*p, "other");1055name = NULL;1056id = -1;1057break;1058case ARCHIVE_ENTRY_ACL_EVERYONE:1059strcpy(*p, "everyone@");1060name = NULL;1061id = -1;1062break;1063}1064*p += strlen(*p);1065*(*p)++ = ':';1066if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||1067tag == ARCHIVE_ENTRY_ACL_USER ||1068tag == ARCHIVE_ENTRY_ACL_GROUP) {1069if (name != NULL) {1070strcpy(*p, name);1071*p += strlen(*p);1072} else if (tag == ARCHIVE_ENTRY_ACL_USER1073|| tag == ARCHIVE_ENTRY_ACL_GROUP) {1074append_id(p, id);1075if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)1076id = -1;1077}1078/* Solaris style has no second colon after other and mask */1079if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)1080|| (tag != ARCHIVE_ENTRY_ACL_OTHER1081&& tag != ARCHIVE_ENTRY_ACL_MASK))1082*(*p)++ = ':';1083}1084if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {1085/* POSIX.1e ACL perms */1086*(*p)++ = (perm & 0444) ? 'r' : '-';1087*(*p)++ = (perm & 0222) ? 'w' : '-';1088*(*p)++ = (perm & 0111) ? 'x' : '-';1089} else {1090/* NFSv4 ACL perms */1091for (i = 0; i < nfsv4_acl_perm_map_size; i++) {1092if (perm & nfsv4_acl_perm_map[i].perm)1093*(*p)++ = nfsv4_acl_perm_map[i].c;1094else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)1095*(*p)++ = '-';1096}1097*(*p)++ = ':';1098for (i = 0; i < nfsv4_acl_flag_map_size; i++) {1099if (perm & nfsv4_acl_flag_map[i].perm)1100*(*p)++ = nfsv4_acl_flag_map[i].c;1101else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)1102*(*p)++ = '-';1103}1104*(*p)++ = ':';1105switch (type) {1106case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:1107strcpy(*p, "allow");1108break;1109case ARCHIVE_ENTRY_ACL_TYPE_DENY:1110strcpy(*p, "deny");1111break;1112case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:1113strcpy(*p, "audit");1114break;1115case ARCHIVE_ENTRY_ACL_TYPE_ALARM:1116strcpy(*p, "alarm");1117break;1118}1119*p += strlen(*p);1120}1121if (id != -1) {1122*(*p)++ = ':';1123append_id(p, id);1124}1125}11261127/*1128* Parse a wide ACL text string.1129*1130* The want_type argument may be one of the following:1131* ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS1132* ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT1133* ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL1134*1135* POSIX.1e ACL entries prefixed with "default:" are treated as1136* ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS41137*/1138int1139archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,1140int want_type)1141{1142struct {1143const wchar_t *start;1144const wchar_t *end;1145} field[6], name;11461147const wchar_t *s, *st;11481149int numfields, fields, n, r, sol, ret;1150int type, types, tag, permset, id;1151size_t len;1152wchar_t sep;11531154ret = ARCHIVE_OK;1155types = 0;11561157switch (want_type) {1158case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:1159want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;1160__LA_FALLTHROUGH;1161case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:1162case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:1163numfields = 5;1164break;1165case ARCHIVE_ENTRY_ACL_TYPE_NFS4:1166numfields = 6;1167break;1168default:1169return (ARCHIVE_FATAL);1170}11711172while (text != NULL && *text != L'\0') {1173/*1174* Parse the fields out of the next entry,1175* advance 'text' to start of next entry.1176*/1177fields = 0;1178do {1179const wchar_t *start, *end;1180next_field_w(&text, &start, &end, &sep);1181if (fields < numfields) {1182field[fields].start = start;1183field[fields].end = end;1184}1185++fields;1186} while (sep == L':');11871188/* Set remaining fields to blank. */1189for (n = fields; n < numfields; ++n)1190field[n].start = field[n].end = NULL;11911192if (field[0].start != NULL && *(field[0].start) == L'#') {1193/* Comment, skip entry */1194continue;1195}11961197n = 0;1198sol = 0;1199id = -1;1200permset = 0;1201name.start = name.end = NULL;12021203if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {1204/* POSIX.1e ACLs */1205/*1206* Default keyword "default:user::rwx"1207* if found, we have one more field1208*1209* We also support old Solaris extension:1210* "defaultuser::rwx" is the default ACL corresponding1211* to "user::rwx", etc. valid only for first field1212*/1213s = field[0].start;1214#ifdef __clang_analyzer__1215assert(s);1216#endif1217len = field[0].end - field[0].start;1218if (*s == L'd' && (len == 1 || (len >= 71219&& wmemcmp((s + 1), L"efault", 6) == 0))) {1220type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;1221if (len > 7)1222field[0].start += 7;1223else1224n = 1;1225} else1226type = want_type;12271228/* Check for a numeric ID in field n+1 or n+3. */1229isint_w(field[n + 1].start, field[n + 1].end, &id);1230/* Field n+3 is optional. */1231if (id == -1 && fields > n+3)1232isint_w(field[n + 3].start, field[n + 3].end,1233&id);12341235tag = 0;1236s = field[n].start;1237st = field[n].start + 1;1238len = field[n].end - field[n].start;12391240switch (*s) {1241case L'u':1242if (len == 1 || (len == 41243&& wmemcmp(st, L"ser", 3) == 0))1244tag = ARCHIVE_ENTRY_ACL_USER_OBJ;1245break;1246case L'g':1247if (len == 1 || (len == 51248&& wmemcmp(st, L"roup", 4) == 0))1249tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;1250break;1251case L'o':1252if (len == 1 || (len == 51253&& wmemcmp(st, L"ther", 4) == 0))1254tag = ARCHIVE_ENTRY_ACL_OTHER;1255break;1256case L'm':1257if (len == 1 || (len == 41258&& wmemcmp(st, L"ask", 3) == 0))1259tag = ARCHIVE_ENTRY_ACL_MASK;1260break;1261default:1262break;1263}12641265switch (tag) {1266case ARCHIVE_ENTRY_ACL_OTHER:1267case ARCHIVE_ENTRY_ACL_MASK:1268if (fields == (n + 2)1269&& field[n + 1].start < field[n + 1].end1270&& ismode_w(field[n + 1].start,1271field[n + 1].end, &permset)) {1272/* This is Solaris-style "other:rwx" */1273sol = 1;1274} else if (fields == (n + 3) &&1275field[n + 1].start < field[n + 1].end) {1276/* Invalid mask or other field */1277ret = ARCHIVE_WARN;1278continue;1279}1280break;1281case ARCHIVE_ENTRY_ACL_USER_OBJ:1282case ARCHIVE_ENTRY_ACL_GROUP_OBJ:1283if (id != -1 ||1284field[n + 1].start < field[n + 1].end) {1285name = field[n + 1];1286if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)1287tag = ARCHIVE_ENTRY_ACL_USER;1288else1289tag = ARCHIVE_ENTRY_ACL_GROUP;1290}1291break;1292default:1293/* Invalid tag, skip entry */1294ret = ARCHIVE_WARN;1295continue;1296}12971298/*1299* Without "default:" we expect mode in field 21300* Exception: Solaris other and mask fields1301*/1302if (permset == 0 && !ismode_w(field[n + 2 - sol].start,1303field[n + 2 - sol].end, &permset)) {1304/* Invalid mode, skip entry */1305ret = ARCHIVE_WARN;1306continue;1307}1308} else {1309/* NFS4 ACLs */1310s = field[0].start;1311len = field[0].end - field[0].start;1312tag = 0;13131314switch (len) {1315case 4:1316if (wmemcmp(s, L"user", 4) == 0)1317tag = ARCHIVE_ENTRY_ACL_USER;1318break;1319case 5:1320if (wmemcmp(s, L"group", 5) == 0)1321tag = ARCHIVE_ENTRY_ACL_GROUP;1322break;1323case 6:1324if (wmemcmp(s, L"owner@", 6) == 0)1325tag = ARCHIVE_ENTRY_ACL_USER_OBJ;1326else if (wmemcmp(s, L"group@", len) == 0)1327tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;1328break;1329case 9:1330if (wmemcmp(s, L"everyone@", 9) == 0)1331tag = ARCHIVE_ENTRY_ACL_EVERYONE;1332default:1333break;1334}13351336if (tag == 0) {1337/* Invalid tag, skip entry */1338ret = ARCHIVE_WARN;1339continue;1340} else if (tag == ARCHIVE_ENTRY_ACL_USER ||1341tag == ARCHIVE_ENTRY_ACL_GROUP) {1342n = 1;1343name = field[1];1344isint_w(name.start, name.end, &id);1345} else1346n = 0;13471348if (!is_nfs4_perms_w(field[1 + n].start,1349field[1 + n].end, &permset)) {1350/* Invalid NFSv4 perms, skip entry */1351ret = ARCHIVE_WARN;1352continue;1353}1354if (!is_nfs4_flags_w(field[2 + n].start,1355field[2 + n].end, &permset)) {1356/* Invalid NFSv4 flags, skip entry */1357ret = ARCHIVE_WARN;1358continue;1359}1360s = field[3 + n].start;1361len = field[3 + n].end - field[3 + n].start;1362type = 0;1363if (len == 4) {1364if (wmemcmp(s, L"deny", 4) == 0)1365type = ARCHIVE_ENTRY_ACL_TYPE_DENY;1366} else if (len == 5) {1367if (wmemcmp(s, L"allow", 5) == 0)1368type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;1369else if (wmemcmp(s, L"audit", 5) == 0)1370type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;1371else if (wmemcmp(s, L"alarm", 5) == 0)1372type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;1373}1374if (type == 0) {1375/* Invalid entry type, skip entry */1376ret = ARCHIVE_WARN;1377continue;1378}1379isint_w(field[4 + n].start, field[4 + n].end, &id);1380}13811382/* Add entry to the internal list. */1383r = archive_acl_add_entry_w_len(acl, type, permset,1384tag, id, name.start, name.end - name.start);1385if (r < ARCHIVE_WARN)1386return (r);1387if (r != ARCHIVE_OK)1388ret = ARCHIVE_WARN;1389types |= type;1390}13911392/* Reset ACL */1393archive_acl_reset(acl, types);13941395return (ret);1396}13971398/*1399* Parse a string to a positive decimal integer. Returns true if1400* the string is non-empty and consists only of decimal digits,1401* false otherwise.1402*/1403static int1404isint_w(const wchar_t *start, const wchar_t *end, int *result)1405{1406int n = 0;1407if (start >= end)1408return (0);1409while (start < end) {1410if (*start < L'0' || *start > L'9')1411return (0);1412if (n > (INT_MAX / 10) ||1413(n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {1414n = INT_MAX;1415} else {1416n *= 10;1417n += *start - L'0';1418}1419start++;1420}1421*result = n;1422return (1);1423}14241425/*1426* Parse a string as a mode field. Returns true if1427* the string is non-empty and consists only of mode characters,1428* false otherwise.1429*/1430static int1431ismode_w(const wchar_t *start, const wchar_t *end, int *permset)1432{1433const wchar_t *p;14341435if (start >= end)1436return (0);1437p = start;1438*permset = 0;1439while (p < end) {1440switch (*p++) {1441case L'r': case L'R':1442*permset |= ARCHIVE_ENTRY_ACL_READ;1443break;1444case L'w': case L'W':1445*permset |= ARCHIVE_ENTRY_ACL_WRITE;1446break;1447case L'x': case L'X':1448*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;1449break;1450case L'-':1451break;1452default:1453return (0);1454}1455}1456return (1);1457}14581459/*1460* Parse a string as a NFS4 ACL permission field.1461* Returns true if the string is non-empty and consists only of NFS4 ACL1462* permission characters, false otherwise1463*/1464static int1465is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)1466{1467const wchar_t *p = start;14681469while (p < end) {1470switch (*p++) {1471case L'r':1472*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;1473break;1474case L'w':1475*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;1476break;1477case L'x':1478*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;1479break;1480case L'p':1481*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;1482break;1483case L'D':1484*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;1485break;1486case L'd':1487*permset |= ARCHIVE_ENTRY_ACL_DELETE;1488break;1489case L'a':1490*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;1491break;1492case L'A':1493*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;1494break;1495case L'R':1496*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;1497break;1498case L'W':1499*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;1500break;1501case L'c':1502*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;1503break;1504case L'C':1505*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;1506break;1507case L'o':1508*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;1509break;1510case L's':1511*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;1512break;1513case L'-':1514break;1515default:1516return(0);1517}1518}1519return (1);1520}15211522/*1523* Parse a string as a NFS4 ACL flags field.1524* Returns true if the string is non-empty and consists only of NFS4 ACL1525* flag characters, false otherwise1526*/1527static int1528is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)1529{1530const wchar_t *p = start;15311532while (p < end) {1533switch(*p++) {1534case L'f':1535*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;1536break;1537case L'd':1538*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;1539break;1540case L'i':1541*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;1542break;1543case L'n':1544*permset |=1545ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;1546break;1547case L'S':1548*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;1549break;1550case L'F':1551*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;1552break;1553case L'I':1554*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;1555break;1556case L'-':1557break;1558default:1559return (0);1560}1561}1562return (1);1563}15641565/*1566* Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated1567* to point to just after the separator. *start points to the first1568* character of the matched text and *end just after the last1569* character of the matched identifier. In particular *end - *start1570* is the length of the field body, not including leading or trailing1571* whitespace.1572*/1573static void1574next_field_w(const wchar_t **wp, const wchar_t **start,1575const wchar_t **end, wchar_t *sep)1576{1577/* Skip leading whitespace to find start of field. */1578while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {1579(*wp)++;1580}1581*start = *wp;15821583/* Scan for the separator. */1584while (**wp != L'\0' && **wp != L',' && **wp != L':' &&1585**wp != L'\n' && **wp != L'#') {1586(*wp)++;1587}1588*sep = **wp;15891590/* Locate end of field, trim trailing whitespace if necessary */1591if (*wp == *start) {1592*end = *wp;1593} else {1594*end = *wp - 1;1595while (**end == L' ' || **end == L'\t' || **end == L'\n') {1596(*end)--;1597}1598(*end)++;1599}16001601/* Handle in-field comments */1602if (*sep == L'#') {1603while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {1604(*wp)++;1605}1606*sep = **wp;1607}16081609/* Adjust scanner location. */1610if (**wp != L'\0')1611(*wp)++;1612}16131614/*1615* Parse an ACL text string.1616*1617* The want_type argument may be one of the following:1618* ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS1619* ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT1620* ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL1621*1622* POSIX.1e ACL entries prefixed with "default:" are treated as1623* ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS41624*/1625int1626archive_acl_from_text_l(struct archive_acl *acl, const char *text,1627int want_type, struct archive_string_conv *sc)1628{1629return archive_acl_from_text_nl(acl, text, strlen(text), want_type, sc);1630}16311632int1633archive_acl_from_text_nl(struct archive_acl *acl, const char *text,1634size_t length, int want_type, struct archive_string_conv *sc)1635{1636struct {1637const char *start;1638const char *end;1639} field[6], name;16401641const char *s, *st;1642int numfields, fields, n, r, sol, ret;1643int type, types, tag, permset, id;1644size_t len;1645char sep;16461647switch (want_type) {1648case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:1649want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;1650__LA_FALLTHROUGH;1651case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:1652case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:1653numfields = 5;1654break;1655case ARCHIVE_ENTRY_ACL_TYPE_NFS4:1656numfields = 6;1657break;1658default:1659return (ARCHIVE_FATAL);1660}16611662ret = ARCHIVE_OK;1663types = 0;16641665while (text != NULL && length > 0 && *text != '\0') {1666/*1667* Parse the fields out of the next entry,1668* advance 'text' to start of next entry.1669*/1670fields = 0;1671do {1672const char *start, *end;1673next_field(&text, &length, &start, &end, &sep);1674if (fields < numfields) {1675field[fields].start = start;1676field[fields].end = end;1677}1678++fields;1679} while (sep == ':');16801681/* Set remaining fields to blank. */1682for (n = fields; n < numfields; ++n)1683field[n].start = field[n].end = NULL;16841685if (field[0].start != NULL && *(field[0].start) == '#') {1686/* Comment, skip entry */1687continue;1688}16891690n = 0;1691sol = 0;1692id = -1;1693permset = 0;1694name.start = name.end = NULL;16951696if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {1697/* POSIX.1e ACLs */1698/*1699* Default keyword "default:user::rwx"1700* if found, we have one more field1701*1702* We also support old Solaris extension:1703* "defaultuser::rwx" is the default ACL corresponding1704* to "user::rwx", etc. valid only for first field1705*/1706s = field[0].start;1707#ifdef __clang_analyzer__1708assert(s);1709#endif1710len = field[0].end - field[0].start;1711if (*s == 'd' && (len == 1 || (len >= 71712&& memcmp((s + 1), "efault", 6) == 0))) {1713type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;1714if (len > 7)1715field[0].start += 7;1716else1717n = 1;1718} else1719type = want_type;17201721/* Check for a numeric ID in field n+1 or n+3. */1722isint(field[n + 1].start, field[n + 1].end, &id);1723/* Field n+3 is optional. */1724if (id == -1 && fields > (n + 3))1725isint(field[n + 3].start, field[n + 3].end,1726&id);17271728tag = 0;1729s = field[n].start;1730st = field[n].start + 1;1731len = field[n].end - field[n].start;17321733if (len == 0) {1734ret = ARCHIVE_WARN;1735continue;1736}17371738switch (*s) {1739case 'u':1740if (len == 1 || (len == 41741&& memcmp(st, "ser", 3) == 0))1742tag = ARCHIVE_ENTRY_ACL_USER_OBJ;1743break;1744case 'g':1745if (len == 1 || (len == 51746&& memcmp(st, "roup", 4) == 0))1747tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;1748break;1749case 'o':1750if (len == 1 || (len == 51751&& memcmp(st, "ther", 4) == 0))1752tag = ARCHIVE_ENTRY_ACL_OTHER;1753break;1754case 'm':1755if (len == 1 || (len == 41756&& memcmp(st, "ask", 3) == 0))1757tag = ARCHIVE_ENTRY_ACL_MASK;1758break;1759default:1760break;1761}17621763switch (tag) {1764case ARCHIVE_ENTRY_ACL_OTHER:1765case ARCHIVE_ENTRY_ACL_MASK:1766if (fields == (n + 2)1767&& field[n + 1].start < field[n + 1].end1768&& ismode(field[n + 1].start,1769field[n + 1].end, &permset)) {1770/* This is Solaris-style "other:rwx" */1771sol = 1;1772} else if (fields == (n + 3) &&1773field[n + 1].start < field[n + 1].end) {1774/* Invalid mask or other field */1775ret = ARCHIVE_WARN;1776continue;1777}1778break;1779case ARCHIVE_ENTRY_ACL_USER_OBJ:1780case ARCHIVE_ENTRY_ACL_GROUP_OBJ:1781if (id != -1 ||1782field[n + 1].start < field[n + 1].end) {1783name = field[n + 1];1784if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)1785tag = ARCHIVE_ENTRY_ACL_USER;1786else1787tag = ARCHIVE_ENTRY_ACL_GROUP;1788}1789break;1790default:1791/* Invalid tag, skip entry */1792ret = ARCHIVE_WARN;1793continue;1794}17951796/*1797* Without "default:" we expect mode in field 31798* Exception: Solaris other and mask fields1799*/1800if (permset == 0 && !ismode(field[n + 2 - sol].start,1801field[n + 2 - sol].end, &permset)) {1802/* Invalid mode, skip entry */1803ret = ARCHIVE_WARN;1804continue;1805}1806} else {1807/* NFS4 ACLs */1808s = field[0].start;1809len = field[0].end - field[0].start;1810tag = 0;18111812switch (len) {1813case 4:1814if (memcmp(s, "user", 4) == 0)1815tag = ARCHIVE_ENTRY_ACL_USER;1816break;1817case 5:1818if (memcmp(s, "group", 5) == 0)1819tag = ARCHIVE_ENTRY_ACL_GROUP;1820break;1821case 6:1822if (memcmp(s, "owner@", 6) == 0)1823tag = ARCHIVE_ENTRY_ACL_USER_OBJ;1824else if (memcmp(s, "group@", 6) == 0)1825tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;1826break;1827case 9:1828if (memcmp(s, "everyone@", 9) == 0)1829tag = ARCHIVE_ENTRY_ACL_EVERYONE;1830break;1831default:1832break;1833}18341835if (tag == 0) {1836/* Invalid tag, skip entry */1837ret = ARCHIVE_WARN;1838continue;1839} else if (tag == ARCHIVE_ENTRY_ACL_USER ||1840tag == ARCHIVE_ENTRY_ACL_GROUP) {1841n = 1;1842name = field[1];1843isint(name.start, name.end, &id);1844} else1845n = 0;18461847if (!is_nfs4_perms(field[1 + n].start,1848field[1 + n].end, &permset)) {1849/* Invalid NFSv4 perms, skip entry */1850ret = ARCHIVE_WARN;1851continue;1852}1853if (!is_nfs4_flags(field[2 + n].start,1854field[2 + n].end, &permset)) {1855/* Invalid NFSv4 flags, skip entry */1856ret = ARCHIVE_WARN;1857continue;1858}1859s = field[3 + n].start;1860len = field[3 + n].end - field[3 + n].start;1861type = 0;1862if (len == 4) {1863if (memcmp(s, "deny", 4) == 0)1864type = ARCHIVE_ENTRY_ACL_TYPE_DENY;1865} else if (len == 5) {1866if (memcmp(s, "allow", 5) == 0)1867type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;1868else if (memcmp(s, "audit", 5) == 0)1869type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;1870else if (memcmp(s, "alarm", 5) == 0)1871type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;1872}1873if (type == 0) {1874/* Invalid entry type, skip entry */1875ret = ARCHIVE_WARN;1876continue;1877}1878isint(field[4 + n].start, field[4 + n].end,1879&id);1880}18811882/* Add entry to the internal list. */1883r = archive_acl_add_entry_len_l(acl, type, permset,1884tag, id, name.start, name.end - name.start, sc);1885if (r < ARCHIVE_WARN)1886return (r);1887if (r != ARCHIVE_OK)1888ret = ARCHIVE_WARN;1889types |= type;1890}18911892/* Reset ACL */1893archive_acl_reset(acl, types);18941895return (ret);1896}18971898/*1899* Parse a string to a positive decimal integer. Returns true if1900* the string is non-empty and consists only of decimal digits,1901* false otherwise.1902*/1903static int1904isint(const char *start, const char *end, int *result)1905{1906int n = 0;1907if (start >= end)1908return (0);1909while (start < end) {1910if (*start < '0' || *start > '9')1911return (0);1912if (n > (INT_MAX / 10) ||1913(n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {1914n = INT_MAX;1915} else {1916n *= 10;1917n += *start - '0';1918}1919start++;1920}1921*result = n;1922return (1);1923}19241925/*1926* Parse a string as a mode field. Returns true if1927* the string is non-empty and consists only of mode characters,1928* false otherwise.1929*/1930static int1931ismode(const char *start, const char *end, int *permset)1932{1933const char *p;19341935if (start >= end)1936return (0);1937p = start;1938*permset = 0;1939while (p < end) {1940switch (*p++) {1941case 'r': case 'R':1942*permset |= ARCHIVE_ENTRY_ACL_READ;1943break;1944case 'w': case 'W':1945*permset |= ARCHIVE_ENTRY_ACL_WRITE;1946break;1947case 'x': case 'X':1948*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;1949break;1950case '-':1951break;1952default:1953return (0);1954}1955}1956return (1);1957}19581959/*1960* Parse a string as a NFS4 ACL permission field.1961* Returns true if the string is non-empty and consists only of NFS4 ACL1962* permission characters, false otherwise1963*/1964static int1965is_nfs4_perms(const char *start, const char *end, int *permset)1966{1967const char *p = start;19681969while (p < end) {1970switch (*p++) {1971case 'r':1972*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;1973break;1974case 'w':1975*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;1976break;1977case 'x':1978*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;1979break;1980case 'p':1981*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;1982break;1983case 'D':1984*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;1985break;1986case 'd':1987*permset |= ARCHIVE_ENTRY_ACL_DELETE;1988break;1989case 'a':1990*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;1991break;1992case 'A':1993*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;1994break;1995case 'R':1996*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;1997break;1998case 'W':1999*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;2000break;2001case 'c':2002*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;2003break;2004case 'C':2005*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;2006break;2007case 'o':2008*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;2009break;2010case 's':2011*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;2012break;2013case '-':2014break;2015default:2016return(0);2017}2018}2019return (1);2020}20212022/*2023* Parse a string as a NFS4 ACL flags field.2024* Returns true if the string is non-empty and consists only of NFS4 ACL2025* flag characters, false otherwise2026*/2027static int2028is_nfs4_flags(const char *start, const char *end, int *permset)2029{2030const char *p = start;20312032while (p < end) {2033switch(*p++) {2034case 'f':2035*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;2036break;2037case 'd':2038*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;2039break;2040case 'i':2041*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;2042break;2043case 'n':2044*permset |=2045ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;2046break;2047case 'S':2048*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;2049break;2050case 'F':2051*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;2052break;2053case 'I':2054*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;2055break;2056case '-':2057break;2058default:2059return (0);2060}2061}2062return (1);2063}20642065/*2066* Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *p is updated2067* to point to just after the separator. *start points to the first2068* character of the matched text and *end just after the last2069* character of the matched identifier. In particular *end - *start2070* is the length of the field body, not including leading or trailing2071* whitespace.2072*/2073static void2074next_field(const char **p, size_t *l, const char **start,2075const char **end, char *sep)2076{2077/* Skip leading whitespace to find start of field. */2078while (*l > 0 && (**p == ' ' || **p == '\t' || **p == '\n')) {2079(*p)++;2080(*l)--;2081}2082*start = *p;20832084/* Locate end of field, trim trailing whitespace if necessary */2085while (*l > 0 && **p != ' ' && **p != '\t' && **p != '\n' && **p != ',' && **p != ':' && **p != '#') {2086(*p)++;2087(*l)--;2088}2089*end = *p;20902091/* Scan for the separator. */2092while (*l > 0 && **p != ',' && **p != ':' && **p != '\n' && **p != '#') {2093(*p)++;2094(*l)--;2095}2096*sep = **p;20972098/* Handle in-field comments */2099if (*sep == '#') {2100while (*l > 0 && **p != ',' && **p != '\n') {2101(*p)++;2102(*l)--;2103}2104*sep = **p;2105}21062107/* Skip separator. */2108if (*l > 0) {2109(*p)++;2110(*l)--;2111}2112}211321142115