Coverage Report

Created: 2026-09-14 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmlibarchive/libarchive/archive_acl.c
Line
Count
Source
1
/*-
2
 * Copyright (c) 2003-2010 Tim Kientzle
3
 * Copyright (c) 2016 Martin Matuska
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 * 1. Redistributions of source code must retain the above copyright
10
 *    notice, this list of conditions and the following disclaimer.
11
 * 2. Redistributions in binary form must reproduce the above copyright
12
 *    notice, this list of conditions and the following disclaimer in the
13
 *    documentation and/or other materials provided with the distribution.
14
 *
15
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 */
26
27
#include "archive_platform.h"
28
29
#ifdef HAVE_ERRNO_H
30
#include <errno.h>
31
#endif
32
#ifdef HAVE_LIMITS_H
33
#include <limits.h>
34
#endif
35
#ifdef HAVE_WCHAR_H
36
#include <wchar.h>
37
#endif
38
39
#ifdef __clang_analyzer__
40
#include <assert.h>
41
#endif
42
43
#include "archive_acl_private.h"
44
#include "archive_entry.h"
45
#include "archive_private.h"
46
47
#undef max
48
#define max(a, b) ((a)>(b)?(a):(b))
49
50
#ifndef HAVE_WMEMCMP
51
/* Good enough for simple equality testing, but not for sorting. */
52
#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
53
#endif
54
55
static int  acl_special(struct archive_acl *acl,
56
        int type, int permset, int tag);
57
static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
58
        int type, int permset, int tag, int id);
59
static int  archive_acl_add_entry_len_l(struct archive_acl *acl,
60
        int type, int permset, int tag, int id, const char *name,
61
        size_t len, struct archive_string_conv *sc);
62
static int  archive_acl_text_want_type(struct archive_acl *acl, int flags);
63
static size_t archive_acl_text_empty(struct archive_acl *acl, int want_type);
64
static int  isint_w(const wchar_t *start, const wchar_t *end, int *result);
65
static int  ismode_w(const wchar_t *start, const wchar_t *end, int *result);
66
static int  is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
67
        int *result);
68
static int  is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
69
        int *result);
70
static void next_field_w(const wchar_t **wp, const wchar_t **start,
71
        const wchar_t **end, wchar_t *sep);
72
static void append_entry_w(struct archive_wstring *ws, const wchar_t *prefix, int type,
73
        int tag, int flags, const wchar_t *wname, int perm, int id);
74
static void append_id_w(struct archive_wstring *ws, int id);
75
static int  isint(const char *start, const char *end, int *result);
76
static int  ismode(const char *start, const char *end, int *result);
77
static int  is_nfs4_flags(const char *start, const char *end,
78
        int *result);
79
static int  is_nfs4_perms(const char *start, const char *end,
80
        int *result);
81
static void next_field(const char **p, size_t *l, const char **start,
82
        const char **end, char *sep);
83
static void append_entry(struct archive_string *s, const char *prefix, int type,
84
        int tag, int flags, const char *name, int perm, int id);
85
static void append_id(struct archive_string *s, int id);
86
87
static const struct {
88
  const int perm;
89
  const char c;
90
  const wchar_t wc;
91
} nfsv4_acl_perm_map[] = {
92
  { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
93
      L'r' },
94
  { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
95
      L'w' },
96
  { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
97
  { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
98
      'p', L'p' },
99
  { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
100
  { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
101
  { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
102
  { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
103
  { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
104
  { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
105
  { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
106
  { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
107
  { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
108
  { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
109
};
110
111
static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
112
    sizeof(nfsv4_acl_perm_map[0]));
113
114
static const struct {
115
  const int perm;
116
  const char c;
117
  const wchar_t wc;
118
} nfsv4_acl_flag_map[] = {
119
  { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
120
  { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
121
  { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
122
  { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
123
  { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
124
  { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
125
  { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
126
};
127
128
static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
129
    sizeof(nfsv4_acl_flag_map[0]));
130
131
void
132
archive_acl_clear(struct archive_acl *acl)
133
205k
{
134
205k
  struct archive_acl_entry *ap;
135
136
205k
  while (acl->acl_head != NULL) {
137
0
    ap = acl->acl_head->next;
138
0
    archive_mstring_clean(&acl->acl_head->name);
139
0
    free(acl->acl_head);
140
0
    acl->acl_head = ap;
141
0
  }
142
205k
  free(acl->acl_text_w);
143
205k
  acl->acl_text_w = NULL;
144
205k
  free(acl->acl_text);
145
205k
  acl->acl_text = NULL;
146
205k
  acl->acl_p = NULL;
147
205k
  acl->acl_types = 0;
148
205k
  acl->acl_state = 0; /* Not counting. */
149
205k
}
150
151
void
152
archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
153
51.8k
{
154
51.8k
  struct archive_acl_entry *ap, *ap2;
155
156
51.8k
  archive_acl_clear(dest);
157
158
51.8k
  dest->mode = src->mode;
159
51.8k
  ap = src->acl_head;
160
51.8k
  while (ap != NULL) {
161
0
    ap2 = acl_new_entry(dest,
162
0
        ap->type, ap->permset, ap->tag, ap->id);
163
0
    if (ap2 != NULL)
164
0
      archive_mstring_copy(&ap2->name, &ap->name);
165
0
    ap = ap->next;
166
0
  }
167
51.8k
}
168
169
int
170
archive_acl_add_entry(struct archive_acl *acl,
171
    int type, int permset, int tag, int id, const char *name)
172
0
{
173
0
  struct archive_acl_entry *ap;
174
175
0
  if (acl_special(acl, type, permset, tag) == 0)
176
0
    return ARCHIVE_OK;
177
0
  ap = acl_new_entry(acl, type, permset, tag, id);
178
0
  if (ap == NULL) {
179
    /* XXX Error XXX */
180
0
    return ARCHIVE_FAILED;
181
0
  }
182
0
  if (name != NULL  &&  *name != '\0')
183
0
    archive_mstring_copy_mbs(&ap->name, name);
184
0
  else
185
0
    archive_mstring_clean(&ap->name);
186
0
  return ARCHIVE_OK;
187
0
}
188
189
int
190
archive_acl_add_entry_w_len(struct archive_acl *acl,
191
    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
192
0
{
193
0
  struct archive_acl_entry *ap;
194
195
0
  if (acl_special(acl, type, permset, tag) == 0)
196
0
    return ARCHIVE_OK;
197
0
  ap = acl_new_entry(acl, type, permset, tag, id);
198
0
  if (ap == NULL) {
199
    /* XXX Error XXX */
200
0
    return ARCHIVE_FAILED;
201
0
  }
202
0
  if (name != NULL  &&  *name != L'\0' && len > 0)
203
0
    archive_mstring_copy_wcs_len(&ap->name, name, len);
204
0
  else
205
0
    archive_mstring_clean(&ap->name);
206
0
  return ARCHIVE_OK;
207
0
}
208
209
static int
210
archive_acl_add_entry_len_l(struct archive_acl *acl,
211
    int type, int permset, int tag, int id, const char *name, size_t len,
212
    struct archive_string_conv *sc)
213
0
{
214
0
  struct archive_acl_entry *ap;
215
0
  int r;
216
217
0
  if (acl_special(acl, type, permset, tag) == 0)
218
0
    return ARCHIVE_OK;
219
0
  ap = acl_new_entry(acl, type, permset, tag, id);
220
0
  if (ap == NULL) {
221
    /* XXX Error XXX */
222
0
    return ARCHIVE_FAILED;
223
0
  }
224
0
  if (name != NULL  &&  *name != '\0' && len > 0) {
225
0
    r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
226
0
  } else {
227
0
    r = 0;
228
0
    archive_mstring_clean(&ap->name);
229
0
  }
230
0
  if (r == 0)
231
0
    return (ARCHIVE_OK);
232
0
  else if (errno == ENOMEM)
233
0
    return (ARCHIVE_FATAL);
234
0
  else
235
0
    return (ARCHIVE_WARN);
236
0
}
237
238
/*
239
 * If this ACL entry is part of the standard POSIX permissions set,
240
 * store the permissions in the stat structure and return zero.
241
 */
242
static int
243
acl_special(struct archive_acl *acl, int type, int permset, int tag)
244
0
{
245
0
  if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
246
0
      && ((permset & ~007) == 0)) {
247
0
    switch (tag) {
248
0
    case ARCHIVE_ENTRY_ACL_USER_OBJ:
249
0
      acl->mode &= ~0700;
250
0
      acl->mode |= (permset & 7) << 6;
251
0
      return (0);
252
0
    case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
253
0
      acl->mode &= ~0070;
254
0
      acl->mode |= (permset & 7) << 3;
255
0
      return (0);
256
0
    case ARCHIVE_ENTRY_ACL_OTHER:
257
0
      acl->mode &= ~0007;
258
0
      acl->mode |= permset & 7;
259
0
      return (0);
260
0
    }
261
0
  }
262
0
  return (1);
263
0
}
264
265
/*
266
 * Allocate and populate a new ACL entry with everything but the
267
 * name.
268
 */
269
static struct archive_acl_entry *
270
acl_new_entry(struct archive_acl *acl,
271
    int type, int permset, int tag, int id)
272
0
{
273
0
  struct archive_acl_entry *ap, *aq;
274
275
  /* Reject an invalid type */
276
0
  switch (type) {
277
0
  case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
278
0
  case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
279
0
  case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
280
0
  case ARCHIVE_ENTRY_ACL_TYPE_DENY:
281
0
  case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
282
0
  case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
283
0
    break;
284
0
  default:
285
0
    return (NULL);
286
0
  }
287
288
  /* Type argument must be a valid NFS4 or POSIX.1e type.
289
   * The type must agree with anything already set and
290
   * the permset must be compatible. */
291
0
  if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
292
0
    if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
293
0
      return (NULL);
294
0
    }
295
0
    if (permset &
296
0
        ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
297
0
      | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
298
0
      return (NULL);
299
0
    }
300
0
  } else  if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
301
0
    if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
302
0
      return (NULL);
303
0
    }
304
0
    if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
305
0
      return (NULL);
306
0
    }
307
0
  } else {
308
0
    return (NULL);
309
0
  }
310
311
  /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
312
0
  switch (tag) {
313
0
  case ARCHIVE_ENTRY_ACL_USER:
314
0
  case ARCHIVE_ENTRY_ACL_USER_OBJ:
315
0
  case ARCHIVE_ENTRY_ACL_GROUP:
316
0
  case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
317
    /* Tags valid in both NFS4 and POSIX.1e */
318
0
    break;
319
0
  case ARCHIVE_ENTRY_ACL_MASK:
320
0
  case ARCHIVE_ENTRY_ACL_OTHER:
321
    /* Tags valid only in POSIX.1e. */
322
0
    if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
323
0
      return (NULL);
324
0
    }
325
0
    break;
326
0
  case ARCHIVE_ENTRY_ACL_EVERYONE:
327
    /* Tags valid only in NFS4. */
328
0
    if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
329
0
      return (NULL);
330
0
    }
331
0
    break;
332
0
  default:
333
    /* No other values are valid. */
334
0
    return (NULL);
335
0
  }
336
337
0
  free(acl->acl_text_w);
338
0
  acl->acl_text_w = NULL;
339
0
  free(acl->acl_text);
340
0
  acl->acl_text = NULL;
341
342
  /*
343
   * If there's a matching entry already in the list, overwrite it.
344
   * NFSv4 entries may be repeated and are not overwritten.
345
   *
346
   * TODO: compare names of no id is provided (needs more rework)
347
   */
348
0
  ap = acl->acl_head;
349
0
  aq = NULL;
350
0
  while (ap != NULL) {
351
0
    if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
352
0
        ap->type == type && ap->tag == tag && ap->id == id) {
353
0
      if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
354
0
          tag != ARCHIVE_ENTRY_ACL_GROUP)) {
355
0
        ap->permset = permset;
356
0
        return (ap);
357
0
      }
358
0
    }
359
0
    aq = ap;
360
0
    ap = ap->next;
361
0
  }
362
363
  /* Add a new entry to the end of the list. */
364
0
  ap = calloc(1, sizeof(*ap));
365
0
  if (ap == NULL)
366
0
    return (NULL);
367
0
  if (aq == NULL)
368
0
    acl->acl_head = ap;
369
0
  else
370
0
    aq->next = ap;
371
0
  ap->type = type;
372
0
  ap->tag = tag;
373
0
  ap->id = id;
374
0
  ap->permset = permset;
375
0
  acl->acl_types |= type;
376
0
  return (ap);
377
0
}
378
379
/*
380
 * Return a count of entries matching "want_type".
381
 */
382
int
383
archive_acl_count(struct archive_acl *acl, int want_type)
384
0
{
385
0
  int count;
386
0
  struct archive_acl_entry *ap;
387
388
0
  count = 0;
389
0
  ap = acl->acl_head;
390
0
  while (ap != NULL) {
391
0
    if ((ap->type & want_type) != 0)
392
0
      count++;
393
0
    ap = ap->next;
394
0
  }
395
396
0
  if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
397
0
    count += 3;
398
0
  return (count);
399
0
}
400
401
/*
402
 * Return a bitmask of stored ACL types in an ACL list
403
 */
404
int
405
archive_acl_types(struct archive_acl *acl)
406
0
{
407
0
  return (acl->acl_types);
408
0
}
409
410
/*
411
 * Prepare for reading entries from the ACL data.  Returns a count
412
 * of entries matching "want_type", or zero if there are no
413
 * non-extended ACL entries of that type.
414
 */
415
int
416
archive_acl_reset(struct archive_acl *acl, int want_type)
417
0
{
418
0
  int count, cutoff;
419
420
0
  count = archive_acl_count(acl, want_type);
421
422
  /*
423
   * If the only entries are the three standard ones,
424
   * then don't return any ACL data.  (In this case,
425
   * client can just use chmod(2) to set permissions.)
426
   */
427
0
  if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
428
0
    cutoff = 3;
429
0
  else
430
0
    cutoff = 0;
431
432
0
  if (count > cutoff)
433
0
    acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
434
0
  else
435
0
    acl->acl_state = 0;
436
0
  acl->acl_p = acl->acl_head;
437
0
  return (count);
438
0
}
439
440
441
/*
442
 * Return the next ACL entry in the list.  Fake entries for the
443
 * standard permissions and include them in the returned list.
444
 */
445
int
446
archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
447
    int *type, int *permset, int *tag, int *id, const char **name)
448
0
{
449
0
  *name = NULL;
450
0
  *id = -1;
451
452
  /*
453
   * The acl_state is either zero (no entries available), -1
454
   * (reading from list), or an entry type (retrieve that type
455
   * from ae_stat.aest_mode).
456
   */
457
0
  if (acl->acl_state == 0)
458
0
    return (ARCHIVE_WARN);
459
460
  /* The first three access entries are special. */
461
0
  if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
462
0
    switch (acl->acl_state) {
463
0
    case ARCHIVE_ENTRY_ACL_USER_OBJ:
464
0
      *permset = (acl->mode >> 6) & 7;
465
0
      *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
466
0
      *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
467
0
      acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
468
0
      return (ARCHIVE_OK);
469
0
    case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
470
0
      *permset = (acl->mode >> 3) & 7;
471
0
      *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
472
0
      *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
473
0
      acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
474
0
      return (ARCHIVE_OK);
475
0
    case ARCHIVE_ENTRY_ACL_OTHER:
476
0
      *permset = acl->mode & 7;
477
0
      *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
478
0
      *tag = ARCHIVE_ENTRY_ACL_OTHER;
479
0
      acl->acl_state = -1;
480
0
      acl->acl_p = acl->acl_head;
481
0
      return (ARCHIVE_OK);
482
0
    default:
483
0
      break;
484
0
    }
485
0
  }
486
487
0
  while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
488
0
    acl->acl_p = acl->acl_p->next;
489
0
  if (acl->acl_p == NULL) {
490
0
    acl->acl_state = 0;
491
0
    *type = 0;
492
0
    *permset = 0;
493
0
    *tag = 0;
494
0
    *id = -1;
495
0
    *name = NULL;
496
0
    return (ARCHIVE_EOF); /* End of ACL entries. */
497
0
  }
498
0
  *type = acl->acl_p->type;
499
0
  *permset = acl->acl_p->permset;
500
0
  *tag = acl->acl_p->tag;
501
0
  *id = acl->acl_p->id;
502
0
  if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
503
0
    if (errno == ENOMEM)
504
0
      return (ARCHIVE_FATAL);
505
0
    *name = NULL;
506
0
  }
507
0
  acl->acl_p = acl->acl_p->next;
508
0
  return (ARCHIVE_OK);
509
0
}
510
511
/*
512
 * Determine what type of ACL do we want
513
 */
514
static int
515
archive_acl_text_want_type(struct archive_acl *acl, int flags)
516
0
{
517
0
  int want_type;
518
519
  /* Check if ACL is NFSv4 */
520
0
  if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
521
    /* NFSv4 should never mix with POSIX.1e */
522
0
    if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
523
0
      return (0);
524
0
    else
525
0
      return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
526
0
  }
527
528
  /* Now deal with POSIX.1e ACLs */
529
530
0
  want_type = 0;
531
0
  if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
532
0
    want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
533
0
  if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
534
0
    want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
535
536
  /* By default we want both access and default ACLs */
537
0
  if (want_type == 0)
538
0
    return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
539
540
0
  return (want_type);
541
0
}
542
543
/*
544
 * Check if ACL text would be empty
545
 */
546
static size_t
547
0
archive_acl_text_empty(struct archive_acl *acl, int want_type) {
548
0
  struct archive_acl_entry *ap;
549
550
0
  for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
551
0
    if ((ap->type & want_type) == 0)
552
0
      continue;
553
    /*
554
     * Filemode-mapping ACL entries are stored exclusively in
555
     * ap->mode so they should not be in the list
556
     */
557
0
    if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
558
0
        && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
559
0
        || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
560
0
        || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
561
0
      continue;
562
563
0
    return (0);
564
0
  }
565
566
0
  return (1);
567
0
}
568
569
/*
570
 * Generate a wide text version of the ACL. The flags parameter controls
571
 * the type and style of the generated ACL.
572
 */
573
wchar_t *
574
archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
575
    struct archive *a)
576
0
{
577
0
  const wchar_t *wname;
578
0
  const wchar_t *prefix;
579
0
  wchar_t separator;
580
0
  struct archive_acl_entry *ap;
581
0
  struct archive_wstring ws;
582
0
  int id, r, want_type;
583
584
0
  want_type = archive_acl_text_want_type(acl, flags);
585
586
  /* Both NFSv4 and POSIX.1 types found */
587
0
  if (want_type == 0)
588
0
    return (NULL);
589
590
0
  if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
591
0
    flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
592
593
0
  if (archive_acl_text_empty(acl, want_type))
594
0
    return (NULL);
595
596
0
  if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
597
0
    separator = L',';
598
0
  else
599
0
    separator = L'\n';
600
601
0
  archive_string_init(&ws);
602
603
0
  if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
604
0
    append_entry_w(&ws, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
605
0
        ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
606
0
        acl->mode & 0700, -1);
607
0
    archive_wstrappend_wchar(&ws, separator);
608
0
    append_entry_w(&ws, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
609
0
        ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
610
0
        acl->mode & 0070, -1);
611
0
    archive_wstrappend_wchar(&ws, separator);
612
0
    append_entry_w(&ws, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
613
0
        ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
614
0
        acl->mode & 0007, -1);
615
0
  }
616
617
0
  for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
618
0
    if ((ap->type & want_type) == 0)
619
0
      continue;
620
    /*
621
     * Filemode-mapping ACL entries are stored exclusively in
622
     * ap->mode so they should not be in the list
623
     */
624
0
    if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
625
0
        && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
626
0
        || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
627
0
        || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
628
0
      continue;
629
0
    if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
630
0
        (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
631
0
      prefix = L"default:";
632
0
    else
633
0
      prefix = NULL;
634
0
    r = archive_mstring_get_wcs(a, &ap->name, &wname);
635
0
    if (r == 0) {
636
0
      if (ws.length > 0)
637
0
        archive_wstrappend_wchar(&ws, separator);
638
0
      if ((flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) ||
639
0
          wname == NULL)
640
0
        id = ap->id;
641
0
      else
642
0
        id = -1;
643
0
      append_entry_w(&ws, prefix, ap->type, ap->tag, flags,
644
0
          wname, ap->permset, id);
645
0
    } else if (r < 0 && errno == ENOMEM) {
646
0
      archive_wstring_free(&ws);
647
0
      return (NULL);
648
0
    }
649
0
  }
650
651
0
  if (text_len != NULL)
652
0
    *text_len = ws.length;
653
654
0
  return (ws.s);
655
0
}
656
657
static void
658
append_id_w(struct archive_wstring *ws, int id)
659
0
{
660
0
  if (id < 0)
661
0
    id = 0;
662
0
  if (id > 9)
663
0
    append_id_w(ws, id / 10);
664
0
  archive_wstrappend_wchar(ws, L"0123456789"[id % 10]);
665
0
}
666
667
static void
668
append_entry_w(struct archive_wstring *ws, const wchar_t *prefix, int type,
669
    int tag, int flags, const wchar_t *wname, int perm, int id)
670
0
{
671
0
  int i;
672
673
0
  if (prefix != NULL)
674
0
    archive_wstrcat(ws, prefix);
675
0
  switch (tag) {
676
0
  case ARCHIVE_ENTRY_ACL_USER_OBJ:
677
0
    wname = NULL;
678
0
    id = -1;
679
0
    if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
680
0
      archive_wstrcat(ws, L"owner@");
681
0
      break;
682
0
    }
683
    /* FALLTHROUGH */
684
0
  case ARCHIVE_ENTRY_ACL_USER:
685
0
    archive_wstrcat(ws, L"user");
686
0
    break;
687
0
  case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
688
0
    wname = NULL;
689
0
    id = -1;
690
0
    if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
691
0
      archive_wstrcat(ws, L"group@");
692
0
      break;
693
0
    }
694
    /* FALLTHROUGH */
695
0
  case ARCHIVE_ENTRY_ACL_GROUP:
696
0
    archive_wstrcat(ws, L"group");
697
0
    break;
698
0
  case ARCHIVE_ENTRY_ACL_MASK:
699
0
    archive_wstrcat(ws, L"mask");
700
0
    wname = NULL;
701
0
    id = -1;
702
0
    break;
703
0
  case ARCHIVE_ENTRY_ACL_OTHER:
704
0
    archive_wstrcat(ws, L"other");
705
0
    wname = NULL;
706
0
    id = -1;
707
0
    break;
708
0
  case ARCHIVE_ENTRY_ACL_EVERYONE:
709
0
    archive_wstrcat(ws, L"everyone@");
710
0
    wname = NULL;
711
0
    id = -1;
712
0
    break;
713
0
  default:
714
0
    break;
715
0
  }
716
0
  archive_wstrappend_wchar(ws, L':');
717
0
  if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
718
0
      tag == ARCHIVE_ENTRY_ACL_USER ||
719
0
      tag == ARCHIVE_ENTRY_ACL_GROUP) {
720
0
    if (wname != NULL) {
721
0
      archive_wstrcat(ws, wname);
722
0
    } else if (tag == ARCHIVE_ENTRY_ACL_USER
723
0
        || tag == ARCHIVE_ENTRY_ACL_GROUP) {
724
0
      append_id_w(ws, id);
725
0
      if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
726
0
        id = -1;
727
0
    }
728
    /* Solaris style has no second colon after other and mask */
729
0
    if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
730
0
        || (tag != ARCHIVE_ENTRY_ACL_OTHER
731
0
        && tag != ARCHIVE_ENTRY_ACL_MASK))
732
0
      archive_wstrappend_wchar(ws, L':');
733
0
  }
734
0
  if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
735
    /* POSIX.1e ACL perms */
736
0
    archive_wstrappend_wchar(ws, (perm & 0444) ? L'r' : L'-');
737
0
    archive_wstrappend_wchar(ws, (perm & 0222) ? L'w' : L'-');
738
0
    archive_wstrappend_wchar(ws, (perm & 0111) ? L'x' : L'-');
739
0
  } else {
740
    /* NFSv4 ACL perms */
741
0
    for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
742
0
      if (perm & nfsv4_acl_perm_map[i].perm)
743
0
        archive_wstrappend_wchar(ws, nfsv4_acl_perm_map[i].wc);
744
0
      else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
745
0
        archive_wstrappend_wchar(ws, L'-');
746
0
    }
747
0
    archive_wstrappend_wchar(ws, L':');
748
0
    for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
749
0
      if (perm & nfsv4_acl_flag_map[i].perm)
750
0
        archive_wstrappend_wchar(ws, nfsv4_acl_flag_map[i].wc);
751
0
      else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
752
0
        archive_wstrappend_wchar(ws, L'-');
753
0
    }
754
0
    archive_wstrappend_wchar(ws, L':');
755
0
    switch (type) {
756
0
    case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
757
0
      archive_wstrcat(ws, L"allow");
758
0
      break;
759
0
    case ARCHIVE_ENTRY_ACL_TYPE_DENY:
760
0
      archive_wstrcat(ws, L"deny");
761
0
      break;
762
0
    case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
763
0
      archive_wstrcat(ws, L"audit");
764
0
      break;
765
0
    case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
766
0
      archive_wstrcat(ws, L"alarm");
767
0
      break;
768
0
    default:
769
0
      break;
770
0
    }
771
0
  }
772
0
  if (id != -1) {
773
0
    archive_wstrappend_wchar(ws, L':');
774
0
    append_id_w(ws, id);
775
0
  }
776
0
}
777
778
/*
779
 * Generate a text version of the ACL. The flags parameter controls
780
 * the type and style of the generated ACL.
781
 */
782
char *
783
archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
784
    struct archive_string_conv *sc)
785
0
{
786
0
  size_t len;
787
0
  const char *name;
788
0
  const char *prefix;
789
0
  char separator;
790
0
  struct archive_acl_entry *ap;
791
0
  struct archive_string s;
792
0
  int id, r, want_type;
793
794
0
  want_type = archive_acl_text_want_type(acl, flags);
795
796
  /* Both NFSv4 and POSIX.1 types found */
797
0
  if (want_type == 0)
798
0
    return (NULL);
799
800
0
  if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
801
0
    flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
802
803
0
  if (archive_acl_text_empty(acl, want_type))
804
0
    return (NULL);
805
806
0
  if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
807
0
    separator = ',';
808
0
  else
809
0
    separator = '\n';
810
811
0
  archive_string_init(&s);
812
813
0
  if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
814
0
    append_entry(&s, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
815
0
        ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
816
0
        acl->mode & 0700, -1);
817
0
    archive_strappend_char(&s, separator);
818
0
    append_entry(&s, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
819
0
        ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
820
0
        acl->mode & 0070, -1);
821
0
    archive_strappend_char(&s, separator);
822
0
    append_entry(&s, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
823
0
        ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
824
0
        acl->mode & 0007, -1);
825
0
  }
826
827
0
  for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
828
0
    if ((ap->type & want_type) == 0)
829
0
      continue;
830
    /*
831
     * Filemode-mapping ACL entries are stored exclusively in
832
     * ap->mode so they should not be in the list
833
     */
834
0
    if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
835
0
        && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
836
0
        || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
837
0
        || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
838
0
      continue;
839
0
    if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
840
0
        (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
841
0
      prefix = "default:";
842
0
    else
843
0
      prefix = NULL;
844
0
    r = archive_mstring_get_mbs_l(
845
0
        NULL, &ap->name, &name, &len, sc);
846
0
    if (r != 0) {
847
0
      archive_string_free(&s);
848
0
      return (NULL);
849
0
    }
850
0
    if (s.length > 0)
851
0
      archive_strappend_char(&s, separator);
852
0
    if (name == NULL ||
853
0
        (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
854
0
      id = ap->id;
855
0
    } else {
856
0
      id = -1;
857
0
    }
858
0
    append_entry(&s, prefix, ap->type, ap->tag, flags, name,
859
0
        ap->permset, id);
860
0
  }
861
862
0
  if (text_len != NULL)
863
0
    *text_len = s.length;
864
865
0
  return (s.s);
866
0
}
867
868
static void
869
append_id(struct archive_string *s, int id)
870
0
{
871
0
  if (id < 0)
872
0
    id = 0;
873
0
  if (id > 9)
874
0
    append_id(s, id / 10);
875
0
  archive_strappend_char(s, "0123456789"[id % 10]);
876
0
}
877
878
static void
879
append_entry(struct archive_string *s, const char *prefix, int type,
880
    int tag, int flags, const char *name, int perm, int id)
881
0
{
882
0
  int i;
883
884
0
  if (prefix != NULL)
885
0
    archive_strcat(s, prefix);
886
0
  switch (tag) {
887
0
  case ARCHIVE_ENTRY_ACL_USER_OBJ:
888
0
    name = NULL;
889
0
    id = -1;
890
0
    if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
891
0
      archive_strcat(s, "owner@");
892
0
      break;
893
0
    }
894
    /* FALLTHROUGH */
895
0
  case ARCHIVE_ENTRY_ACL_USER:
896
0
    archive_strcat(s, "user");
897
0
    break;
898
0
  case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
899
0
    name = NULL;
900
0
    id = -1;
901
0
    if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
902
0
      archive_strcat(s, "group@");
903
0
      break;
904
0
    }
905
    /* FALLTHROUGH */
906
0
  case ARCHIVE_ENTRY_ACL_GROUP:
907
0
    archive_strcat(s, "group");
908
0
    break;
909
0
  case ARCHIVE_ENTRY_ACL_MASK:
910
0
    archive_strcat(s, "mask");
911
0
    name = NULL;
912
0
    id = -1;
913
0
    break;
914
0
  case ARCHIVE_ENTRY_ACL_OTHER:
915
0
    archive_strcat(s, "other");
916
0
    name = NULL;
917
0
    id = -1;
918
0
    break;
919
0
  case ARCHIVE_ENTRY_ACL_EVERYONE:
920
0
    archive_strcat(s, "everyone@");
921
0
    name = NULL;
922
0
    id = -1;
923
0
    break;
924
0
  default:
925
0
    break;
926
0
  }
927
0
  archive_strappend_char(s, ':');
928
0
  if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
929
0
      tag == ARCHIVE_ENTRY_ACL_USER ||
930
0
      tag == ARCHIVE_ENTRY_ACL_GROUP) {
931
0
    if (name != NULL) {
932
0
      archive_strcat(s, name);
933
0
    } else if (tag == ARCHIVE_ENTRY_ACL_USER
934
0
        || tag == ARCHIVE_ENTRY_ACL_GROUP) {
935
0
      append_id(s, id);
936
0
      if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
937
0
        id = -1;
938
0
    }
939
    /* Solaris style has no second colon after other and mask */
940
0
    if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
941
0
        || (tag != ARCHIVE_ENTRY_ACL_OTHER
942
0
        && tag != ARCHIVE_ENTRY_ACL_MASK))
943
0
      archive_strappend_char(s, ':');
944
0
  }
945
0
  if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
946
    /* POSIX.1e ACL perms */
947
0
    archive_strappend_char(s, (perm & 0444) ? 'r' : '-');
948
0
    archive_strappend_char(s, (perm & 0222) ? 'w' : '-');
949
0
    archive_strappend_char(s, (perm & 0111) ? 'x' : '-');
950
0
  } else {
951
    /* NFSv4 ACL perms */
952
0
    for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
953
0
      if (perm & nfsv4_acl_perm_map[i].perm)
954
0
        archive_strappend_char(s, nfsv4_acl_perm_map[i].c);
955
0
      else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
956
0
        archive_strappend_char(s, '-');
957
0
    }
958
0
    archive_strappend_char(s, ':');
959
0
    for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
960
0
      if (perm & nfsv4_acl_flag_map[i].perm)
961
0
        archive_strappend_char(s, nfsv4_acl_flag_map[i].c);
962
0
      else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
963
0
        archive_strappend_char(s, '-');
964
0
    }
965
0
    archive_strappend_char(s, ':');
966
0
    switch (type) {
967
0
    case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
968
0
      archive_strcat(s, "allow");
969
0
      break;
970
0
    case ARCHIVE_ENTRY_ACL_TYPE_DENY:
971
0
      archive_strcat(s, "deny");
972
0
      break;
973
0
    case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
974
0
      archive_strcat(s, "audit");
975
0
      break;
976
0
    case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
977
0
      archive_strcat(s, "alarm");
978
0
      break;
979
0
    default:
980
0
      break;
981
0
    }
982
0
  }
983
0
  if (id != -1) {
984
0
    archive_strappend_char(s, ':');
985
0
    append_id(s, id);
986
0
  }
987
0
}
988
989
/*
990
 * Parse a wide ACL text string.
991
 *
992
 * The want_type argument may be one of the following:
993
 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
994
 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
995
 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
996
 *
997
 * POSIX.1e ACL entries prefixed with "default:" are treated as
998
 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
999
 */
1000
int
1001
archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
1002
    int want_type)
1003
0
{
1004
0
  struct {
1005
0
    const wchar_t *start;
1006
0
    const wchar_t *end;
1007
0
  } field[6], name;
1008
1009
0
  const wchar_t *s, *st;
1010
1011
0
  int numfields, fields, n, r, sol, ret;
1012
0
  int type, types, tag, permset, id;
1013
0
  size_t len;
1014
0
  wchar_t sep;
1015
1016
0
  ret = ARCHIVE_OK;
1017
0
  types = 0;
1018
1019
0
  switch (want_type) {
1020
0
  case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1021
0
    want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1022
0
    __LA_FALLTHROUGH;
1023
0
  case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1024
0
  case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1025
0
    numfields = 5;
1026
0
    break;
1027
0
  case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1028
0
    numfields = 6;
1029
0
    break;
1030
0
  default:
1031
0
    return (ARCHIVE_FATAL);
1032
0
  }
1033
1034
0
  while (text != NULL && *text != L'\0') {
1035
    /*
1036
     * Parse the fields out of the next entry,
1037
     * advance 'text' to start of next entry.
1038
     */
1039
0
    fields = 0;
1040
0
    do {
1041
0
      const wchar_t *start, *end;
1042
0
      next_field_w(&text, &start, &end, &sep);
1043
0
      if (fields < numfields) {
1044
0
        field[fields].start = start;
1045
0
        field[fields].end = end;
1046
0
      }
1047
0
      ++fields;
1048
0
    } while (sep == L':');
1049
1050
    /* Set remaining fields to blank. */
1051
0
    for (n = fields; n < numfields; ++n)
1052
0
      field[n].start = field[n].end = NULL;
1053
    
1054
0
    if (field[0].start == NULL || field[0].end == NULL) {
1055
      /* This should never happen */
1056
0
      return (ARCHIVE_FATAL);
1057
0
    }
1058
1059
0
    if (*(field[0].start) == L'#') {
1060
      /* Comment, skip entry */
1061
0
      continue;
1062
0
    }
1063
1064
0
    n = 0;
1065
0
    sol = 0;
1066
0
    id = -1;
1067
0
    permset = 0;
1068
0
    name.start = name.end = NULL;
1069
1070
0
    if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1071
      /* POSIX.1e ACLs */
1072
      /*
1073
       * Default keyword "default:user::rwx"
1074
       * if found, we have one more field
1075
       *
1076
       * We also support old Solaris extension:
1077
       * "defaultuser::rwx" is the default ACL corresponding
1078
       * to "user::rwx", etc. valid only for first field
1079
       */
1080
0
      s = field[0].start;
1081
      #ifdef __clang_analyzer__
1082
      assert(s);
1083
      #endif
1084
0
      len = field[0].end - field[0].start;
1085
0
      if (*s == L'd' && (len == 1 || (len >= 7
1086
0
          && wmemcmp((s + 1), L"efault", 6) == 0))) {
1087
0
        type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1088
0
        if (len > 7)
1089
0
          field[0].start += 7;
1090
0
        else
1091
0
          n = 1;
1092
0
      } else
1093
0
        type = want_type;
1094
1095
      /* Check for a numeric ID in field n+1 or n+3. */
1096
0
      if (isint_w(field[n + 1].start, field[n + 1].end,
1097
0
          &id) < 0) {
1098
0
        ret = ARCHIVE_WARN;
1099
0
        continue;
1100
0
      }
1101
      /* Field n+3 is optional. */
1102
0
      if (id == -1 && fields > n+3 &&
1103
0
          isint_w(field[n + 3].start, field[n + 3].end,
1104
0
          &id) < 0) {
1105
0
        ret = ARCHIVE_WARN;
1106
0
        continue;
1107
0
      }
1108
1109
0
      tag = 0;
1110
0
      s = field[n].start;
1111
0
      len = field[n].end - field[n].start;
1112
0
      if (len == 0) {
1113
0
        ret = ARCHIVE_WARN;
1114
0
        continue;
1115
0
      }
1116
0
      st = s + 1;
1117
1118
0
      switch (*s) {
1119
0
      case L'u':
1120
0
        if (len == 1 || (len == 4
1121
0
            && wmemcmp(st, L"ser", 3) == 0))
1122
0
          tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1123
0
        break;
1124
0
      case L'g':
1125
0
        if (len == 1 || (len == 5
1126
0
            && wmemcmp(st, L"roup", 4) == 0))
1127
0
          tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1128
0
        break;
1129
0
      case L'o':
1130
0
        if (len == 1 || (len == 5
1131
0
            && wmemcmp(st, L"ther", 4) == 0))
1132
0
          tag = ARCHIVE_ENTRY_ACL_OTHER;
1133
0
        break;
1134
0
      case L'm':
1135
0
        if (len == 1 || (len == 4
1136
0
            && wmemcmp(st, L"ask", 3) == 0))
1137
0
          tag = ARCHIVE_ENTRY_ACL_MASK;
1138
0
        break;
1139
0
      default:
1140
0
          break;
1141
0
      }
1142
1143
0
      switch (tag) {
1144
0
      case ARCHIVE_ENTRY_ACL_OTHER:
1145
0
      case ARCHIVE_ENTRY_ACL_MASK:
1146
0
        if (fields == (n + 2)
1147
0
            && field[n + 1].start < field[n + 1].end
1148
0
            && ismode_w(field[n + 1].start,
1149
0
            field[n + 1].end, &permset)) {
1150
          /* This is Solaris-style "other:rwx" */
1151
0
          sol = 1;
1152
0
        } else if (fields == (n + 3) &&
1153
0
            field[n + 1].start < field[n + 1].end) {
1154
          /* Invalid mask or other field */
1155
0
          ret = ARCHIVE_WARN;
1156
0
          continue;
1157
0
        }
1158
0
        break;
1159
0
      case ARCHIVE_ENTRY_ACL_USER_OBJ:
1160
0
      case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1161
0
        if (id != -1 ||
1162
0
            field[n + 1].start < field[n + 1].end) {
1163
0
          name = field[n + 1];
1164
0
          if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1165
0
            tag = ARCHIVE_ENTRY_ACL_USER;
1166
0
          else
1167
0
            tag = ARCHIVE_ENTRY_ACL_GROUP;
1168
0
        }
1169
0
        break;
1170
0
      default:
1171
        /* Invalid tag, skip entry */
1172
0
        ret = ARCHIVE_WARN;
1173
0
        continue;
1174
0
      }
1175
1176
      /*
1177
       * Without "default:" we expect mode in field 2
1178
       * Exception: Solaris other and mask fields
1179
       */
1180
0
      if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
1181
0
          field[n + 2 - sol].end, &permset)) {
1182
        /* Invalid mode, skip entry */
1183
0
        ret = ARCHIVE_WARN;
1184
0
        continue;
1185
0
      }
1186
0
    } else {
1187
      /* NFS4 ACLs */
1188
0
      s = field[0].start;
1189
0
      len = field[0].end - field[0].start;
1190
0
      tag = 0;
1191
1192
0
      switch (len) {
1193
0
      case 4:
1194
0
        if (wmemcmp(s, L"user", 4) == 0)
1195
0
          tag = ARCHIVE_ENTRY_ACL_USER;
1196
0
        break;
1197
0
      case 5:
1198
0
        if (wmemcmp(s, L"group", 5) == 0)
1199
0
          tag = ARCHIVE_ENTRY_ACL_GROUP;
1200
0
        break;
1201
0
      case 6:
1202
0
        if (wmemcmp(s, L"owner@", 6) == 0)
1203
0
          tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1204
0
        else if (wmemcmp(s, L"group@", len) == 0)
1205
0
          tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1206
0
        break;
1207
0
      case 9:
1208
0
        if (wmemcmp(s, L"everyone@", 9) == 0)
1209
0
          tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1210
0
      default:
1211
0
        break;
1212
0
      }
1213
1214
0
      if (tag == 0) {
1215
        /* Invalid tag, skip entry */
1216
0
        ret = ARCHIVE_WARN;
1217
0
        continue;
1218
0
      } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1219
0
          tag == ARCHIVE_ENTRY_ACL_GROUP) {
1220
0
        n = 1;
1221
0
        name = field[1];
1222
0
        if (isint_w(name.start, name.end, &id) < 0) {
1223
0
          ret = ARCHIVE_WARN;
1224
0
          continue;
1225
0
        }
1226
0
      } else
1227
0
        n = 0;
1228
1229
0
      if (!is_nfs4_perms_w(field[1 + n].start,
1230
0
          field[1 + n].end, &permset)) {
1231
        /* Invalid NFSv4 perms, skip entry */
1232
0
        ret = ARCHIVE_WARN;
1233
0
        continue;
1234
0
      }
1235
0
      if (!is_nfs4_flags_w(field[2 + n].start,
1236
0
          field[2 + n].end, &permset)) {
1237
        /* Invalid NFSv4 flags, skip entry */
1238
0
        ret = ARCHIVE_WARN;
1239
0
        continue;
1240
0
      }
1241
0
      s = field[3 + n].start;
1242
0
      len = field[3 + n].end - field[3 + n].start;
1243
0
      type = 0;
1244
0
      if (len == 4) {
1245
0
        if (wmemcmp(s, L"deny", 4) == 0)
1246
0
          type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1247
0
      } else if (len == 5) {
1248
0
        if (wmemcmp(s, L"allow", 5) == 0)
1249
0
          type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1250
0
        else if (wmemcmp(s, L"audit", 5) == 0)
1251
0
          type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1252
0
        else if (wmemcmp(s, L"alarm", 5) == 0)
1253
0
          type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1254
0
      }
1255
0
      if (type == 0) {
1256
        /* Invalid entry type, skip entry */
1257
0
        ret = ARCHIVE_WARN;
1258
0
        continue;
1259
0
      }
1260
0
      if (isint_w(field[4 + n].start, field[4 + n].end,
1261
0
          &id) < 0) {
1262
0
        ret = ARCHIVE_WARN;
1263
0
        continue;
1264
0
      }
1265
0
    }
1266
1267
    /* Add entry to the internal list. */
1268
0
    r = archive_acl_add_entry_w_len(acl, type, permset,
1269
0
        tag, id, name.start, name.end - name.start);
1270
0
    if (r < ARCHIVE_WARN)
1271
0
      return (r);
1272
0
    if (r != ARCHIVE_OK)
1273
0
      ret = ARCHIVE_WARN;
1274
0
    types |= type;
1275
0
  }
1276
1277
  /* Reset ACL */
1278
0
  archive_acl_reset(acl, types);
1279
1280
0
  return (ret);
1281
0
}
1282
1283
/*
1284
 * Parse a string to a positive decimal integer.  Returns true if
1285
 * the string is non-empty and consists only of decimal digits,
1286
 * false otherwise.
1287
 */
1288
static int
1289
isint_w(const wchar_t *start, const wchar_t *end, int *result)
1290
0
{
1291
0
  int n = 0;
1292
0
  if (start >= end)
1293
0
    return (0);
1294
0
  while (start < end) {
1295
0
    if (*start < L'0' || *start > L'9')
1296
0
      return (0);
1297
0
    if (n > (INT_MAX / 10) ||
1298
0
        (n == INT_MAX / 10 && (*start - L'0') >= INT_MAX % 10)) {
1299
0
      return (-1);
1300
0
    } else {
1301
0
      n *= 10;
1302
0
      n += *start - L'0';
1303
0
    }
1304
0
    start++;
1305
0
  }
1306
0
  *result = n;
1307
0
  return (1);
1308
0
}
1309
1310
/*
1311
 * Parse a string as a mode field.  Returns true if
1312
 * the string is non-empty and consists only of mode characters,
1313
 * false otherwise.
1314
 */
1315
static int
1316
ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
1317
0
{
1318
0
  const wchar_t *p;
1319
1320
0
  if (start >= end)
1321
0
    return (0);
1322
0
  p = start;
1323
0
  *permset = 0;
1324
0
  while (p < end) {
1325
0
    switch (*p++) {
1326
0
    case L'r': case L'R':
1327
0
      *permset |= ARCHIVE_ENTRY_ACL_READ;
1328
0
      break;
1329
0
    case L'w': case L'W':
1330
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1331
0
      break;
1332
0
    case L'x': case L'X':
1333
0
      *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1334
0
      break;
1335
0
    case L'-':
1336
0
      break;
1337
0
    default:
1338
0
      return (0);
1339
0
    }
1340
0
  }
1341
0
  return (1);
1342
0
}
1343
1344
/*
1345
 * Parse a string as a NFS4 ACL permission field.
1346
 * Returns true if the string is non-empty and consists only of NFS4 ACL
1347
 * permission characters, false otherwise
1348
 */
1349
static int
1350
is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
1351
0
{
1352
0
  const wchar_t *p = start;
1353
1354
0
  while (p < end) {
1355
0
    switch (*p++) {
1356
0
    case L'r':
1357
0
      *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1358
0
      break;
1359
0
    case L'w':
1360
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1361
0
      break;
1362
0
    case L'x':
1363
0
      *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1364
0
      break;
1365
0
    case L'p':
1366
0
      *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1367
0
      break;
1368
0
    case L'D':
1369
0
      *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1370
0
      break;
1371
0
    case L'd':
1372
0
      *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1373
0
      break;
1374
0
    case L'a':
1375
0
      *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1376
0
      break;
1377
0
    case L'A':
1378
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1379
0
      break;
1380
0
    case L'R':
1381
0
      *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1382
0
      break;
1383
0
    case L'W':
1384
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1385
0
      break;
1386
0
    case L'c':
1387
0
      *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1388
0
      break;
1389
0
    case L'C':
1390
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1391
0
      break;
1392
0
    case L'o':
1393
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1394
0
      break;
1395
0
    case L's':
1396
0
      *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1397
0
      break;
1398
0
    case L'-':
1399
0
      break;
1400
0
    default:
1401
0
      return(0);
1402
0
    }
1403
0
  }
1404
0
  return (1);
1405
0
}
1406
1407
/*
1408
 * Parse a string as a NFS4 ACL flags field.
1409
 * Returns true if the string is non-empty and consists only of NFS4 ACL
1410
 * flag characters, false otherwise
1411
 */
1412
static int
1413
is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
1414
0
{
1415
0
  const wchar_t *p = start;
1416
1417
0
  while (p < end) {
1418
0
    switch(*p++) {
1419
0
    case L'f':
1420
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1421
0
      break;
1422
0
    case L'd':
1423
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1424
0
      break;
1425
0
    case L'i':
1426
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1427
0
      break;
1428
0
    case L'n':
1429
0
      *permset |=
1430
0
          ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1431
0
      break;
1432
0
    case L'S':
1433
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1434
0
      break;
1435
0
    case L'F':
1436
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1437
0
      break;
1438
0
    case L'I':
1439
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1440
0
      break;
1441
0
    case L'-':
1442
0
      break;
1443
0
    default:
1444
0
      return (0);
1445
0
    }
1446
0
  }
1447
0
  return (1);
1448
0
}
1449
1450
/*
1451
 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
1452
 * to point to just after the separator.  *start points to the first
1453
 * character of the matched text and *end just after the last
1454
 * character of the matched identifier.  In particular *end - *start
1455
 * is the length of the field body, not including leading or trailing
1456
 * whitespace.
1457
 */
1458
static void
1459
next_field_w(const wchar_t **wp, const wchar_t **start,
1460
    const wchar_t **end, wchar_t *sep)
1461
0
{
1462
  /* Skip leading whitespace to find start of field. */
1463
0
  while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
1464
0
    (*wp)++;
1465
0
  }
1466
0
  *start = *wp;
1467
1468
  /* Scan for the separator. */
1469
0
  while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
1470
0
      **wp != L'\n' && **wp != L'#') {
1471
0
    (*wp)++;
1472
0
  }
1473
0
  *sep = **wp;
1474
1475
  /* Locate end of field, trim trailing whitespace if necessary */
1476
0
  if (*wp == *start) {
1477
0
    *end = *wp;
1478
0
  } else {
1479
0
    *end = *wp - 1;
1480
0
    while (**end == L' ' || **end == L'\t' || **end == L'\n') {
1481
0
      (*end)--;
1482
0
    }
1483
0
    (*end)++;
1484
0
  }
1485
1486
  /* Handle in-field comments */
1487
0
  if (*sep == L'#') {
1488
0
    while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
1489
0
      (*wp)++;
1490
0
    }
1491
0
    *sep = **wp;
1492
0
  }
1493
1494
  /* Adjust scanner location. */
1495
0
  if (**wp != L'\0')
1496
0
    (*wp)++;
1497
0
}
1498
1499
/*
1500
 * Parse an ACL text string.
1501
 *
1502
 * The want_type argument may be one of the following:
1503
 * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
1504
 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
1505
 * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
1506
 *
1507
 * POSIX.1e ACL entries prefixed with "default:" are treated as
1508
 * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
1509
 */
1510
int
1511
archive_acl_from_text_l(struct archive_acl *acl, const char *text,
1512
    int want_type, struct archive_string_conv *sc)
1513
0
{
1514
0
  return archive_acl_from_text_nl(acl, text, strlen(text), want_type, sc);
1515
0
}
1516
1517
int
1518
archive_acl_from_text_nl(struct archive_acl *acl, const char *text,
1519
    size_t length, int want_type, struct archive_string_conv *sc)
1520
0
{
1521
0
  struct {
1522
0
    const char *start;
1523
0
    const char *end;
1524
0
  } field[6], name;
1525
1526
0
  const char *s, *st, *text_end;
1527
0
  int numfields, fields, n, r, sol, ret;
1528
0
  int type, types, tag, permset, id;
1529
0
  size_t len;
1530
0
  char sep;
1531
1532
0
  switch (want_type) {
1533
0
  case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
1534
0
    want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
1535
0
    __LA_FALLTHROUGH;
1536
0
  case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
1537
0
  case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
1538
0
    numfields = 5;
1539
0
    break;
1540
0
  case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
1541
0
    numfields = 6;
1542
0
    break;
1543
0
  default:
1544
0
    return (ARCHIVE_FATAL);
1545
0
  }
1546
1547
0
  ret = ARCHIVE_OK;
1548
0
  types = 0;
1549
0
  text_end = (text == NULL) ? text : text + length;
1550
1551
0
  while (text != NULL && length > 0 && *text != '\0') {
1552
    /*
1553
     * Parse the fields out of the next entry,
1554
     * advance 'text' to start of next entry.
1555
     */
1556
0
    fields = 0;
1557
0
    do {
1558
0
      const char *start, *end;
1559
0
      next_field(&text, &length, &start, &end, &sep);
1560
0
      if (fields < numfields) {
1561
0
        field[fields].start = start;
1562
0
        field[fields].end = end;
1563
0
      }
1564
0
      ++fields;
1565
0
    } while (sep == ':');
1566
1567
    /* Set remaining fields to blank. */
1568
0
    for (n = fields; n < numfields; ++n)
1569
0
      field[n].start = field[n].end = NULL;
1570
1571
0
    if (field[0].start == NULL || field[0].end == NULL) {
1572
      /* This should never happen */
1573
0
      return (ARCHIVE_FATAL);
1574
0
    }
1575
1576
0
    if (field[0].start == text_end) {
1577
      /*
1578
       * Empty entry: next_field() consumed the rest of the
1579
       * buffer as separators or whitespace, leaving
1580
       * field[0].start one past the end.  'text' is not
1581
       * guaranteed to be NUL terminated, so the byte after it
1582
       * must not be dereferenced.
1583
       */
1584
0
      ret = ARCHIVE_WARN;
1585
0
      continue;
1586
0
    }
1587
1588
0
    if (*(field[0].start) == '#') {
1589
      /* Comment, skip entry */
1590
0
      continue;
1591
0
    }
1592
1593
0
    n = 0;
1594
0
    sol = 0;
1595
0
    id = -1;
1596
0
    permset = 0;
1597
0
    name.start = name.end = NULL;
1598
1599
0
    if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
1600
      /* POSIX.1e ACLs */
1601
      /*
1602
       * Default keyword "default:user::rwx"
1603
       * if found, we have one more field
1604
       *
1605
       * We also support old Solaris extension:
1606
       * "defaultuser::rwx" is the default ACL corresponding
1607
       * to "user::rwx", etc. valid only for first field
1608
       */
1609
0
      s = field[0].start;
1610
      #ifdef __clang_analyzer__
1611
      assert(s);
1612
      #endif
1613
0
      len = field[0].end - field[0].start;
1614
0
      if (*s == 'd' && (len == 1 || (len >= 7
1615
0
          && memcmp((s + 1), "efault", 6) == 0))) {
1616
0
        type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
1617
0
        if (len > 7)
1618
0
          field[0].start += 7;
1619
0
        else
1620
0
          n = 1;
1621
0
      } else
1622
0
        type = want_type;
1623
1624
      /* Check for a numeric ID in field n+1 or n+3. */
1625
0
      if (isint(field[n + 1].start, field[n + 1].end,
1626
0
          &id) < 0) {
1627
0
        ret = ARCHIVE_WARN;
1628
0
        continue;
1629
0
      }
1630
      /* Field n+3 is optional. */
1631
0
      if (id == -1 && fields > (n + 3) &&
1632
0
          isint(field[n + 3].start, field[n + 3].end,
1633
0
          &id) < 0) {
1634
0
        ret = ARCHIVE_WARN;
1635
0
        continue;
1636
0
      }
1637
1638
0
      tag = 0;
1639
0
      s = field[n].start;
1640
0
      len = field[n].end - field[n].start;
1641
1642
0
      if (len == 0) {
1643
0
        ret = ARCHIVE_WARN;
1644
0
        continue;
1645
0
      }
1646
1647
0
      st = s + 1; 
1648
1649
0
      switch (*s) {
1650
0
      case 'u':
1651
0
        if (len == 1 || (len == 4
1652
0
            && memcmp(st, "ser", 3) == 0))
1653
0
          tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1654
0
        break;
1655
0
      case 'g':
1656
0
        if (len == 1 || (len == 5
1657
0
            && memcmp(st, "roup", 4) == 0))
1658
0
          tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1659
0
        break;
1660
0
      case 'o':
1661
0
        if (len == 1 || (len == 5
1662
0
            && memcmp(st, "ther", 4) == 0))
1663
0
          tag = ARCHIVE_ENTRY_ACL_OTHER;
1664
0
        break;
1665
0
      case 'm':
1666
0
        if (len == 1 || (len == 4
1667
0
            && memcmp(st, "ask", 3) == 0))
1668
0
          tag = ARCHIVE_ENTRY_ACL_MASK;
1669
0
        break;
1670
0
      default:
1671
0
          break;
1672
0
      }
1673
1674
0
      switch (tag) {
1675
0
      case ARCHIVE_ENTRY_ACL_OTHER:
1676
0
      case ARCHIVE_ENTRY_ACL_MASK:
1677
0
        if (fields == (n + 2)
1678
0
            && field[n + 1].start < field[n + 1].end
1679
0
            && ismode(field[n + 1].start,
1680
0
            field[n + 1].end, &permset)) {
1681
          /* This is Solaris-style "other:rwx" */
1682
0
          sol = 1;
1683
0
        } else if (fields == (n + 3) &&
1684
0
            field[n + 1].start < field[n + 1].end) {
1685
          /* Invalid mask or other field */
1686
0
          ret = ARCHIVE_WARN;
1687
0
          continue;
1688
0
        }
1689
0
        break;
1690
0
      case ARCHIVE_ENTRY_ACL_USER_OBJ:
1691
0
      case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
1692
0
        if (id != -1 ||
1693
0
            field[n + 1].start < field[n + 1].end) {
1694
0
          name = field[n + 1];
1695
0
          if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
1696
0
            tag = ARCHIVE_ENTRY_ACL_USER;
1697
0
          else
1698
0
            tag = ARCHIVE_ENTRY_ACL_GROUP;
1699
0
        }
1700
0
        break;
1701
0
      default:
1702
        /* Invalid tag, skip entry */
1703
0
        ret = ARCHIVE_WARN;
1704
0
        continue;
1705
0
      }
1706
1707
      /*
1708
       * Without "default:" we expect mode in field 3
1709
       * Exception: Solaris other and mask fields
1710
       */
1711
0
      if (permset == 0 && !ismode(field[n + 2 - sol].start,
1712
0
          field[n + 2 - sol].end, &permset)) {
1713
        /* Invalid mode, skip entry */
1714
0
        ret = ARCHIVE_WARN;
1715
0
        continue;
1716
0
      }
1717
0
    } else {
1718
      /* NFS4 ACLs */
1719
0
      s = field[0].start;
1720
0
      len = field[0].end - field[0].start;
1721
0
      tag = 0;
1722
1723
0
      switch (len) {
1724
0
      case 4:
1725
0
        if (memcmp(s, "user", 4) == 0)
1726
0
          tag = ARCHIVE_ENTRY_ACL_USER;
1727
0
        break;
1728
0
      case 5:
1729
0
        if (memcmp(s, "group", 5) == 0)
1730
0
          tag = ARCHIVE_ENTRY_ACL_GROUP;
1731
0
        break;
1732
0
      case 6:
1733
0
        if (memcmp(s, "owner@", 6) == 0)
1734
0
          tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
1735
0
        else if (memcmp(s, "group@", 6) == 0)
1736
0
          tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
1737
0
        break;
1738
0
      case 9:
1739
0
        if (memcmp(s, "everyone@", 9) == 0)
1740
0
          tag = ARCHIVE_ENTRY_ACL_EVERYONE;
1741
0
        break;
1742
0
      default:
1743
0
        break;
1744
0
      }
1745
1746
0
      if (tag == 0) {
1747
        /* Invalid tag, skip entry */
1748
0
        ret = ARCHIVE_WARN;
1749
0
        continue;
1750
0
      } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
1751
0
          tag == ARCHIVE_ENTRY_ACL_GROUP) {
1752
0
        n = 1;
1753
0
        name = field[1];
1754
0
        if (isint(name.start, name.end, &id) < 0) {
1755
0
          ret = ARCHIVE_WARN;
1756
0
          continue;
1757
0
        }
1758
0
      } else
1759
0
        n = 0;
1760
1761
0
      if (!is_nfs4_perms(field[1 + n].start,
1762
0
          field[1 + n].end, &permset)) {
1763
        /* Invalid NFSv4 perms, skip entry */
1764
0
        ret = ARCHIVE_WARN;
1765
0
        continue;
1766
0
      }
1767
0
      if (!is_nfs4_flags(field[2 + n].start,
1768
0
          field[2 + n].end, &permset)) {
1769
        /* Invalid NFSv4 flags, skip entry */
1770
0
        ret = ARCHIVE_WARN;
1771
0
        continue;
1772
0
      }
1773
0
      s = field[3 + n].start;
1774
0
      len = field[3 + n].end - field[3 + n].start;
1775
0
      type = 0;
1776
0
      if (len == 4) {
1777
0
        if (memcmp(s, "deny", 4) == 0)
1778
0
          type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
1779
0
      } else if (len == 5) {
1780
0
        if (memcmp(s, "allow", 5) == 0)
1781
0
          type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
1782
0
        else if (memcmp(s, "audit", 5) == 0)
1783
0
          type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
1784
0
        else if (memcmp(s, "alarm", 5) == 0)
1785
0
          type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
1786
0
      }
1787
0
      if (type == 0) {
1788
        /* Invalid entry type, skip entry */
1789
0
        ret = ARCHIVE_WARN;
1790
0
        continue;
1791
0
      }
1792
0
      if (isint(field[4 + n].start, field[4 + n].end,
1793
0
          &id) < 0) {
1794
0
        ret = ARCHIVE_WARN;
1795
0
        continue;
1796
0
      }
1797
0
    }
1798
1799
    /* Add entry to the internal list. */
1800
0
    r = archive_acl_add_entry_len_l(acl, type, permset,
1801
0
        tag, id, name.start, name.end - name.start, sc);
1802
0
    if (r < ARCHIVE_WARN)
1803
0
      return (r);
1804
0
    if (r != ARCHIVE_OK)
1805
0
      ret = ARCHIVE_WARN;
1806
0
    types |= type;
1807
0
  }
1808
1809
  /* Reset ACL */
1810
0
  archive_acl_reset(acl, types);
1811
1812
0
  return (ret);
1813
0
}
1814
1815
/*
1816
 * Parse a string to a positive decimal integer.  Returns true if
1817
 * the string is non-empty and consists only of decimal digits,
1818
 * false otherwise.
1819
 */
1820
static int
1821
isint(const char *start, const char *end, int *result)
1822
0
{
1823
0
  int n = 0;
1824
0
  if (start >= end)
1825
0
    return (0);
1826
0
  while (start < end) {
1827
0
    if (*start < '0' || *start > '9')
1828
0
      return (0);
1829
0
    if (n > (INT_MAX / 10) ||
1830
0
        (n == INT_MAX / 10 && (*start - '0') >= INT_MAX % 10)) {
1831
0
      return (-1);
1832
0
    } else {
1833
0
      n *= 10;
1834
0
      n += *start - '0';
1835
0
    }
1836
0
    start++;
1837
0
  }
1838
0
  *result = n;
1839
0
  return (1);
1840
0
}
1841
1842
/*
1843
 * Parse a string as a mode field.  Returns true if
1844
 * the string is non-empty and consists only of mode characters,
1845
 * false otherwise.
1846
 */
1847
static int
1848
ismode(const char *start, const char *end, int *permset)
1849
0
{
1850
0
  const char *p;
1851
1852
0
  if (start >= end)
1853
0
    return (0);
1854
0
  p = start;
1855
0
  *permset = 0;
1856
0
  while (p < end) {
1857
0
    switch (*p++) {
1858
0
    case 'r': case 'R':
1859
0
      *permset |= ARCHIVE_ENTRY_ACL_READ;
1860
0
      break;
1861
0
    case 'w': case 'W':
1862
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE;
1863
0
      break;
1864
0
    case 'x': case 'X':
1865
0
      *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1866
0
      break;
1867
0
    case '-':
1868
0
      break;
1869
0
    default:
1870
0
      return (0);
1871
0
    }
1872
0
  }
1873
0
  return (1);
1874
0
}
1875
1876
/*
1877
 * Parse a string as a NFS4 ACL permission field.
1878
 * Returns true if the string is non-empty and consists only of NFS4 ACL
1879
 * permission characters, false otherwise
1880
 */
1881
static int
1882
is_nfs4_perms(const char *start, const char *end, int *permset)
1883
0
{
1884
0
  const char *p = start;
1885
1886
0
  while (p < end) {
1887
0
    switch (*p++) {
1888
0
    case 'r':
1889
0
      *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
1890
0
      break;
1891
0
    case 'w':
1892
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
1893
0
      break;
1894
0
    case 'x':
1895
0
      *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
1896
0
      break;
1897
0
    case 'p':
1898
0
      *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
1899
0
      break;
1900
0
    case 'D':
1901
0
      *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
1902
0
      break;
1903
0
    case 'd':
1904
0
      *permset |= ARCHIVE_ENTRY_ACL_DELETE;
1905
0
      break;
1906
0
    case 'a':
1907
0
      *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
1908
0
      break;
1909
0
    case 'A':
1910
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
1911
0
      break;
1912
0
    case 'R':
1913
0
      *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
1914
0
      break;
1915
0
    case 'W':
1916
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
1917
0
      break;
1918
0
    case 'c':
1919
0
      *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
1920
0
      break;
1921
0
    case 'C':
1922
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
1923
0
      break;
1924
0
    case 'o':
1925
0
      *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
1926
0
      break;
1927
0
    case 's':
1928
0
      *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
1929
0
      break;
1930
0
    case '-':
1931
0
      break;
1932
0
    default:
1933
0
      return(0);
1934
0
    }
1935
0
  }
1936
0
  return (1);
1937
0
}
1938
1939
/*
1940
 * Parse a string as a NFS4 ACL flags field.
1941
 * Returns true if the string is non-empty and consists only of NFS4 ACL
1942
 * flag characters, false otherwise
1943
 */
1944
static int
1945
is_nfs4_flags(const char *start, const char *end, int *permset)
1946
0
{
1947
0
  const char *p = start;
1948
1949
0
  while (p < end) {
1950
0
    switch(*p++) {
1951
0
    case 'f':
1952
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
1953
0
      break;
1954
0
    case 'd':
1955
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
1956
0
      break;
1957
0
    case 'i':
1958
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
1959
0
      break;
1960
0
    case 'n':
1961
0
      *permset |=
1962
0
          ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
1963
0
      break;
1964
0
    case 'S':
1965
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
1966
0
      break;
1967
0
    case 'F':
1968
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
1969
0
      break;
1970
0
    case 'I':
1971
0
      *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
1972
0
      break;
1973
0
    case '-':
1974
0
      break;
1975
0
    default:
1976
0
      return (0);
1977
0
    }
1978
0
  }
1979
0
  return (1);
1980
0
}
1981
1982
/*
1983
 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *p is updated
1984
 * to point to just after the separator.  *start points to the first
1985
 * character of the matched text and *end just after the last
1986
 * character of the matched identifier.  In particular *end - *start
1987
 * is the length of the field body, not including leading or trailing
1988
 * whitespace.
1989
 */
1990
static void
1991
next_field(const char **p, size_t *l, const char **start,
1992
    const char **end, char *sep)
1993
0
{
1994
  /* Skip leading whitespace to find start of field. */
1995
0
  while (*l > 0 && (**p == ' ' || **p == '\t' || **p == '\n')) {
1996
0
    (*p)++;
1997
0
    (*l)--;
1998
0
  }
1999
0
  *start = *p;
2000
2001
  /* Locate end of field, trim trailing whitespace if necessary */
2002
0
  while (*l > 0 && **p != ' ' && **p != '\t' && **p != '\n' && **p != ',' && **p != ':' && **p != '#') {
2003
0
    (*p)++;
2004
0
    (*l)--;
2005
0
  }
2006
0
  *end = *p;
2007
2008
  /* Scan for the separator. */
2009
0
  while (*l > 0 && **p != ',' && **p != ':' && **p != '\n' && **p != '#') {
2010
0
    (*p)++;
2011
0
    (*l)--;
2012
0
  }
2013
0
  if (*l > 0)
2014
0
    *sep = **p;
2015
0
  else
2016
0
    *sep = '\0';
2017
2018
  /* Handle in-field comments */
2019
0
  if (*sep == '#') {
2020
0
    while (*l > 0 && **p != ',' && **p != '\n') {
2021
0
      (*p)++;
2022
0
      (*l)--;
2023
0
    }
2024
0
    if (*l > 0)
2025
0
      *sep = **p;
2026
0
    else
2027
0
      *sep = '\0';
2028
0
  }
2029
2030
  /* Skip separator. */
2031
0
  if (*l > 0) {
2032
0
    (*p)++;
2033
0
    (*l)--;
2034
0
  }
2035
0
}