Coverage Report

Created: 2025-06-20 06:31

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