Coverage Report

Created: 2026-09-01 06:28

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