Coverage Report

Created: 2026-03-12 06:35

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