~ubuntu-branches/ubuntu/quantal/libarchive/quantal

« back to all changes in this revision

Viewing changes to libarchive/archive_acl.c

  • Committer: Package Import Robot
  • Author(s): Andres Mejia
  • Date: 2012-02-23 19:29:24 UTC
  • mfrom: (8.1.10 sid)
  • Revision ID: package-import@ubuntu.com-20120223192924-73n4iedok5fwgsyr
Tags: 3.0.3-5
* Detect if locales or locales-all is installed for use with test suite.
* Bump Standards-Version to 3.9.3.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-
 
2
 * Copyright (c) 2003-2010 Tim Kientzle
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 * 1. Redistributions of source code must retain the above copyright
 
9
 *    notice, this list of conditions and the following disclaimer.
 
10
 * 2. Redistributions in binary form must reproduce the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer in the
 
12
 *    documentation and/or other materials provided with the distribution.
 
13
 *
 
14
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 
15
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
16
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
17
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 
18
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
19
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
20
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
21
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
22
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
23
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
24
 */
 
25
 
 
26
#include "archive_platform.h"
 
27
__FBSDID("$FreeBSD$");
 
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      isint_w(const wchar_t *start, const wchar_t *end, int *result);
 
56
static int      ismode_w(const wchar_t *start, const wchar_t *end, int *result);
 
57
static void     next_field_w(const wchar_t **wp, const wchar_t **start,
 
58
                    const wchar_t **end, wchar_t *sep);
 
59
static int      prefix_w(const wchar_t *start, const wchar_t *end,
 
60
                    const wchar_t *test);
 
61
static void     append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
 
62
                    const wchar_t *wname, int perm, int id);
 
63
static void     append_id_w(wchar_t **wp, int id);
 
64
static int      isint(const char *start, const char *end, int *result);
 
65
static int      ismode(const char *start, const char *end, int *result);
 
66
static void     next_field(const char **p, const char **start,
 
67
                    const char **end, char *sep);
 
68
static int      prefix(const char *start, const char *end,
 
69
                    const char *test);
 
70
static void     append_entry(char **p, const char *prefix, int tag,
 
71
                    const char *name, int perm, int id);
 
72
static void     append_id(char **p, int id);
 
73
 
 
74
void
 
75
archive_acl_clear(struct archive_acl *acl)
 
76
{
 
77
        struct archive_acl_entry *ap;
 
78
 
 
79
        while (acl->acl_head != NULL) {
 
80
                ap = acl->acl_head->next;
 
81
                archive_mstring_clean(&acl->acl_head->name);
 
82
                free(acl->acl_head);
 
83
                acl->acl_head = ap;
 
84
        }
 
85
        if (acl->acl_text_w != NULL) {
 
86
                free(acl->acl_text_w);
 
87
                acl->acl_text_w = NULL;
 
88
        }
 
89
        if (acl->acl_text != NULL) {
 
90
                free(acl->acl_text);
 
91
                acl->acl_text = NULL;
 
92
        }
 
93
        acl->acl_p = NULL;
 
94
        acl->acl_state = 0; /* Not counting. */
 
95
}
 
96
 
 
97
void
 
98
archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
 
99
{
 
100
        struct archive_acl_entry *ap, *ap2;
 
101
 
 
102
        archive_acl_clear(dest);
 
103
 
 
104
        dest->mode = src->mode;
 
105
        ap = src->acl_head;
 
106
        while (ap != NULL) {
 
107
                ap2 = acl_new_entry(dest,
 
108
                    ap->type, ap->permset, ap->tag, ap->id);
 
109
                if (ap2 != NULL)
 
110
                        archive_mstring_copy(&ap2->name, &ap->name);
 
111
                ap = ap->next;
 
112
        }
 
113
}
 
114
 
 
115
int
 
116
archive_acl_add_entry(struct archive_acl *acl,
 
117
    int type, int permset, int tag, int id, const char *name)
 
118
{
 
119
        struct archive_acl_entry *ap;
 
120
 
 
121
        if (acl_special(acl, type, permset, tag) == 0)
 
122
                return ARCHIVE_OK;
 
123
        ap = acl_new_entry(acl, type, permset, tag, id);
 
124
        if (ap == NULL) {
 
125
                /* XXX Error XXX */
 
126
                return ARCHIVE_FAILED;
 
127
        }
 
128
        if (name != NULL  &&  *name != '\0')
 
129
                archive_mstring_copy_mbs(&ap->name, name);
 
130
        else
 
131
                archive_mstring_clean(&ap->name);
 
132
        return ARCHIVE_OK;
 
133
}
 
134
 
 
135
int
 
136
archive_acl_add_entry_w_len(struct archive_acl *acl,
 
137
    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
 
138
{
 
139
        struct archive_acl_entry *ap;
 
140
 
 
141
        if (acl_special(acl, type, permset, tag) == 0)
 
142
                return ARCHIVE_OK;
 
143
        ap = acl_new_entry(acl, type, permset, tag, id);
 
144
        if (ap == NULL) {
 
145
                /* XXX Error XXX */
 
146
                return ARCHIVE_FAILED;
 
147
        }
 
148
        if (name != NULL  &&  *name != L'\0' && len > 0)
 
149
                archive_mstring_copy_wcs_len(&ap->name, name, len);
 
150
        else
 
151
                archive_mstring_clean(&ap->name);
 
152
        return ARCHIVE_OK;
 
153
}
 
154
 
 
155
int
 
156
archive_acl_add_entry_len_l(struct archive_acl *acl,
 
157
    int type, int permset, int tag, int id, const char *name, size_t len,
 
158
    struct archive_string_conv *sc)
 
159
{
 
160
        struct archive_acl_entry *ap;
 
161
        int r;
 
162
 
 
163
        if (acl_special(acl, type, permset, tag) == 0)
 
164
                return ARCHIVE_OK;
 
165
        ap = acl_new_entry(acl, type, permset, tag, id);
 
166
        if (ap == NULL) {
 
167
                /* XXX Error XXX */
 
168
                return ARCHIVE_FAILED;
 
169
        }
 
170
        if (name != NULL  &&  *name != '\0' && len > 0) {
 
171
                r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
 
172
        } else {
 
173
                r = 0;
 
174
                archive_mstring_clean(&ap->name);
 
175
        }
 
176
        if (r == 0)
 
177
                return (ARCHIVE_OK);
 
178
        else if (errno == ENOMEM)
 
179
                return (ARCHIVE_FATAL);
 
180
        else
 
181
                return (ARCHIVE_WARN);
 
182
}
 
183
 
 
184
/*
 
185
 * If this ACL entry is part of the standard POSIX permissions set,
 
186
 * store the permissions in the stat structure and return zero.
 
187
 */
 
188
static int
 
189
acl_special(struct archive_acl *acl, int type, int permset, int tag)
 
190
{
 
191
        if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
 
192
            && ((permset & ~007) == 0)) {
 
193
                switch (tag) {
 
194
                case ARCHIVE_ENTRY_ACL_USER_OBJ:
 
195
                        acl->mode &= ~0700;
 
196
                        acl->mode |= (permset & 7) << 6;
 
197
                        return (0);
 
198
                case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 
199
                        acl->mode &= ~0070;
 
200
                        acl->mode |= (permset & 7) << 3;
 
201
                        return (0);
 
202
                case ARCHIVE_ENTRY_ACL_OTHER:
 
203
                        acl->mode &= ~0007;
 
204
                        acl->mode |= permset & 7;
 
205
                        return (0);
 
206
                }
 
207
        }
 
208
        return (1);
 
209
}
 
210
 
 
211
/*
 
212
 * Allocate and populate a new ACL entry with everything but the
 
213
 * name.
 
214
 */
 
215
static struct archive_acl_entry *
 
216
acl_new_entry(struct archive_acl *acl,
 
217
    int type, int permset, int tag, int id)
 
218
{
 
219
        struct archive_acl_entry *ap, *aq;
 
220
 
 
221
        /* Type argument must be a valid NFS4 or POSIX.1e type.
 
222
         * The type must agree with anything already set and
 
223
         * the permset must be compatible. */
 
224
        if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
 
225
                if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
 
226
                        return (NULL);
 
227
                }
 
228
                if (permset &
 
229
                    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
 
230
                        | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
 
231
                        return (NULL);
 
232
                }
 
233
        } else  if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
 
234
                if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
 
235
                        return (NULL);
 
236
                }
 
237
                if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
 
238
                        return (NULL);
 
239
                }
 
240
        } else {
 
241
                return (NULL);
 
242
        }
 
243
 
 
244
        /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
 
245
        switch (tag) {
 
246
        case ARCHIVE_ENTRY_ACL_USER:
 
247
        case ARCHIVE_ENTRY_ACL_USER_OBJ:
 
248
        case ARCHIVE_ENTRY_ACL_GROUP:
 
249
        case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 
250
                /* Tags valid in both NFS4 and POSIX.1e */
 
251
                break;
 
252
        case ARCHIVE_ENTRY_ACL_MASK:
 
253
        case ARCHIVE_ENTRY_ACL_OTHER:
 
254
                /* Tags valid only in POSIX.1e. */
 
255
                if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
 
256
                        return (NULL);
 
257
                }
 
258
                break;
 
259
        case ARCHIVE_ENTRY_ACL_EVERYONE:
 
260
                /* Tags valid only in NFS4. */
 
261
                if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
 
262
                        return (NULL);
 
263
                }
 
264
                break;
 
265
        default:
 
266
                /* No other values are valid. */
 
267
                return (NULL);
 
268
        }
 
269
 
 
270
        if (acl->acl_text_w != NULL) {
 
271
                free(acl->acl_text_w);
 
272
                acl->acl_text_w = NULL;
 
273
        }
 
274
        if (acl->acl_text != NULL) {
 
275
                free(acl->acl_text);
 
276
                acl->acl_text = NULL;
 
277
        }
 
278
 
 
279
        /* If there's a matching entry already in the list, overwrite it. */
 
280
        ap = acl->acl_head;
 
281
        aq = NULL;
 
282
        while (ap != NULL) {
 
283
                if (ap->type == type && ap->tag == tag && ap->id == id) {
 
284
                        ap->permset = permset;
 
285
                        return (ap);
 
286
                }
 
287
                aq = ap;
 
288
                ap = ap->next;
 
289
        }
 
290
 
 
291
        /* Add a new entry to the end of the list. */
 
292
        ap = (struct archive_acl_entry *)malloc(sizeof(*ap));
 
293
        if (ap == NULL)
 
294
                return (NULL);
 
295
        memset(ap, 0, sizeof(*ap));
 
296
        if (aq == NULL)
 
297
                acl->acl_head = ap;
 
298
        else
 
299
                aq->next = ap;
 
300
        ap->type = type;
 
301
        ap->tag = tag;
 
302
        ap->id = id;
 
303
        ap->permset = permset;
 
304
        acl->acl_types |= type;
 
305
        return (ap);
 
306
}
 
307
 
 
308
/*
 
309
 * Return a count of entries matching "want_type".
 
310
 */
 
311
int
 
312
archive_acl_count(struct archive_acl *acl, int want_type)
 
313
{
 
314
        int count;
 
315
        struct archive_acl_entry *ap;
 
316
 
 
317
        count = 0;
 
318
        ap = acl->acl_head;
 
319
        while (ap != NULL) {
 
320
                if ((ap->type & want_type) != 0)
 
321
                        count++;
 
322
                ap = ap->next;
 
323
        }
 
324
 
 
325
        if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
 
326
                count += 3;
 
327
        return (count);
 
328
}
 
329
 
 
330
/*
 
331
 * Prepare for reading entries from the ACL data.  Returns a count
 
332
 * of entries matching "want_type", or zero if there are no
 
333
 * non-extended ACL entries of that type.
 
334
 */
 
335
int
 
336
archive_acl_reset(struct archive_acl *acl, int want_type)
 
337
{
 
338
        int count, cutoff;
 
339
 
 
340
        count = archive_acl_count(acl, want_type);
 
341
 
 
342
        /*
 
343
         * If the only entries are the three standard ones,
 
344
         * then don't return any ACL data.  (In this case,
 
345
         * client can just use chmod(2) to set permissions.)
 
346
         */
 
347
        if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
 
348
                cutoff = 3;
 
349
        else
 
350
                cutoff = 0;
 
351
 
 
352
        if (count > cutoff)
 
353
                acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
 
354
        else
 
355
                acl->acl_state = 0;
 
356
        acl->acl_p = acl->acl_head;
 
357
        return (count);
 
358
}
 
359
 
 
360
 
 
361
/*
 
362
 * Return the next ACL entry in the list.  Fake entries for the
 
363
 * standard permissions and include them in the returned list.
 
364
 */
 
365
int
 
366
archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
 
367
    int *permset, int *tag, int *id, const char **name)
 
368
{
 
369
        *name = NULL;
 
370
        *id = -1;
 
371
 
 
372
        /*
 
373
         * The acl_state is either zero (no entries available), -1
 
374
         * (reading from list), or an entry type (retrieve that type
 
375
         * from ae_stat.aest_mode).
 
376
         */
 
377
        if (acl->acl_state == 0)
 
378
                return (ARCHIVE_WARN);
 
379
 
 
380
        /* The first three access entries are special. */
 
381
        if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
 
382
                switch (acl->acl_state) {
 
383
                case ARCHIVE_ENTRY_ACL_USER_OBJ:
 
384
                        *permset = (acl->mode >> 6) & 7;
 
385
                        *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 
386
                        *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
 
387
                        acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
 
388
                        return (ARCHIVE_OK);
 
389
                case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 
390
                        *permset = (acl->mode >> 3) & 7;
 
391
                        *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 
392
                        *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
 
393
                        acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
 
394
                        return (ARCHIVE_OK);
 
395
                case ARCHIVE_ENTRY_ACL_OTHER:
 
396
                        *permset = acl->mode & 7;
 
397
                        *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 
398
                        *tag = ARCHIVE_ENTRY_ACL_OTHER;
 
399
                        acl->acl_state = -1;
 
400
                        acl->acl_p = acl->acl_head;
 
401
                        return (ARCHIVE_OK);
 
402
                default:
 
403
                        break;
 
404
                }
 
405
        }
 
406
 
 
407
        while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
 
408
                acl->acl_p = acl->acl_p->next;
 
409
        if (acl->acl_p == NULL) {
 
410
                acl->acl_state = 0;
 
411
                *type = 0;
 
412
                *permset = 0;
 
413
                *tag = 0;
 
414
                *id = -1;
 
415
                *name = NULL;
 
416
                return (ARCHIVE_EOF); /* End of ACL entries. */
 
417
        }
 
418
        *type = acl->acl_p->type;
 
419
        *permset = acl->acl_p->permset;
 
420
        *tag = acl->acl_p->tag;
 
421
        *id = acl->acl_p->id;
 
422
        if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0)
 
423
                *name = NULL;
 
424
        acl->acl_p = acl->acl_p->next;
 
425
        return (ARCHIVE_OK);
 
426
}
 
427
 
 
428
/*
 
429
 * Generate a text version of the ACL.  The flags parameter controls
 
430
 * the style of the generated ACL.
 
431
 */
 
432
const wchar_t *
 
433
archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
 
434
{
 
435
        int count;
 
436
        size_t length;
 
437
        const wchar_t *wname;
 
438
        const wchar_t *prefix;
 
439
        wchar_t separator;
 
440
        struct archive_acl_entry *ap;
 
441
        int id;
 
442
        wchar_t *wp;
 
443
 
 
444
        if (acl->acl_text_w != NULL) {
 
445
                free (acl->acl_text_w);
 
446
                acl->acl_text_w = NULL;
 
447
        }
 
448
 
 
449
        separator = L',';
 
450
        count = 0;
 
451
        length = 0;
 
452
        ap = acl->acl_head;
 
453
        while (ap != NULL) {
 
454
                if ((ap->type & flags) != 0) {
 
455
                        count++;
 
456
                        if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
 
457
                            (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
 
458
                                length += 8; /* "default:" */
 
459
                        length += 5; /* tag name */
 
460
                        length += 1; /* colon */
 
461
                        if (archive_mstring_get_wcs(a, &ap->name, &wname) == 0 &&
 
462
                            wname != NULL)
 
463
                                length += wcslen(wname);
 
464
                        else
 
465
                                length += sizeof(uid_t) * 3 + 1;
 
466
                        length ++; /* colon */
 
467
                        length += 3; /* rwx */
 
468
                        length += 1; /* colon */
 
469
                        length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
 
470
                        length ++; /* newline */
 
471
                }
 
472
                ap = ap->next;
 
473
        }
 
474
 
 
475
        if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
 
476
                length += 10; /* "user::rwx\n" */
 
477
                length += 11; /* "group::rwx\n" */
 
478
                length += 11; /* "other::rwx\n" */
 
479
        }
 
480
 
 
481
        if (count == 0)
 
482
                return (NULL);
 
483
 
 
484
        /* Now, allocate the string and actually populate it. */
 
485
        wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
 
486
        if (wp == NULL)
 
487
                __archive_errx(1, "No memory to generate the text version of the ACL");
 
488
        count = 0;
 
489
        if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
 
490
                append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
 
491
                    acl->mode & 0700, -1);
 
492
                *wp++ = ',';
 
493
                append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
 
494
                    acl->mode & 0070, -1);
 
495
                *wp++ = ',';
 
496
                append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
 
497
                    acl->mode & 0007, -1);
 
498
                count += 3;
 
499
 
 
500
                ap = acl->acl_head;
 
501
                while (ap != NULL) {
 
502
                        if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 &&
 
503
                                archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
 
504
                                *wp++ = separator;
 
505
                                if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
 
506
                                        id = ap->id;
 
507
                                else
 
508
                                        id = -1;
 
509
                                append_entry_w(&wp, NULL, ap->tag, wname,
 
510
                                    ap->permset, id);
 
511
                                count++;
 
512
                        }
 
513
                        ap = ap->next;
 
514
                }
 
515
        }
 
516
 
 
517
 
 
518
        if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
 
519
                if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
 
520
                        prefix = L"default:";
 
521
                else
 
522
                        prefix = NULL;
 
523
                ap = acl->acl_head;
 
524
                count = 0;
 
525
                while (ap != NULL) {
 
526
                        if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 &&
 
527
                                archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
 
528
                                if (count > 0)
 
529
                                        *wp++ = separator;
 
530
                                if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
 
531
                                        id = ap->id;
 
532
                                else
 
533
                                        id = -1;
 
534
                                append_entry_w(&wp, prefix, ap->tag,
 
535
                                    wname, ap->permset, id);
 
536
                                count ++;
 
537
                        }
 
538
                        ap = ap->next;
 
539
                }
 
540
        }
 
541
 
 
542
        return (acl->acl_text_w);
 
543
}
 
544
 
 
545
 
 
546
static void
 
547
append_id_w(wchar_t **wp, int id)
 
548
{
 
549
        if (id < 0)
 
550
                id = 0;
 
551
        if (id > 9)
 
552
                append_id_w(wp, id / 10);
 
553
        *(*wp)++ = L"0123456789"[id % 10];
 
554
}
 
555
 
 
556
static void
 
557
append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
 
558
    const wchar_t *wname, int perm, int id)
 
559
{
 
560
        if (prefix != NULL) {
 
561
                wcscpy(*wp, prefix);
 
562
                *wp += wcslen(*wp);
 
563
        }
 
564
        switch (tag) {
 
565
        case ARCHIVE_ENTRY_ACL_USER_OBJ:
 
566
                wname = NULL;
 
567
                id = -1;
 
568
                /* FALLTHROUGH */
 
569
        case ARCHIVE_ENTRY_ACL_USER:
 
570
                wcscpy(*wp, L"user");
 
571
                break;
 
572
        case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 
573
                wname = NULL;
 
574
                id = -1;
 
575
                /* FALLTHROUGH */
 
576
        case ARCHIVE_ENTRY_ACL_GROUP:
 
577
                wcscpy(*wp, L"group");
 
578
                break;
 
579
        case ARCHIVE_ENTRY_ACL_MASK:
 
580
                wcscpy(*wp, L"mask");
 
581
                wname = NULL;
 
582
                id = -1;
 
583
                break;
 
584
        case ARCHIVE_ENTRY_ACL_OTHER:
 
585
                wcscpy(*wp, L"other");
 
586
                wname = NULL;
 
587
                id = -1;
 
588
                break;
 
589
        }
 
590
        *wp += wcslen(*wp);
 
591
        *(*wp)++ = L':';
 
592
        if (wname != NULL) {
 
593
                wcscpy(*wp, wname);
 
594
                *wp += wcslen(*wp);
 
595
        } else if (tag == ARCHIVE_ENTRY_ACL_USER
 
596
            || tag == ARCHIVE_ENTRY_ACL_GROUP) {
 
597
                append_id_w(wp, id);
 
598
                id = -1;
 
599
        }
 
600
        *(*wp)++ = L':';
 
601
        *(*wp)++ = (perm & 0444) ? L'r' : L'-';
 
602
        *(*wp)++ = (perm & 0222) ? L'w' : L'-';
 
603
        *(*wp)++ = (perm & 0111) ? L'x' : L'-';
 
604
        if (id != -1) {
 
605
                *(*wp)++ = L':';
 
606
                append_id_w(wp, id);
 
607
        }
 
608
        **wp = L'\0';
 
609
}
 
610
 
 
611
int
 
612
archive_acl_text_l(struct archive_acl *acl, int flags,
 
613
    const char **acl_text, size_t *acl_text_len,
 
614
    struct archive_string_conv *sc)
 
615
{
 
616
        int count;
 
617
        size_t length;
 
618
        const char *name;
 
619
        const char *prefix;
 
620
        char separator;
 
621
        struct archive_acl_entry *ap;
 
622
        size_t len;
 
623
        int id, r;
 
624
        char *p;
 
625
 
 
626
        if (acl->acl_text != NULL) {
 
627
                free (acl->acl_text);
 
628
                acl->acl_text = NULL;
 
629
        }
 
630
 
 
631
        *acl_text = NULL;
 
632
        if (acl_text_len != NULL)
 
633
                *acl_text_len = 0;
 
634
        separator = ',';
 
635
        count = 0;
 
636
        length = 0;
 
637
        ap = acl->acl_head;
 
638
        while (ap != NULL) {
 
639
                if ((ap->type & flags) != 0) {
 
640
                        count++;
 
641
                        if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
 
642
                            (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
 
643
                                length += 8; /* "default:" */
 
644
                        length += 5; /* tag name */
 
645
                        length += 1; /* colon */
 
646
                        r = archive_mstring_get_mbs_l(
 
647
                            &ap->name, &name, &len, sc);
 
648
                        if (r != 0)
 
649
                                return (-1);
 
650
                        if (len > 0 && name != NULL)
 
651
                                length += len;
 
652
                        else
 
653
                                length += sizeof(uid_t) * 3 + 1;
 
654
                        length ++; /* colon */
 
655
                        length += 3; /* rwx */
 
656
                        length += 1; /* colon */
 
657
                        length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
 
658
                        length ++; /* newline */
 
659
                }
 
660
                ap = ap->next;
 
661
        }
 
662
 
 
663
        if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
 
664
                length += 10; /* "user::rwx\n" */
 
665
                length += 11; /* "group::rwx\n" */
 
666
                length += 11; /* "other::rwx\n" */
 
667
        }
 
668
 
 
669
        if (count == 0)
 
670
                return (0);
 
671
 
 
672
        /* Now, allocate the string and actually populate it. */
 
673
        p = acl->acl_text = (char *)malloc(length);
 
674
        if (p == NULL)
 
675
                __archive_errx(1, "No memory to generate the text version of the ACL");
 
676
        count = 0;
 
677
        if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
 
678
                append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
 
679
                    acl->mode & 0700, -1);
 
680
                *p++ = ',';
 
681
                append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
 
682
                    acl->mode & 0070, -1);
 
683
                *p++ = ',';
 
684
                append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
 
685
                    acl->mode & 0007, -1);
 
686
                count += 3;
 
687
 
 
688
                for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
 
689
                        if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) == 0)
 
690
                                continue;
 
691
                        r = archive_mstring_get_mbs_l(
 
692
                            &ap->name, &name, &len, sc);
 
693
                        if (r != 0)
 
694
                                return (-1);
 
695
                        *p++ = separator;
 
696
                        if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
 
697
                                id = ap->id;
 
698
                        else
 
699
                                id = -1;
 
700
                        append_entry(&p, NULL, ap->tag, name,
 
701
                            ap->permset, id);
 
702
                        count++;
 
703
                }
 
704
        }
 
705
 
 
706
 
 
707
        if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
 
708
                if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
 
709
                        prefix = "default:";
 
710
                else
 
711
                        prefix = NULL;
 
712
                count = 0;
 
713
                for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
 
714
                        if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) == 0)
 
715
                                continue;
 
716
                        r = archive_mstring_get_mbs_l(
 
717
                            &ap->name, &name, &len, sc);
 
718
                        if (r != 0)
 
719
                                return (-1);
 
720
                        if (count > 0)
 
721
                                *p++ = separator;
 
722
                        if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
 
723
                                id = ap->id;
 
724
                        else
 
725
                                id = -1;
 
726
                        append_entry(&p, prefix, ap->tag,
 
727
                            name, ap->permset, id);
 
728
                        count ++;
 
729
                }
 
730
        }
 
731
 
 
732
        *acl_text = acl->acl_text;
 
733
        if (acl_text_len != NULL)
 
734
                *acl_text_len = strlen(acl->acl_text);
 
735
        return (0);
 
736
}
 
737
 
 
738
static void
 
739
append_id(char **p, int id)
 
740
{
 
741
        if (id < 0)
 
742
                id = 0;
 
743
        if (id > 9)
 
744
                append_id(p, id / 10);
 
745
        *(*p)++ = "0123456789"[id % 10];
 
746
}
 
747
 
 
748
static void
 
749
append_entry(char **p, const char *prefix, int tag,
 
750
    const char *name, int perm, int id)
 
751
{
 
752
        if (prefix != NULL) {
 
753
                strcpy(*p, prefix);
 
754
                *p += strlen(*p);
 
755
        }
 
756
        switch (tag) {
 
757
        case ARCHIVE_ENTRY_ACL_USER_OBJ:
 
758
                name = NULL;
 
759
                id = -1;
 
760
                /* FALLTHROUGH */
 
761
        case ARCHIVE_ENTRY_ACL_USER:
 
762
                strcpy(*p, "user");
 
763
                break;
 
764
        case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 
765
                name = NULL;
 
766
                id = -1;
 
767
                /* FALLTHROUGH */
 
768
        case ARCHIVE_ENTRY_ACL_GROUP:
 
769
                strcpy(*p, "group");
 
770
                break;
 
771
        case ARCHIVE_ENTRY_ACL_MASK:
 
772
                strcpy(*p, "mask");
 
773
                name = NULL;
 
774
                id = -1;
 
775
                break;
 
776
        case ARCHIVE_ENTRY_ACL_OTHER:
 
777
                strcpy(*p, "other");
 
778
                name = NULL;
 
779
                id = -1;
 
780
                break;
 
781
        }
 
782
        *p += strlen(*p);
 
783
        *(*p)++ = ':';
 
784
        if (name != NULL) {
 
785
                strcpy(*p, name);
 
786
                *p += strlen(*p);
 
787
        } else if (tag == ARCHIVE_ENTRY_ACL_USER
 
788
            || tag == ARCHIVE_ENTRY_ACL_GROUP) {
 
789
                append_id(p, id);
 
790
                id = -1;
 
791
        }
 
792
        *(*p)++ = ':';
 
793
        *(*p)++ = (perm & 0444) ? 'r' : '-';
 
794
        *(*p)++ = (perm & 0222) ? 'w' : '-';
 
795
        *(*p)++ = (perm & 0111) ? 'x' : '-';
 
796
        if (id != -1) {
 
797
                *(*p)++ = ':';
 
798
                append_id(p, id);
 
799
        }
 
800
        **p = '\0';
 
801
}
 
802
 
 
803
/*
 
804
 * Parse a textual ACL.  This automatically recognizes and supports
 
805
 * extensions described above.  The 'type' argument is used to
 
806
 * indicate the type that should be used for any entries not
 
807
 * explicitly marked as "default:".
 
808
 */
 
809
int
 
810
archive_acl_parse_w(struct archive_acl *acl,
 
811
    const wchar_t *text, int default_type)
 
812
{
 
813
        struct {
 
814
                const wchar_t *start;
 
815
                const wchar_t *end;
 
816
        } field[4], name;
 
817
 
 
818
        int fields, n;
 
819
        int type, tag, permset, id;
 
820
        wchar_t sep;
 
821
 
 
822
        while (text != NULL  &&  *text != L'\0') {
 
823
                /*
 
824
                 * Parse the fields out of the next entry,
 
825
                 * advance 'text' to start of next entry.
 
826
                 */
 
827
                fields = 0;
 
828
                do {
 
829
                        const wchar_t *start, *end;
 
830
                        next_field_w(&text, &start, &end, &sep);
 
831
                        if (fields < 4) {
 
832
                                field[fields].start = start;
 
833
                                field[fields].end = end;
 
834
                        }
 
835
                        ++fields;
 
836
                } while (sep == L':');
 
837
 
 
838
                /* Set remaining fields to blank. */
 
839
                for (n = fields; n < 4; ++n)
 
840
                        field[n].start = field[n].end = NULL;
 
841
 
 
842
                /* Check for a numeric ID in field 1 or 3. */
 
843
                id = -1;
 
844
                isint_w(field[1].start, field[1].end, &id);
 
845
                /* Field 3 is optional. */
 
846
                if (id == -1 && fields > 3)
 
847
                        isint_w(field[3].start, field[3].end, &id);
 
848
 
 
849
                /*
 
850
                 * Solaris extension:  "defaultuser::rwx" is the
 
851
                 * default ACL corresponding to "user::rwx", etc.
 
852
                 */
 
853
                if (field[0].end - field[0].start > 7
 
854
                    && wmemcmp(field[0].start, L"default", 7) == 0) {
 
855
                        type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
 
856
                        field[0].start += 7;
 
857
                } else
 
858
                        type = default_type;
 
859
 
 
860
                name.start = name.end = NULL;
 
861
                if (prefix_w(field[0].start, field[0].end, L"user")) {
 
862
                        if (!ismode_w(field[2].start, field[2].end, &permset))
 
863
                                return (ARCHIVE_WARN);
 
864
                        if (id != -1 || field[1].start < field[1].end) {
 
865
                                tag = ARCHIVE_ENTRY_ACL_USER;
 
866
                                name = field[1];
 
867
                        } else
 
868
                                tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
 
869
                } else if (prefix_w(field[0].start, field[0].end, L"group")) {
 
870
                        if (!ismode_w(field[2].start, field[2].end, &permset))
 
871
                                return (ARCHIVE_WARN);
 
872
                        if (id != -1 || field[1].start < field[1].end) {
 
873
                                tag = ARCHIVE_ENTRY_ACL_GROUP;
 
874
                                name = field[1];
 
875
                        } else
 
876
                                tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
 
877
                } else if (prefix_w(field[0].start, field[0].end, L"other")) {
 
878
                        if (fields == 2
 
879
                            && field[1].start < field[1].end
 
880
                            && ismode_w(field[1].start, field[1].end, &permset)) {
 
881
                                /* This is Solaris-style "other:rwx" */
 
882
                        } else if (fields == 3
 
883
                            && field[1].start == field[1].end
 
884
                            && field[2].start < field[2].end
 
885
                            && ismode_w(field[2].start, field[2].end, &permset)) {
 
886
                                /* This is FreeBSD-style "other::rwx" */
 
887
                        } else
 
888
                                return (ARCHIVE_WARN);
 
889
                        tag = ARCHIVE_ENTRY_ACL_OTHER;
 
890
                } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
 
891
                        if (fields == 2
 
892
                            && field[1].start < field[1].end
 
893
                            && ismode_w(field[1].start, field[1].end, &permset)) {
 
894
                                /* This is Solaris-style "mask:rwx" */
 
895
                        } else if (fields == 3
 
896
                            && field[1].start == field[1].end
 
897
                            && field[2].start < field[2].end
 
898
                            && ismode_w(field[2].start, field[2].end, &permset)) {
 
899
                                /* This is FreeBSD-style "mask::rwx" */
 
900
                        } else
 
901
                                return (ARCHIVE_WARN);
 
902
                        tag = ARCHIVE_ENTRY_ACL_MASK;
 
903
                } else
 
904
                        return (ARCHIVE_WARN);
 
905
 
 
906
                /* Add entry to the internal list. */
 
907
                archive_acl_add_entry_w_len(acl, type, permset,
 
908
                    tag, id, name.start, name.end - name.start);
 
909
        }
 
910
        return (ARCHIVE_OK);
 
911
}
 
912
 
 
913
/*
 
914
 * Parse a string to a positive decimal integer.  Returns true if
 
915
 * the string is non-empty and consists only of decimal digits,
 
916
 * false otherwise.
 
917
 */
 
918
static int
 
919
isint_w(const wchar_t *start, const wchar_t *end, int *result)
 
920
{
 
921
        int n = 0;
 
922
        if (start >= end)
 
923
                return (0);
 
924
        while (start < end) {
 
925
                if (*start < '0' || *start > '9')
 
926
                        return (0);
 
927
                if (n > (INT_MAX / 10) ||
 
928
                    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
 
929
                        n = INT_MAX;
 
930
                } else {
 
931
                        n *= 10;
 
932
                        n += *start - '0';
 
933
                }
 
934
                start++;
 
935
        }
 
936
        *result = n;
 
937
        return (1);
 
938
}
 
939
 
 
940
/*
 
941
 * Parse a string as a mode field.  Returns true if
 
942
 * the string is non-empty and consists only of mode characters,
 
943
 * false otherwise.
 
944
 */
 
945
static int
 
946
ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
 
947
{
 
948
        const wchar_t *p;
 
949
 
 
950
        if (start >= end)
 
951
                return (0);
 
952
        p = start;
 
953
        *permset = 0;
 
954
        while (p < end) {
 
955
                switch (*p++) {
 
956
                case 'r': case 'R':
 
957
                        *permset |= ARCHIVE_ENTRY_ACL_READ;
 
958
                        break;
 
959
                case 'w': case 'W':
 
960
                        *permset |= ARCHIVE_ENTRY_ACL_WRITE;
 
961
                        break;
 
962
                case 'x': case 'X':
 
963
                        *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
 
964
                        break;
 
965
                case '-':
 
966
                        break;
 
967
                default:
 
968
                        return (0);
 
969
                }
 
970
        }
 
971
        return (1);
 
972
}
 
973
 
 
974
/*
 
975
 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
 
976
 * to point to just after the separator.  *start points to the first
 
977
 * character of the matched text and *end just after the last
 
978
 * character of the matched identifier.  In particular *end - *start
 
979
 * is the length of the field body, not including leading or trailing
 
980
 * whitespace.
 
981
 */
 
982
static void
 
983
next_field_w(const wchar_t **wp, const wchar_t **start,
 
984
    const wchar_t **end, wchar_t *sep)
 
985
{
 
986
        /* Skip leading whitespace to find start of field. */
 
987
        while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
 
988
                (*wp)++;
 
989
        }
 
990
        *start = *wp;
 
991
 
 
992
        /* Scan for the separator. */
 
993
        while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
 
994
            **wp != L'\n') {
 
995
                (*wp)++;
 
996
        }
 
997
        *sep = **wp;
 
998
 
 
999
        /* Trim trailing whitespace to locate end of field. */
 
1000
        *end = *wp - 1;
 
1001
        while (**end == L' ' || **end == L'\t' || **end == L'\n') {
 
1002
                (*end)--;
 
1003
        }
 
1004
        (*end)++;
 
1005
 
 
1006
        /* Adjust scanner location. */
 
1007
        if (**wp != L'\0')
 
1008
                (*wp)++;
 
1009
}
 
1010
 
 
1011
/*
 
1012
 * Return true if the characters [start...end) are a prefix of 'test'.
 
1013
 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
 
1014
 */
 
1015
static int
 
1016
prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
 
1017
{
 
1018
        if (start == end)
 
1019
                return (0);
 
1020
 
 
1021
        if (*start++ != *test++)
 
1022
                return (0);
 
1023
 
 
1024
        while (start < end  &&  *start++ == *test++)
 
1025
                ;
 
1026
 
 
1027
        if (start < end)
 
1028
                return (0);
 
1029
 
 
1030
        return (1);
 
1031
}
 
1032
 
 
1033
/*
 
1034
 * Parse a textual ACL.  This automatically recognizes and supports
 
1035
 * extensions described above.  The 'type' argument is used to
 
1036
 * indicate the type that should be used for any entries not
 
1037
 * explicitly marked as "default:".
 
1038
 */
 
1039
int
 
1040
archive_acl_parse_l(struct archive_acl *acl,
 
1041
    const char *text, int default_type, struct archive_string_conv *sc)
 
1042
{
 
1043
        struct {
 
1044
                const char *start;
 
1045
                const char *end;
 
1046
        } field[4], name;
 
1047
 
 
1048
        int fields, n, r, ret = ARCHIVE_OK;
 
1049
        int type, tag, permset, id;
 
1050
        char sep;
 
1051
 
 
1052
        while (text != NULL  &&  *text != '\0') {
 
1053
                /*
 
1054
                 * Parse the fields out of the next entry,
 
1055
                 * advance 'text' to start of next entry.
 
1056
                 */
 
1057
                fields = 0;
 
1058
                do {
 
1059
                        const char *start, *end;
 
1060
                        next_field(&text, &start, &end, &sep);
 
1061
                        if (fields < 4) {
 
1062
                                field[fields].start = start;
 
1063
                                field[fields].end = end;
 
1064
                        }
 
1065
                        ++fields;
 
1066
                } while (sep == ':');
 
1067
 
 
1068
                /* Set remaining fields to blank. */
 
1069
                for (n = fields; n < 4; ++n)
 
1070
                        field[n].start = field[n].end = NULL;
 
1071
 
 
1072
                /* Check for a numeric ID in field 1 or 3. */
 
1073
                id = -1;
 
1074
                isint(field[1].start, field[1].end, &id);
 
1075
                /* Field 3 is optional. */
 
1076
                if (id == -1 && fields > 3)
 
1077
                        isint(field[3].start, field[3].end, &id);
 
1078
 
 
1079
                /*
 
1080
                 * Solaris extension:  "defaultuser::rwx" is the
 
1081
                 * default ACL corresponding to "user::rwx", etc.
 
1082
                 */
 
1083
                if (field[0].end - field[0].start > 7
 
1084
                    && memcmp(field[0].start, "default", 7) == 0) {
 
1085
                        type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
 
1086
                        field[0].start += 7;
 
1087
                } else
 
1088
                        type = default_type;
 
1089
 
 
1090
                name.start = name.end = NULL;
 
1091
                if (prefix(field[0].start, field[0].end, "user")) {
 
1092
                        if (!ismode(field[2].start, field[2].end, &permset))
 
1093
                                return (ARCHIVE_WARN);
 
1094
                        if (id != -1 || field[1].start < field[1].end) {
 
1095
                                tag = ARCHIVE_ENTRY_ACL_USER;
 
1096
                                name = field[1];
 
1097
                        } else
 
1098
                                tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
 
1099
                } else if (prefix(field[0].start, field[0].end, "group")) {
 
1100
                        if (!ismode(field[2].start, field[2].end, &permset))
 
1101
                                return (ARCHIVE_WARN);
 
1102
                        if (id != -1 || field[1].start < field[1].end) {
 
1103
                                tag = ARCHIVE_ENTRY_ACL_GROUP;
 
1104
                                name = field[1];
 
1105
                        } else
 
1106
                                tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
 
1107
                } else if (prefix(field[0].start, field[0].end, "other")) {
 
1108
                        if (fields == 2
 
1109
                            && field[1].start < field[1].end
 
1110
                            && ismode(field[1].start, field[1].end, &permset)) {
 
1111
                                /* This is Solaris-style "other:rwx" */
 
1112
                        } else if (fields == 3
 
1113
                            && field[1].start == field[1].end
 
1114
                            && field[2].start < field[2].end
 
1115
                            && ismode(field[2].start, field[2].end, &permset)) {
 
1116
                                /* This is FreeBSD-style "other::rwx" */
 
1117
                        } else
 
1118
                                return (ARCHIVE_WARN);
 
1119
                        tag = ARCHIVE_ENTRY_ACL_OTHER;
 
1120
                } else if (prefix(field[0].start, field[0].end, "mask")) {
 
1121
                        if (fields == 2
 
1122
                            && field[1].start < field[1].end
 
1123
                            && ismode(field[1].start, field[1].end, &permset)) {
 
1124
                                /* This is Solaris-style "mask:rwx" */
 
1125
                        } else if (fields == 3
 
1126
                            && field[1].start == field[1].end
 
1127
                            && field[2].start < field[2].end
 
1128
                            && ismode(field[2].start, field[2].end, &permset)) {
 
1129
                                /* This is FreeBSD-style "mask::rwx" */
 
1130
                        } else
 
1131
                                return (ARCHIVE_WARN);
 
1132
                        tag = ARCHIVE_ENTRY_ACL_MASK;
 
1133
                } else
 
1134
                        return (ARCHIVE_WARN);
 
1135
 
 
1136
                /* Add entry to the internal list. */
 
1137
                r = archive_acl_add_entry_len_l(acl, type, permset,
 
1138
                    tag, id, name.start, name.end - name.start, sc);
 
1139
                if (r < ARCHIVE_WARN)
 
1140
                        return (r);
 
1141
                if (r != ARCHIVE_OK)
 
1142
                        ret = ARCHIVE_WARN;
 
1143
        }
 
1144
        return (ret);
 
1145
}
 
1146
 
 
1147
/*
 
1148
 * Parse a string to a positive decimal integer.  Returns true if
 
1149
 * the string is non-empty and consists only of decimal digits,
 
1150
 * false otherwise.
 
1151
 */
 
1152
static int
 
1153
isint(const char *start, const char *end, int *result)
 
1154
{
 
1155
        int n = 0;
 
1156
        if (start >= end)
 
1157
                return (0);
 
1158
        while (start < end) {
 
1159
                if (*start < '0' || *start > '9')
 
1160
                        return (0);
 
1161
                if (n > (INT_MAX / 10) ||
 
1162
                    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
 
1163
                        n = INT_MAX;
 
1164
                } else {
 
1165
                        n *= 10;
 
1166
                        n += *start - '0';
 
1167
                }
 
1168
                start++;
 
1169
        }
 
1170
        *result = n;
 
1171
        return (1);
 
1172
}
 
1173
 
 
1174
/*
 
1175
 * Parse a string as a mode field.  Returns true if
 
1176
 * the string is non-empty and consists only of mode characters,
 
1177
 * false otherwise.
 
1178
 */
 
1179
static int
 
1180
ismode(const char *start, const char *end, int *permset)
 
1181
{
 
1182
        const char *p;
 
1183
 
 
1184
        if (start >= end)
 
1185
                return (0);
 
1186
        p = start;
 
1187
        *permset = 0;
 
1188
        while (p < end) {
 
1189
                switch (*p++) {
 
1190
                case 'r': case 'R':
 
1191
                        *permset |= ARCHIVE_ENTRY_ACL_READ;
 
1192
                        break;
 
1193
                case 'w': case 'W':
 
1194
                        *permset |= ARCHIVE_ENTRY_ACL_WRITE;
 
1195
                        break;
 
1196
                case 'x': case 'X':
 
1197
                        *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
 
1198
                        break;
 
1199
                case '-':
 
1200
                        break;
 
1201
                default:
 
1202
                        return (0);
 
1203
                }
 
1204
        }
 
1205
        return (1);
 
1206
}
 
1207
 
 
1208
/*
 
1209
 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
 
1210
 * to point to just after the separator.  *start points to the first
 
1211
 * character of the matched text and *end just after the last
 
1212
 * character of the matched identifier.  In particular *end - *start
 
1213
 * is the length of the field body, not including leading or trailing
 
1214
 * whitespace.
 
1215
 */
 
1216
static void
 
1217
next_field(const char **p, const char **start,
 
1218
    const char **end, char *sep)
 
1219
{
 
1220
        /* Skip leading whitespace to find start of field. */
 
1221
        while (**p == ' ' || **p == '\t' || **p == '\n') {
 
1222
                (*p)++;
 
1223
        }
 
1224
        *start = *p;
 
1225
 
 
1226
        /* Scan for the separator. */
 
1227
        while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
 
1228
                (*p)++;
 
1229
        }
 
1230
        *sep = **p;
 
1231
 
 
1232
        /* Trim trailing whitespace to locate end of field. */
 
1233
        *end = *p - 1;
 
1234
        while (**end == ' ' || **end == '\t' || **end == '\n') {
 
1235
                (*end)--;
 
1236
        }
 
1237
        (*end)++;
 
1238
 
 
1239
        /* Adjust scanner location. */
 
1240
        if (**p != '\0')
 
1241
                (*p)++;
 
1242
}
 
1243
 
 
1244
/*
 
1245
 * Return true if the characters [start...end) are a prefix of 'test'.
 
1246
 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
 
1247
 */
 
1248
static int
 
1249
prefix(const char *start, const char *end, const char *test)
 
1250
{
 
1251
        if (start == end)
 
1252
                return (0);
 
1253
 
 
1254
        if (*start++ != *test++)
 
1255
                return (0);
 
1256
 
 
1257
        while (start < end  &&  *start++ == *test++)
 
1258
                ;
 
1259
 
 
1260
        if (start < end)
 
1261
                return (0);
 
1262
 
 
1263
        return (1);
 
1264
}