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