~akopytov/percona-xtrabackup/bug1166888-2.1

« back to all changes in this revision

Viewing changes to src/libarchive/libarchive/archive_entry.c

  • Committer: Alexey Kopytov
  • Date: 2012-02-10 20:05:56 UTC
  • mto: (391.1.5 staging)
  • mto: This revision was merged to the branch mainline in revision 390.
  • Revision ID: akopytov@gmail.com-20120210200556-6kx41z8wwrqfucro
Rebase of the parallel compression patch on new trunk + post-review
fixes.

Implementation of parallel compression and streaming for XtraBackup.

This revision implements the following changes:

* InnoDB files are now streamed by the xtrabackup binary rather than
innobackupex. As a result, integrity is now verified by xtrabackup and
thus tar4ibd is no longer needed, so it was removed.

* xtrabackup binary now accepts the new '--stream' option which has
exactly the same semantics as the '--stream' option in
innobackupex: it tells xtrabackup to stream all files to the standard
output in the specified format rather than storing them locally.

* The xtrabackup binary can now do parallel compression using the
quicklz library. Two new options were added to xtrabackup to support
this feature:

- '--compress' tells xtrabackup to compress all output data, including
the transaction log file and meta data files, using the specified
compression algorithm. The only currently supported algorithm is
'quicklz'. The resulting files have the qpress archive format,
i.e. every *.qp file produced by xtrabackup is essentially a one-file
qpress archive and can be extracted and uncompressed by the qpress
file archiver (http://www.quicklz.com/).

- '--compress-threads' specifies the number of worker threads used by
xtrabackup for parallel data compression. This option defaults to 1.

Parallel compression ('--compress-threads') can be used together with
parallel file copying ('--parallel'). For example, '--parallel=4
--compress --compress-threads=2' will create 4 IO threads that will
read the data and pipe it to 2 compression threads.

* To support simultaneous compression and streaming, a new custom
streaming format called 'xbstream' was introduced to XtraBackup in
addition to the 'tar' format. That was required to overcome some
limitations of traditional archive formats such as 'tar', 'cpio' and
others that do not allow streaming dynamically generated files, for
example dynamically compressed files.  Other advantages of xbstream over
traditional streaming/archive formats include ability to stream multiple
files concurrently (so it is possible to use streaming in the xbstream
format together with the --parallel option) and more compact data
storage.

* To allow streaming and extracting files to/from the xbstream format
produced by xtrabackup, a new utility aptly called 'xbstream' was
added to the XtraBackup distribution. This utility has a tar-like
interface:

- with the '-x' option it extracts files from the stream read from its
standard input to the current directory unless specified otherwise
with the '-C' option.

- with the '-c' option it streams files specified on the command line
to its standard output.

The utility also tries to minimize its impact on the OS page cache by
using the appropriate posix_fadvise() calls when available.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-
 
2
 * Copyright (c) 2003-2007 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: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41:27Z kientzle $");
 
28
 
 
29
#ifdef HAVE_SYS_STAT_H
 
30
#include <sys/stat.h>
 
31
#endif
 
32
#ifdef HAVE_SYS_TYPES_H
 
33
#include <sys/types.h>
 
34
#endif
 
35
#if MAJOR_IN_MKDEV
 
36
#include <sys/mkdev.h>
 
37
#define HAVE_MAJOR
 
38
#elif MAJOR_IN_SYSMACROS
 
39
#include <sys/sysmacros.h>
 
40
#define HAVE_MAJOR
 
41
#endif
 
42
#ifdef HAVE_LIMITS_H
 
43
#include <limits.h>
 
44
#endif
 
45
#ifdef HAVE_LINUX_FS_H
 
46
#include <linux/fs.h>   /* for Linux file flags */
 
47
#endif
 
48
/*
 
49
 * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
 
50
 * As the include guards don't agree, the order of include is important.
 
51
 */
 
52
#ifdef HAVE_LINUX_EXT2_FS_H
 
53
#include <linux/ext2_fs.h>      /* for Linux file flags */
 
54
#endif
 
55
#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
 
56
#include <ext2fs/ext2_fs.h>     /* for Linux file flags */
 
57
#endif
 
58
#include <stddef.h>
 
59
#include <stdio.h>
 
60
#ifdef HAVE_STDLIB_H
 
61
#include <stdlib.h>
 
62
#endif
 
63
#ifdef HAVE_STRING_H
 
64
#include <string.h>
 
65
#endif
 
66
#ifdef HAVE_WCHAR_H
 
67
#include <wchar.h>
 
68
#endif
 
69
 
 
70
#include "archive.h"
 
71
#include "archive_entry.h"
 
72
#include "archive_private.h"
 
73
#include "archive_entry_private.h"
 
74
 
 
75
#undef max
 
76
#define max(a, b)       ((a)>(b)?(a):(b))
 
77
 
 
78
#if !defined(HAVE_MAJOR) && !defined(major)
 
79
/* Replacement for major/minor/makedev. */
 
80
#define major(x) ((int)(0x00ff & ((x) >> 8)))
 
81
#define minor(x) ((int)(0xffff00ff & (x)))
 
82
#define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))
 
83
#endif
 
84
 
 
85
/* Play games to come up with a suitable makedev() definition. */
 
86
#ifdef __QNXNTO__
 
87
/* QNX.  <sigh> */
 
88
#include <sys/netmgr.h>
 
89
#define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
 
90
#elif defined makedev
 
91
/* There's a "makedev" macro. */
 
92
#define ae_makedev(maj, min) makedev((maj), (min))
 
93
#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))
 
94
/* Windows. <sigh> */
 
95
#define ae_makedev(maj, min) mkdev((maj), (min))
 
96
#else
 
97
/* There's a "makedev" function. */
 
98
#define ae_makedev(maj, min) makedev((maj), (min))
 
99
#endif
 
100
 
 
101
static void     aes_clean(struct aes *);
 
102
static void     aes_copy(struct aes *dest, struct aes *src);
 
103
static const char *     aes_get_mbs(struct aes *);
 
104
static const wchar_t *  aes_get_wcs(struct aes *);
 
105
static int      aes_set_mbs(struct aes *, const char *mbs);
 
106
static int      aes_copy_mbs(struct aes *, const char *mbs);
 
107
/* static void  aes_set_wcs(struct aes *, const wchar_t *wcs); */
 
108
static int      aes_copy_wcs(struct aes *, const wchar_t *wcs);
 
109
static int      aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t);
 
110
 
 
111
static char *    ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
 
112
static const wchar_t    *ae_wcstofflags(const wchar_t *stringp,
 
113
                    unsigned long *setp, unsigned long *clrp);
 
114
static const char       *ae_strtofflags(const char *stringp,
 
115
                    unsigned long *setp, unsigned long *clrp);
 
116
static void     append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
 
117
                    const wchar_t *wname, int perm, int id);
 
118
static void     append_id_w(wchar_t **wp, int id);
 
119
 
 
120
static int      acl_special(struct archive_entry *entry,
 
121
                    int type, int permset, int tag);
 
122
static struct ae_acl *acl_new_entry(struct archive_entry *entry,
 
123
                    int type, int permset, int tag, int id);
 
124
static int      isint_w(const wchar_t *start, const wchar_t *end, int *result);
 
125
static int      ismode_w(const wchar_t *start, const wchar_t *end, int *result);
 
126
static void     next_field_w(const wchar_t **wp, const wchar_t **start,
 
127
                    const wchar_t **end, wchar_t *sep);
 
128
static int      prefix_w(const wchar_t *start, const wchar_t *end,
 
129
                    const wchar_t *test);
 
130
static void
 
131
archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type,
 
132
                    int permset, int tag, int id, const wchar_t *name, size_t);
 
133
 
 
134
 
 
135
#ifndef HAVE_WCSCPY
 
136
static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
 
137
{
 
138
        wchar_t *dest = s1;
 
139
        while ((*s1 = *s2) != L'\0')
 
140
                ++s1, ++s2;
 
141
        return dest;
 
142
}
 
143
#endif
 
144
#ifndef HAVE_WCSLEN
 
145
static size_t wcslen(const wchar_t *s)
 
146
{
 
147
        const wchar_t *p = s;
 
148
        while (*p != L'\0')
 
149
                ++p;
 
150
        return p - s;
 
151
}
 
152
#endif
 
153
#ifndef HAVE_WMEMCMP
 
154
/* Good enough for simple equality testing, but not for sorting. */
 
155
#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
 
156
#endif
 
157
#ifndef HAVE_WMEMCPY
 
158
#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
 
159
#endif
 
160
 
 
161
static void
 
162
aes_clean(struct aes *aes)
 
163
{
 
164
        if (aes->aes_wcs) {
 
165
                free((wchar_t *)(uintptr_t)aes->aes_wcs);
 
166
                aes->aes_wcs = NULL;
 
167
        }
 
168
        archive_string_free(&(aes->aes_mbs));
 
169
        archive_string_free(&(aes->aes_utf8));
 
170
        aes->aes_set = 0;
 
171
}
 
172
 
 
173
static void
 
174
aes_copy(struct aes *dest, struct aes *src)
 
175
{
 
176
        wchar_t *wp;
 
177
 
 
178
        dest->aes_set = src->aes_set;
 
179
        archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
 
180
        archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
 
181
 
 
182
        if (src->aes_wcs != NULL) {
 
183
                wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1)
 
184
                    * sizeof(wchar_t));
 
185
                if (wp == NULL)
 
186
                        __archive_errx(1, "No memory for aes_copy()");
 
187
                wcscpy(wp, src->aes_wcs);
 
188
                dest->aes_wcs = wp;
 
189
        }
 
190
}
 
191
 
 
192
static const char *
 
193
aes_get_utf8(struct aes *aes)
 
194
{
 
195
        if (aes->aes_set & AES_SET_UTF8)
 
196
                return (aes->aes_utf8.s);
 
197
        if ((aes->aes_set & AES_SET_WCS)
 
198
            && archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) {
 
199
                aes->aes_set |= AES_SET_UTF8;
 
200
                return (aes->aes_utf8.s);
 
201
        }
 
202
        return (NULL);
 
203
}
 
204
 
 
205
static const char *
 
206
aes_get_mbs(struct aes *aes)
 
207
{
 
208
        /* If we already have an MBS form, return that immediately. */
 
209
        if (aes->aes_set & AES_SET_MBS)
 
210
                return (aes->aes_mbs.s);
 
211
        /* If there's a WCS form, try converting with the native locale. */
 
212
        if ((aes->aes_set & AES_SET_WCS)
 
213
            && archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) {
 
214
                aes->aes_set |= AES_SET_MBS;
 
215
                return (aes->aes_mbs.s);
 
216
        }
 
217
        /* We'll use UTF-8 for MBS if all else fails. */
 
218
        return (aes_get_utf8(aes));
 
219
}
 
220
 
 
221
static const wchar_t *
 
222
aes_get_wcs(struct aes *aes)
 
223
{
 
224
        wchar_t *w;
 
225
        size_t r;
 
226
 
 
227
        /* Return WCS form if we already have it. */
 
228
        if (aes->aes_set & AES_SET_WCS)
 
229
                return (aes->aes_wcs);
 
230
 
 
231
        if (aes->aes_set & AES_SET_MBS) {
 
232
                /* Try converting MBS to WCS using native locale. */
 
233
                /*
 
234
                 * No single byte will be more than one wide character,
 
235
                 * so this length estimate will always be big enough.
 
236
                 */
 
237
                size_t wcs_length = aes->aes_mbs.length;
 
238
 
 
239
                w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
 
240
                if (w == NULL)
 
241
                        __archive_errx(1, "No memory for aes_get_wcs()");
 
242
                r = mbstowcs(w, aes->aes_mbs.s, wcs_length);
 
243
                if (r != (size_t)-1 && r != 0) {
 
244
                        w[r] = 0;
 
245
                        aes->aes_set |= AES_SET_WCS;
 
246
                        return (aes->aes_wcs = w);
 
247
                }
 
248
                free(w);
 
249
        }
 
250
 
 
251
        if (aes->aes_set & AES_SET_UTF8) {
 
252
                /* Try converting UTF8 to WCS. */
 
253
                aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
 
254
                if (aes->aes_wcs != NULL)
 
255
                        aes->aes_set |= AES_SET_WCS;
 
256
                return (aes->aes_wcs);
 
257
        }
 
258
        return (NULL);
 
259
}
 
260
 
 
261
static int
 
262
aes_set_mbs(struct aes *aes, const char *mbs)
 
263
{
 
264
        return (aes_copy_mbs(aes, mbs));
 
265
}
 
266
 
 
267
static int
 
268
aes_copy_mbs(struct aes *aes, const char *mbs)
 
269
{
 
270
        if (mbs == NULL) {
 
271
                aes->aes_set = 0;
 
272
                return (0);
 
273
        }
 
274
        aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
 
275
        archive_strcpy(&(aes->aes_mbs), mbs);
 
276
        archive_string_empty(&(aes->aes_utf8));
 
277
        if (aes->aes_wcs) {
 
278
                free((wchar_t *)(uintptr_t)aes->aes_wcs);
 
279
                aes->aes_wcs = NULL;
 
280
        }
 
281
        return (0);
 
282
}
 
283
 
 
284
/*
 
285
 * The 'update' form tries to proactively update all forms of
 
286
 * this string (WCS and MBS) and returns an error if any of
 
287
 * them fail.  This is used by the 'pax' handler, for instance,
 
288
 * to detect and report character-conversion failures early while
 
289
 * still allowing clients to get potentially useful values from
 
290
 * the more tolerant lazy conversions.  (get_mbs and get_wcs will
 
291
 * strive to give the user something useful, so you can get hopefully
 
292
 * usable values even if some of the character conversions are failing.)
 
293
 */
 
294
static int
 
295
aes_update_utf8(struct aes *aes, const char *utf8)
 
296
{
 
297
        if (utf8 == NULL) {
 
298
                aes->aes_set = 0;
 
299
                return (1); /* Succeeded in clearing everything. */
 
300
        }
 
301
 
 
302
        /* Save the UTF8 string. */
 
303
        archive_strcpy(&(aes->aes_utf8), utf8);
 
304
 
 
305
        /* Empty the mbs and wcs strings. */
 
306
        archive_string_empty(&(aes->aes_mbs));
 
307
        if (aes->aes_wcs) {
 
308
                free((wchar_t *)(uintptr_t)aes->aes_wcs);
 
309
                aes->aes_wcs = NULL;
 
310
        }
 
311
 
 
312
        aes->aes_set = AES_SET_UTF8;    /* Only UTF8 is set now. */
 
313
 
 
314
        /* TODO: We should just do a direct UTF-8 to MBS conversion
 
315
         * here.  That would be faster, use less space, and give the
 
316
         * same information.  (If a UTF-8 to MBS conversion succeeds,
 
317
         * then UTF-8->WCS and Unicode->MBS conversions will both
 
318
         * succeed.) */
 
319
 
 
320
        /* Try converting UTF8 to WCS, return false on failure. */
 
321
        aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
 
322
        if (aes->aes_wcs == NULL)
 
323
                return (0);
 
324
        aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */
 
325
 
 
326
        /* Try converting WCS to MBS, return false on failure. */
 
327
        if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL)
 
328
                return (0);
 
329
        aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
 
330
 
 
331
        /* All conversions succeeded. */
 
332
        return (1);
 
333
}
 
334
 
 
335
static int
 
336
aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
 
337
{
 
338
        return aes_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs));
 
339
}
 
340
 
 
341
static int
 
342
aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len)
 
343
{
 
344
        wchar_t *w;
 
345
 
 
346
        if (wcs == NULL) {
 
347
                aes->aes_set = 0;
 
348
                return (0);
 
349
        }
 
350
        aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
 
351
        archive_string_empty(&(aes->aes_mbs));
 
352
        archive_string_empty(&(aes->aes_utf8));
 
353
        if (aes->aes_wcs) {
 
354
                free((wchar_t *)(uintptr_t)aes->aes_wcs);
 
355
                aes->aes_wcs = NULL;
 
356
        }
 
357
        w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
 
358
        if (w == NULL)
 
359
                __archive_errx(1, "No memory for aes_copy_wcs()");
 
360
        wmemcpy(w, wcs, len);
 
361
        w[len] = L'\0';
 
362
        aes->aes_wcs = w;
 
363
        return (0);
 
364
}
 
365
 
 
366
/****************************************************************************
 
367
 *
 
368
 * Public Interface
 
369
 *
 
370
 ****************************************************************************/
 
371
 
 
372
struct archive_entry *
 
373
archive_entry_clear(struct archive_entry *entry)
 
374
{
 
375
        if (entry == NULL)
 
376
                return (NULL);
 
377
        aes_clean(&entry->ae_fflags_text);
 
378
        aes_clean(&entry->ae_gname);
 
379
        aes_clean(&entry->ae_hardlink);
 
380
        aes_clean(&entry->ae_pathname);
 
381
        aes_clean(&entry->ae_sourcepath);
 
382
        aes_clean(&entry->ae_symlink);
 
383
        aes_clean(&entry->ae_uname);
 
384
        archive_entry_acl_clear(entry);
 
385
        archive_entry_xattr_clear(entry);
 
386
        free(entry->stat);
 
387
        memset(entry, 0, sizeof(*entry));
 
388
        return entry;
 
389
}
 
390
 
 
391
struct archive_entry *
 
392
archive_entry_clone(struct archive_entry *entry)
 
393
{
 
394
        struct archive_entry *entry2;
 
395
        struct ae_acl *ap, *ap2;
 
396
        struct ae_xattr *xp;
 
397
 
 
398
        /* Allocate new structure and copy over all of the fields. */
 
399
        entry2 = (struct archive_entry *)malloc(sizeof(*entry2));
 
400
        if (entry2 == NULL)
 
401
                return (NULL);
 
402
        memset(entry2, 0, sizeof(*entry2));
 
403
        entry2->ae_stat = entry->ae_stat;
 
404
        entry2->ae_fflags_set = entry->ae_fflags_set;
 
405
        entry2->ae_fflags_clear = entry->ae_fflags_clear;
 
406
 
 
407
        aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
 
408
        aes_copy(&entry2->ae_gname, &entry->ae_gname);
 
409
        aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
 
410
        aes_copy(&entry2->ae_pathname, &entry->ae_pathname);
 
411
        aes_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath);
 
412
        aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
 
413
        entry2->ae_set = entry->ae_set;
 
414
        aes_copy(&entry2->ae_uname, &entry->ae_uname);
 
415
 
 
416
        /* Copy ACL data over. */
 
417
        ap = entry->acl_head;
 
418
        while (ap != NULL) {
 
419
                ap2 = acl_new_entry(entry2,
 
420
                    ap->type, ap->permset, ap->tag, ap->id);
 
421
                if (ap2 != NULL)
 
422
                        aes_copy(&ap2->name, &ap->name);
 
423
                ap = ap->next;
 
424
        }
 
425
 
 
426
        /* Copy xattr data over. */
 
427
        xp = entry->xattr_head;
 
428
        while (xp != NULL) {
 
429
                archive_entry_xattr_add_entry(entry2,
 
430
                    xp->name, xp->value, xp->size);
 
431
                xp = xp->next;
 
432
        }
 
433
 
 
434
        return (entry2);
 
435
}
 
436
 
 
437
void
 
438
archive_entry_free(struct archive_entry *entry)
 
439
{
 
440
        archive_entry_clear(entry);
 
441
        free(entry);
 
442
}
 
443
 
 
444
struct archive_entry *
 
445
archive_entry_new(void)
 
446
{
 
447
        struct archive_entry *entry;
 
448
 
 
449
        entry = (struct archive_entry *)malloc(sizeof(*entry));
 
450
        if (entry == NULL)
 
451
                return (NULL);
 
452
        memset(entry, 0, sizeof(*entry));
 
453
        return (entry);
 
454
}
 
455
 
 
456
/*
 
457
 * Functions for reading fields from an archive_entry.
 
458
 */
 
459
 
 
460
time_t
 
461
archive_entry_atime(struct archive_entry *entry)
 
462
{
 
463
        return (entry->ae_stat.aest_atime);
 
464
}
 
465
 
 
466
long
 
467
archive_entry_atime_nsec(struct archive_entry *entry)
 
468
{
 
469
        return (entry->ae_stat.aest_atime_nsec);
 
470
}
 
471
 
 
472
int
 
473
archive_entry_atime_is_set(struct archive_entry *entry)
 
474
{
 
475
        return (entry->ae_set & AE_SET_ATIME);
 
476
}
 
477
 
 
478
time_t
 
479
archive_entry_birthtime(struct archive_entry *entry)
 
480
{
 
481
        return (entry->ae_stat.aest_birthtime);
 
482
}
 
483
 
 
484
long
 
485
archive_entry_birthtime_nsec(struct archive_entry *entry)
 
486
{
 
487
        return (entry->ae_stat.aest_birthtime_nsec);
 
488
}
 
489
 
 
490
int
 
491
archive_entry_birthtime_is_set(struct archive_entry *entry)
 
492
{
 
493
        return (entry->ae_set & AE_SET_BIRTHTIME);
 
494
}
 
495
 
 
496
time_t
 
497
archive_entry_ctime(struct archive_entry *entry)
 
498
{
 
499
        return (entry->ae_stat.aest_ctime);
 
500
}
 
501
 
 
502
int
 
503
archive_entry_ctime_is_set(struct archive_entry *entry)
 
504
{
 
505
        return (entry->ae_set & AE_SET_CTIME);
 
506
}
 
507
 
 
508
long
 
509
archive_entry_ctime_nsec(struct archive_entry *entry)
 
510
{
 
511
        return (entry->ae_stat.aest_ctime_nsec);
 
512
}
 
513
 
 
514
dev_t
 
515
archive_entry_dev(struct archive_entry *entry)
 
516
{
 
517
        if (entry->ae_stat.aest_dev_is_broken_down)
 
518
                return ae_makedev(entry->ae_stat.aest_devmajor,
 
519
                    entry->ae_stat.aest_devminor);
 
520
        else
 
521
                return (entry->ae_stat.aest_dev);
 
522
}
 
523
 
 
524
dev_t
 
525
archive_entry_devmajor(struct archive_entry *entry)
 
526
{
 
527
        if (entry->ae_stat.aest_dev_is_broken_down)
 
528
                return (entry->ae_stat.aest_devmajor);
 
529
        else
 
530
                return major(entry->ae_stat.aest_dev);
 
531
}
 
532
 
 
533
dev_t
 
534
archive_entry_devminor(struct archive_entry *entry)
 
535
{
 
536
        if (entry->ae_stat.aest_dev_is_broken_down)
 
537
                return (entry->ae_stat.aest_devminor);
 
538
        else
 
539
                return minor(entry->ae_stat.aest_dev);
 
540
}
 
541
 
 
542
mode_t
 
543
archive_entry_filetype(struct archive_entry *entry)
 
544
{
 
545
        return (AE_IFMT & entry->ae_stat.aest_mode);
 
546
}
 
547
 
 
548
void
 
549
archive_entry_fflags(struct archive_entry *entry,
 
550
    unsigned long *set, unsigned long *clear)
 
551
{
 
552
        *set = entry->ae_fflags_set;
 
553
        *clear = entry->ae_fflags_clear;
 
554
}
 
555
 
 
556
/*
 
557
 * Note: if text was provided, this just returns that text.  If you
 
558
 * really need the text to be rebuilt in a canonical form, set the
 
559
 * text, ask for the bitmaps, then set the bitmaps.  (Setting the
 
560
 * bitmaps clears any stored text.)  This design is deliberate: if
 
561
 * we're editing archives, we don't want to discard flags just because
 
562
 * they aren't supported on the current system.  The bitmap<->text
 
563
 * conversions are platform-specific (see below).
 
564
 */
 
565
const char *
 
566
archive_entry_fflags_text(struct archive_entry *entry)
 
567
{
 
568
        const char *f;
 
569
        char *p;
 
570
 
 
571
        f = aes_get_mbs(&entry->ae_fflags_text);
 
572
        if (f != NULL)
 
573
                return (f);
 
574
 
 
575
        if (entry->ae_fflags_set == 0  &&  entry->ae_fflags_clear == 0)
 
576
                return (NULL);
 
577
 
 
578
        p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear);
 
579
        if (p == NULL)
 
580
                return (NULL);
 
581
 
 
582
        aes_copy_mbs(&entry->ae_fflags_text, p);
 
583
        free(p);
 
584
        f = aes_get_mbs(&entry->ae_fflags_text);
 
585
        return (f);
 
586
}
 
587
 
 
588
gid_t
 
589
archive_entry_gid(struct archive_entry *entry)
 
590
{
 
591
        return (entry->ae_stat.aest_gid);
 
592
}
 
593
 
 
594
const char *
 
595
archive_entry_gname(struct archive_entry *entry)
 
596
{
 
597
        return (aes_get_mbs(&entry->ae_gname));
 
598
}
 
599
 
 
600
const wchar_t *
 
601
archive_entry_gname_w(struct archive_entry *entry)
 
602
{
 
603
        return (aes_get_wcs(&entry->ae_gname));
 
604
}
 
605
 
 
606
const char *
 
607
archive_entry_hardlink(struct archive_entry *entry)
 
608
{
 
609
        if (entry->ae_set & AE_SET_HARDLINK)
 
610
                return (aes_get_mbs(&entry->ae_hardlink));
 
611
        return (NULL);
 
612
}
 
613
 
 
614
const wchar_t *
 
615
archive_entry_hardlink_w(struct archive_entry *entry)
 
616
{
 
617
        if (entry->ae_set & AE_SET_HARDLINK)
 
618
                return (aes_get_wcs(&entry->ae_hardlink));
 
619
        return (NULL);
 
620
}
 
621
 
 
622
ino_t
 
623
archive_entry_ino(struct archive_entry *entry)
 
624
{
 
625
        return (entry->ae_stat.aest_ino);
 
626
}
 
627
 
 
628
int64_t
 
629
archive_entry_ino64(struct archive_entry *entry)
 
630
{
 
631
        return (entry->ae_stat.aest_ino);
 
632
}
 
633
 
 
634
mode_t
 
635
archive_entry_mode(struct archive_entry *entry)
 
636
{
 
637
        return (entry->ae_stat.aest_mode);
 
638
}
 
639
 
 
640
time_t
 
641
archive_entry_mtime(struct archive_entry *entry)
 
642
{
 
643
        return (entry->ae_stat.aest_mtime);
 
644
}
 
645
 
 
646
long
 
647
archive_entry_mtime_nsec(struct archive_entry *entry)
 
648
{
 
649
        return (entry->ae_stat.aest_mtime_nsec);
 
650
}
 
651
 
 
652
int
 
653
archive_entry_mtime_is_set(struct archive_entry *entry)
 
654
{
 
655
        return (entry->ae_set & AE_SET_MTIME);
 
656
}
 
657
 
 
658
unsigned int
 
659
archive_entry_nlink(struct archive_entry *entry)
 
660
{
 
661
        return (entry->ae_stat.aest_nlink);
 
662
}
 
663
 
 
664
const char *
 
665
archive_entry_pathname(struct archive_entry *entry)
 
666
{
 
667
        return (aes_get_mbs(&entry->ae_pathname));
 
668
}
 
669
 
 
670
const wchar_t *
 
671
archive_entry_pathname_w(struct archive_entry *entry)
 
672
{
 
673
        return (aes_get_wcs(&entry->ae_pathname));
 
674
}
 
675
 
 
676
dev_t
 
677
archive_entry_rdev(struct archive_entry *entry)
 
678
{
 
679
        if (entry->ae_stat.aest_rdev_is_broken_down)
 
680
                return ae_makedev(entry->ae_stat.aest_rdevmajor,
 
681
                    entry->ae_stat.aest_rdevminor);
 
682
        else
 
683
                return (entry->ae_stat.aest_rdev);
 
684
}
 
685
 
 
686
dev_t
 
687
archive_entry_rdevmajor(struct archive_entry *entry)
 
688
{
 
689
        if (entry->ae_stat.aest_rdev_is_broken_down)
 
690
                return (entry->ae_stat.aest_rdevmajor);
 
691
        else
 
692
                return major(entry->ae_stat.aest_rdev);
 
693
}
 
694
 
 
695
dev_t
 
696
archive_entry_rdevminor(struct archive_entry *entry)
 
697
{
 
698
        if (entry->ae_stat.aest_rdev_is_broken_down)
 
699
                return (entry->ae_stat.aest_rdevminor);
 
700
        else
 
701
                return minor(entry->ae_stat.aest_rdev);
 
702
}
 
703
 
 
704
int64_t
 
705
archive_entry_size(struct archive_entry *entry)
 
706
{
 
707
        return (entry->ae_stat.aest_size);
 
708
}
 
709
 
 
710
int
 
711
archive_entry_size_is_set(struct archive_entry *entry)
 
712
{
 
713
        return (entry->ae_set & AE_SET_SIZE);
 
714
}
 
715
 
 
716
const char *
 
717
archive_entry_sourcepath(struct archive_entry *entry)
 
718
{
 
719
        return (aes_get_mbs(&entry->ae_sourcepath));
 
720
}
 
721
 
 
722
const char *
 
723
archive_entry_symlink(struct archive_entry *entry)
 
724
{
 
725
        if (entry->ae_set & AE_SET_SYMLINK)
 
726
                return (aes_get_mbs(&entry->ae_symlink));
 
727
        return (NULL);
 
728
}
 
729
 
 
730
const wchar_t *
 
731
archive_entry_symlink_w(struct archive_entry *entry)
 
732
{
 
733
        if (entry->ae_set & AE_SET_SYMLINK)
 
734
                return (aes_get_wcs(&entry->ae_symlink));
 
735
        return (NULL);
 
736
}
 
737
 
 
738
uid_t
 
739
archive_entry_uid(struct archive_entry *entry)
 
740
{
 
741
        return (entry->ae_stat.aest_uid);
 
742
}
 
743
 
 
744
const char *
 
745
archive_entry_uname(struct archive_entry *entry)
 
746
{
 
747
        return (aes_get_mbs(&entry->ae_uname));
 
748
}
 
749
 
 
750
const wchar_t *
 
751
archive_entry_uname_w(struct archive_entry *entry)
 
752
{
 
753
        return (aes_get_wcs(&entry->ae_uname));
 
754
}
 
755
 
 
756
/*
 
757
 * Functions to set archive_entry properties.
 
758
 */
 
759
 
 
760
void
 
761
archive_entry_set_filetype(struct archive_entry *entry, unsigned int type)
 
762
{
 
763
        entry->stat_valid = 0;
 
764
        entry->ae_stat.aest_mode &= ~AE_IFMT;
 
765
        entry->ae_stat.aest_mode |= AE_IFMT & type;
 
766
}
 
767
 
 
768
void
 
769
archive_entry_set_fflags(struct archive_entry *entry,
 
770
    unsigned long set, unsigned long clear)
 
771
{
 
772
        aes_clean(&entry->ae_fflags_text);
 
773
        entry->ae_fflags_set = set;
 
774
        entry->ae_fflags_clear = clear;
 
775
}
 
776
 
 
777
const char *
 
778
archive_entry_copy_fflags_text(struct archive_entry *entry,
 
779
    const char *flags)
 
780
{
 
781
        aes_copy_mbs(&entry->ae_fflags_text, flags);
 
782
        return (ae_strtofflags(flags,
 
783
                    &entry->ae_fflags_set, &entry->ae_fflags_clear));
 
784
}
 
785
 
 
786
const wchar_t *
 
787
archive_entry_copy_fflags_text_w(struct archive_entry *entry,
 
788
    const wchar_t *flags)
 
789
{
 
790
        aes_copy_wcs(&entry->ae_fflags_text, flags);
 
791
        return (ae_wcstofflags(flags,
 
792
                    &entry->ae_fflags_set, &entry->ae_fflags_clear));
 
793
}
 
794
 
 
795
void
 
796
archive_entry_set_gid(struct archive_entry *entry, gid_t g)
 
797
{
 
798
        entry->stat_valid = 0;
 
799
        entry->ae_stat.aest_gid = g;
 
800
}
 
801
 
 
802
void
 
803
archive_entry_set_gname(struct archive_entry *entry, const char *name)
 
804
{
 
805
        aes_set_mbs(&entry->ae_gname, name);
 
806
}
 
807
 
 
808
void
 
809
archive_entry_copy_gname(struct archive_entry *entry, const char *name)
 
810
{
 
811
        aes_copy_mbs(&entry->ae_gname, name);
 
812
}
 
813
 
 
814
void
 
815
archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
 
816
{
 
817
        aes_copy_wcs(&entry->ae_gname, name);
 
818
}
 
819
 
 
820
int
 
821
archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
 
822
{
 
823
        return (aes_update_utf8(&entry->ae_gname, name));
 
824
}
 
825
 
 
826
void
 
827
archive_entry_set_ino(struct archive_entry *entry, unsigned long ino)
 
828
{
 
829
        entry->stat_valid = 0;
 
830
        entry->ae_stat.aest_ino = ino;
 
831
}
 
832
 
 
833
void
 
834
archive_entry_set_ino64(struct archive_entry *entry, int64_t ino)
 
835
{
 
836
        entry->stat_valid = 0;
 
837
        entry->ae_stat.aest_ino = ino;
 
838
}
 
839
 
 
840
void
 
841
archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
 
842
{
 
843
        aes_set_mbs(&entry->ae_hardlink, target);
 
844
        if (target != NULL)
 
845
                entry->ae_set |= AE_SET_HARDLINK;
 
846
        else
 
847
                entry->ae_set &= ~AE_SET_HARDLINK;
 
848
}
 
849
 
 
850
void
 
851
archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
 
852
{
 
853
        aes_copy_mbs(&entry->ae_hardlink, target);
 
854
        if (target != NULL)
 
855
                entry->ae_set |= AE_SET_HARDLINK;
 
856
        else
 
857
                entry->ae_set &= ~AE_SET_HARDLINK;
 
858
}
 
859
 
 
860
void
 
861
archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
 
862
{
 
863
        aes_copy_wcs(&entry->ae_hardlink, target);
 
864
        if (target != NULL)
 
865
                entry->ae_set |= AE_SET_HARDLINK;
 
866
        else
 
867
                entry->ae_set &= ~AE_SET_HARDLINK;
 
868
}
 
869
 
 
870
int
 
871
archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target)
 
872
{
 
873
        if (target != NULL)
 
874
                entry->ae_set |= AE_SET_HARDLINK;
 
875
        else
 
876
                entry->ae_set &= ~AE_SET_HARDLINK;
 
877
        return (aes_update_utf8(&entry->ae_hardlink, target));
 
878
}
 
879
 
 
880
void
 
881
archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
 
882
{
 
883
        entry->stat_valid = 0;
 
884
        entry->ae_set |= AE_SET_ATIME;
 
885
        entry->ae_stat.aest_atime = t;
 
886
        entry->ae_stat.aest_atime_nsec = ns;
 
887
}
 
888
 
 
889
void
 
890
archive_entry_unset_atime(struct archive_entry *entry)
 
891
{
 
892
        archive_entry_set_atime(entry, 0, 0);
 
893
        entry->ae_set &= ~AE_SET_ATIME;
 
894
}
 
895
 
 
896
void
 
897
archive_entry_set_birthtime(struct archive_entry *entry, time_t m, long ns)
 
898
{
 
899
        entry->stat_valid = 0;
 
900
        entry->ae_set |= AE_SET_BIRTHTIME;
 
901
        entry->ae_stat.aest_birthtime = m;
 
902
        entry->ae_stat.aest_birthtime_nsec = ns;
 
903
}
 
904
 
 
905
void
 
906
archive_entry_unset_birthtime(struct archive_entry *entry)
 
907
{
 
908
        archive_entry_set_birthtime(entry, 0, 0);
 
909
        entry->ae_set &= ~AE_SET_BIRTHTIME;
 
910
}
 
911
 
 
912
void
 
913
archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
 
914
{
 
915
        entry->stat_valid = 0;
 
916
        entry->ae_set |= AE_SET_CTIME;
 
917
        entry->ae_stat.aest_ctime = t;
 
918
        entry->ae_stat.aest_ctime_nsec = ns;
 
919
}
 
920
 
 
921
void
 
922
archive_entry_unset_ctime(struct archive_entry *entry)
 
923
{
 
924
        archive_entry_set_ctime(entry, 0, 0);
 
925
        entry->ae_set &= ~AE_SET_CTIME;
 
926
}
 
927
 
 
928
void
 
929
archive_entry_set_dev(struct archive_entry *entry, dev_t d)
 
930
{
 
931
        entry->stat_valid = 0;
 
932
        entry->ae_stat.aest_dev_is_broken_down = 0;
 
933
        entry->ae_stat.aest_dev = d;
 
934
}
 
935
 
 
936
void
 
937
archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)
 
938
{
 
939
        entry->stat_valid = 0;
 
940
        entry->ae_stat.aest_dev_is_broken_down = 1;
 
941
        entry->ae_stat.aest_devmajor = m;
 
942
}
 
943
 
 
944
void
 
945
archive_entry_set_devminor(struct archive_entry *entry, dev_t m)
 
946
{
 
947
        entry->stat_valid = 0;
 
948
        entry->ae_stat.aest_dev_is_broken_down = 1;
 
949
        entry->ae_stat.aest_devminor = m;
 
950
}
 
951
 
 
952
/* Set symlink if symlink is already set, else set hardlink. */
 
953
void
 
954
archive_entry_set_link(struct archive_entry *entry, const char *target)
 
955
{
 
956
        if (entry->ae_set & AE_SET_SYMLINK)
 
957
                aes_set_mbs(&entry->ae_symlink, target);
 
958
        else
 
959
                aes_set_mbs(&entry->ae_hardlink, target);
 
960
}
 
961
 
 
962
/* Set symlink if symlink is already set, else set hardlink. */
 
963
void
 
964
archive_entry_copy_link(struct archive_entry *entry, const char *target)
 
965
{
 
966
        if (entry->ae_set & AE_SET_SYMLINK)
 
967
                aes_copy_mbs(&entry->ae_symlink, target);
 
968
        else
 
969
                aes_copy_mbs(&entry->ae_hardlink, target);
 
970
}
 
971
 
 
972
/* Set symlink if symlink is already set, else set hardlink. */
 
973
void
 
974
archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
 
975
{
 
976
        if (entry->ae_set & AE_SET_SYMLINK)
 
977
                aes_copy_wcs(&entry->ae_symlink, target);
 
978
        else
 
979
                aes_copy_wcs(&entry->ae_hardlink, target);
 
980
}
 
981
 
 
982
int
 
983
archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
 
984
{
 
985
        if (entry->ae_set & AE_SET_SYMLINK)
 
986
                return (aes_update_utf8(&entry->ae_symlink, target));
 
987
        else
 
988
                return (aes_update_utf8(&entry->ae_hardlink, target));
 
989
}
 
990
 
 
991
void
 
992
archive_entry_set_mode(struct archive_entry *entry, mode_t m)
 
993
{
 
994
        entry->stat_valid = 0;
 
995
        entry->ae_stat.aest_mode = m;
 
996
}
 
997
 
 
998
void
 
999
archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
 
1000
{
 
1001
        entry->stat_valid = 0;
 
1002
        entry->ae_set |= AE_SET_MTIME;
 
1003
        entry->ae_stat.aest_mtime = m;
 
1004
        entry->ae_stat.aest_mtime_nsec = ns;
 
1005
}
 
1006
 
 
1007
void
 
1008
archive_entry_unset_mtime(struct archive_entry *entry)
 
1009
{
 
1010
        archive_entry_set_mtime(entry, 0, 0);
 
1011
        entry->ae_set &= ~AE_SET_MTIME;
 
1012
}
 
1013
 
 
1014
void
 
1015
archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)
 
1016
{
 
1017
        entry->stat_valid = 0;
 
1018
        entry->ae_stat.aest_nlink = nlink;
 
1019
}
 
1020
 
 
1021
void
 
1022
archive_entry_set_pathname(struct archive_entry *entry, const char *name)
 
1023
{
 
1024
        aes_set_mbs(&entry->ae_pathname, name);
 
1025
}
 
1026
 
 
1027
void
 
1028
archive_entry_copy_pathname(struct archive_entry *entry, const char *name)
 
1029
{
 
1030
        aes_copy_mbs(&entry->ae_pathname, name);
 
1031
}
 
1032
 
 
1033
void
 
1034
archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
 
1035
{
 
1036
        aes_copy_wcs(&entry->ae_pathname, name);
 
1037
}
 
1038
 
 
1039
int
 
1040
archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)
 
1041
{
 
1042
        return (aes_update_utf8(&entry->ae_pathname, name));
 
1043
}
 
1044
 
 
1045
void
 
1046
archive_entry_set_perm(struct archive_entry *entry, mode_t p)
 
1047
{
 
1048
        entry->stat_valid = 0;
 
1049
        entry->ae_stat.aest_mode &= AE_IFMT;
 
1050
        entry->ae_stat.aest_mode |= ~AE_IFMT & p;
 
1051
}
 
1052
 
 
1053
void
 
1054
archive_entry_set_rdev(struct archive_entry *entry, dev_t m)
 
1055
{
 
1056
        entry->stat_valid = 0;
 
1057
        entry->ae_stat.aest_rdev = m;
 
1058
        entry->ae_stat.aest_rdev_is_broken_down = 0;
 
1059
}
 
1060
 
 
1061
void
 
1062
archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
 
1063
{
 
1064
        entry->stat_valid = 0;
 
1065
        entry->ae_stat.aest_rdev_is_broken_down = 1;
 
1066
        entry->ae_stat.aest_rdevmajor = m;
 
1067
}
 
1068
 
 
1069
void
 
1070
archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
 
1071
{
 
1072
        entry->stat_valid = 0;
 
1073
        entry->ae_stat.aest_rdev_is_broken_down = 1;
 
1074
        entry->ae_stat.aest_rdevminor = m;
 
1075
}
 
1076
 
 
1077
void
 
1078
archive_entry_set_size(struct archive_entry *entry, int64_t s)
 
1079
{
 
1080
        entry->stat_valid = 0;
 
1081
        entry->ae_stat.aest_size = s;
 
1082
        entry->ae_set |= AE_SET_SIZE;
 
1083
}
 
1084
 
 
1085
void
 
1086
archive_entry_unset_size(struct archive_entry *entry)
 
1087
{
 
1088
        archive_entry_set_size(entry, 0);
 
1089
        entry->ae_set &= ~AE_SET_SIZE;
 
1090
}
 
1091
 
 
1092
void
 
1093
archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path)
 
1094
{
 
1095
        aes_set_mbs(&entry->ae_sourcepath, path);
 
1096
}
 
1097
 
 
1098
void
 
1099
archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
 
1100
{
 
1101
        aes_set_mbs(&entry->ae_symlink, linkname);
 
1102
        if (linkname != NULL)
 
1103
                entry->ae_set |= AE_SET_SYMLINK;
 
1104
        else
 
1105
                entry->ae_set &= ~AE_SET_SYMLINK;
 
1106
}
 
1107
 
 
1108
void
 
1109
archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
 
1110
{
 
1111
        aes_copy_mbs(&entry->ae_symlink, linkname);
 
1112
        if (linkname != NULL)
 
1113
                entry->ae_set |= AE_SET_SYMLINK;
 
1114
        else
 
1115
                entry->ae_set &= ~AE_SET_SYMLINK;
 
1116
}
 
1117
 
 
1118
void
 
1119
archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
 
1120
{
 
1121
        aes_copy_wcs(&entry->ae_symlink, linkname);
 
1122
        if (linkname != NULL)
 
1123
                entry->ae_set |= AE_SET_SYMLINK;
 
1124
        else
 
1125
                entry->ae_set &= ~AE_SET_SYMLINK;
 
1126
}
 
1127
 
 
1128
int
 
1129
archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname)
 
1130
{
 
1131
        if (linkname != NULL)
 
1132
                entry->ae_set |= AE_SET_SYMLINK;
 
1133
        else
 
1134
                entry->ae_set &= ~AE_SET_SYMLINK;
 
1135
        return (aes_update_utf8(&entry->ae_symlink, linkname));
 
1136
}
 
1137
 
 
1138
void
 
1139
archive_entry_set_uid(struct archive_entry *entry, uid_t u)
 
1140
{
 
1141
        entry->stat_valid = 0;
 
1142
        entry->ae_stat.aest_uid = u;
 
1143
}
 
1144
 
 
1145
void
 
1146
archive_entry_set_uname(struct archive_entry *entry, const char *name)
 
1147
{
 
1148
        aes_set_mbs(&entry->ae_uname, name);
 
1149
}
 
1150
 
 
1151
void
 
1152
archive_entry_copy_uname(struct archive_entry *entry, const char *name)
 
1153
{
 
1154
        aes_copy_mbs(&entry->ae_uname, name);
 
1155
}
 
1156
 
 
1157
void
 
1158
archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
 
1159
{
 
1160
        aes_copy_wcs(&entry->ae_uname, name);
 
1161
}
 
1162
 
 
1163
int
 
1164
archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
 
1165
{
 
1166
        return (aes_update_utf8(&entry->ae_uname, name));
 
1167
}
 
1168
 
 
1169
/*
 
1170
 * ACL management.  The following would, of course, be a lot simpler
 
1171
 * if: 1) the last draft of POSIX.1e were a really thorough and
 
1172
 * complete standard that addressed the needs of ACL archiving and 2)
 
1173
 * everyone followed it faithfully.  Alas, neither is true, so the
 
1174
 * following is a lot more complex than might seem necessary to the
 
1175
 * uninitiated.
 
1176
 */
 
1177
 
 
1178
void
 
1179
archive_entry_acl_clear(struct archive_entry *entry)
 
1180
{
 
1181
        struct ae_acl   *ap;
 
1182
 
 
1183
        while (entry->acl_head != NULL) {
 
1184
                ap = entry->acl_head->next;
 
1185
                aes_clean(&entry->acl_head->name);
 
1186
                free(entry->acl_head);
 
1187
                entry->acl_head = ap;
 
1188
        }
 
1189
        if (entry->acl_text_w != NULL) {
 
1190
                free(entry->acl_text_w);
 
1191
                entry->acl_text_w = NULL;
 
1192
        }
 
1193
        entry->acl_p = NULL;
 
1194
        entry->acl_state = 0; /* Not counting. */
 
1195
}
 
1196
 
 
1197
/*
 
1198
 * Add a single ACL entry to the internal list of ACL data.
 
1199
 */
 
1200
void
 
1201
archive_entry_acl_add_entry(struct archive_entry *entry,
 
1202
    int type, int permset, int tag, int id, const char *name)
 
1203
{
 
1204
        struct ae_acl *ap;
 
1205
 
 
1206
        if (acl_special(entry, type, permset, tag) == 0)
 
1207
                return;
 
1208
        ap = acl_new_entry(entry, type, permset, tag, id);
 
1209
        if (ap == NULL) {
 
1210
                /* XXX Error XXX */
 
1211
                return;
 
1212
        }
 
1213
        if (name != NULL  &&  *name != '\0')
 
1214
                aes_copy_mbs(&ap->name, name);
 
1215
        else
 
1216
                aes_clean(&ap->name);
 
1217
}
 
1218
 
 
1219
/*
 
1220
 * As above, but with a wide-character name.
 
1221
 */
 
1222
void
 
1223
archive_entry_acl_add_entry_w(struct archive_entry *entry,
 
1224
    int type, int permset, int tag, int id, const wchar_t *name)
 
1225
{
 
1226
        archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name));
 
1227
}
 
1228
 
 
1229
static void
 
1230
archive_entry_acl_add_entry_w_len(struct archive_entry *entry,
 
1231
    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
 
1232
{
 
1233
        struct ae_acl *ap;
 
1234
 
 
1235
        if (acl_special(entry, type, permset, tag) == 0)
 
1236
                return;
 
1237
        ap = acl_new_entry(entry, type, permset, tag, id);
 
1238
        if (ap == NULL) {
 
1239
                /* XXX Error XXX */
 
1240
                return;
 
1241
        }
 
1242
        if (name != NULL  &&  *name != L'\0' && len > 0)
 
1243
                aes_copy_wcs_len(&ap->name, name, len);
 
1244
        else
 
1245
                aes_clean(&ap->name);
 
1246
}
 
1247
 
 
1248
/*
 
1249
 * If this ACL entry is part of the standard POSIX permissions set,
 
1250
 * store the permissions in the stat structure and return zero.
 
1251
 */
 
1252
static int
 
1253
acl_special(struct archive_entry *entry, int type, int permset, int tag)
 
1254
{
 
1255
        if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
 
1256
                switch (tag) {
 
1257
                case ARCHIVE_ENTRY_ACL_USER_OBJ:
 
1258
                        entry->ae_stat.aest_mode &= ~0700;
 
1259
                        entry->ae_stat.aest_mode |= (permset & 7) << 6;
 
1260
                        return (0);
 
1261
                case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 
1262
                        entry->ae_stat.aest_mode &= ~0070;
 
1263
                        entry->ae_stat.aest_mode |= (permset & 7) << 3;
 
1264
                        return (0);
 
1265
                case ARCHIVE_ENTRY_ACL_OTHER:
 
1266
                        entry->ae_stat.aest_mode &= ~0007;
 
1267
                        entry->ae_stat.aest_mode |= permset & 7;
 
1268
                        return (0);
 
1269
                }
 
1270
        }
 
1271
        return (1);
 
1272
}
 
1273
 
 
1274
/*
 
1275
 * Allocate and populate a new ACL entry with everything but the
 
1276
 * name.
 
1277
 */
 
1278
static struct ae_acl *
 
1279
acl_new_entry(struct archive_entry *entry,
 
1280
    int type, int permset, int tag, int id)
 
1281
{
 
1282
        struct ae_acl *ap, *aq;
 
1283
 
 
1284
        if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS &&
 
1285
            type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
 
1286
                return (NULL);
 
1287
        if (entry->acl_text_w != NULL) {
 
1288
                free(entry->acl_text_w);
 
1289
                entry->acl_text_w = NULL;
 
1290
        }
 
1291
 
 
1292
        /* XXX TODO: More sanity-checks on the arguments XXX */
 
1293
 
 
1294
        /* If there's a matching entry already in the list, overwrite it. */
 
1295
        ap = entry->acl_head;
 
1296
        aq = NULL;
 
1297
        while (ap != NULL) {
 
1298
                if (ap->type == type && ap->tag == tag && ap->id == id) {
 
1299
                        ap->permset = permset;
 
1300
                        return (ap);
 
1301
                }
 
1302
                aq = ap;
 
1303
                ap = ap->next;
 
1304
        }
 
1305
 
 
1306
        /* Add a new entry to the end of the list. */
 
1307
        ap = (struct ae_acl *)malloc(sizeof(*ap));
 
1308
        if (ap == NULL)
 
1309
                return (NULL);
 
1310
        memset(ap, 0, sizeof(*ap));
 
1311
        if (aq == NULL)
 
1312
                entry->acl_head = ap;
 
1313
        else
 
1314
                aq->next = ap;
 
1315
        ap->type = type;
 
1316
        ap->tag = tag;
 
1317
        ap->id = id;
 
1318
        ap->permset = permset;
 
1319
        return (ap);
 
1320
}
 
1321
 
 
1322
/*
 
1323
 * Return a count of entries matching "want_type".
 
1324
 */
 
1325
int
 
1326
archive_entry_acl_count(struct archive_entry *entry, int want_type)
 
1327
{
 
1328
        int count;
 
1329
        struct ae_acl *ap;
 
1330
 
 
1331
        count = 0;
 
1332
        ap = entry->acl_head;
 
1333
        while (ap != NULL) {
 
1334
                if ((ap->type & want_type) != 0)
 
1335
                        count++;
 
1336
                ap = ap->next;
 
1337
        }
 
1338
 
 
1339
        if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
 
1340
                count += 3;
 
1341
        return (count);
 
1342
}
 
1343
 
 
1344
/*
 
1345
 * Prepare for reading entries from the ACL data.  Returns a count
 
1346
 * of entries matching "want_type", or zero if there are no
 
1347
 * non-extended ACL entries of that type.
 
1348
 */
 
1349
int
 
1350
archive_entry_acl_reset(struct archive_entry *entry, int want_type)
 
1351
{
 
1352
        int count, cutoff;
 
1353
 
 
1354
        count = archive_entry_acl_count(entry, want_type);
 
1355
 
 
1356
        /*
 
1357
         * If the only entries are the three standard ones,
 
1358
         * then don't return any ACL data.  (In this case,
 
1359
         * client can just use chmod(2) to set permissions.)
 
1360
         */
 
1361
        if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
 
1362
                cutoff = 3;
 
1363
        else
 
1364
                cutoff = 0;
 
1365
 
 
1366
        if (count > cutoff)
 
1367
                entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
 
1368
        else
 
1369
                entry->acl_state = 0;
 
1370
        entry->acl_p = entry->acl_head;
 
1371
        return (count);
 
1372
}
 
1373
 
 
1374
/*
 
1375
 * Return the next ACL entry in the list.  Fake entries for the
 
1376
 * standard permissions and include them in the returned list.
 
1377
 */
 
1378
 
 
1379
int
 
1380
archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
 
1381
    int *permset, int *tag, int *id, const char **name)
 
1382
{
 
1383
        *name = NULL;
 
1384
        *id = -1;
 
1385
 
 
1386
        /*
 
1387
         * The acl_state is either zero (no entries available), -1
 
1388
         * (reading from list), or an entry type (retrieve that type
 
1389
         * from ae_stat.aest_mode).
 
1390
         */
 
1391
        if (entry->acl_state == 0)
 
1392
                return (ARCHIVE_WARN);
 
1393
 
 
1394
        /* The first three access entries are special. */
 
1395
        if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
 
1396
                switch (entry->acl_state) {
 
1397
                case ARCHIVE_ENTRY_ACL_USER_OBJ:
 
1398
                        *permset = (entry->ae_stat.aest_mode >> 6) & 7;
 
1399
                        *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 
1400
                        *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
 
1401
                        entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
 
1402
                        return (ARCHIVE_OK);
 
1403
                case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 
1404
                        *permset = (entry->ae_stat.aest_mode >> 3) & 7;
 
1405
                        *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 
1406
                        *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
 
1407
                        entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
 
1408
                        return (ARCHIVE_OK);
 
1409
                case ARCHIVE_ENTRY_ACL_OTHER:
 
1410
                        *permset = entry->ae_stat.aest_mode & 7;
 
1411
                        *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 
1412
                        *tag = ARCHIVE_ENTRY_ACL_OTHER;
 
1413
                        entry->acl_state = -1;
 
1414
                        entry->acl_p = entry->acl_head;
 
1415
                        return (ARCHIVE_OK);
 
1416
                default:
 
1417
                        break;
 
1418
                }
 
1419
        }
 
1420
 
 
1421
        while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0)
 
1422
                entry->acl_p = entry->acl_p->next;
 
1423
        if (entry->acl_p == NULL) {
 
1424
                entry->acl_state = 0;
 
1425
                *type = 0;
 
1426
                *permset = 0;
 
1427
                *tag = 0;
 
1428
                *id = -1;
 
1429
                *name = NULL;
 
1430
                return (ARCHIVE_EOF); /* End of ACL entries. */
 
1431
        }
 
1432
        *type = entry->acl_p->type;
 
1433
        *permset = entry->acl_p->permset;
 
1434
        *tag = entry->acl_p->tag;
 
1435
        *id = entry->acl_p->id;
 
1436
        *name = aes_get_mbs(&entry->acl_p->name);
 
1437
        entry->acl_p = entry->acl_p->next;
 
1438
        return (ARCHIVE_OK);
 
1439
}
 
1440
 
 
1441
/*
 
1442
 * Generate a text version of the ACL.  The flags parameter controls
 
1443
 * the style of the generated ACL.
 
1444
 */
 
1445
const wchar_t *
 
1446
archive_entry_acl_text_w(struct archive_entry *entry, int flags)
 
1447
{
 
1448
        int count;
 
1449
        size_t length;
 
1450
        const wchar_t *wname;
 
1451
        const wchar_t *prefix;
 
1452
        wchar_t separator;
 
1453
        struct ae_acl *ap;
 
1454
        int id;
 
1455
        wchar_t *wp;
 
1456
 
 
1457
        if (entry->acl_text_w != NULL) {
 
1458
                free (entry->acl_text_w);
 
1459
                entry->acl_text_w = NULL;
 
1460
        }
 
1461
 
 
1462
        separator = L',';
 
1463
        count = 0;
 
1464
        length = 0;
 
1465
        ap = entry->acl_head;
 
1466
        while (ap != NULL) {
 
1467
                if ((ap->type & flags) != 0) {
 
1468
                        count++;
 
1469
                        if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
 
1470
                            (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
 
1471
                                length += 8; /* "default:" */
 
1472
                        length += 5; /* tag name */
 
1473
                        length += 1; /* colon */
 
1474
                        wname = aes_get_wcs(&ap->name);
 
1475
                        if (wname != NULL)
 
1476
                                length += wcslen(wname);
 
1477
                        else
 
1478
                                length += sizeof(uid_t) * 3 + 1;
 
1479
                        length ++; /* colon */
 
1480
                        length += 3; /* rwx */
 
1481
                        length += 1; /* colon */
 
1482
                        length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
 
1483
                        length ++; /* newline */
 
1484
                }
 
1485
                ap = ap->next;
 
1486
        }
 
1487
 
 
1488
        if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
 
1489
                length += 10; /* "user::rwx\n" */
 
1490
                length += 11; /* "group::rwx\n" */
 
1491
                length += 11; /* "other::rwx\n" */
 
1492
        }
 
1493
 
 
1494
        if (count == 0)
 
1495
                return (NULL);
 
1496
 
 
1497
        /* Now, allocate the string and actually populate it. */
 
1498
        wp = entry->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
 
1499
        if (wp == NULL)
 
1500
                __archive_errx(1, "No memory to generate the text version of the ACL");
 
1501
        count = 0;
 
1502
        if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
 
1503
                append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
 
1504
                    entry->ae_stat.aest_mode & 0700, -1);
 
1505
                *wp++ = ',';
 
1506
                append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
 
1507
                    entry->ae_stat.aest_mode & 0070, -1);
 
1508
                *wp++ = ',';
 
1509
                append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
 
1510
                    entry->ae_stat.aest_mode & 0007, -1);
 
1511
                count += 3;
 
1512
 
 
1513
                ap = entry->acl_head;
 
1514
                while (ap != NULL) {
 
1515
                        if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
 
1516
                                wname = aes_get_wcs(&ap->name);
 
1517
                                *wp++ = separator;
 
1518
                                if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
 
1519
                                        id = ap->id;
 
1520
                                else
 
1521
                                        id = -1;
 
1522
                                append_entry_w(&wp, NULL, ap->tag, wname,
 
1523
                                    ap->permset, id);
 
1524
                                count++;
 
1525
                        }
 
1526
                        ap = ap->next;
 
1527
                }
 
1528
        }
 
1529
 
 
1530
 
 
1531
        if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
 
1532
                if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
 
1533
                        prefix = L"default:";
 
1534
                else
 
1535
                        prefix = NULL;
 
1536
                ap = entry->acl_head;
 
1537
                count = 0;
 
1538
                while (ap != NULL) {
 
1539
                        if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
 
1540
                                wname = aes_get_wcs(&ap->name);
 
1541
                                if (count > 0)
 
1542
                                        *wp++ = separator;
 
1543
                                if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
 
1544
                                        id = ap->id;
 
1545
                                else
 
1546
                                        id = -1;
 
1547
                                append_entry_w(&wp, prefix, ap->tag,
 
1548
                                    wname, ap->permset, id);
 
1549
                                count ++;
 
1550
                        }
 
1551
                        ap = ap->next;
 
1552
                }
 
1553
        }
 
1554
 
 
1555
        return (entry->acl_text_w);
 
1556
}
 
1557
 
 
1558
static void
 
1559
append_id_w(wchar_t **wp, int id)
 
1560
{
 
1561
        if (id < 0)
 
1562
                id = 0;
 
1563
        if (id > 9)
 
1564
                append_id_w(wp, id / 10);
 
1565
        *(*wp)++ = L"0123456789"[id % 10];
 
1566
}
 
1567
 
 
1568
static void
 
1569
append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
 
1570
    const wchar_t *wname, int perm, int id)
 
1571
{
 
1572
        if (prefix != NULL) {
 
1573
                wcscpy(*wp, prefix);
 
1574
                *wp += wcslen(*wp);
 
1575
        }
 
1576
        switch (tag) {
 
1577
        case ARCHIVE_ENTRY_ACL_USER_OBJ:
 
1578
                wname = NULL;
 
1579
                id = -1;
 
1580
                /* FALLTHROUGH */
 
1581
        case ARCHIVE_ENTRY_ACL_USER:
 
1582
                wcscpy(*wp, L"user");
 
1583
                break;
 
1584
        case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 
1585
                wname = NULL;
 
1586
                id = -1;
 
1587
                /* FALLTHROUGH */
 
1588
        case ARCHIVE_ENTRY_ACL_GROUP:
 
1589
                wcscpy(*wp, L"group");
 
1590
                break;
 
1591
        case ARCHIVE_ENTRY_ACL_MASK:
 
1592
                wcscpy(*wp, L"mask");
 
1593
                wname = NULL;
 
1594
                id = -1;
 
1595
                break;
 
1596
        case ARCHIVE_ENTRY_ACL_OTHER:
 
1597
                wcscpy(*wp, L"other");
 
1598
                wname = NULL;
 
1599
                id = -1;
 
1600
                break;
 
1601
        }
 
1602
        *wp += wcslen(*wp);
 
1603
        *(*wp)++ = L':';
 
1604
        if (wname != NULL) {
 
1605
                wcscpy(*wp, wname);
 
1606
                *wp += wcslen(*wp);
 
1607
        } else if (tag == ARCHIVE_ENTRY_ACL_USER
 
1608
            || tag == ARCHIVE_ENTRY_ACL_GROUP) {
 
1609
                append_id_w(wp, id);
 
1610
                id = -1;
 
1611
        }
 
1612
        *(*wp)++ = L':';
 
1613
        *(*wp)++ = (perm & 0444) ? L'r' : L'-';
 
1614
        *(*wp)++ = (perm & 0222) ? L'w' : L'-';
 
1615
        *(*wp)++ = (perm & 0111) ? L'x' : L'-';
 
1616
        if (id != -1) {
 
1617
                *(*wp)++ = L':';
 
1618
                append_id_w(wp, id);
 
1619
        }
 
1620
        **wp = L'\0';
 
1621
}
 
1622
 
 
1623
/*
 
1624
 * Parse a textual ACL.  This automatically recognizes and supports
 
1625
 * extensions described above.  The 'type' argument is used to
 
1626
 * indicate the type that should be used for any entries not
 
1627
 * explicitly marked as "default:".
 
1628
 */
 
1629
int
 
1630
__archive_entry_acl_parse_w(struct archive_entry *entry,
 
1631
    const wchar_t *text, int default_type)
 
1632
{
 
1633
        struct {
 
1634
                const wchar_t *start;
 
1635
                const wchar_t *end;
 
1636
        } field[4], name;
 
1637
 
 
1638
        int fields, n;
 
1639
        int type, tag, permset, id;
 
1640
        wchar_t sep;
 
1641
 
 
1642
        while (text != NULL  &&  *text != L'\0') {
 
1643
                /*
 
1644
                 * Parse the fields out of the next entry,
 
1645
                 * advance 'text' to start of next entry.
 
1646
                 */
 
1647
                fields = 0;
 
1648
                do {
 
1649
                        const wchar_t *start, *end;
 
1650
                        next_field_w(&text, &start, &end, &sep);
 
1651
                        if (fields < 4) {
 
1652
                                field[fields].start = start;
 
1653
                                field[fields].end = end;
 
1654
                        }
 
1655
                        ++fields;
 
1656
                } while (sep == L':');
 
1657
 
 
1658
                /* Set remaining fields to blank. */
 
1659
                for (n = fields; n < 4; ++n)
 
1660
                        field[n].start = field[n].end = NULL;
 
1661
 
 
1662
                /* Check for a numeric ID in field 1 or 3. */
 
1663
                id = -1;
 
1664
                isint_w(field[1].start, field[1].end, &id);
 
1665
                /* Field 3 is optional. */
 
1666
                if (id == -1 && fields > 3)
 
1667
                        isint_w(field[3].start, field[3].end, &id);
 
1668
 
 
1669
                /*
 
1670
                 * Solaris extension:  "defaultuser::rwx" is the
 
1671
                 * default ACL corresponding to "user::rwx", etc.
 
1672
                 */
 
1673
                if (field[0].end - field[0].start > 7
 
1674
                    && wmemcmp(field[0].start, L"default", 7) == 0) {
 
1675
                        type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
 
1676
                        field[0].start += 7;
 
1677
                } else
 
1678
                        type = default_type;
 
1679
 
 
1680
                name.start = name.end = NULL;
 
1681
                if (prefix_w(field[0].start, field[0].end, L"user")) {
 
1682
                        if (!ismode_w(field[2].start, field[2].end, &permset))
 
1683
                                return (ARCHIVE_WARN);
 
1684
                        if (id != -1 || field[1].start < field[1].end) {
 
1685
                                tag = ARCHIVE_ENTRY_ACL_USER;
 
1686
                                name = field[1];
 
1687
                        } else
 
1688
                                tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
 
1689
                } else if (prefix_w(field[0].start, field[0].end, L"group")) {
 
1690
                        if (!ismode_w(field[2].start, field[2].end, &permset))
 
1691
                                return (ARCHIVE_WARN);
 
1692
                        if (id != -1 || field[1].start < field[1].end) {
 
1693
                                tag = ARCHIVE_ENTRY_ACL_GROUP;
 
1694
                                name = field[1];
 
1695
                        } else
 
1696
                                tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
 
1697
                } else if (prefix_w(field[0].start, field[0].end, L"other")) {
 
1698
                        if (fields == 2
 
1699
                            && field[1].start < field[1].end
 
1700
                            && ismode_w(field[1].start, field[1].end, &permset)) {
 
1701
                                /* This is Solaris-style "other:rwx" */
 
1702
                        } else if (fields == 3
 
1703
                            && field[1].start == field[1].end
 
1704
                            && field[2].start < field[2].end
 
1705
                            && ismode_w(field[2].start, field[2].end, &permset)) {
 
1706
                                /* This is FreeBSD-style "other::rwx" */
 
1707
                        } else
 
1708
                                return (ARCHIVE_WARN);
 
1709
                        tag = ARCHIVE_ENTRY_ACL_OTHER;
 
1710
                } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
 
1711
                        if (fields == 2
 
1712
                            && field[1].start < field[1].end
 
1713
                            && ismode_w(field[1].start, field[1].end, &permset)) {
 
1714
                                /* This is Solaris-style "mask:rwx" */
 
1715
                        } else if (fields == 3
 
1716
                            && field[1].start == field[1].end
 
1717
                            && field[2].start < field[2].end
 
1718
                            && ismode_w(field[2].start, field[2].end, &permset)) {
 
1719
                                /* This is FreeBSD-style "mask::rwx" */
 
1720
                        } else
 
1721
                                return (ARCHIVE_WARN);
 
1722
                        tag = ARCHIVE_ENTRY_ACL_MASK;
 
1723
                } else
 
1724
                        return (ARCHIVE_WARN);
 
1725
 
 
1726
                /* Add entry to the internal list. */
 
1727
                archive_entry_acl_add_entry_w_len(entry, type, permset,
 
1728
                    tag, id, name.start, name.end - name.start);
 
1729
        }
 
1730
        return (ARCHIVE_OK);
 
1731
}
 
1732
 
 
1733
/*
 
1734
 * Parse a string to a positive decimal integer.  Returns true if
 
1735
 * the string is non-empty and consists only of decimal digits,
 
1736
 * false otherwise.
 
1737
 */
 
1738
static int
 
1739
isint_w(const wchar_t *start, const wchar_t *end, int *result)
 
1740
{
 
1741
        int n = 0;
 
1742
        if (start >= end)
 
1743
                return (0);
 
1744
        while (start < end) {
 
1745
                if (*start < '0' || *start > '9')
 
1746
                        return (0);
 
1747
                if (n > (INT_MAX / 10))
 
1748
                        n = INT_MAX;
 
1749
                else {
 
1750
                        n *= 10;
 
1751
                        n += *start - '0';
 
1752
                }
 
1753
                start++;
 
1754
        }
 
1755
        *result = n;
 
1756
        return (1);
 
1757
}
 
1758
 
 
1759
/*
 
1760
 * Parse a string as a mode field.  Returns true if
 
1761
 * the string is non-empty and consists only of mode characters,
 
1762
 * false otherwise.
 
1763
 */
 
1764
static int
 
1765
ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
 
1766
{
 
1767
        const wchar_t *p;
 
1768
 
 
1769
        if (start >= end)
 
1770
                return (0);
 
1771
        p = start;
 
1772
        *permset = 0;
 
1773
        while (p < end) {
 
1774
                switch (*p++) {
 
1775
                case 'r': case 'R':
 
1776
                        *permset |= ARCHIVE_ENTRY_ACL_READ;
 
1777
                        break;
 
1778
                case 'w': case 'W':
 
1779
                        *permset |= ARCHIVE_ENTRY_ACL_WRITE;
 
1780
                        break;
 
1781
                case 'x': case 'X':
 
1782
                        *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
 
1783
                        break;
 
1784
                case '-':
 
1785
                        break;
 
1786
                default:
 
1787
                        return (0);
 
1788
                }
 
1789
        }
 
1790
        return (1);
 
1791
}
 
1792
 
 
1793
/*
 
1794
 * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
 
1795
 * to point to just after the separator.  *start points to the first
 
1796
 * character of the matched text and *end just after the last
 
1797
 * character of the matched identifier.  In particular *end - *start
 
1798
 * is the length of the field body, not including leading or trailing
 
1799
 * whitespace.
 
1800
 */
 
1801
static void
 
1802
next_field_w(const wchar_t **wp, const wchar_t **start,
 
1803
    const wchar_t **end, wchar_t *sep)
 
1804
{
 
1805
        /* Skip leading whitespace to find start of field. */
 
1806
        while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
 
1807
                (*wp)++;
 
1808
        }
 
1809
        *start = *wp;
 
1810
 
 
1811
        /* Scan for the separator. */
 
1812
        while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
 
1813
            **wp != L'\n') {
 
1814
                (*wp)++;
 
1815
        }
 
1816
        *sep = **wp;
 
1817
 
 
1818
        /* Trim trailing whitespace to locate end of field. */
 
1819
        *end = *wp - 1;
 
1820
        while (**end == L' ' || **end == L'\t' || **end == L'\n') {
 
1821
                (*end)--;
 
1822
        }
 
1823
        (*end)++;
 
1824
 
 
1825
        /* Adjust scanner location. */
 
1826
        if (**wp != L'\0')
 
1827
                (*wp)++;
 
1828
}
 
1829
 
 
1830
/*
 
1831
 * Return true if the characters [start...end) are a prefix of 'test'.
 
1832
 * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
 
1833
 */
 
1834
static int
 
1835
prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
 
1836
{
 
1837
        if (start == end)
 
1838
                return (0);
 
1839
 
 
1840
        if (*start++ != *test++)
 
1841
                return (0);
 
1842
 
 
1843
        while (start < end  &&  *start++ == *test++)
 
1844
                ;
 
1845
 
 
1846
        if (start < end)
 
1847
                return (0);
 
1848
 
 
1849
        return (1);
 
1850
}
 
1851
 
 
1852
 
 
1853
/*
 
1854
 * Following code is modified from UC Berkeley sources, and
 
1855
 * is subject to the following copyright notice.
 
1856
 */
 
1857
 
 
1858
/*-
 
1859
 * Copyright (c) 1993
 
1860
 *      The Regents of the University of California.  All rights reserved.
 
1861
 *
 
1862
 * Redistribution and use in source and binary forms, with or without
 
1863
 * modification, are permitted provided that the following conditions
 
1864
 * are met:
 
1865
 * 1. Redistributions of source code must retain the above copyright
 
1866
 *    notice, this list of conditions and the following disclaimer.
 
1867
 * 2. Redistributions in binary form must reproduce the above copyright
 
1868
 *    notice, this list of conditions and the following disclaimer in the
 
1869
 *    documentation and/or other materials provided with the distribution.
 
1870
 * 4. Neither the name of the University nor the names of its contributors
 
1871
 *    may be used to endorse or promote products derived from this software
 
1872
 *    without specific prior written permission.
 
1873
 *
 
1874
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 
1875
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
1876
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
1877
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 
1878
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
1879
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 
1880
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
1881
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
1882
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 
1883
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
1884
 * SUCH DAMAGE.
 
1885
 */
 
1886
 
 
1887
static struct flag {
 
1888
        const char      *name;
 
1889
        const wchar_t   *wname;
 
1890
        unsigned long    set;
 
1891
        unsigned long    clear;
 
1892
} flags[] = {
 
1893
        /* Preferred (shorter) names per flag first, all prefixed by "no" */
 
1894
#ifdef SF_APPEND
 
1895
        { "nosappnd",   L"nosappnd",            SF_APPEND,      0 },
 
1896
        { "nosappend",  L"nosappend",           SF_APPEND,      0 },
 
1897
#endif
 
1898
#ifdef  EXT2_APPEND_FL                          /* 'a' */
 
1899
        { "nosappnd",   L"nosappnd",            EXT2_APPEND_FL, 0 },
 
1900
        { "nosappend",  L"nosappend",           EXT2_APPEND_FL, 0 },
 
1901
#endif
 
1902
#ifdef SF_ARCHIVED
 
1903
        { "noarch",     L"noarch",              SF_ARCHIVED,    0 },
 
1904
        { "noarchived", L"noarchived",          SF_ARCHIVED,    0 },
 
1905
#endif
 
1906
#ifdef SF_IMMUTABLE
 
1907
        { "noschg",     L"noschg",              SF_IMMUTABLE,   0 },
 
1908
        { "noschange",  L"noschange",           SF_IMMUTABLE,   0 },
 
1909
        { "nosimmutable",       L"nosimmutable",        SF_IMMUTABLE,   0 },
 
1910
#endif
 
1911
#ifdef EXT2_IMMUTABLE_FL                        /* 'i' */
 
1912
        { "noschg",     L"noschg",              EXT2_IMMUTABLE_FL,      0 },
 
1913
        { "noschange",  L"noschange",           EXT2_IMMUTABLE_FL,      0 },
 
1914
        { "nosimmutable",       L"nosimmutable",        EXT2_IMMUTABLE_FL,      0 },
 
1915
#endif
 
1916
#ifdef SF_NOUNLINK
 
1917
        { "nosunlnk",   L"nosunlnk",            SF_NOUNLINK,    0 },
 
1918
        { "nosunlink",  L"nosunlink",           SF_NOUNLINK,    0 },
 
1919
#endif
 
1920
#ifdef SF_SNAPSHOT
 
1921
        { "nosnapshot", L"nosnapshot",  SF_SNAPSHOT,    0 },
 
1922
#endif
 
1923
#ifdef UF_APPEND
 
1924
        { "nouappnd",   L"nouappnd",            UF_APPEND,      0 },
 
1925
        { "nouappend",  L"nouappend",           UF_APPEND,      0 },
 
1926
#endif
 
1927
#ifdef UF_IMMUTABLE
 
1928
        { "nouchg",     L"nouchg",              UF_IMMUTABLE,   0 },
 
1929
        { "nouchange",  L"nouchange",           UF_IMMUTABLE,   0 },
 
1930
        { "nouimmutable",       L"nouimmutable",        UF_IMMUTABLE,   0 },
 
1931
#endif
 
1932
#ifdef UF_NODUMP
 
1933
        { "nodump",     L"nodump",              0,              UF_NODUMP},
 
1934
#endif
 
1935
#ifdef EXT2_NODUMP_FL                           /* 'd' */
 
1936
        { "nodump",     L"nodump",              0,              EXT2_NODUMP_FL},
 
1937
#endif
 
1938
#ifdef UF_OPAQUE
 
1939
        { "noopaque",   L"noopaque",            UF_OPAQUE,      0 },
 
1940
#endif
 
1941
#ifdef UF_NOUNLINK
 
1942
        { "nouunlnk",   L"nouunlnk",            UF_NOUNLINK,    0 },
 
1943
        { "nouunlink",  L"nouunlink",           UF_NOUNLINK,    0 },
 
1944
#endif
 
1945
#ifdef EXT2_UNRM_FL
 
1946
        { "nouunlink",  L"nouunlink",           EXT2_UNRM_FL,   0},
 
1947
#endif
 
1948
 
 
1949
#ifdef EXT2_BTREE_FL
 
1950
        { "nobtree",    L"nobtree",             EXT2_BTREE_FL,  0 },
 
1951
#endif
 
1952
 
 
1953
#ifdef EXT2_ECOMPR_FL
 
1954
        { "nocomperr",  L"nocomperr",           EXT2_ECOMPR_FL, 0 },
 
1955
#endif
 
1956
 
 
1957
#ifdef EXT2_COMPR_FL                            /* 'c' */
 
1958
        { "nocompress", L"nocompress",          EXT2_COMPR_FL,  0 },
 
1959
#endif
 
1960
 
 
1961
#ifdef EXT2_NOATIME_FL                          /* 'A' */
 
1962
        { "noatime",    L"noatime",             0,              EXT2_NOATIME_FL},
 
1963
#endif
 
1964
 
 
1965
#ifdef EXT2_DIRTY_FL
 
1966
        { "nocompdirty",L"nocompdirty",         EXT2_DIRTY_FL,          0},
 
1967
#endif
 
1968
 
 
1969
#ifdef EXT2_COMPRBLK_FL
 
1970
#ifdef EXT2_NOCOMPR_FL
 
1971
        { "nocomprblk", L"nocomprblk",          EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL},
 
1972
#else
 
1973
        { "nocomprblk", L"nocomprblk",          EXT2_COMPRBLK_FL,       0},
 
1974
#endif
 
1975
#endif
 
1976
#ifdef EXT2_DIRSYNC_FL
 
1977
        { "nodirsync",  L"nodirsync",           EXT2_DIRSYNC_FL,        0},
 
1978
#endif
 
1979
#ifdef EXT2_INDEX_FL
 
1980
        { "nohashidx",  L"nohashidx",           EXT2_INDEX_FL,          0},
 
1981
#endif
 
1982
#ifdef EXT2_IMAGIC_FL
 
1983
        { "noimagic",   L"noimagic",            EXT2_IMAGIC_FL,         0},
 
1984
#endif
 
1985
#ifdef EXT3_JOURNAL_DATA_FL
 
1986
        { "nojournal",  L"nojournal",           EXT3_JOURNAL_DATA_FL,   0},
 
1987
#endif
 
1988
#ifdef EXT2_SECRM_FL
 
1989
        { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL,         0},
 
1990
#endif
 
1991
#ifdef EXT2_SYNC_FL
 
1992
        { "nosync",     L"nosync",              EXT2_SYNC_FL,           0},
 
1993
#endif
 
1994
#ifdef EXT2_NOTAIL_FL
 
1995
        { "notail",     L"notail",              0,              EXT2_NOTAIL_FL},
 
1996
#endif
 
1997
#ifdef EXT2_TOPDIR_FL
 
1998
        { "notopdir",   L"notopdir",            EXT2_TOPDIR_FL,         0},
 
1999
#endif
 
2000
#ifdef EXT2_RESERVED_FL
 
2001
        { "noreserved", L"noreserved",          EXT2_RESERVED_FL,       0},
 
2002
#endif
 
2003
 
 
2004
        { NULL,         NULL,                   0,              0 }
 
2005
};
 
2006
 
 
2007
/*
 
2008
 * fflagstostr --
 
2009
 *      Convert file flags to a comma-separated string.  If no flags
 
2010
 *      are set, return the empty string.
 
2011
 */
 
2012
static char *
 
2013
ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
 
2014
{
 
2015
        char *string, *dp;
 
2016
        const char *sp;
 
2017
        unsigned long bits;
 
2018
        struct flag *flag;
 
2019
        size_t  length;
 
2020
 
 
2021
        bits = bitset | bitclear;
 
2022
        length = 0;
 
2023
        for (flag = flags; flag->name != NULL; flag++)
 
2024
                if (bits & (flag->set | flag->clear)) {
 
2025
                        length += strlen(flag->name) + 1;
 
2026
                        bits &= ~(flag->set | flag->clear);
 
2027
                }
 
2028
 
 
2029
        if (length == 0)
 
2030
                return (NULL);
 
2031
        string = (char *)malloc(length);
 
2032
        if (string == NULL)
 
2033
                return (NULL);
 
2034
 
 
2035
        dp = string;
 
2036
        for (flag = flags; flag->name != NULL; flag++) {
 
2037
                if (bitset & flag->set || bitclear & flag->clear) {
 
2038
                        sp = flag->name + 2;
 
2039
                } else if (bitset & flag->clear  ||  bitclear & flag->set) {
 
2040
                        sp = flag->name;
 
2041
                } else
 
2042
                        continue;
 
2043
                bitset &= ~(flag->set | flag->clear);
 
2044
                bitclear &= ~(flag->set | flag->clear);
 
2045
                if (dp > string)
 
2046
                        *dp++ = ',';
 
2047
                while ((*dp++ = *sp++) != '\0')
 
2048
                        ;
 
2049
                dp--;
 
2050
        }
 
2051
 
 
2052
        *dp = '\0';
 
2053
        return (string);
 
2054
}
 
2055
 
 
2056
/*
 
2057
 * strtofflags --
 
2058
 *      Take string of arguments and return file flags.  This
 
2059
 *      version works a little differently than strtofflags(3).
 
2060
 *      In particular, it always tests every token, skipping any
 
2061
 *      unrecognized tokens.  It returns a pointer to the first
 
2062
 *      unrecognized token, or NULL if every token was recognized.
 
2063
 *      This version is also const-correct and does not modify the
 
2064
 *      provided string.
 
2065
 */
 
2066
static const char *
 
2067
ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)
 
2068
{
 
2069
        const char *start, *end;
 
2070
        struct flag *flag;
 
2071
        unsigned long set, clear;
 
2072
        const char *failed;
 
2073
 
 
2074
        set = clear = 0;
 
2075
        start = s;
 
2076
        failed = NULL;
 
2077
        /* Find start of first token. */
 
2078
        while (*start == '\t'  ||  *start == ' '  ||  *start == ',')
 
2079
                start++;
 
2080
        while (*start != '\0') {
 
2081
                /* Locate end of token. */
 
2082
                end = start;
 
2083
                while (*end != '\0'  &&  *end != '\t'  &&
 
2084
                    *end != ' '  &&  *end != ',')
 
2085
                        end++;
 
2086
                for (flag = flags; flag->name != NULL; flag++) {
 
2087
                        if (memcmp(start, flag->name, end - start) == 0) {
 
2088
                                /* Matched "noXXXX", so reverse the sense. */
 
2089
                                clear |= flag->set;
 
2090
                                set |= flag->clear;
 
2091
                                break;
 
2092
                        } else if (memcmp(start, flag->name + 2, end - start)
 
2093
                            == 0) {
 
2094
                                /* Matched "XXXX", so don't reverse. */
 
2095
                                set |= flag->set;
 
2096
                                clear |= flag->clear;
 
2097
                                break;
 
2098
                        }
 
2099
                }
 
2100
                /* Ignore unknown flag names. */
 
2101
                if (flag->name == NULL  &&  failed == NULL)
 
2102
                        failed = start;
 
2103
 
 
2104
                /* Find start of next token. */
 
2105
                start = end;
 
2106
                while (*start == '\t'  ||  *start == ' '  ||  *start == ',')
 
2107
                        start++;
 
2108
 
 
2109
        }
 
2110
 
 
2111
        if (setp)
 
2112
                *setp = set;
 
2113
        if (clrp)
 
2114
                *clrp = clear;
 
2115
 
 
2116
        /* Return location of first failure. */
 
2117
        return (failed);
 
2118
}
 
2119
 
 
2120
/*
 
2121
 * wcstofflags --
 
2122
 *      Take string of arguments and return file flags.  This
 
2123
 *      version works a little differently than strtofflags(3).
 
2124
 *      In particular, it always tests every token, skipping any
 
2125
 *      unrecognized tokens.  It returns a pointer to the first
 
2126
 *      unrecognized token, or NULL if every token was recognized.
 
2127
 *      This version is also const-correct and does not modify the
 
2128
 *      provided string.
 
2129
 */
 
2130
static const wchar_t *
 
2131
ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
 
2132
{
 
2133
        const wchar_t *start, *end;
 
2134
        struct flag *flag;
 
2135
        unsigned long set, clear;
 
2136
        const wchar_t *failed;
 
2137
 
 
2138
        set = clear = 0;
 
2139
        start = s;
 
2140
        failed = NULL;
 
2141
        /* Find start of first token. */
 
2142
        while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')
 
2143
                start++;
 
2144
        while (*start != L'\0') {
 
2145
                /* Locate end of token. */
 
2146
                end = start;
 
2147
                while (*end != L'\0'  &&  *end != L'\t'  &&
 
2148
                    *end != L' '  &&  *end != L',')
 
2149
                        end++;
 
2150
                for (flag = flags; flag->wname != NULL; flag++) {
 
2151
                        if (wmemcmp(start, flag->wname, end - start) == 0) {
 
2152
                                /* Matched "noXXXX", so reverse the sense. */
 
2153
                                clear |= flag->set;
 
2154
                                set |= flag->clear;
 
2155
                                break;
 
2156
                        } else if (wmemcmp(start, flag->wname + 2, end - start)
 
2157
                            == 0) {
 
2158
                                /* Matched "XXXX", so don't reverse. */
 
2159
                                set |= flag->set;
 
2160
                                clear |= flag->clear;
 
2161
                                break;
 
2162
                        }
 
2163
                }
 
2164
                /* Ignore unknown flag names. */
 
2165
                if (flag->wname == NULL  &&  failed == NULL)
 
2166
                        failed = start;
 
2167
 
 
2168
                /* Find start of next token. */
 
2169
                start = end;
 
2170
                while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')
 
2171
                        start++;
 
2172
 
 
2173
        }
 
2174
 
 
2175
        if (setp)
 
2176
                *setp = set;
 
2177
        if (clrp)
 
2178
                *clrp = clear;
 
2179
 
 
2180
        /* Return location of first failure. */
 
2181
        return (failed);
 
2182
}
 
2183
 
 
2184
 
 
2185
#ifdef TEST
 
2186
#include <stdio.h>
 
2187
int
 
2188
main(int argc, char **argv)
 
2189
{
 
2190
        struct archive_entry *entry = archive_entry_new();
 
2191
        unsigned long set, clear;
 
2192
        const wchar_t *remainder;
 
2193
 
 
2194
        remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,");
 
2195
        archive_entry_fflags(entry, &set, &clear);
 
2196
 
 
2197
        wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder);
 
2198
 
 
2199
        wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry));
 
2200
        return (0);
 
2201
}
 
2202
#endif