Coverage Report

Created: 2026-03-12 06:55

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