1231200Smm/*-
2232153Smm * Copyright (c) 2010-2012 Michihiro NAKAJIMA
3231200Smm * All rights reserved.
4231200Smm *
5231200Smm * Redistribution and use in source and binary forms, with or without
6231200Smm * modification, are permitted provided that the following conditions
7231200Smm * are met:
8231200Smm * 1. Redistributions of source code must retain the above copyright
9231200Smm *    notice, this list of conditions and the following disclaimer.
10231200Smm * 2. Redistributions in binary form must reproduce the above copyright
11231200Smm *    notice, this list of conditions and the following disclaimer in the
12231200Smm *    documentation and/or other materials provided with the distribution.
13231200Smm *
14231200Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15231200Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16231200Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17231200Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18231200Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19231200Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20231200Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21231200Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22231200Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23231200Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24231200Smm */
25231200Smm
26231200Smm#include "archive_platform.h"
27231200Smm__FBSDID("$FreeBSD$");
28231200Smm
29231200Smm#ifdef HAVE_ERRNO_H
30231200Smm#include <errno.h>
31231200Smm#endif
32231200Smm#ifdef HAVE_LIMITS_H
33231200Smm#include <limits.h>
34231200Smm#endif
35231200Smm#include <stdlib.h>
36231200Smm#if HAVE_LIBXML_XMLWRITER_H
37231200Smm#include <libxml/xmlwriter.h>
38231200Smm#endif
39231200Smm#ifdef HAVE_BZLIB_H
40231200Smm#include <bzlib.h>
41231200Smm#endif
42231200Smm#if HAVE_LZMA_H
43231200Smm#include <lzma.h>
44231200Smm#endif
45231200Smm#ifdef HAVE_ZLIB_H
46231200Smm#include <zlib.h>
47231200Smm#endif
48231200Smm
49231200Smm#include "archive.h"
50302001Smm#include "archive_digest_private.h"
51231200Smm#include "archive_endian.h"
52231200Smm#include "archive_entry.h"
53231200Smm#include "archive_entry_locale.h"
54231200Smm#include "archive_private.h"
55231200Smm#include "archive_rb.h"
56231200Smm#include "archive_string.h"
57231200Smm#include "archive_write_private.h"
58231200Smm
59231200Smm/*
60231200Smm * Differences to xar utility.
61231200Smm * - Subdocument is not supported yet.
62231200Smm * - ACL is not supported yet.
63231200Smm * - When writing an XML element <link type="<file-type>">, <file-type>
64231200Smm *   which is a file type a symbolic link is referencing is always marked
65231200Smm *   as "broken". Xar utility uses stat(2) to get the file type, but, in
66313571Smm *   libarchive format writer, we should not use it; if it is needed, we
67231200Smm *   should get about it at archive_read_disk.c.
68231200Smm * - It is possible to appear both <flags> and <ext2> elements.
69231200Smm *   Xar utility generates <flags> on BSD platform and <ext2> on Linux
70231200Smm *   platform.
71231200Smm *
72231200Smm */
73231200Smm
74231200Smm#if !(defined(HAVE_LIBXML_XMLWRITER_H) && defined(LIBXML_VERSION) &&\
75231200Smm	LIBXML_VERSION >= 20703) ||\
76231200Smm	!defined(HAVE_ZLIB_H) || \
77231200Smm	!defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
78231200Smm/*
79231200Smm * xar needs several external libraries.
80231200Smm *   o libxml2
81231200Smm *   o openssl or MD5/SHA1 hash function
82231200Smm *   o zlib
83231200Smm *   o bzlib2 (option)
84231200Smm *   o liblzma (option)
85231200Smm */
86231200Smmint
87231200Smmarchive_write_set_format_xar(struct archive *_a)
88231200Smm{
89231200Smm	struct archive_write *a = (struct archive_write *)_a;
90231200Smm
91231200Smm	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
92231200Smm	    "Xar not supported on this platform");
93231200Smm	return (ARCHIVE_WARN);
94231200Smm}
95231200Smm
96231200Smm#else	/* Support xar format */
97231200Smm
98231200Smm/*#define DEBUG_PRINT_TOC		1 */
99231200Smm
100232153Smm#define BAD_CAST_CONST (const xmlChar *)
101232153Smm
102231200Smm#define HEADER_MAGIC	0x78617221
103231200Smm#define HEADER_SIZE	28
104231200Smm#define HEADER_VERSION	1
105231200Smm
106231200Smmenum sumalg {
107231200Smm	CKSUM_NONE = 0,
108231200Smm	CKSUM_SHA1 = 1,
109231200Smm	CKSUM_MD5 = 2
110231200Smm};
111231200Smm
112231200Smm#define MD5_SIZE	16
113231200Smm#define SHA1_SIZE	20
114231200Smm#define MAX_SUM_SIZE	20
115231200Smm#define MD5_NAME	"md5"
116231200Smm#define SHA1_NAME	"sha1"
117302001Smm
118231200Smmenum enctype {
119231200Smm	NONE,
120231200Smm	GZIP,
121231200Smm	BZIP2,
122231200Smm	LZMA,
123231200Smm	XZ,
124231200Smm};
125231200Smm
126231200Smmstruct chksumwork {
127231200Smm	enum sumalg		 alg;
128231200Smm#ifdef ARCHIVE_HAS_MD5
129231200Smm	archive_md5_ctx		 md5ctx;
130231200Smm#endif
131231200Smm#ifdef ARCHIVE_HAS_SHA1
132231200Smm	archive_sha1_ctx	 sha1ctx;
133231200Smm#endif
134231200Smm};
135231200Smm
136231200Smmenum la_zaction {
137231200Smm	ARCHIVE_Z_FINISH,
138231200Smm	ARCHIVE_Z_RUN
139231200Smm};
140231200Smm
141231200Smm/*
142231200Smm * Universal zstream.
143231200Smm */
144231200Smmstruct la_zstream {
145231200Smm	const unsigned char	*next_in;
146231200Smm	size_t			 avail_in;
147231200Smm	uint64_t		 total_in;
148231200Smm
149231200Smm	unsigned char		*next_out;
150231200Smm	size_t			 avail_out;
151231200Smm	uint64_t		 total_out;
152231200Smm
153231200Smm	int			 valid;
154231200Smm	void			*real_stream;
155231200Smm	int			 (*code) (struct archive *a,
156231200Smm				    struct la_zstream *lastrm,
157231200Smm				    enum la_zaction action);
158231200Smm	int			 (*end)(struct archive *a,
159231200Smm				    struct la_zstream *lastrm);
160231200Smm};
161231200Smm
162231200Smmstruct chksumval {
163231200Smm	enum sumalg		 alg;
164231200Smm	size_t			 len;
165231200Smm	unsigned char		 val[MAX_SUM_SIZE];
166231200Smm};
167231200Smm
168231200Smmstruct heap_data {
169231200Smm	int			 id;
170231200Smm	struct heap_data	*next;
171231200Smm	uint64_t		 temp_offset;
172231200Smm	uint64_t		 length;	/* archived size.	*/
173231200Smm	uint64_t		 size;		/* extracted size.	*/
174231200Smm	enum enctype		 compression;
175231200Smm	struct chksumval	 a_sum;		/* archived checksum.	*/
176231200Smm	struct chksumval	 e_sum;		/* extracted checksum.	*/
177231200Smm};
178231200Smm
179231200Smmstruct file {
180231200Smm	struct archive_rb_node	 rbnode;
181231200Smm
182231200Smm	int			 id;
183231200Smm	struct archive_entry	*entry;
184231200Smm
185231200Smm	struct archive_rb_tree	 rbtree;
186231200Smm	struct file		*next;
187231200Smm	struct file		*chnext;
188231200Smm	struct file		*hlnext;
189231200Smm	/* For hardlinked files.
190231200Smm	 * Use only when archive_entry_nlink() > 1 */
191231200Smm	struct file		*hardlink_target;
192231200Smm	struct file		*parent;	/* parent directory entry */
193231200Smm	/*
194231200Smm	 * To manage sub directory files.
195311042Smm	 * We use 'chnext' (a member of struct file) to chain.
196231200Smm	 */
197231200Smm	struct {
198231200Smm		struct file	*first;
199231200Smm		struct file	**last;
200231200Smm	}			 children;
201231200Smm
202231200Smm	/* For making a directory tree. */
203231200Smm        struct archive_string    parentdir;
204231200Smm        struct archive_string    basename;
205231200Smm        struct archive_string    symlink;
206231200Smm
207231200Smm	int			 ea_idx;
208231200Smm	struct {
209231200Smm		struct heap_data *first;
210231200Smm		struct heap_data **last;
211231200Smm	}			 xattr;
212231200Smm	struct heap_data	 data;
213231200Smm        struct archive_string    script;
214231200Smm
215358090Smm	signed int		 virtual:1;
216358090Smm	signed int		 dir:1;
217231200Smm};
218231200Smm
219231200Smmstruct hardlink {
220231200Smm	struct archive_rb_node	 rbnode;
221231200Smm	int			 nlink;
222231200Smm	struct {
223231200Smm		struct file	*first;
224231200Smm		struct file	**last;
225231200Smm	}			 file_list;
226231200Smm};
227231200Smm
228231200Smmstruct xar {
229231200Smm	int			 temp_fd;
230231200Smm	uint64_t		 temp_offset;
231231200Smm
232231200Smm	int			 file_idx;
233231200Smm	struct file		*root;
234231200Smm	struct file		*cur_dirent;
235231200Smm	struct archive_string	 cur_dirstr;
236231200Smm	struct file		*cur_file;
237231200Smm	uint64_t		 bytes_remaining;
238231200Smm	struct archive_string	 tstr;
239231200Smm	struct archive_string	 vstr;
240231200Smm
241231200Smm	enum sumalg		 opt_toc_sumalg;
242231200Smm	enum sumalg		 opt_sumalg;
243231200Smm	enum enctype		 opt_compression;
244231200Smm	int			 opt_compression_level;
245302001Smm	uint32_t		 opt_threads;
246231200Smm
247231200Smm	struct chksumwork	 a_sumwrk;	/* archived checksum.	*/
248231200Smm	struct chksumwork	 e_sumwrk;	/* extracted checksum.	*/
249231200Smm	struct la_zstream	 stream;
250231200Smm	struct archive_string_conv *sconv;
251231200Smm	/*
252231200Smm	 * Compressed data buffer.
253231200Smm	 */
254231200Smm	unsigned char		 wbuff[1024 * 64];
255231200Smm	size_t			 wbuff_remaining;
256231200Smm
257231200Smm	struct heap_data	 toc;
258231200Smm	/*
259231200Smm	 * The list of all file entries is used to manage struct file
260231200Smm	 * objects.
261311042Smm	 * We use 'next' (a member of struct file) to chain.
262231200Smm	 */
263231200Smm	struct {
264231200Smm		struct file	*first;
265231200Smm		struct file	**last;
266231200Smm	}			 file_list;
267231200Smm	/*
268231200Smm	 * The list of hard-linked file entries.
269311042Smm	 * We use 'hlnext' (a member of struct file) to chain.
270231200Smm	 */
271231200Smm	struct archive_rb_tree	 hardlink_rbtree;
272231200Smm};
273231200Smm
274231200Smmstatic int	xar_options(struct archive_write *,
275231200Smm		    const char *, const char *);
276231200Smmstatic int	xar_write_header(struct archive_write *,
277231200Smm		    struct archive_entry *);
278231200Smmstatic ssize_t	xar_write_data(struct archive_write *,
279231200Smm		    const void *, size_t);
280231200Smmstatic int	xar_finish_entry(struct archive_write *);
281231200Smmstatic int	xar_close(struct archive_write *);
282231200Smmstatic int	xar_free(struct archive_write *);
283231200Smm
284231200Smmstatic struct file *file_new(struct archive_write *a, struct archive_entry *);
285231200Smmstatic void	file_free(struct file *);
286231200Smmstatic struct file *file_create_virtual_dir(struct archive_write *a, struct xar *,
287231200Smm		    const char *);
288231200Smmstatic int	file_add_child_tail(struct file *, struct file *);
289231200Smmstatic struct file *file_find_child(struct file *, const char *);
290231200Smmstatic int	file_gen_utility_names(struct archive_write *,
291231200Smm		    struct file *);
292231200Smmstatic int	get_path_component(char *, int, const char *);
293231200Smmstatic int	file_tree(struct archive_write *, struct file **);
294231200Smmstatic void	file_register(struct xar *, struct file *);
295231200Smmstatic void	file_init_register(struct xar *);
296231200Smmstatic void	file_free_register(struct xar *);
297231200Smmstatic int	file_register_hardlink(struct archive_write *,
298231200Smm		    struct file *);
299231200Smmstatic void	file_connect_hardlink_files(struct xar *);
300231200Smmstatic void	file_init_hardlinks(struct xar *);
301231200Smmstatic void	file_free_hardlinks(struct xar *);
302231200Smm
303231200Smmstatic void	checksum_init(struct chksumwork *, enum sumalg);
304231200Smmstatic void	checksum_update(struct chksumwork *, const void *, size_t);
305231200Smmstatic void	checksum_final(struct chksumwork *, struct chksumval *);
306231200Smmstatic int	compression_init_encoder_gzip(struct archive *,
307231200Smm		    struct la_zstream *, int, int);
308231200Smmstatic int	compression_code_gzip(struct archive *,
309231200Smm		    struct la_zstream *, enum la_zaction);
310231200Smmstatic int	compression_end_gzip(struct archive *, struct la_zstream *);
311231200Smmstatic int	compression_init_encoder_bzip2(struct archive *,
312231200Smm		    struct la_zstream *, int);
313231200Smm#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
314231200Smmstatic int	compression_code_bzip2(struct archive *,
315231200Smm		    struct la_zstream *, enum la_zaction);
316231200Smmstatic int	compression_end_bzip2(struct archive *, struct la_zstream *);
317231200Smm#endif
318231200Smmstatic int	compression_init_encoder_lzma(struct archive *,
319231200Smm		    struct la_zstream *, int);
320231200Smmstatic int	compression_init_encoder_xz(struct archive *,
321302001Smm		    struct la_zstream *, int, int);
322231200Smm#if defined(HAVE_LZMA_H)
323231200Smmstatic int	compression_code_lzma(struct archive *,
324231200Smm		    struct la_zstream *, enum la_zaction);
325231200Smmstatic int	compression_end_lzma(struct archive *, struct la_zstream *);
326231200Smm#endif
327231200Smmstatic int	xar_compression_init_encoder(struct archive_write *);
328231200Smmstatic int	compression_code(struct archive *,
329231200Smm		    struct la_zstream *, enum la_zaction);
330231200Smmstatic int	compression_end(struct archive *,
331231200Smm		    struct la_zstream *);
332231200Smmstatic int	save_xattrs(struct archive_write *, struct file *);
333231200Smmstatic int	getalgsize(enum sumalg);
334231200Smmstatic const char *getalgname(enum sumalg);
335231200Smm
336231200Smmint
337231200Smmarchive_write_set_format_xar(struct archive *_a)
338231200Smm{
339231200Smm	struct archive_write *a = (struct archive_write *)_a;
340231200Smm	struct xar *xar;
341231200Smm
342231200Smm	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
343231200Smm	    ARCHIVE_STATE_NEW, "archive_write_set_format_xar");
344231200Smm
345231200Smm	/* If another format was already registered, unregister it. */
346231200Smm	if (a->format_free != NULL)
347231200Smm		(a->format_free)(a);
348231200Smm
349231200Smm	xar = calloc(1, sizeof(*xar));
350231200Smm	if (xar == NULL) {
351231200Smm		archive_set_error(&a->archive, ENOMEM,
352231200Smm		    "Can't allocate xar data");
353231200Smm		return (ARCHIVE_FATAL);
354231200Smm	}
355231200Smm	xar->temp_fd = -1;
356231200Smm	file_init_register(xar);
357231200Smm	file_init_hardlinks(xar);
358231200Smm	archive_string_init(&(xar->tstr));
359231200Smm	archive_string_init(&(xar->vstr));
360231200Smm
361231200Smm	/*
362231200Smm	 * Create the root directory.
363231200Smm	 */
364231200Smm	xar->root = file_create_virtual_dir(a, xar, "");
365231200Smm	if (xar->root == NULL) {
366231200Smm		free(xar);
367231200Smm		archive_set_error(&a->archive, ENOMEM,
368231200Smm		    "Can't allocate xar data");
369231200Smm		return (ARCHIVE_FATAL);
370231200Smm	}
371231200Smm	xar->root->parent = xar->root;
372231200Smm	file_register(xar, xar->root);
373231200Smm	xar->cur_dirent = xar->root;
374231200Smm	archive_string_init(&(xar->cur_dirstr));
375231200Smm	archive_string_ensure(&(xar->cur_dirstr), 1);
376231200Smm	xar->cur_dirstr.s[0] = 0;
377231200Smm
378231200Smm	/*
379231200Smm	 * Initialize option.
380231200Smm	 */
381231200Smm	/* Set default checksum type. */
382231200Smm	xar->opt_toc_sumalg = CKSUM_SHA1;
383231200Smm	xar->opt_sumalg = CKSUM_SHA1;
384302001Smm	/* Set default compression type, level, and number of threads. */
385231200Smm	xar->opt_compression = GZIP;
386231200Smm	xar->opt_compression_level = 6;
387302001Smm	xar->opt_threads = 1;
388231200Smm
389231200Smm	a->format_data = xar;
390231200Smm
391231200Smm	a->format_name = "xar";
392231200Smm	a->format_options = xar_options;
393231200Smm	a->format_write_header = xar_write_header;
394231200Smm	a->format_write_data = xar_write_data;
395231200Smm	a->format_finish_entry = xar_finish_entry;
396231200Smm	a->format_close = xar_close;
397231200Smm	a->format_free = xar_free;
398231200Smm	a->archive.archive_format = ARCHIVE_FORMAT_XAR;
399231200Smm	a->archive.archive_format_name = "xar";
400231200Smm
401231200Smm	return (ARCHIVE_OK);
402231200Smm}
403231200Smm
404231200Smmstatic int
405231200Smmxar_options(struct archive_write *a, const char *key, const char *value)
406231200Smm{
407231200Smm	struct xar *xar;
408231200Smm
409231200Smm	xar = (struct xar *)a->format_data;
410231200Smm
411231200Smm	if (strcmp(key, "checksum") == 0) {
412231200Smm		if (value == NULL)
413231200Smm			xar->opt_sumalg = CKSUM_NONE;
414358090Smm		else if (strcmp(value, "none") == 0)
415358090Smm			xar->opt_sumalg = CKSUM_NONE;
416231200Smm		else if (strcmp(value, "sha1") == 0)
417231200Smm			xar->opt_sumalg = CKSUM_SHA1;
418231200Smm		else if (strcmp(value, "md5") == 0)
419231200Smm			xar->opt_sumalg = CKSUM_MD5;
420231200Smm		else {
421231200Smm			archive_set_error(&(a->archive),
422231200Smm			    ARCHIVE_ERRNO_MISC,
423248616Smm			    "Unknown checksum name: `%s'",
424231200Smm			    value);
425231200Smm			return (ARCHIVE_FAILED);
426231200Smm		}
427231200Smm		return (ARCHIVE_OK);
428231200Smm	}
429231200Smm	if (strcmp(key, "compression") == 0) {
430231200Smm		const char *name = NULL;
431231200Smm
432231200Smm		if (value == NULL)
433231200Smm			xar->opt_compression = NONE;
434358090Smm		else if (strcmp(value, "none") == 0)
435358090Smm			xar->opt_compression = NONE;
436231200Smm		else if (strcmp(value, "gzip") == 0)
437231200Smm			xar->opt_compression = GZIP;
438231200Smm		else if (strcmp(value, "bzip2") == 0)
439231200Smm#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
440231200Smm			xar->opt_compression = BZIP2;
441231200Smm#else
442231200Smm			name = "bzip2";
443231200Smm#endif
444231200Smm		else if (strcmp(value, "lzma") == 0)
445231200Smm#if HAVE_LZMA_H
446231200Smm			xar->opt_compression = LZMA;
447231200Smm#else
448231200Smm			name = "lzma";
449231200Smm#endif
450231200Smm		else if (strcmp(value, "xz") == 0)
451231200Smm#if HAVE_LZMA_H
452231200Smm			xar->opt_compression = XZ;
453231200Smm#else
454231200Smm			name = "xz";
455231200Smm#endif
456231200Smm		else {
457231200Smm			archive_set_error(&(a->archive),
458231200Smm			    ARCHIVE_ERRNO_MISC,
459248616Smm			    "Unknown compression name: `%s'",
460231200Smm			    value);
461231200Smm			return (ARCHIVE_FAILED);
462231200Smm		}
463231200Smm		if (name != NULL) {
464231200Smm			archive_set_error(&(a->archive),
465231200Smm			    ARCHIVE_ERRNO_MISC,
466231200Smm			    "`%s' compression not supported "
467231200Smm			    "on this platform",
468231200Smm			    name);
469231200Smm			return (ARCHIVE_FAILED);
470231200Smm		}
471231200Smm		return (ARCHIVE_OK);
472231200Smm	}
473231200Smm	if (strcmp(key, "compression-level") == 0) {
474231200Smm		if (value == NULL ||
475231200Smm		    !(value[0] >= '0' && value[0] <= '9') ||
476231200Smm		    value[1] != '\0') {
477231200Smm			archive_set_error(&(a->archive),
478231200Smm			    ARCHIVE_ERRNO_MISC,
479248616Smm			    "Illegal value `%s'",
480231200Smm			    value);
481231200Smm			return (ARCHIVE_FAILED);
482231200Smm		}
483231200Smm		xar->opt_compression_level = value[0] - '0';
484231200Smm		return (ARCHIVE_OK);
485231200Smm	}
486231200Smm	if (strcmp(key, "toc-checksum") == 0) {
487231200Smm		if (value == NULL)
488231200Smm			xar->opt_toc_sumalg = CKSUM_NONE;
489358090Smm		else if (strcmp(value, "none") == 0)
490358090Smm			xar->opt_toc_sumalg = CKSUM_NONE;
491231200Smm		else if (strcmp(value, "sha1") == 0)
492231200Smm			xar->opt_toc_sumalg = CKSUM_SHA1;
493231200Smm		else if (strcmp(value, "md5") == 0)
494231200Smm			xar->opt_toc_sumalg = CKSUM_MD5;
495231200Smm		else {
496231200Smm			archive_set_error(&(a->archive),
497231200Smm			    ARCHIVE_ERRNO_MISC,
498248616Smm			    "Unknown checksum name: `%s'",
499231200Smm			    value);
500231200Smm			return (ARCHIVE_FAILED);
501231200Smm		}
502231200Smm		return (ARCHIVE_OK);
503231200Smm	}
504302001Smm	if (strcmp(key, "threads") == 0) {
505348608Smm		char *endptr;
506348608Smm
507302001Smm		if (value == NULL)
508302001Smm			return (ARCHIVE_FAILED);
509348608Smm		errno = 0;
510348608Smm		xar->opt_threads = (int)strtoul(value, &endptr, 10);
511348608Smm		if (errno != 0 || *endptr != '\0') {
512302001Smm			xar->opt_threads = 1;
513302001Smm			archive_set_error(&(a->archive),
514302001Smm			    ARCHIVE_ERRNO_MISC,
515302001Smm			    "Illegal value `%s'",
516302001Smm			    value);
517302001Smm			return (ARCHIVE_FAILED);
518302001Smm		}
519302001Smm		if (xar->opt_threads == 0) {
520302001Smm#ifdef HAVE_LZMA_STREAM_ENCODER_MT
521302001Smm			xar->opt_threads = lzma_cputhreads();
522302001Smm#else
523302001Smm			xar->opt_threads = 1;
524302001Smm#endif
525302001Smm		}
526302001Smm	}
527231200Smm
528232153Smm	/* Note: The "warn" return is just to inform the options
529232153Smm	 * supervisor that we didn't handle it.  It will generate
530232153Smm	 * a suitable error if no one used this option. */
531232153Smm	return (ARCHIVE_WARN);
532231200Smm}
533231200Smm
534231200Smmstatic int
535231200Smmxar_write_header(struct archive_write *a, struct archive_entry *entry)
536231200Smm{
537231200Smm	struct xar *xar;
538231200Smm	struct file *file;
539231200Smm	struct archive_entry *file_entry;
540231200Smm	int r, r2;
541231200Smm
542231200Smm	xar = (struct xar *)a->format_data;
543231200Smm	xar->cur_file = NULL;
544231200Smm	xar->bytes_remaining = 0;
545231200Smm
546231200Smm	if (xar->sconv == NULL) {
547231200Smm		xar->sconv = archive_string_conversion_to_charset(
548231200Smm		    &a->archive, "UTF-8", 1);
549231200Smm		if (xar->sconv == NULL)
550231200Smm			return (ARCHIVE_FATAL);
551231200Smm	}
552231200Smm
553231200Smm	file = file_new(a, entry);
554231200Smm	if (file == NULL) {
555231200Smm		archive_set_error(&a->archive, ENOMEM,
556231200Smm		    "Can't allocate data");
557231200Smm		return (ARCHIVE_FATAL);
558231200Smm	}
559231200Smm	r2 = file_gen_utility_names(a, file);
560231200Smm	if (r2 < ARCHIVE_WARN)
561231200Smm		return (r2);
562231200Smm
563231200Smm	/*
564231200Smm	 * Ignore a path which looks like the top of directory name
565231200Smm	 * since we have already made the root directory of an Xar archive.
566231200Smm	 */
567231200Smm	if (archive_strlen(&(file->parentdir)) == 0 &&
568231200Smm	    archive_strlen(&(file->basename)) == 0) {
569231200Smm		file_free(file);
570231200Smm		return (r2);
571231200Smm	}
572231200Smm
573231200Smm	/* Add entry into tree */
574231200Smm	file_entry = file->entry;
575231200Smm	r = file_tree(a, &file);
576231200Smm	if (r != ARCHIVE_OK)
577231200Smm		return (r);
578231200Smm	/* There is the same file in tree and
579231200Smm	 * the current file is older than the file in tree.
580231200Smm	 * So we don't need the current file data anymore. */
581231200Smm	if (file->entry != file_entry)
582231200Smm		return (r2);
583231200Smm	if (file->id == 0)
584231200Smm		file_register(xar, file);
585231200Smm
586231200Smm	/* A virtual file, which is a directory, does not have
587231200Smm	 * any contents and we won't store it into a archive
588231200Smm	 * file other than its name. */
589231200Smm	if (file->virtual)
590231200Smm		return (r2);
591231200Smm
592231200Smm	/*
593231200Smm	 * Prepare to save the contents of the file.
594231200Smm	 */
595231200Smm	if (xar->temp_fd == -1) {
596231200Smm		int algsize;
597231200Smm		xar->temp_offset = 0;
598231200Smm		xar->temp_fd = __archive_mktemp(NULL);
599231200Smm		if (xar->temp_fd < 0) {
600231200Smm			archive_set_error(&a->archive, errno,
601231200Smm			    "Couldn't create temporary file");
602231200Smm			return (ARCHIVE_FATAL);
603231200Smm		}
604231200Smm		algsize = getalgsize(xar->opt_toc_sumalg);
605231200Smm		if (algsize > 0) {
606231200Smm			if (lseek(xar->temp_fd, algsize, SEEK_SET) < 0) {
607231200Smm				archive_set_error(&(a->archive), errno,
608231200Smm				    "lseek failed");
609231200Smm				return (ARCHIVE_FATAL);
610231200Smm			}
611231200Smm			xar->temp_offset = algsize;
612231200Smm		}
613231200Smm	}
614231200Smm
615231200Smm	if (archive_entry_hardlink(file->entry) == NULL) {
616231200Smm		r = save_xattrs(a, file);
617231200Smm		if (r != ARCHIVE_OK)
618231200Smm			return (ARCHIVE_FATAL);
619231200Smm	}
620231200Smm
621231200Smm	/* Non regular files contents are unneeded to be saved to
622231200Smm	 * a temporary file. */
623231200Smm	if (archive_entry_filetype(file->entry) != AE_IFREG)
624231200Smm		return (r2);
625231200Smm
626231200Smm	/*
627231200Smm	 * Set the current file to cur_file to read its contents.
628231200Smm	 */
629231200Smm	xar->cur_file = file;
630231200Smm
631231200Smm	if (archive_entry_nlink(file->entry) > 1) {
632231200Smm		r = file_register_hardlink(a, file);
633231200Smm		if (r != ARCHIVE_OK)
634231200Smm			return (r);
635231200Smm		if (archive_entry_hardlink(file->entry) != NULL) {
636231200Smm			archive_entry_unset_size(file->entry);
637231200Smm			return (r2);
638231200Smm		}
639231200Smm	}
640231200Smm
641231200Smm	/* Save a offset of current file in temporary file. */
642231200Smm	file->data.temp_offset = xar->temp_offset;
643231200Smm	file->data.size = archive_entry_size(file->entry);
644231200Smm	file->data.compression = xar->opt_compression;
645231200Smm	xar->bytes_remaining = archive_entry_size(file->entry);
646231200Smm	checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
647231200Smm	checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
648231200Smm	r = xar_compression_init_encoder(a);
649231200Smm
650231200Smm	if (r != ARCHIVE_OK)
651231200Smm		return (r);
652231200Smm	else
653231200Smm		return (r2);
654231200Smm}
655231200Smm
656231200Smmstatic int
657231200Smmwrite_to_temp(struct archive_write *a, const void *buff, size_t s)
658231200Smm{
659231200Smm	struct xar *xar;
660232153Smm	const unsigned char *p;
661231200Smm	ssize_t ws;
662231200Smm
663231200Smm	xar = (struct xar *)a->format_data;
664232153Smm	p = (const unsigned char *)buff;
665231200Smm	while (s) {
666231200Smm		ws = write(xar->temp_fd, p, s);
667231200Smm		if (ws < 0) {
668231200Smm			archive_set_error(&(a->archive), errno,
669231200Smm			    "fwrite function failed");
670231200Smm			return (ARCHIVE_FATAL);
671231200Smm		}
672231200Smm		s -= ws;
673231200Smm		p += ws;
674231200Smm		xar->temp_offset += ws;
675231200Smm	}
676231200Smm	return (ARCHIVE_OK);
677231200Smm}
678231200Smm
679231200Smmstatic ssize_t
680231200Smmxar_write_data(struct archive_write *a, const void *buff, size_t s)
681231200Smm{
682231200Smm	struct xar *xar;
683231200Smm	enum la_zaction run;
684358927Smm	size_t size = 0;
685358927Smm	size_t rsize;
686231200Smm	int r;
687231200Smm
688231200Smm	xar = (struct xar *)a->format_data;
689231200Smm
690231200Smm	if (s > xar->bytes_remaining)
691238856Smm		s = (size_t)xar->bytes_remaining;
692231200Smm	if (s == 0 || xar->cur_file == NULL)
693231200Smm		return (0);
694231200Smm	if (xar->cur_file->data.compression == NONE) {
695231200Smm		checksum_update(&(xar->e_sumwrk), buff, s);
696231200Smm		checksum_update(&(xar->a_sumwrk), buff, s);
697231200Smm		size = rsize = s;
698231200Smm	} else {
699231200Smm		xar->stream.next_in = (const unsigned char *)buff;
700231200Smm		xar->stream.avail_in = s;
701231200Smm		if (xar->bytes_remaining > s)
702231200Smm			run = ARCHIVE_Z_RUN;
703231200Smm		else
704231200Smm			run = ARCHIVE_Z_FINISH;
705231200Smm		/* Compress file data. */
706358090Smm		for (;;) {
707358090Smm			r = compression_code(&(a->archive), &(xar->stream),
708358090Smm			    run);
709358090Smm			if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
710358090Smm				return (ARCHIVE_FATAL);
711358090Smm			if (xar->stream.avail_out == 0 ||
712358090Smm			    run == ARCHIVE_Z_FINISH) {
713358090Smm				size = sizeof(xar->wbuff) -
714358090Smm				    xar->stream.avail_out;
715358090Smm				checksum_update(&(xar->a_sumwrk), xar->wbuff,
716358090Smm				    size);
717358090Smm				xar->cur_file->data.length += size;
718358090Smm				if (write_to_temp(a, xar->wbuff,
719358090Smm				    size) != ARCHIVE_OK)
720358090Smm					return (ARCHIVE_FATAL);
721358090Smm				if (r == ARCHIVE_OK) {
722358090Smm					/* Output buffer was full */
723358090Smm					xar->stream.next_out = xar->wbuff;
724358090Smm					xar->stream.avail_out =
725358090Smm					    sizeof(xar->wbuff);
726358090Smm				} else {
727358090Smm					/* ARCHIVE_EOF - We are done */
728358090Smm					break;
729358090Smm				}
730358090Smm			} else {
731358090Smm				/* Compressor wants more input */
732358090Smm				break;
733358090Smm			}
734358090Smm		}
735231200Smm		rsize = s - xar->stream.avail_in;
736231200Smm		checksum_update(&(xar->e_sumwrk), buff, rsize);
737231200Smm	}
738231200Smm#if !defined(_WIN32) || defined(__CYGWIN__)
739231200Smm	if (xar->bytes_remaining ==
740232153Smm	    (uint64_t)archive_entry_size(xar->cur_file->entry)) {
741231200Smm		/*
742231200Smm		 * Get the path of a shell script if so.
743231200Smm		 */
744231200Smm		const unsigned char *b = (const unsigned char *)buff;
745231200Smm
746231200Smm		archive_string_empty(&(xar->cur_file->script));
747231200Smm		if (rsize > 2 && b[0] == '#' && b[1] == '!') {
748231200Smm			size_t i, end, off;
749231200Smm
750231200Smm			off = 2;
751231200Smm			if (b[off] == ' ')
752231200Smm				off++;
753231200Smm#ifdef PATH_MAX
754231200Smm			if ((rsize - off) > PATH_MAX)
755231200Smm				end = off + PATH_MAX;
756231200Smm			else
757231200Smm#endif
758231200Smm				end = rsize;
759231200Smm			/* Find the end of a script path. */
760231200Smm			for (i = off; i < end && b[i] != '\0' &&
761231200Smm			    b[i] != '\n' && b[i] != '\r' &&
762231200Smm			    b[i] != ' ' && b[i] != '\t'; i++)
763231200Smm				;
764231200Smm			archive_strncpy(&(xar->cur_file->script), b + off,
765231200Smm			    i - off);
766231200Smm		}
767231200Smm	}
768231200Smm#endif
769231200Smm
770231200Smm	if (xar->cur_file->data.compression == NONE) {
771231200Smm		if (write_to_temp(a, buff, size) != ARCHIVE_OK)
772231200Smm			return (ARCHIVE_FATAL);
773358090Smm		xar->cur_file->data.length += size;
774231200Smm	}
775231200Smm	xar->bytes_remaining -= rsize;
776231200Smm
777231200Smm	return (rsize);
778231200Smm}
779231200Smm
780231200Smmstatic int
781231200Smmxar_finish_entry(struct archive_write *a)
782231200Smm{
783231200Smm	struct xar *xar;
784231200Smm	struct file *file;
785231200Smm	size_t s;
786231200Smm	ssize_t w;
787231200Smm
788231200Smm	xar = (struct xar *)a->format_data;
789231200Smm	if (xar->cur_file == NULL)
790231200Smm		return (ARCHIVE_OK);
791231200Smm
792231200Smm	while (xar->bytes_remaining > 0) {
793238856Smm		s = (size_t)xar->bytes_remaining;
794231200Smm		if (s > a->null_length)
795231200Smm			s = a->null_length;
796231200Smm		w = xar_write_data(a, a->nulls, s);
797231200Smm		if (w > 0)
798231200Smm			xar->bytes_remaining -= w;
799231200Smm		else
800231200Smm			return (w);
801231200Smm	}
802231200Smm	file = xar->cur_file;
803231200Smm	checksum_final(&(xar->e_sumwrk), &(file->data.e_sum));
804231200Smm	checksum_final(&(xar->a_sumwrk), &(file->data.a_sum));
805231200Smm	xar->cur_file = NULL;
806231200Smm
807231200Smm	return (ARCHIVE_OK);
808231200Smm}
809231200Smm
810231200Smmstatic int
811231200Smmxmlwrite_string_attr(struct archive_write *a, xmlTextWriterPtr writer,
812231200Smm	const char *key, const char *value,
813231200Smm	const char *attrkey, const char *attrvalue)
814231200Smm{
815231200Smm	int r;
816231200Smm
817232153Smm	r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));
818231200Smm	if (r < 0) {
819231200Smm		archive_set_error(&a->archive,
820231200Smm		    ARCHIVE_ERRNO_MISC,
821231200Smm		    "xmlTextWriterStartElement() failed: %d", r);
822231200Smm		return (ARCHIVE_FATAL);
823231200Smm	}
824231200Smm	if (attrkey != NULL && attrvalue != NULL) {
825231200Smm		r = xmlTextWriterWriteAttribute(writer,
826232153Smm		    BAD_CAST_CONST(attrkey), BAD_CAST_CONST(attrvalue));
827231200Smm		if (r < 0) {
828231200Smm			archive_set_error(&a->archive,
829231200Smm			    ARCHIVE_ERRNO_MISC,
830231200Smm			    "xmlTextWriterWriteAttribute() failed: %d", r);
831231200Smm			return (ARCHIVE_FATAL);
832231200Smm		}
833231200Smm	}
834231200Smm	if (value != NULL) {
835232153Smm		r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));
836231200Smm		if (r < 0) {
837231200Smm			archive_set_error(&a->archive,
838231200Smm			    ARCHIVE_ERRNO_MISC,
839231200Smm			    "xmlTextWriterWriteString() failed: %d", r);
840231200Smm			return (ARCHIVE_FATAL);
841231200Smm		}
842231200Smm	}
843231200Smm	r = xmlTextWriterEndElement(writer);
844231200Smm	if (r < 0) {
845231200Smm		archive_set_error(&a->archive,
846231200Smm		    ARCHIVE_ERRNO_MISC,
847231200Smm		    "xmlTextWriterEndElement() failed: %d", r);
848231200Smm		return (ARCHIVE_FATAL);
849231200Smm	}
850231200Smm	return (ARCHIVE_OK);
851231200Smm}
852231200Smm
853231200Smmstatic int
854231200Smmxmlwrite_string(struct archive_write *a, xmlTextWriterPtr writer,
855231200Smm	const char *key, const char *value)
856231200Smm{
857231200Smm	int r;
858231200Smm
859231200Smm	if (value == NULL)
860231200Smm		return (ARCHIVE_OK);
861302001Smm
862232153Smm	r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));
863231200Smm	if (r < 0) {
864231200Smm		archive_set_error(&a->archive,
865231200Smm		    ARCHIVE_ERRNO_MISC,
866231200Smm		    "xmlTextWriterStartElement() failed: %d", r);
867231200Smm		return (ARCHIVE_FATAL);
868231200Smm	}
869231200Smm	if (value != NULL) {
870232153Smm		r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));
871231200Smm		if (r < 0) {
872231200Smm			archive_set_error(&a->archive,
873231200Smm			    ARCHIVE_ERRNO_MISC,
874231200Smm			    "xmlTextWriterWriteString() failed: %d", r);
875231200Smm			return (ARCHIVE_FATAL);
876231200Smm		}
877231200Smm	}
878231200Smm	r = xmlTextWriterEndElement(writer);
879231200Smm	if (r < 0) {
880231200Smm		archive_set_error(&a->archive,
881231200Smm		    ARCHIVE_ERRNO_MISC,
882231200Smm		    "xmlTextWriterEndElement() failed: %d", r);
883231200Smm		return (ARCHIVE_FATAL);
884231200Smm	}
885231200Smm	return (ARCHIVE_OK);
886231200Smm}
887231200Smm
888231200Smmstatic int
889231200Smmxmlwrite_fstring(struct archive_write *a, xmlTextWriterPtr writer,
890231200Smm	const char *key, const char *fmt, ...)
891231200Smm{
892231200Smm	struct xar *xar;
893231200Smm	va_list ap;
894231200Smm
895231200Smm	xar = (struct xar *)a->format_data;
896231200Smm	va_start(ap, fmt);
897231200Smm	archive_string_empty(&xar->vstr);
898231200Smm	archive_string_vsprintf(&xar->vstr, fmt, ap);
899231200Smm	va_end(ap);
900231200Smm	return (xmlwrite_string(a, writer, key, xar->vstr.s));
901231200Smm}
902231200Smm
903231200Smmstatic int
904231200Smmxmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer,
905231200Smm	const char *key, time_t t, int z)
906231200Smm{
907231200Smm	char timestr[100];
908231200Smm	struct tm tm;
909358090Smm#if defined(HAVE__GMTIME64_S)
910358090Smm	__time64_t tmptime;
911358090Smm#endif
912231200Smm
913231200Smm#if defined(HAVE_GMTIME_R)
914231200Smm	gmtime_r(&t, &tm);
915231200Smm#elif defined(HAVE__GMTIME64_S)
916358090Smm	tmptime = t;
917358090Smm	_gmtime64_s(&tm, &tmptime);
918231200Smm#else
919231200Smm	memcpy(&tm, gmtime(&t), sizeof(tm));
920231200Smm#endif
921231200Smm	memset(&timestr, 0, sizeof(timestr));
922231200Smm	/* Do not use %F and %T for portability. */
923231200Smm	strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", &tm);
924231200Smm	if (z)
925231200Smm		strcat(timestr, "Z");
926231200Smm	return (xmlwrite_string(a, writer, key, timestr));
927231200Smm}
928231200Smm
929231200Smmstatic int
930231200Smmxmlwrite_mode(struct archive_write *a, xmlTextWriterPtr writer,
931231200Smm	const char *key, mode_t mode)
932231200Smm{
933231200Smm	char ms[5];
934231200Smm
935231200Smm	ms[0] = '0';
936231200Smm	ms[1] = '0' + ((mode >> 6) & 07);
937231200Smm	ms[2] = '0' + ((mode >> 3) & 07);
938231200Smm	ms[3] = '0' + (mode & 07);
939231200Smm	ms[4] = '\0';
940231200Smm
941231200Smm	return (xmlwrite_string(a, writer, key, ms));
942231200Smm}
943231200Smm
944231200Smmstatic int
945231200Smmxmlwrite_sum(struct archive_write *a, xmlTextWriterPtr writer,
946231200Smm	const char *key, struct chksumval *sum)
947231200Smm{
948231200Smm	const char *algname;
949231200Smm	int algsize;
950231200Smm	char buff[MAX_SUM_SIZE*2 + 1];
951231200Smm	char *p;
952231200Smm	unsigned char *s;
953231200Smm	int i, r;
954231200Smm
955231200Smm	if (sum->len > 0) {
956231200Smm		algname = getalgname(sum->alg);
957231200Smm		algsize = getalgsize(sum->alg);
958231200Smm		if (algname != NULL) {
959231200Smm			const char *hex = "0123456789abcdef";
960231200Smm			p = buff;
961231200Smm			s = sum->val;
962231200Smm			for (i = 0; i < algsize; i++) {
963231200Smm				*p++ = hex[(*s >> 4)];
964231200Smm				*p++ = hex[(*s & 0x0f)];
965231200Smm				s++;
966231200Smm			}
967231200Smm			*p = '\0';
968231200Smm			r = xmlwrite_string_attr(a, writer,
969231200Smm			    key, buff,
970231200Smm			    "style", algname);
971231200Smm			if (r < 0)
972231200Smm				return (ARCHIVE_FATAL);
973231200Smm		}
974231200Smm	}
975231200Smm	return (ARCHIVE_OK);
976231200Smm}
977231200Smm
978231200Smmstatic int
979231200Smmxmlwrite_heap(struct archive_write *a, xmlTextWriterPtr writer,
980231200Smm	struct heap_data *heap)
981231200Smm{
982231200Smm	const char *encname;
983231200Smm	int r;
984231200Smm
985231200Smm	r = xmlwrite_fstring(a, writer, "length", "%ju", heap->length);
986231200Smm	if (r < 0)
987231200Smm		return (ARCHIVE_FATAL);
988231200Smm	r = xmlwrite_fstring(a, writer, "offset", "%ju", heap->temp_offset);
989231200Smm	if (r < 0)
990231200Smm		return (ARCHIVE_FATAL);
991231200Smm	r = xmlwrite_fstring(a, writer, "size", "%ju", heap->size);
992231200Smm	if (r < 0)
993231200Smm		return (ARCHIVE_FATAL);
994231200Smm	switch (heap->compression) {
995231200Smm	case GZIP:
996231200Smm		encname = "application/x-gzip"; break;
997231200Smm	case BZIP2:
998231200Smm		encname = "application/x-bzip2"; break;
999231200Smm	case LZMA:
1000231200Smm		encname = "application/x-lzma"; break;
1001231200Smm	case XZ:
1002231200Smm		encname = "application/x-xz"; break;
1003231200Smm	default:
1004231200Smm		encname = "application/octet-stream"; break;
1005231200Smm	}
1006231200Smm	r = xmlwrite_string_attr(a, writer, "encoding", NULL,
1007231200Smm	    "style", encname);
1008231200Smm	if (r < 0)
1009231200Smm		return (ARCHIVE_FATAL);
1010231200Smm	r = xmlwrite_sum(a, writer, "archived-checksum", &(heap->a_sum));
1011231200Smm	if (r < 0)
1012231200Smm		return (ARCHIVE_FATAL);
1013231200Smm	r = xmlwrite_sum(a, writer, "extracted-checksum", &(heap->e_sum));
1014231200Smm	if (r < 0)
1015231200Smm		return (ARCHIVE_FATAL);
1016231200Smm	return (ARCHIVE_OK);
1017231200Smm}
1018231200Smm
1019231200Smm/*
1020231200Smm * xar utility records fflags as following xml elements:
1021231200Smm *   <flags>
1022231200Smm *     <UserNoDump/>
1023231200Smm *     .....
1024231200Smm *   </flags>
1025231200Smm * or
1026231200Smm *   <ext2>
1027231200Smm *     <NoDump/>
1028231200Smm *     .....
1029231200Smm *   </ext2>
1030231200Smm * If xar is running on BSD platform, records <flags>..</flags>;
1031231200Smm * if xar is running on linux platform, records <ext2>..</ext2>;
1032231200Smm * otherwise does not record.
1033231200Smm *
1034231200Smm * Our implements records both <flags> and <ext2> if it's necessary.
1035231200Smm */
1036231200Smmstatic int
1037231200Smmmake_fflags_entry(struct archive_write *a, xmlTextWriterPtr writer,
1038231200Smm    const char *element, const char *fflags_text)
1039231200Smm{
1040231200Smm	static const struct flagentry {
1041231200Smm		const char	*name;
1042231200Smm		const char	*xarname;
1043231200Smm	}
1044231200Smm	flagbsd[] = {
1045231200Smm		{ "sappnd",	"SystemAppend"},
1046231200Smm		{ "sappend",	"SystemAppend"},
1047231200Smm		{ "arch",	"SystemArchived"},
1048231200Smm		{ "archived",	"SystemArchived"},
1049231200Smm		{ "schg",	"SystemImmutable"},
1050231200Smm		{ "schange",	"SystemImmutable"},
1051231200Smm		{ "simmutable",	"SystemImmutable"},
1052231200Smm		{ "nosunlnk",	"SystemNoUnlink"},
1053231200Smm		{ "nosunlink",	"SystemNoUnlink"},
1054231200Smm		{ "snapshot",	"SystemSnapshot"},
1055231200Smm		{ "uappnd",	"UserAppend"},
1056231200Smm		{ "uappend",	"UserAppend"},
1057231200Smm		{ "uchg",	"UserImmutable"},
1058231200Smm		{ "uchange",	"UserImmutable"},
1059231200Smm		{ "uimmutable",	"UserImmutable"},
1060231200Smm		{ "nodump",	"UserNoDump"},
1061231200Smm		{ "noopaque",	"UserOpaque"},
1062231200Smm		{ "nouunlnk",	"UserNoUnlink"},
1063231200Smm		{ "nouunlink",	"UserNoUnlink"},
1064231200Smm		{ NULL, NULL}
1065231200Smm	},
1066231200Smm	flagext2[] = {
1067231200Smm		{ "sappnd",	"AppendOnly"},
1068231200Smm		{ "sappend",	"AppendOnly"},
1069231200Smm		{ "schg",	"Immutable"},
1070231200Smm		{ "schange",	"Immutable"},
1071231200Smm		{ "simmutable",	"Immutable"},
1072231200Smm		{ "nodump",	"NoDump"},
1073231200Smm		{ "nouunlnk",	"Undelete"},
1074231200Smm		{ "nouunlink",	"Undelete"},
1075231200Smm		{ "btree",	"BTree"},
1076231200Smm		{ "comperr",	"CompError"},
1077231200Smm		{ "compress",	"Compress"},
1078231200Smm		{ "noatime",	"NoAtime"},
1079231200Smm		{ "compdirty",	"CompDirty"},
1080231200Smm		{ "comprblk",	"CompBlock"},
1081231200Smm		{ "dirsync",	"DirSync"},
1082231200Smm		{ "hashidx",	"HashIndexed"},
1083231200Smm		{ "imagic",	"iMagic"},
1084231200Smm		{ "journal",	"Journaled"},
1085231200Smm		{ "securedeletion",	"SecureDeletion"},
1086231200Smm		{ "sync",	"Synchronous"},
1087231200Smm		{ "notail",	"NoTail"},
1088231200Smm		{ "topdir",	"TopDir"},
1089231200Smm		{ "reserved",	"Reserved"},
1090231200Smm		{ NULL, NULL}
1091231200Smm	};
1092231200Smm	const struct flagentry *fe, *flagentry;
1093231200Smm#define FLAGENTRY_MAXSIZE ((sizeof(flagbsd)+sizeof(flagext2))/sizeof(flagbsd))
1094231200Smm	const struct flagentry *avail[FLAGENTRY_MAXSIZE];
1095231200Smm	const char *p;
1096231200Smm	int i, n, r;
1097231200Smm
1098231200Smm	if (strcmp(element, "ext2") == 0)
1099231200Smm		flagentry = flagext2;
1100231200Smm	else
1101231200Smm		flagentry = flagbsd;
1102231200Smm	n = 0;
1103231200Smm	p = fflags_text;
1104231200Smm	do {
1105231200Smm		const char *cp;
1106231200Smm
1107231200Smm		cp = strchr(p, ',');
1108231200Smm		if (cp == NULL)
1109231200Smm			cp = p + strlen(p);
1110231200Smm
1111231200Smm		for (fe = flagentry; fe->name != NULL; fe++) {
1112231200Smm			if (fe->name[cp - p] != '\0'
1113231200Smm			    || p[0] != fe->name[0])
1114231200Smm				continue;
1115231200Smm			if (strncmp(p, fe->name, cp - p) == 0) {
1116231200Smm				avail[n++] = fe;
1117231200Smm				break;
1118231200Smm			}
1119231200Smm		}
1120231200Smm		if (*cp == ',')
1121231200Smm			p = cp + 1;
1122231200Smm		else
1123231200Smm			p = NULL;
1124231200Smm	} while (p != NULL);
1125231200Smm
1126231200Smm	if (n > 0) {
1127232153Smm		r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(element));
1128231200Smm		if (r < 0) {
1129231200Smm			archive_set_error(&a->archive,
1130231200Smm			    ARCHIVE_ERRNO_MISC,
1131231200Smm			    "xmlTextWriterStartElement() failed: %d", r);
1132231200Smm			return (ARCHIVE_FATAL);
1133231200Smm		}
1134231200Smm		for (i = 0; i < n; i++) {
1135231200Smm			r = xmlwrite_string(a, writer,
1136231200Smm			    avail[i]->xarname, NULL);
1137231200Smm			if (r != ARCHIVE_OK)
1138231200Smm				return (r);
1139231200Smm		}
1140231200Smm
1141231200Smm		r = xmlTextWriterEndElement(writer);
1142231200Smm		if (r < 0) {
1143231200Smm			archive_set_error(&a->archive,
1144231200Smm			    ARCHIVE_ERRNO_MISC,
1145231200Smm			    "xmlTextWriterEndElement() failed: %d", r);
1146231200Smm			return (ARCHIVE_FATAL);
1147231200Smm		}
1148231200Smm	}
1149231200Smm	return (ARCHIVE_OK);
1150231200Smm}
1151231200Smm
1152231200Smmstatic int
1153231200Smmmake_file_entry(struct archive_write *a, xmlTextWriterPtr writer,
1154231200Smm    struct file *file)
1155231200Smm{
1156231200Smm	struct xar *xar;
1157231200Smm	const char *filetype, *filelink, *fflags;
1158231200Smm	struct archive_string linkto;
1159231200Smm	struct heap_data *heap;
1160231200Smm	unsigned char *tmp;
1161231200Smm	const char *p;
1162231200Smm	size_t len;
1163231200Smm	int r, r2, l, ll;
1164231200Smm
1165231200Smm	xar = (struct xar *)a->format_data;
1166231200Smm	r2 = ARCHIVE_OK;
1167231200Smm
1168231200Smm	/*
1169231200Smm	 * Make a file name entry, "<name>".
1170231200Smm	 */
1171231200Smm	l = ll = archive_strlen(&(file->basename));
1172231200Smm	tmp = malloc(l);
1173231200Smm	if (tmp == NULL) {
1174231200Smm		archive_set_error(&a->archive, ENOMEM,
1175231200Smm		    "Can't allocate memory");
1176231200Smm		return (ARCHIVE_FATAL);
1177231200Smm	}
1178231200Smm	r = UTF8Toisolat1(tmp, &l, BAD_CAST(file->basename.s), &ll);
1179231200Smm	free(tmp);
1180231200Smm	if (r < 0) {
1181231200Smm		r = xmlTextWriterStartElement(writer, BAD_CAST("name"));
1182231200Smm		if (r < 0) {
1183231200Smm			archive_set_error(&a->archive,
1184231200Smm			    ARCHIVE_ERRNO_MISC,
1185231200Smm			    "xmlTextWriterStartElement() failed: %d", r);
1186231200Smm			return (ARCHIVE_FATAL);
1187231200Smm		}
1188231200Smm		r = xmlTextWriterWriteAttribute(writer,
1189231200Smm		    BAD_CAST("enctype"), BAD_CAST("base64"));
1190231200Smm		if (r < 0) {
1191231200Smm			archive_set_error(&a->archive,
1192231200Smm			    ARCHIVE_ERRNO_MISC,
1193231200Smm			    "xmlTextWriterWriteAttribute() failed: %d", r);
1194231200Smm			return (ARCHIVE_FATAL);
1195231200Smm		}
1196231200Smm		r = xmlTextWriterWriteBase64(writer, file->basename.s,
1197231200Smm		    0, archive_strlen(&(file->basename)));
1198231200Smm		if (r < 0) {
1199231200Smm			archive_set_error(&a->archive,
1200231200Smm			    ARCHIVE_ERRNO_MISC,
1201231200Smm			    "xmlTextWriterWriteBase64() failed: %d", r);
1202231200Smm			return (ARCHIVE_FATAL);
1203231200Smm		}
1204231200Smm		r = xmlTextWriterEndElement(writer);
1205231200Smm		if (r < 0) {
1206231200Smm			archive_set_error(&a->archive,
1207231200Smm			    ARCHIVE_ERRNO_MISC,
1208231200Smm			    "xmlTextWriterEndElement() failed: %d", r);
1209231200Smm			return (ARCHIVE_FATAL);
1210231200Smm		}
1211231200Smm	} else {
1212231200Smm		r = xmlwrite_string(a, writer, "name", file->basename.s);
1213231200Smm		if (r < 0)
1214231200Smm			return (ARCHIVE_FATAL);
1215231200Smm	}
1216231200Smm
1217231200Smm	/*
1218231200Smm	 * Make a file type entry, "<type>".
1219231200Smm	 */
1220231200Smm	filelink = NULL;
1221231200Smm	archive_string_init(&linkto);
1222231200Smm	switch (archive_entry_filetype(file->entry)) {
1223231200Smm	case AE_IFDIR:
1224231200Smm		filetype = "directory"; break;
1225231200Smm	case AE_IFLNK:
1226231200Smm		filetype = "symlink"; break;
1227231200Smm	case AE_IFCHR:
1228231200Smm		filetype = "character special"; break;
1229231200Smm	case AE_IFBLK:
1230231200Smm		filetype = "block special"; break;
1231231200Smm	case AE_IFSOCK:
1232231200Smm		filetype = "socket"; break;
1233231200Smm	case AE_IFIFO:
1234231200Smm		filetype = "fifo"; break;
1235231200Smm	case AE_IFREG:
1236231200Smm	default:
1237231200Smm		if (file->hardlink_target != NULL) {
1238231200Smm			filetype = "hardlink";
1239231200Smm			filelink = "link";
1240231200Smm			if (file->hardlink_target == file)
1241231200Smm				archive_strcpy(&linkto, "original");
1242231200Smm			else
1243231200Smm				archive_string_sprintf(&linkto, "%d",
1244231200Smm				    file->hardlink_target->id);
1245231200Smm		} else
1246231200Smm			filetype = "file";
1247231200Smm		break;
1248231200Smm	}
1249231200Smm	r = xmlwrite_string_attr(a, writer, "type", filetype,
1250231200Smm	    filelink, linkto.s);
1251231200Smm	archive_string_free(&linkto);
1252231200Smm	if (r < 0)
1253231200Smm		return (ARCHIVE_FATAL);
1254231200Smm
1255231200Smm	/*
1256231200Smm	 * On a virtual directory, we record "name" and "type" only.
1257231200Smm	 */
1258231200Smm	if (file->virtual)
1259231200Smm		return (ARCHIVE_OK);
1260231200Smm
1261231200Smm	switch (archive_entry_filetype(file->entry)) {
1262231200Smm	case AE_IFLNK:
1263231200Smm		/*
1264231200Smm		 * xar utility has checked a file type, which
1265313571Smm		 * a symbolic-link file has referenced.
1266231200Smm		 * For example:
1267231200Smm		 *   <link type="directory">../ref/</link>
1268231200Smm		 *   The symlink target file is "../ref/" and its
1269231200Smm		 *   file type is a directory.
1270231200Smm		 *
1271231200Smm		 *   <link type="file">../f</link>
1272231200Smm		 *   The symlink target file is "../f" and its
1273231200Smm		 *   file type is a regular file.
1274231200Smm		 *
1275313571Smm		 * But our implementation cannot do it, and then we
1276313571Smm		 * always record that a attribute "type" is "broken",
1277231200Smm		 * for example:
1278231200Smm		 *   <link type="broken">foo/bar</link>
1279231200Smm		 *   It means "foo/bar" is not reachable.
1280231200Smm		 */
1281231200Smm		r = xmlwrite_string_attr(a, writer, "link",
1282231200Smm		    file->symlink.s,
1283231200Smm		    "type", "broken");
1284231200Smm		if (r < 0)
1285231200Smm			return (ARCHIVE_FATAL);
1286231200Smm		break;
1287231200Smm	case AE_IFCHR:
1288231200Smm	case AE_IFBLK:
1289231200Smm		r = xmlTextWriterStartElement(writer, BAD_CAST("device"));
1290231200Smm		if (r < 0) {
1291231200Smm			archive_set_error(&a->archive,
1292231200Smm			    ARCHIVE_ERRNO_MISC,
1293231200Smm			    "xmlTextWriterStartElement() failed: %d", r);
1294231200Smm			return (ARCHIVE_FATAL);
1295231200Smm		}
1296231200Smm		r = xmlwrite_fstring(a, writer, "major",
1297231200Smm		    "%d", archive_entry_rdevmajor(file->entry));
1298231200Smm		if (r < 0)
1299231200Smm			return (ARCHIVE_FATAL);
1300231200Smm		r = xmlwrite_fstring(a, writer, "minor",
1301231200Smm		    "%d", archive_entry_rdevminor(file->entry));
1302231200Smm		if (r < 0)
1303231200Smm			return (ARCHIVE_FATAL);
1304231200Smm		r = xmlTextWriterEndElement(writer);
1305231200Smm		if (r < 0) {
1306231200Smm			archive_set_error(&a->archive,
1307231200Smm			    ARCHIVE_ERRNO_MISC,
1308231200Smm			    "xmlTextWriterEndElement() failed: %d", r);
1309231200Smm			return (ARCHIVE_FATAL);
1310231200Smm		}
1311231200Smm		break;
1312231200Smm	default:
1313231200Smm		break;
1314231200Smm	}
1315231200Smm
1316231200Smm	/*
1317231200Smm	 * Make a inode entry, "<inode>".
1318231200Smm	 */
1319231200Smm	r = xmlwrite_fstring(a, writer, "inode",
1320231200Smm	    "%jd", archive_entry_ino64(file->entry));
1321231200Smm	if (r < 0)
1322231200Smm		return (ARCHIVE_FATAL);
1323231200Smm	if (archive_entry_dev(file->entry) != 0) {
1324231200Smm		r = xmlwrite_fstring(a, writer, "deviceno",
1325231200Smm		    "%d", archive_entry_dev(file->entry));
1326231200Smm		if (r < 0)
1327231200Smm			return (ARCHIVE_FATAL);
1328231200Smm	}
1329231200Smm
1330231200Smm	/*
1331231200Smm	 * Make a file mode entry, "<mode>".
1332231200Smm	 */
1333231200Smm	r = xmlwrite_mode(a, writer, "mode",
1334231200Smm	    archive_entry_mode(file->entry));
1335231200Smm	if (r < 0)
1336231200Smm		return (ARCHIVE_FATAL);
1337231200Smm
1338231200Smm	/*
1339231200Smm	 * Make a user entry, "<uid>" and "<user>.
1340231200Smm	 */
1341231200Smm	r = xmlwrite_fstring(a, writer, "uid",
1342231200Smm	    "%d", archive_entry_uid(file->entry));
1343231200Smm	if (r < 0)
1344231200Smm		return (ARCHIVE_FATAL);
1345231200Smm	r = archive_entry_uname_l(file->entry, &p, &len, xar->sconv);
1346231200Smm	if (r != 0) {
1347231200Smm		if (errno == ENOMEM) {
1348231200Smm			archive_set_error(&a->archive, ENOMEM,
1349231200Smm			    "Can't allocate memory for Uname");
1350231200Smm			return (ARCHIVE_FATAL);
1351231200Smm		}
1352231200Smm		archive_set_error(&a->archive,
1353231200Smm		    ARCHIVE_ERRNO_FILE_FORMAT,
1354231200Smm		    "Can't translate uname '%s' to UTF-8",
1355231200Smm		    archive_entry_uname(file->entry));
1356231200Smm		r2 = ARCHIVE_WARN;
1357231200Smm	}
1358231200Smm	if (len > 0) {
1359231200Smm		r = xmlwrite_string(a, writer, "user", p);
1360231200Smm		if (r < 0)
1361231200Smm			return (ARCHIVE_FATAL);
1362231200Smm	}
1363231200Smm
1364231200Smm	/*
1365231200Smm	 * Make a group entry, "<gid>" and "<group>.
1366231200Smm	 */
1367231200Smm	r = xmlwrite_fstring(a, writer, "gid",
1368231200Smm	    "%d", archive_entry_gid(file->entry));
1369231200Smm	if (r < 0)
1370231200Smm		return (ARCHIVE_FATAL);
1371231200Smm	r = archive_entry_gname_l(file->entry, &p, &len, xar->sconv);
1372231200Smm	if (r != 0) {
1373231200Smm		if (errno == ENOMEM) {
1374231200Smm			archive_set_error(&a->archive, ENOMEM,
1375231200Smm			    "Can't allocate memory for Gname");
1376231200Smm			return (ARCHIVE_FATAL);
1377231200Smm		}
1378231200Smm		archive_set_error(&a->archive,
1379231200Smm		    ARCHIVE_ERRNO_FILE_FORMAT,
1380231200Smm		    "Can't translate gname '%s' to UTF-8",
1381231200Smm		    archive_entry_gname(file->entry));
1382231200Smm		r2 = ARCHIVE_WARN;
1383231200Smm	}
1384231200Smm	if (len > 0) {
1385231200Smm		r = xmlwrite_string(a, writer, "group", p);
1386231200Smm		if (r < 0)
1387231200Smm			return (ARCHIVE_FATAL);
1388231200Smm	}
1389231200Smm
1390231200Smm	/*
1391231200Smm	 * Make a ctime entry, "<ctime>".
1392231200Smm	 */
1393231200Smm	if (archive_entry_ctime_is_set(file->entry)) {
1394231200Smm		r = xmlwrite_time(a, writer, "ctime",
1395231200Smm		    archive_entry_ctime(file->entry), 1);
1396231200Smm		if (r < 0)
1397231200Smm			return (ARCHIVE_FATAL);
1398231200Smm	}
1399231200Smm
1400231200Smm	/*
1401231200Smm	 * Make a mtime entry, "<mtime>".
1402231200Smm	 */
1403231200Smm	if (archive_entry_mtime_is_set(file->entry)) {
1404231200Smm		r = xmlwrite_time(a, writer, "mtime",
1405231200Smm		    archive_entry_mtime(file->entry), 1);
1406231200Smm		if (r < 0)
1407231200Smm			return (ARCHIVE_FATAL);
1408231200Smm	}
1409231200Smm
1410231200Smm	/*
1411231200Smm	 * Make a atime entry, "<atime>".
1412231200Smm	 */
1413231200Smm	if (archive_entry_atime_is_set(file->entry)) {
1414231200Smm		r = xmlwrite_time(a, writer, "atime",
1415231200Smm		    archive_entry_atime(file->entry), 1);
1416231200Smm		if (r < 0)
1417231200Smm			return (ARCHIVE_FATAL);
1418231200Smm	}
1419231200Smm
1420231200Smm	/*
1421231200Smm	 * Make fflags entries, "<flags>" and "<ext2>".
1422231200Smm	 */
1423231200Smm	fflags = archive_entry_fflags_text(file->entry);
1424231200Smm	if (fflags != NULL) {
1425231200Smm		r = make_fflags_entry(a, writer, "flags", fflags);
1426231200Smm		if (r < 0)
1427231200Smm			return (r);
1428231200Smm		r = make_fflags_entry(a, writer, "ext2", fflags);
1429231200Smm		if (r < 0)
1430231200Smm			return (r);
1431231200Smm	}
1432231200Smm
1433231200Smm	/*
1434231200Smm	 * Make extended attribute entries, "<ea>".
1435231200Smm	 */
1436231200Smm	archive_entry_xattr_reset(file->entry);
1437231200Smm	for (heap = file->xattr.first; heap != NULL; heap = heap->next) {
1438231200Smm		const char *name;
1439231200Smm		const void *value;
1440231200Smm		size_t size;
1441231200Smm
1442231200Smm		archive_entry_xattr_next(file->entry,
1443231200Smm		    &name, &value, &size);
1444231200Smm		r = xmlTextWriterStartElement(writer, BAD_CAST("ea"));
1445231200Smm		if (r < 0) {
1446231200Smm			archive_set_error(&a->archive,
1447231200Smm			    ARCHIVE_ERRNO_MISC,
1448231200Smm			    "xmlTextWriterStartElement() failed: %d", r);
1449231200Smm			return (ARCHIVE_FATAL);
1450231200Smm		}
1451231200Smm		r = xmlTextWriterWriteFormatAttribute(writer,
1452231200Smm		    BAD_CAST("id"), "%d", heap->id);
1453231200Smm		if (r < 0) {
1454231200Smm			archive_set_error(&a->archive,
1455231200Smm			    ARCHIVE_ERRNO_MISC,
1456231200Smm			    "xmlTextWriterWriteAttribute() failed: %d", r);
1457231200Smm			return (ARCHIVE_FATAL);
1458231200Smm		}
1459231200Smm		r = xmlwrite_heap(a, writer, heap);
1460231200Smm		if (r < 0)
1461231200Smm			return (ARCHIVE_FATAL);
1462231200Smm		r = xmlwrite_string(a, writer, "name", name);
1463231200Smm		if (r < 0)
1464231200Smm			return (ARCHIVE_FATAL);
1465231200Smm
1466231200Smm		r = xmlTextWriterEndElement(writer);
1467231200Smm		if (r < 0) {
1468231200Smm			archive_set_error(&a->archive,
1469231200Smm			    ARCHIVE_ERRNO_MISC,
1470231200Smm			    "xmlTextWriterEndElement() failed: %d", r);
1471231200Smm			return (ARCHIVE_FATAL);
1472231200Smm		}
1473231200Smm	}
1474231200Smm
1475231200Smm	/*
1476231200Smm	 * Make a file data entry, "<data>".
1477231200Smm	 */
1478231200Smm	if (file->data.length > 0) {
1479231200Smm		r = xmlTextWriterStartElement(writer, BAD_CAST("data"));
1480231200Smm		if (r < 0) {
1481231200Smm			archive_set_error(&a->archive,
1482231200Smm			    ARCHIVE_ERRNO_MISC,
1483231200Smm			    "xmlTextWriterStartElement() failed: %d", r);
1484231200Smm			return (ARCHIVE_FATAL);
1485231200Smm		}
1486231200Smm
1487231200Smm		r = xmlwrite_heap(a, writer, &(file->data));
1488231200Smm		if (r < 0)
1489231200Smm			return (ARCHIVE_FATAL);
1490231200Smm
1491231200Smm		r = xmlTextWriterEndElement(writer);
1492231200Smm		if (r < 0) {
1493231200Smm			archive_set_error(&a->archive,
1494231200Smm			    ARCHIVE_ERRNO_MISC,
1495231200Smm			    "xmlTextWriterEndElement() failed: %d", r);
1496231200Smm			return (ARCHIVE_FATAL);
1497231200Smm		}
1498231200Smm	}
1499231200Smm
1500231200Smm	if (archive_strlen(&file->script) > 0) {
1501231200Smm		r = xmlTextWriterStartElement(writer, BAD_CAST("content"));
1502231200Smm		if (r < 0) {
1503231200Smm			archive_set_error(&a->archive,
1504231200Smm			    ARCHIVE_ERRNO_MISC,
1505231200Smm			    "xmlTextWriterStartElement() failed: %d", r);
1506231200Smm			return (ARCHIVE_FATAL);
1507231200Smm		}
1508231200Smm
1509231200Smm		r = xmlwrite_string(a, writer,
1510231200Smm		    "interpreter", file->script.s);
1511231200Smm		if (r < 0)
1512231200Smm			return (ARCHIVE_FATAL);
1513231200Smm
1514231200Smm		r = xmlwrite_string(a, writer, "type", "script");
1515231200Smm		if (r < 0)
1516231200Smm			return (ARCHIVE_FATAL);
1517231200Smm
1518231200Smm		r = xmlTextWriterEndElement(writer);
1519231200Smm		if (r < 0) {
1520231200Smm			archive_set_error(&a->archive,
1521231200Smm			    ARCHIVE_ERRNO_MISC,
1522231200Smm			    "xmlTextWriterEndElement() failed: %d", r);
1523231200Smm			return (ARCHIVE_FATAL);
1524231200Smm		}
1525231200Smm	}
1526231200Smm
1527231200Smm	return (r2);
1528231200Smm}
1529231200Smm
1530231200Smm/*
1531231200Smm * Make the TOC
1532231200Smm */
1533231200Smmstatic int
1534231200Smmmake_toc(struct archive_write *a)
1535231200Smm{
1536231200Smm	struct xar *xar;
1537231200Smm	struct file *np;
1538231200Smm	xmlBufferPtr bp;
1539231200Smm	xmlTextWriterPtr writer;
1540231200Smm	int algsize;
1541231200Smm	int r, ret;
1542231200Smm
1543231200Smm	xar = (struct xar *)a->format_data;
1544231200Smm
1545231200Smm	ret = ARCHIVE_FATAL;
1546231200Smm
1547231200Smm	/*
1548231200Smm	 * Initialize xml writer.
1549231200Smm	 */
1550231200Smm	writer = NULL;
1551231200Smm	bp = xmlBufferCreate();
1552231200Smm	if (bp == NULL) {
1553231200Smm		archive_set_error(&a->archive, ENOMEM,
1554231200Smm		    "xmlBufferCreate() "
1555231200Smm		    "couldn't create xml buffer");
1556231200Smm		goto exit_toc;
1557231200Smm	}
1558231200Smm	writer = xmlNewTextWriterMemory(bp, 0);
1559231200Smm	if (writer == NULL) {
1560231200Smm		archive_set_error(&a->archive,
1561231200Smm		    ARCHIVE_ERRNO_MISC,
1562231200Smm		    "xmlNewTextWriterMemory() "
1563231200Smm		    "couldn't create xml writer");
1564231200Smm		goto exit_toc;
1565231200Smm	}
1566231200Smm	r = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
1567231200Smm	if (r < 0) {
1568231200Smm		archive_set_error(&a->archive,
1569231200Smm		    ARCHIVE_ERRNO_MISC,
1570231200Smm		    "xmlTextWriterStartDocument() failed: %d", r);
1571231200Smm		goto exit_toc;
1572231200Smm	}
1573231200Smm	r = xmlTextWriterSetIndent(writer, 4);
1574231200Smm	if (r < 0) {
1575231200Smm		archive_set_error(&a->archive,
1576231200Smm		    ARCHIVE_ERRNO_MISC,
1577231200Smm		    "xmlTextWriterSetIndent() failed: %d", r);
1578231200Smm		goto exit_toc;
1579231200Smm	}
1580231200Smm
1581231200Smm	/*
1582313571Smm	 * Start recording TOC
1583231200Smm	 */
1584231200Smm	r = xmlTextWriterStartElement(writer, BAD_CAST("xar"));
1585231200Smm	if (r < 0) {
1586231200Smm		archive_set_error(&a->archive,
1587231200Smm		    ARCHIVE_ERRNO_MISC,
1588231200Smm		    "xmlTextWriterStartElement() failed: %d", r);
1589231200Smm		goto exit_toc;
1590231200Smm	}
1591231200Smm	r = xmlTextWriterStartElement(writer, BAD_CAST("toc"));
1592231200Smm	if (r < 0) {
1593231200Smm		archive_set_error(&a->archive,
1594231200Smm		    ARCHIVE_ERRNO_MISC,
1595231200Smm		    "xmlTextWriterStartDocument() failed: %d", r);
1596231200Smm		goto exit_toc;
1597231200Smm	}
1598231200Smm
1599231200Smm	/*
1600231200Smm	 * Record the creation time of the archive file.
1601231200Smm	 */
1602231200Smm	r = xmlwrite_time(a, writer, "creation-time", time(NULL), 0);
1603231200Smm	if (r < 0)
1604231200Smm		goto exit_toc;
1605231200Smm
1606231200Smm	/*
1607231200Smm	 * Record the checksum value of TOC
1608231200Smm	 */
1609231200Smm	algsize = getalgsize(xar->opt_toc_sumalg);
1610231200Smm	if (algsize) {
1611231200Smm		/*
1612231200Smm		 * Record TOC checksum
1613231200Smm		 */
1614231200Smm		r = xmlTextWriterStartElement(writer, BAD_CAST("checksum"));
1615231200Smm		if (r < 0) {
1616231200Smm			archive_set_error(&a->archive,
1617231200Smm			    ARCHIVE_ERRNO_MISC,
1618231200Smm			    "xmlTextWriterStartElement() failed: %d", r);
1619231200Smm			goto exit_toc;
1620231200Smm		}
1621231200Smm		r = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"),
1622232153Smm		    BAD_CAST_CONST(getalgname(xar->opt_toc_sumalg)));
1623231200Smm		if (r < 0) {
1624231200Smm			archive_set_error(&a->archive,
1625231200Smm			    ARCHIVE_ERRNO_MISC,
1626231200Smm			    "xmlTextWriterWriteAttribute() failed: %d", r);
1627231200Smm			goto exit_toc;
1628231200Smm		}
1629231200Smm
1630231200Smm		/*
1631231200Smm		 * Record the offset of the value of checksum of TOC
1632231200Smm		 */
1633231200Smm		r = xmlwrite_string(a, writer, "offset", "0");
1634231200Smm		if (r < 0)
1635231200Smm			goto exit_toc;
1636231200Smm
1637231200Smm		/*
1638231200Smm		 * Record the size of the value of checksum of TOC
1639231200Smm		 */
1640231200Smm		r = xmlwrite_fstring(a, writer, "size", "%d", algsize);
1641231200Smm		if (r < 0)
1642231200Smm			goto exit_toc;
1643231200Smm
1644231200Smm		r = xmlTextWriterEndElement(writer);
1645231200Smm		if (r < 0) {
1646231200Smm			archive_set_error(&a->archive,
1647231200Smm			    ARCHIVE_ERRNO_MISC,
1648231200Smm			    "xmlTextWriterEndElement() failed: %d", r);
1649231200Smm			goto exit_toc;
1650231200Smm		}
1651231200Smm	}
1652231200Smm
1653231200Smm	np = xar->root;
1654231200Smm	do {
1655231200Smm		if (np != np->parent) {
1656231200Smm			r = make_file_entry(a, writer, np);
1657231200Smm			if (r != ARCHIVE_OK)
1658231200Smm				goto exit_toc;
1659231200Smm		}
1660231200Smm
1661231200Smm		if (np->dir && np->children.first != NULL) {
1662231200Smm			/* Enter to sub directories. */
1663231200Smm			np = np->children.first;
1664231200Smm			r = xmlTextWriterStartElement(writer,
1665231200Smm			    BAD_CAST("file"));
1666231200Smm			if (r < 0) {
1667231200Smm				archive_set_error(&a->archive,
1668231200Smm				    ARCHIVE_ERRNO_MISC,
1669231200Smm				    "xmlTextWriterStartElement() "
1670231200Smm				    "failed: %d", r);
1671231200Smm				goto exit_toc;
1672231200Smm			}
1673231200Smm			r = xmlTextWriterWriteFormatAttribute(
1674231200Smm			    writer, BAD_CAST("id"), "%d", np->id);
1675231200Smm			if (r < 0) {
1676231200Smm				archive_set_error(&a->archive,
1677231200Smm				    ARCHIVE_ERRNO_MISC,
1678231200Smm				    "xmlTextWriterWriteAttribute() "
1679231200Smm				    "failed: %d", r);
1680231200Smm				goto exit_toc;
1681231200Smm			}
1682231200Smm			continue;
1683231200Smm		}
1684231200Smm		while (np != np->parent) {
1685231200Smm			r = xmlTextWriterEndElement(writer);
1686231200Smm			if (r < 0) {
1687231200Smm				archive_set_error(&a->archive,
1688231200Smm				    ARCHIVE_ERRNO_MISC,
1689231200Smm				    "xmlTextWriterEndElement() "
1690231200Smm				    "failed: %d", r);
1691231200Smm				goto exit_toc;
1692231200Smm			}
1693231200Smm			if (np->chnext == NULL) {
1694231200Smm				/* Return to the parent directory. */
1695231200Smm				np = np->parent;
1696231200Smm			} else {
1697231200Smm				np = np->chnext;
1698231200Smm				r = xmlTextWriterStartElement(writer,
1699231200Smm				    BAD_CAST("file"));
1700231200Smm				if (r < 0) {
1701231200Smm					archive_set_error(&a->archive,
1702231200Smm					    ARCHIVE_ERRNO_MISC,
1703231200Smm					    "xmlTextWriterStartElement() "
1704231200Smm					    "failed: %d", r);
1705231200Smm					goto exit_toc;
1706231200Smm				}
1707231200Smm				r = xmlTextWriterWriteFormatAttribute(
1708231200Smm				    writer, BAD_CAST("id"), "%d", np->id);
1709231200Smm				if (r < 0) {
1710231200Smm					archive_set_error(&a->archive,
1711231200Smm					    ARCHIVE_ERRNO_MISC,
1712231200Smm					    "xmlTextWriterWriteAttribute() "
1713231200Smm					    "failed: %d", r);
1714231200Smm					goto exit_toc;
1715231200Smm				}
1716231200Smm				break;
1717231200Smm			}
1718231200Smm		}
1719231200Smm	} while (np != np->parent);
1720231200Smm
1721231200Smm	r = xmlTextWriterEndDocument(writer);
1722231200Smm	if (r < 0) {
1723231200Smm		archive_set_error(&a->archive,
1724231200Smm		    ARCHIVE_ERRNO_MISC,
1725231200Smm		    "xmlTextWriterEndDocument() failed: %d", r);
1726231200Smm		goto exit_toc;
1727231200Smm	}
1728231200Smm#if DEBUG_PRINT_TOC
1729231200Smm	fprintf(stderr, "\n---TOC-- %d bytes --\n%s\n",
1730231200Smm	    strlen((const char *)bp->content), bp->content);
1731231200Smm#endif
1732231200Smm
1733231200Smm	/*
1734231200Smm	 * Compress the TOC and calculate the sum of the TOC.
1735231200Smm	 */
1736231200Smm	xar->toc.temp_offset = xar->temp_offset;
1737231200Smm	xar->toc.size = bp->use;
1738231200Smm	checksum_init(&(xar->a_sumwrk), xar->opt_toc_sumalg);
1739231200Smm
1740231200Smm	r = compression_init_encoder_gzip(&(a->archive),
1741231200Smm	    &(xar->stream), 6, 1);
1742231200Smm	if (r != ARCHIVE_OK)
1743231200Smm		goto exit_toc;
1744231200Smm	xar->stream.next_in = bp->content;
1745231200Smm	xar->stream.avail_in = bp->use;
1746231200Smm	xar->stream.total_in = 0;
1747231200Smm	xar->stream.next_out = xar->wbuff;
1748231200Smm	xar->stream.avail_out = sizeof(xar->wbuff);
1749231200Smm	xar->stream.total_out = 0;
1750231200Smm	for (;;) {
1751231200Smm		size_t size;
1752231200Smm
1753231200Smm		r = compression_code(&(a->archive),
1754231200Smm		    &(xar->stream), ARCHIVE_Z_FINISH);
1755231200Smm		if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
1756231200Smm			goto exit_toc;
1757231200Smm		size = sizeof(xar->wbuff) - xar->stream.avail_out;
1758231200Smm		checksum_update(&(xar->a_sumwrk), xar->wbuff, size);
1759231200Smm		if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK)
1760231200Smm			goto exit_toc;
1761231200Smm		if (r == ARCHIVE_EOF)
1762231200Smm			break;
1763231200Smm		xar->stream.next_out = xar->wbuff;
1764231200Smm		xar->stream.avail_out = sizeof(xar->wbuff);
1765231200Smm	}
1766231200Smm	r = compression_end(&(a->archive), &(xar->stream));
1767231200Smm	if (r != ARCHIVE_OK)
1768231200Smm		goto exit_toc;
1769231200Smm	xar->toc.length = xar->stream.total_out;
1770231200Smm	xar->toc.compression = GZIP;
1771231200Smm	checksum_final(&(xar->a_sumwrk), &(xar->toc.a_sum));
1772231200Smm
1773231200Smm	ret = ARCHIVE_OK;
1774231200Smmexit_toc:
1775231200Smm	if (writer)
1776231200Smm		xmlFreeTextWriter(writer);
1777231200Smm	if (bp)
1778231200Smm		xmlBufferFree(bp);
1779231200Smm
1780231200Smm	return (ret);
1781231200Smm}
1782231200Smm
1783231200Smmstatic int
1784231200Smmflush_wbuff(struct archive_write *a)
1785231200Smm{
1786231200Smm	struct xar *xar;
1787231200Smm	int r;
1788231200Smm	size_t s;
1789231200Smm
1790231200Smm	xar = (struct xar *)a->format_data;
1791231200Smm	s = sizeof(xar->wbuff) - xar->wbuff_remaining;
1792231200Smm	r = __archive_write_output(a, xar->wbuff, s);
1793231200Smm	if (r != ARCHIVE_OK)
1794231200Smm		return (r);
1795231200Smm	xar->wbuff_remaining = sizeof(xar->wbuff);
1796231200Smm	return (r);
1797231200Smm}
1798231200Smm
1799231200Smmstatic int
1800231200Smmcopy_out(struct archive_write *a, uint64_t offset, uint64_t length)
1801231200Smm{
1802231200Smm	struct xar *xar;
1803231200Smm	int r;
1804231200Smm
1805231200Smm	xar = (struct xar *)a->format_data;
1806231200Smm	if (lseek(xar->temp_fd, offset, SEEK_SET) < 0) {
1807231200Smm		archive_set_error(&(a->archive), errno, "lseek failed");
1808231200Smm		return (ARCHIVE_FATAL);
1809231200Smm	}
1810231200Smm	while (length) {
1811231200Smm		size_t rsize;
1812231200Smm		ssize_t rs;
1813231200Smm		unsigned char *wb;
1814231200Smm
1815231200Smm		if (length > xar->wbuff_remaining)
1816231200Smm			rsize = xar->wbuff_remaining;
1817231200Smm		else
1818231200Smm			rsize = (size_t)length;
1819231200Smm		wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
1820231200Smm		rs = read(xar->temp_fd, wb, rsize);
1821231200Smm		if (rs < 0) {
1822231200Smm			archive_set_error(&(a->archive), errno,
1823231200Smm			    "Can't read temporary file(%jd)",
1824231200Smm			    (intmax_t)rs);
1825231200Smm			return (ARCHIVE_FATAL);
1826231200Smm		}
1827231200Smm		if (rs == 0) {
1828231200Smm			archive_set_error(&(a->archive), 0,
1829231200Smm			    "Truncated xar archive");
1830231200Smm			return (ARCHIVE_FATAL);
1831231200Smm		}
1832231200Smm		xar->wbuff_remaining -= rs;
1833231200Smm		length -= rs;
1834231200Smm		if (xar->wbuff_remaining == 0) {
1835231200Smm			r = flush_wbuff(a);
1836231200Smm			if (r != ARCHIVE_OK)
1837231200Smm				return (r);
1838231200Smm		}
1839231200Smm	}
1840231200Smm	return (ARCHIVE_OK);
1841231200Smm}
1842231200Smm
1843231200Smmstatic int
1844231200Smmxar_close(struct archive_write *a)
1845231200Smm{
1846231200Smm	struct xar *xar;
1847231200Smm	unsigned char *wb;
1848231200Smm	uint64_t length;
1849231200Smm	int r;
1850231200Smm
1851231200Smm	xar = (struct xar *)a->format_data;
1852231200Smm
1853231200Smm	/* Empty! */
1854231200Smm	if (xar->root->children.first == NULL)
1855231200Smm		return (ARCHIVE_OK);
1856231200Smm
1857231200Smm	/* Save the length of all file extended attributes and contents. */
1858231200Smm	length = xar->temp_offset;
1859231200Smm
1860231200Smm	/* Connect hardlinked files */
1861231200Smm	file_connect_hardlink_files(xar);
1862231200Smm
1863231200Smm	/* Make the TOC */
1864231200Smm	r = make_toc(a);
1865231200Smm	if (r != ARCHIVE_OK)
1866231200Smm		return (r);
1867231200Smm	/*
1868231200Smm	 * Make the xar header on wbuff(write buffer).
1869231200Smm	 */
1870231200Smm	wb = xar->wbuff;
1871231200Smm	xar->wbuff_remaining = sizeof(xar->wbuff);
1872231200Smm	archive_be32enc(&wb[0], HEADER_MAGIC);
1873231200Smm	archive_be16enc(&wb[4], HEADER_SIZE);
1874231200Smm	archive_be16enc(&wb[6], HEADER_VERSION);
1875231200Smm	archive_be64enc(&wb[8], xar->toc.length);
1876231200Smm	archive_be64enc(&wb[16], xar->toc.size);
1877231200Smm	archive_be32enc(&wb[24], xar->toc.a_sum.alg);
1878231200Smm	xar->wbuff_remaining -= HEADER_SIZE;
1879231200Smm
1880231200Smm	/*
1881231200Smm	 * Write the TOC
1882231200Smm	 */
1883231200Smm	r = copy_out(a, xar->toc.temp_offset, xar->toc.length);
1884231200Smm	if (r != ARCHIVE_OK)
1885231200Smm		return (r);
1886231200Smm
1887231200Smm	/* Write the checksum value of the TOC. */
1888231200Smm	if (xar->toc.a_sum.len) {
1889231200Smm		if (xar->wbuff_remaining < xar->toc.a_sum.len) {
1890231200Smm			r = flush_wbuff(a);
1891231200Smm			if (r != ARCHIVE_OK)
1892231200Smm				return (r);
1893231200Smm		}
1894231200Smm		wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
1895231200Smm		memcpy(wb, xar->toc.a_sum.val, xar->toc.a_sum.len);
1896231200Smm		xar->wbuff_remaining -= xar->toc.a_sum.len;
1897231200Smm	}
1898231200Smm
1899231200Smm	/*
1900231200Smm	 * Write all file extended attributes and contents.
1901231200Smm	 */
1902231200Smm	r = copy_out(a, xar->toc.a_sum.len, length);
1903231200Smm	if (r != ARCHIVE_OK)
1904231200Smm		return (r);
1905231200Smm	r = flush_wbuff(a);
1906231200Smm	return (r);
1907231200Smm}
1908231200Smm
1909231200Smmstatic int
1910231200Smmxar_free(struct archive_write *a)
1911231200Smm{
1912231200Smm	struct xar *xar;
1913231200Smm
1914231200Smm	xar = (struct xar *)a->format_data;
1915302001Smm
1916302001Smm	/* Close the temporary file. */
1917302001Smm	if (xar->temp_fd >= 0)
1918302001Smm		close(xar->temp_fd);
1919302001Smm
1920231200Smm	archive_string_free(&(xar->cur_dirstr));
1921231200Smm	archive_string_free(&(xar->tstr));
1922231200Smm	archive_string_free(&(xar->vstr));
1923231200Smm	file_free_hardlinks(xar);
1924231200Smm	file_free_register(xar);
1925231200Smm	compression_end(&(a->archive), &(xar->stream));
1926231200Smm	free(xar);
1927231200Smm
1928231200Smm	return (ARCHIVE_OK);
1929231200Smm}
1930231200Smm
1931231200Smmstatic int
1932231200Smmfile_cmp_node(const struct archive_rb_node *n1,
1933231200Smm    const struct archive_rb_node *n2)
1934231200Smm{
1935232153Smm	const struct file *f1 = (const struct file *)n1;
1936232153Smm	const struct file *f2 = (const struct file *)n2;
1937231200Smm
1938231200Smm	return (strcmp(f1->basename.s, f2->basename.s));
1939231200Smm}
1940302001Smm
1941231200Smmstatic int
1942231200Smmfile_cmp_key(const struct archive_rb_node *n, const void *key)
1943231200Smm{
1944232153Smm	const struct file *f = (const struct file *)n;
1945231200Smm
1946231200Smm	return (strcmp(f->basename.s, (const char *)key));
1947231200Smm}
1948231200Smm
1949231200Smmstatic struct file *
1950231200Smmfile_new(struct archive_write *a, struct archive_entry *entry)
1951231200Smm{
1952231200Smm	struct file *file;
1953231200Smm	static const struct archive_rb_tree_ops rb_ops = {
1954231200Smm		file_cmp_node, file_cmp_key
1955231200Smm	};
1956231200Smm
1957231200Smm	file = calloc(1, sizeof(*file));
1958231200Smm	if (file == NULL)
1959231200Smm		return (NULL);
1960231200Smm
1961231200Smm	if (entry != NULL)
1962231200Smm		file->entry = archive_entry_clone(entry);
1963231200Smm	else
1964231200Smm		file->entry = archive_entry_new2(&a->archive);
1965231200Smm	if (file->entry == NULL) {
1966231200Smm		free(file);
1967231200Smm		return (NULL);
1968231200Smm	}
1969231200Smm	__archive_rb_tree_init(&(file->rbtree), &rb_ops);
1970231200Smm	file->children.first = NULL;
1971231200Smm	file->children.last = &(file->children.first);
1972231200Smm	file->xattr.first = NULL;
1973231200Smm	file->xattr.last = &(file->xattr.first);
1974231200Smm	archive_string_init(&(file->parentdir));
1975231200Smm	archive_string_init(&(file->basename));
1976231200Smm	archive_string_init(&(file->symlink));
1977231200Smm	archive_string_init(&(file->script));
1978231200Smm	if (entry != NULL && archive_entry_filetype(entry) == AE_IFDIR)
1979231200Smm		file->dir = 1;
1980231200Smm
1981231200Smm	return (file);
1982231200Smm}
1983231200Smm
1984231200Smmstatic void
1985231200Smmfile_free(struct file *file)
1986231200Smm{
1987231200Smm	struct heap_data *heap, *next_heap;
1988231200Smm
1989231200Smm	heap = file->xattr.first;
1990231200Smm	while (heap != NULL) {
1991231200Smm		next_heap = heap->next;
1992231200Smm		free(heap);
1993231200Smm		heap = next_heap;
1994231200Smm	}
1995231200Smm	archive_string_free(&(file->parentdir));
1996231200Smm	archive_string_free(&(file->basename));
1997231200Smm	archive_string_free(&(file->symlink));
1998231200Smm	archive_string_free(&(file->script));
1999313571Smm	archive_entry_free(file->entry);
2000231200Smm	free(file);
2001231200Smm}
2002231200Smm
2003231200Smmstatic struct file *
2004231200Smmfile_create_virtual_dir(struct archive_write *a, struct xar *xar,
2005231200Smm    const char *pathname)
2006231200Smm{
2007231200Smm	struct file *file;
2008231200Smm
2009232153Smm	(void)xar; /* UNUSED */
2010232153Smm
2011231200Smm	file = file_new(a, NULL);
2012231200Smm	if (file == NULL)
2013231200Smm		return (NULL);
2014231200Smm	archive_entry_set_pathname(file->entry, pathname);
2015231200Smm	archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);
2016231200Smm
2017231200Smm	file->dir = 1;
2018231200Smm	file->virtual = 1;
2019231200Smm
2020231200Smm	return (file);
2021231200Smm}
2022231200Smm
2023231200Smmstatic int
2024231200Smmfile_add_child_tail(struct file *parent, struct file *child)
2025231200Smm{
2026231200Smm	if (!__archive_rb_tree_insert_node(
2027231200Smm	    &(parent->rbtree), (struct archive_rb_node *)child))
2028231200Smm		return (0);
2029231200Smm	child->chnext = NULL;
2030231200Smm	*parent->children.last = child;
2031231200Smm	parent->children.last = &(child->chnext);
2032231200Smm	child->parent = parent;
2033231200Smm	return (1);
2034231200Smm}
2035231200Smm
2036231200Smm/*
2037231200Smm * Find a entry from `parent'
2038231200Smm */
2039231200Smmstatic struct file *
2040231200Smmfile_find_child(struct file *parent, const char *child_name)
2041231200Smm{
2042231200Smm	struct file *np;
2043231200Smm
2044231200Smm	np = (struct file *)__archive_rb_tree_find_node(
2045231200Smm	    &(parent->rbtree), child_name);
2046231200Smm	return (np);
2047231200Smm}
2048231200Smm
2049231200Smm#if defined(_WIN32) || defined(__CYGWIN__)
2050231200Smmstatic void
2051231200Smmcleanup_backslash(char *utf8, size_t len)
2052231200Smm{
2053231200Smm
2054231200Smm	/* Convert a path-separator from '\' to  '/' */
2055231200Smm	while (*utf8 != '\0' && len) {
2056231200Smm		if (*utf8 == '\\')
2057231200Smm			*utf8 = '/';
2058231200Smm		++utf8;
2059231200Smm		--len;
2060231200Smm	}
2061231200Smm}
2062231200Smm#else
2063231200Smm#define cleanup_backslash(p, len)	/* nop */
2064231200Smm#endif
2065231200Smm
2066231200Smm/*
2067231200Smm * Generate a parent directory name and a base name from a pathname.
2068231200Smm */
2069231200Smmstatic int
2070231200Smmfile_gen_utility_names(struct archive_write *a, struct file *file)
2071231200Smm{
2072231200Smm	struct xar *xar;
2073231200Smm	const char *pp;
2074231200Smm	char *p, *dirname, *slash;
2075231200Smm	size_t len;
2076231200Smm	int r = ARCHIVE_OK;
2077231200Smm
2078231200Smm	xar = (struct xar *)a->format_data;
2079231200Smm	archive_string_empty(&(file->parentdir));
2080231200Smm	archive_string_empty(&(file->basename));
2081231200Smm	archive_string_empty(&(file->symlink));
2082231200Smm
2083231200Smm	if (file->parent == file)/* virtual root */
2084231200Smm		return (ARCHIVE_OK);
2085231200Smm
2086231200Smm	if (archive_entry_pathname_l(file->entry, &pp, &len, xar->sconv)
2087231200Smm	    != 0) {
2088231200Smm		if (errno == ENOMEM) {
2089231200Smm			archive_set_error(&a->archive, ENOMEM,
2090231200Smm			    "Can't allocate memory for Pathname");
2091231200Smm			return (ARCHIVE_FATAL);
2092231200Smm		}
2093231200Smm		archive_set_error(&a->archive,
2094231200Smm		    ARCHIVE_ERRNO_FILE_FORMAT,
2095231200Smm		    "Can't translate pathname '%s' to UTF-8",
2096231200Smm		    archive_entry_pathname(file->entry));
2097231200Smm		r = ARCHIVE_WARN;
2098231200Smm	}
2099231200Smm	archive_strncpy(&(file->parentdir), pp, len);
2100231200Smm	len = file->parentdir.length;
2101231200Smm	p = dirname = file->parentdir.s;
2102231200Smm	/*
2103231200Smm	 * Convert a path-separator from '\' to  '/'
2104231200Smm	 */
2105231200Smm	cleanup_backslash(p, len);
2106231200Smm
2107231200Smm	/*
2108231200Smm	 * Remove leading '/', '../' and './' elements
2109231200Smm	 */
2110231200Smm	while (*p) {
2111231200Smm		if (p[0] == '/') {
2112231200Smm			p++;
2113231200Smm			len--;
2114231200Smm		} else if (p[0] != '.')
2115231200Smm			break;
2116231200Smm		else if (p[1] == '.' && p[2] == '/') {
2117231200Smm			p += 3;
2118231200Smm			len -= 3;
2119231200Smm		} else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {
2120231200Smm			p += 2;
2121231200Smm			len -= 2;
2122231200Smm		} else if (p[1] == '\0') {
2123231200Smm			p++;
2124231200Smm			len--;
2125231200Smm		} else
2126231200Smm			break;
2127231200Smm	}
2128231200Smm	if (p != dirname) {
2129231200Smm		memmove(dirname, p, len+1);
2130231200Smm		p = dirname;
2131231200Smm	}
2132231200Smm	/*
2133231200Smm	 * Remove "/","/." and "/.." elements from tail.
2134231200Smm	 */
2135231200Smm	while (len > 0) {
2136231200Smm		size_t ll = len;
2137231200Smm
2138358090Smm		if (p[len-1] == '/') {
2139231200Smm			p[len-1] = '\0';
2140231200Smm			len--;
2141231200Smm		}
2142231200Smm		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
2143231200Smm			p[len-2] = '\0';
2144231200Smm			len -= 2;
2145231200Smm		}
2146231200Smm		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
2147231200Smm		    p[len-1] == '.') {
2148231200Smm			p[len-3] = '\0';
2149231200Smm			len -= 3;
2150231200Smm		}
2151231200Smm		if (ll == len)
2152231200Smm			break;
2153231200Smm	}
2154231200Smm	while (*p) {
2155231200Smm		if (p[0] == '/') {
2156231200Smm			if (p[1] == '/')
2157231200Smm				/* Convert '//' --> '/' */
2158231200Smm				strcpy(p, p+1);
2159231200Smm			else if (p[1] == '.' && p[2] == '/')
2160231200Smm				/* Convert '/./' --> '/' */
2161231200Smm				strcpy(p, p+2);
2162231200Smm			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
2163231200Smm				/* Convert 'dir/dir1/../dir2/'
2164231200Smm				 *     --> 'dir/dir2/'
2165231200Smm				 */
2166231200Smm				char *rp = p -1;
2167231200Smm				while (rp >= dirname) {
2168231200Smm					if (*rp == '/')
2169231200Smm						break;
2170231200Smm					--rp;
2171231200Smm				}
2172231200Smm				if (rp > dirname) {
2173231200Smm					strcpy(rp, p+3);
2174231200Smm					p = rp;
2175231200Smm				} else {
2176231200Smm					strcpy(dirname, p+4);
2177231200Smm					p = dirname;
2178231200Smm				}
2179231200Smm			} else
2180231200Smm				p++;
2181231200Smm		} else
2182231200Smm			p++;
2183231200Smm	}
2184231200Smm	p = dirname;
2185231200Smm	len = strlen(p);
2186231200Smm
2187231200Smm	if (archive_entry_filetype(file->entry) == AE_IFLNK) {
2188231200Smm		size_t len2;
2189231200Smm		/* Convert symlink name too. */
2190231200Smm		if (archive_entry_symlink_l(file->entry, &pp, &len2,
2191231200Smm		    xar->sconv) != 0) {
2192231200Smm			if (errno == ENOMEM) {
2193231200Smm				archive_set_error(&a->archive, ENOMEM,
2194231200Smm				    "Can't allocate memory for Linkname");
2195231200Smm				return (ARCHIVE_FATAL);
2196231200Smm			}
2197231200Smm			archive_set_error(&a->archive,
2198231200Smm			    ARCHIVE_ERRNO_FILE_FORMAT,
2199231200Smm			    "Can't translate symlink '%s' to UTF-8",
2200231200Smm			    archive_entry_symlink(file->entry));
2201231200Smm			r = ARCHIVE_WARN;
2202231200Smm		}
2203231200Smm		archive_strncpy(&(file->symlink), pp, len2);
2204231200Smm		cleanup_backslash(file->symlink.s, file->symlink.length);
2205231200Smm	}
2206231200Smm	/*
2207231200Smm	 * - Count up directory elements.
2208231200Smm	 * - Find out the position which points the last position of
2209231200Smm	 *   path separator('/').
2210231200Smm	 */
2211231200Smm	slash = NULL;
2212231200Smm	for (; *p != '\0'; p++)
2213231200Smm		if (*p == '/')
2214231200Smm			slash = p;
2215231200Smm	if (slash == NULL) {
2216231200Smm		/* The pathname doesn't have a parent directory. */
2217231200Smm		file->parentdir.length = len;
2218231200Smm		archive_string_copy(&(file->basename), &(file->parentdir));
2219231200Smm		archive_string_empty(&(file->parentdir));
2220302001Smm		*file->parentdir.s = '\0';
2221231200Smm		return (r);
2222231200Smm	}
2223231200Smm
2224231200Smm	/* Make a basename from dirname and slash */
2225231200Smm	*slash  = '\0';
2226231200Smm	file->parentdir.length = slash - dirname;
2227231200Smm	archive_strcpy(&(file->basename),  slash + 1);
2228231200Smm	return (r);
2229231200Smm}
2230231200Smm
2231231200Smmstatic int
2232231200Smmget_path_component(char *name, int n, const char *fn)
2233231200Smm{
2234231200Smm	char *p;
2235231200Smm	int l;
2236231200Smm
2237231200Smm	p = strchr(fn, '/');
2238231200Smm	if (p == NULL) {
2239231200Smm		if ((l = strlen(fn)) == 0)
2240231200Smm			return (0);
2241231200Smm	} else
2242231200Smm		l = p - fn;
2243231200Smm	if (l > n -1)
2244231200Smm		return (-1);
2245231200Smm	memcpy(name, fn, l);
2246231200Smm	name[l] = '\0';
2247231200Smm
2248231200Smm	return (l);
2249231200Smm}
2250231200Smm
2251231200Smm/*
2252231200Smm * Add a new entry into the tree.
2253231200Smm */
2254231200Smmstatic int
2255231200Smmfile_tree(struct archive_write *a, struct file **filepp)
2256231200Smm{
2257231200Smm#if defined(_WIN32) && !defined(__CYGWIN__)
2258231200Smm	char name[_MAX_FNAME];/* Included null terminator size. */
2259231200Smm#elif defined(NAME_MAX) && NAME_MAX >= 255
2260231200Smm	char name[NAME_MAX+1];
2261231200Smm#else
2262231200Smm	char name[256];
2263231200Smm#endif
2264231200Smm	struct xar *xar = (struct xar *)a->format_data;
2265231200Smm	struct file *dent, *file, *np;
2266231200Smm	struct archive_entry *ent;
2267231200Smm	const char *fn, *p;
2268231200Smm	int l;
2269231200Smm
2270231200Smm	file = *filepp;
2271231200Smm	dent = xar->root;
2272231200Smm	if (file->parentdir.length > 0)
2273231200Smm		fn = p = file->parentdir.s;
2274231200Smm	else
2275231200Smm		fn = p = "";
2276231200Smm
2277231200Smm	/*
2278231200Smm	 * If the path of the parent directory of `file' entry is
2279231200Smm	 * the same as the path of `cur_dirent', add isoent to
2280231200Smm	 * `cur_dirent'.
2281231200Smm	 */
2282231200Smm	if (archive_strlen(&(xar->cur_dirstr))
2283231200Smm	      == archive_strlen(&(file->parentdir)) &&
2284231200Smm	    strcmp(xar->cur_dirstr.s, fn) == 0) {
2285231200Smm		if (!file_add_child_tail(xar->cur_dirent, file)) {
2286231200Smm			np = (struct file *)__archive_rb_tree_find_node(
2287231200Smm			    &(xar->cur_dirent->rbtree),
2288231200Smm			    file->basename.s);
2289231200Smm			goto same_entry;
2290231200Smm		}
2291231200Smm		return (ARCHIVE_OK);
2292231200Smm	}
2293231200Smm
2294231200Smm	for (;;) {
2295231200Smm		l = get_path_component(name, sizeof(name), fn);
2296231200Smm		if (l == 0) {
2297231200Smm			np = NULL;
2298231200Smm			break;
2299231200Smm		}
2300231200Smm		if (l < 0) {
2301231200Smm			archive_set_error(&a->archive,
2302231200Smm			    ARCHIVE_ERRNO_MISC,
2303231200Smm			    "A name buffer is too small");
2304231200Smm			file_free(file);
2305231200Smm			*filepp = NULL;
2306231200Smm			return (ARCHIVE_FATAL);
2307231200Smm		}
2308231200Smm
2309231200Smm		np = file_find_child(dent, name);
2310231200Smm		if (np == NULL || fn[0] == '\0')
2311231200Smm			break;
2312231200Smm
2313231200Smm		/* Find next subdirectory. */
2314231200Smm		if (!np->dir) {
2315231200Smm			/* NOT Directory! */
2316231200Smm			archive_set_error(&a->archive,
2317231200Smm			    ARCHIVE_ERRNO_MISC,
2318231200Smm			    "`%s' is not directory, we cannot insert `%s' ",
2319231200Smm			    archive_entry_pathname(np->entry),
2320231200Smm			    archive_entry_pathname(file->entry));
2321231200Smm			file_free(file);
2322231200Smm			*filepp = NULL;
2323231200Smm			return (ARCHIVE_FAILED);
2324231200Smm		}
2325231200Smm		fn += l;
2326231200Smm		if (fn[0] == '/')
2327231200Smm			fn++;
2328231200Smm		dent = np;
2329231200Smm	}
2330231200Smm	if (np == NULL) {
2331231200Smm		/*
2332231200Smm		 * Create virtual parent directories.
2333231200Smm		 */
2334231200Smm		while (fn[0] != '\0') {
2335231200Smm			struct file *vp;
2336231200Smm			struct archive_string as;
2337231200Smm
2338231200Smm			archive_string_init(&as);
2339231200Smm			archive_strncat(&as, p, fn - p + l);
2340231200Smm			if (as.s[as.length-1] == '/') {
2341231200Smm				as.s[as.length-1] = '\0';
2342231200Smm				as.length--;
2343231200Smm			}
2344231200Smm			vp = file_create_virtual_dir(a, xar, as.s);
2345231200Smm			if (vp == NULL) {
2346231200Smm				archive_string_free(&as);
2347231200Smm				archive_set_error(&a->archive, ENOMEM,
2348231200Smm				    "Can't allocate memory");
2349231200Smm				file_free(file);
2350231200Smm				*filepp = NULL;
2351231200Smm				return (ARCHIVE_FATAL);
2352231200Smm			}
2353231200Smm			archive_string_free(&as);
2354231200Smm			if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED)
2355231200Smm				return (ARCHIVE_FATAL);
2356231200Smm			file_add_child_tail(dent, vp);
2357231200Smm			file_register(xar, vp);
2358231200Smm			np = vp;
2359231200Smm
2360231200Smm			fn += l;
2361231200Smm			if (fn[0] == '/')
2362231200Smm				fn++;
2363231200Smm			l = get_path_component(name, sizeof(name), fn);
2364231200Smm			if (l < 0) {
2365231200Smm				archive_string_free(&as);
2366231200Smm				archive_set_error(&a->archive,
2367231200Smm				    ARCHIVE_ERRNO_MISC,
2368231200Smm				    "A name buffer is too small");
2369231200Smm				file_free(file);
2370231200Smm				*filepp = NULL;
2371231200Smm				return (ARCHIVE_FATAL);
2372231200Smm			}
2373231200Smm			dent = np;
2374231200Smm		}
2375231200Smm
2376231200Smm		/* Found out the parent directory where isoent can be
2377231200Smm		 * inserted. */
2378231200Smm		xar->cur_dirent = dent;
2379231200Smm		archive_string_empty(&(xar->cur_dirstr));
2380231200Smm		archive_string_ensure(&(xar->cur_dirstr),
2381231200Smm		    archive_strlen(&(dent->parentdir)) +
2382231200Smm		    archive_strlen(&(dent->basename)) + 2);
2383231200Smm		if (archive_strlen(&(dent->parentdir)) +
2384231200Smm		    archive_strlen(&(dent->basename)) == 0)
2385231200Smm			xar->cur_dirstr.s[0] = 0;
2386231200Smm		else {
2387231200Smm			if (archive_strlen(&(dent->parentdir)) > 0) {
2388231200Smm				archive_string_copy(&(xar->cur_dirstr),
2389231200Smm				    &(dent->parentdir));
2390231200Smm				archive_strappend_char(&(xar->cur_dirstr), '/');
2391231200Smm			}
2392231200Smm			archive_string_concat(&(xar->cur_dirstr),
2393231200Smm			    &(dent->basename));
2394231200Smm		}
2395231200Smm
2396231200Smm		if (!file_add_child_tail(dent, file)) {
2397231200Smm			np = (struct file *)__archive_rb_tree_find_node(
2398231200Smm			    &(dent->rbtree), file->basename.s);
2399231200Smm			goto same_entry;
2400231200Smm		}
2401231200Smm		return (ARCHIVE_OK);
2402231200Smm	}
2403231200Smm
2404231200Smmsame_entry:
2405231200Smm	/*
2406231200Smm	 * We have already has the entry the filename of which is
2407231200Smm	 * the same.
2408231200Smm	 */
2409231200Smm	if (archive_entry_filetype(np->entry) !=
2410231200Smm	    archive_entry_filetype(file->entry)) {
2411231200Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
2412231200Smm		    "Found duplicate entries `%s' and its file type is "
2413231200Smm		    "different",
2414231200Smm		    archive_entry_pathname(np->entry));
2415231200Smm		file_free(file);
2416231200Smm		*filepp = NULL;
2417231200Smm		return (ARCHIVE_FAILED);
2418231200Smm	}
2419231200Smm
2420231200Smm	/* Swap files. */
2421231200Smm	ent = np->entry;
2422231200Smm	np->entry = file->entry;
2423231200Smm	file->entry = ent;
2424231200Smm	np->virtual = 0;
2425231200Smm
2426231200Smm	file_free(file);
2427231200Smm	*filepp = np;
2428231200Smm	return (ARCHIVE_OK);
2429231200Smm}
2430231200Smm
2431231200Smmstatic void
2432231200Smmfile_register(struct xar *xar, struct file *file)
2433231200Smm{
2434231200Smm	file->id = xar->file_idx++;
2435231200Smm        file->next = NULL;
2436231200Smm        *xar->file_list.last = file;
2437231200Smm        xar->file_list.last = &(file->next);
2438231200Smm}
2439231200Smm
2440231200Smmstatic void
2441231200Smmfile_init_register(struct xar *xar)
2442231200Smm{
2443231200Smm	xar->file_list.first = NULL;
2444231200Smm	xar->file_list.last = &(xar->file_list.first);
2445231200Smm}
2446231200Smm
2447231200Smmstatic void
2448231200Smmfile_free_register(struct xar *xar)
2449231200Smm{
2450231200Smm	struct file *file, *file_next;
2451231200Smm
2452231200Smm	file = xar->file_list.first;
2453231200Smm	while (file != NULL) {
2454231200Smm		file_next = file->next;
2455231200Smm		file_free(file);
2456231200Smm		file = file_next;
2457231200Smm	}
2458231200Smm}
2459231200Smm
2460231200Smm/*
2461231200Smm * Register entry to get a hardlink target.
2462231200Smm */
2463231200Smmstatic int
2464231200Smmfile_register_hardlink(struct archive_write *a, struct file *file)
2465231200Smm{
2466231200Smm	struct xar *xar = (struct xar *)a->format_data;
2467231200Smm	struct hardlink *hl;
2468231200Smm	const char *pathname;
2469231200Smm
2470231200Smm	archive_entry_set_nlink(file->entry, 1);
2471231200Smm	pathname = archive_entry_hardlink(file->entry);
2472231200Smm	if (pathname == NULL) {
2473231200Smm		/* This `file` is a hardlink target. */
2474231200Smm		hl = malloc(sizeof(*hl));
2475231200Smm		if (hl == NULL) {
2476231200Smm			archive_set_error(&a->archive, ENOMEM,
2477231200Smm			    "Can't allocate memory");
2478231200Smm			return (ARCHIVE_FATAL);
2479231200Smm		}
2480231200Smm		hl->nlink = 1;
2481231200Smm		/* A hardlink target must be the first position. */
2482231200Smm		file->hlnext = NULL;
2483231200Smm		hl->file_list.first = file;
2484231200Smm		hl->file_list.last = &(file->hlnext);
2485231200Smm		__archive_rb_tree_insert_node(&(xar->hardlink_rbtree),
2486231200Smm		    (struct archive_rb_node *)hl);
2487231200Smm	} else {
2488231200Smm		hl = (struct hardlink *)__archive_rb_tree_find_node(
2489231200Smm		    &(xar->hardlink_rbtree), pathname);
2490231200Smm		if (hl != NULL) {
2491231200Smm			/* Insert `file` entry into the tail. */
2492231200Smm			file->hlnext = NULL;
2493231200Smm			*hl->file_list.last = file;
2494231200Smm			hl->file_list.last = &(file->hlnext);
2495231200Smm			hl->nlink++;
2496231200Smm		}
2497231200Smm		archive_entry_unset_size(file->entry);
2498231200Smm	}
2499231200Smm
2500231200Smm	return (ARCHIVE_OK);
2501231200Smm}
2502231200Smm
2503231200Smm/*
2504231200Smm * Hardlinked files have to have the same location of extent.
2505231200Smm * We have to find out hardlink target entries for entries which
2506231200Smm * have a hardlink target name.
2507231200Smm */
2508231200Smmstatic void
2509231200Smmfile_connect_hardlink_files(struct xar *xar)
2510231200Smm{
2511231200Smm	struct archive_rb_node *n;
2512231200Smm	struct hardlink *hl;
2513231200Smm	struct file *target, *nf;
2514231200Smm
2515231200Smm	ARCHIVE_RB_TREE_FOREACH(n, &(xar->hardlink_rbtree)) {
2516231200Smm		hl = (struct hardlink *)n;
2517231200Smm
2518231200Smm		/* The first entry must be a hardlink target. */
2519231200Smm		target = hl->file_list.first;
2520231200Smm		archive_entry_set_nlink(target->entry, hl->nlink);
2521231200Smm		if (hl->nlink > 1)
2522231200Smm			/* It means this file is a hardlink
2523313571Smm			 * target itself. */
2524231200Smm			target->hardlink_target = target;
2525231200Smm		for (nf = target->hlnext;
2526231200Smm		    nf != NULL; nf = nf->hlnext) {
2527231200Smm			nf->hardlink_target = target;
2528231200Smm			archive_entry_set_nlink(nf->entry, hl->nlink);
2529231200Smm		}
2530231200Smm	}
2531231200Smm}
2532231200Smm
2533231200Smmstatic int
2534231200Smmfile_hd_cmp_node(const struct archive_rb_node *n1,
2535231200Smm    const struct archive_rb_node *n2)
2536231200Smm{
2537232153Smm	const struct hardlink *h1 = (const struct hardlink *)n1;
2538232153Smm	const struct hardlink *h2 = (const struct hardlink *)n2;
2539231200Smm
2540231200Smm	return (strcmp(archive_entry_pathname(h1->file_list.first->entry),
2541231200Smm		       archive_entry_pathname(h2->file_list.first->entry)));
2542231200Smm}
2543231200Smm
2544231200Smmstatic int
2545231200Smmfile_hd_cmp_key(const struct archive_rb_node *n, const void *key)
2546231200Smm{
2547232153Smm	const struct hardlink *h = (const struct hardlink *)n;
2548231200Smm
2549231200Smm	return (strcmp(archive_entry_pathname(h->file_list.first->entry),
2550231200Smm		       (const char *)key));
2551231200Smm}
2552231200Smm
2553231200Smm
2554231200Smmstatic void
2555231200Smmfile_init_hardlinks(struct xar *xar)
2556231200Smm{
2557231200Smm	static const struct archive_rb_tree_ops rb_ops = {
2558231200Smm		file_hd_cmp_node, file_hd_cmp_key,
2559231200Smm	};
2560302001Smm
2561231200Smm	__archive_rb_tree_init(&(xar->hardlink_rbtree), &rb_ops);
2562231200Smm}
2563231200Smm
2564231200Smmstatic void
2565231200Smmfile_free_hardlinks(struct xar *xar)
2566231200Smm{
2567358090Smm	struct archive_rb_node *n, *tmp;
2568231200Smm
2569358090Smm	ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(xar->hardlink_rbtree), tmp) {
2570358090Smm		__archive_rb_tree_remove_node(&(xar->hardlink_rbtree), n);
2571231200Smm		free(n);
2572231200Smm	}
2573231200Smm}
2574231200Smm
2575231200Smmstatic void
2576231200Smmchecksum_init(struct chksumwork *sumwrk, enum sumalg sum_alg)
2577231200Smm{
2578231200Smm	sumwrk->alg = sum_alg;
2579231200Smm	switch (sum_alg) {
2580231200Smm	case CKSUM_NONE:
2581231200Smm		break;
2582231200Smm	case CKSUM_SHA1:
2583231200Smm		archive_sha1_init(&(sumwrk->sha1ctx));
2584231200Smm		break;
2585231200Smm	case CKSUM_MD5:
2586231200Smm		archive_md5_init(&(sumwrk->md5ctx));
2587231200Smm		break;
2588231200Smm	}
2589231200Smm}
2590231200Smm
2591231200Smmstatic void
2592231200Smmchecksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
2593231200Smm{
2594231200Smm
2595231200Smm	switch (sumwrk->alg) {
2596231200Smm	case CKSUM_NONE:
2597231200Smm		break;
2598231200Smm	case CKSUM_SHA1:
2599231200Smm		archive_sha1_update(&(sumwrk->sha1ctx), buff, size);
2600231200Smm		break;
2601231200Smm	case CKSUM_MD5:
2602231200Smm		archive_md5_update(&(sumwrk->md5ctx), buff, size);
2603231200Smm		break;
2604231200Smm	}
2605231200Smm}
2606231200Smm
2607231200Smmstatic void
2608231200Smmchecksum_final(struct chksumwork *sumwrk, struct chksumval *sumval)
2609231200Smm{
2610231200Smm
2611231200Smm	switch (sumwrk->alg) {
2612231200Smm	case CKSUM_NONE:
2613231200Smm		sumval->len = 0;
2614231200Smm		break;
2615231200Smm	case CKSUM_SHA1:
2616231200Smm		archive_sha1_final(&(sumwrk->sha1ctx), sumval->val);
2617231200Smm		sumval->len = SHA1_SIZE;
2618231200Smm		break;
2619231200Smm	case CKSUM_MD5:
2620231200Smm		archive_md5_final(&(sumwrk->md5ctx), sumval->val);
2621231200Smm		sumval->len = MD5_SIZE;
2622231200Smm		break;
2623231200Smm	}
2624231200Smm	sumval->alg = sumwrk->alg;
2625231200Smm}
2626231200Smm
2627231200Smm#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)
2628231200Smmstatic int
2629231200Smmcompression_unsupported_encoder(struct archive *a,
2630231200Smm    struct la_zstream *lastrm, const char *name)
2631231200Smm{
2632231200Smm
2633231200Smm	archive_set_error(a, ARCHIVE_ERRNO_MISC,
2634231200Smm	    "%s compression not supported on this platform", name);
2635231200Smm	lastrm->valid = 0;
2636231200Smm	lastrm->real_stream = NULL;
2637231200Smm	return (ARCHIVE_FAILED);
2638231200Smm}
2639231200Smm#endif
2640231200Smm
2641231200Smmstatic int
2642231200Smmcompression_init_encoder_gzip(struct archive *a,
2643231200Smm    struct la_zstream *lastrm, int level, int withheader)
2644231200Smm{
2645231200Smm	z_stream *strm;
2646231200Smm
2647231200Smm	if (lastrm->valid)
2648231200Smm		compression_end(a, lastrm);
2649231200Smm	strm = calloc(1, sizeof(*strm));
2650231200Smm	if (strm == NULL) {
2651231200Smm		archive_set_error(a, ENOMEM,
2652231200Smm		    "Can't allocate memory for gzip stream");
2653231200Smm		return (ARCHIVE_FATAL);
2654231200Smm	}
2655231200Smm	/* zlib.h is not const-correct, so we need this one bit
2656231200Smm	 * of ugly hackery to convert a const * pointer to
2657231200Smm	 * a non-const pointer. */
2658231200Smm	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
2659231200Smm	strm->avail_in = lastrm->avail_in;
2660238856Smm	strm->total_in = (uLong)lastrm->total_in;
2661231200Smm	strm->next_out = lastrm->next_out;
2662231200Smm	strm->avail_out = lastrm->avail_out;
2663238856Smm	strm->total_out = (uLong)lastrm->total_out;
2664231200Smm	if (deflateInit2(strm, level, Z_DEFLATED,
2665231200Smm	    (withheader)?15:-15,
2666231200Smm	    8, Z_DEFAULT_STRATEGY) != Z_OK) {
2667231200Smm		free(strm);
2668231200Smm		lastrm->real_stream = NULL;
2669231200Smm		archive_set_error(a, ARCHIVE_ERRNO_MISC,
2670231200Smm		    "Internal error initializing compression library");
2671231200Smm		return (ARCHIVE_FATAL);
2672231200Smm	}
2673231200Smm	lastrm->real_stream = strm;
2674231200Smm	lastrm->valid = 1;
2675231200Smm	lastrm->code = compression_code_gzip;
2676231200Smm	lastrm->end = compression_end_gzip;
2677231200Smm	return (ARCHIVE_OK);
2678231200Smm}
2679231200Smm
2680231200Smmstatic int
2681231200Smmcompression_code_gzip(struct archive *a,
2682231200Smm    struct la_zstream *lastrm, enum la_zaction action)
2683231200Smm{
2684231200Smm	z_stream *strm;
2685231200Smm	int r;
2686231200Smm
2687231200Smm	strm = (z_stream *)lastrm->real_stream;
2688231200Smm	/* zlib.h is not const-correct, so we need this one bit
2689231200Smm	 * of ugly hackery to convert a const * pointer to
2690231200Smm	 * a non-const pointer. */
2691231200Smm	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
2692231200Smm	strm->avail_in = lastrm->avail_in;
2693238856Smm	strm->total_in = (uLong)lastrm->total_in;
2694231200Smm	strm->next_out = lastrm->next_out;
2695231200Smm	strm->avail_out = lastrm->avail_out;
2696238856Smm	strm->total_out = (uLong)lastrm->total_out;
2697231200Smm	r = deflate(strm,
2698231200Smm	    (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);
2699231200Smm	lastrm->next_in = strm->next_in;
2700231200Smm	lastrm->avail_in = strm->avail_in;
2701231200Smm	lastrm->total_in = strm->total_in;
2702231200Smm	lastrm->next_out = strm->next_out;
2703231200Smm	lastrm->avail_out = strm->avail_out;
2704231200Smm	lastrm->total_out = strm->total_out;
2705231200Smm	switch (r) {
2706231200Smm	case Z_OK:
2707231200Smm		return (ARCHIVE_OK);
2708231200Smm	case Z_STREAM_END:
2709231200Smm		return (ARCHIVE_EOF);
2710231200Smm	default:
2711231200Smm		archive_set_error(a, ARCHIVE_ERRNO_MISC,
2712231200Smm		    "GZip compression failed:"
2713231200Smm		    " deflate() call returned status %d", r);
2714231200Smm		return (ARCHIVE_FATAL);
2715231200Smm	}
2716231200Smm}
2717231200Smm
2718231200Smmstatic int
2719231200Smmcompression_end_gzip(struct archive *a, struct la_zstream *lastrm)
2720231200Smm{
2721231200Smm	z_stream *strm;
2722231200Smm	int r;
2723231200Smm
2724231200Smm	strm = (z_stream *)lastrm->real_stream;
2725231200Smm	r = deflateEnd(strm);
2726231200Smm	free(strm);
2727231200Smm	lastrm->real_stream = NULL;
2728231200Smm	lastrm->valid = 0;
2729231200Smm	if (r != Z_OK) {
2730231200Smm		archive_set_error(a, ARCHIVE_ERRNO_MISC,
2731231200Smm		    "Failed to clean up compressor");
2732231200Smm		return (ARCHIVE_FATAL);
2733231200Smm	}
2734231200Smm	return (ARCHIVE_OK);
2735231200Smm}
2736231200Smm
2737231200Smm#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
2738231200Smmstatic int
2739231200Smmcompression_init_encoder_bzip2(struct archive *a,
2740231200Smm    struct la_zstream *lastrm, int level)
2741231200Smm{
2742231200Smm	bz_stream *strm;
2743231200Smm
2744231200Smm	if (lastrm->valid)
2745231200Smm		compression_end(a, lastrm);
2746231200Smm	strm = calloc(1, sizeof(*strm));
2747231200Smm	if (strm == NULL) {
2748231200Smm		archive_set_error(a, ENOMEM,
2749231200Smm		    "Can't allocate memory for bzip2 stream");
2750231200Smm		return (ARCHIVE_FATAL);
2751231200Smm	}
2752231200Smm	/* bzlib.h is not const-correct, so we need this one bit
2753231200Smm	 * of ugly hackery to convert a const * pointer to
2754231200Smm	 * a non-const pointer. */
2755231200Smm	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
2756231200Smm	strm->avail_in = lastrm->avail_in;
2757231200Smm	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
2758231200Smm	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
2759231200Smm	strm->next_out = (char *)lastrm->next_out;
2760231200Smm	strm->avail_out = lastrm->avail_out;
2761231200Smm	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
2762231200Smm	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
2763231200Smm	if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
2764231200Smm		free(strm);
2765231200Smm		lastrm->real_stream = NULL;
2766231200Smm		archive_set_error(a, ARCHIVE_ERRNO_MISC,
2767231200Smm		    "Internal error initializing compression library");
2768231200Smm		return (ARCHIVE_FATAL);
2769231200Smm	}
2770231200Smm	lastrm->real_stream = strm;
2771231200Smm	lastrm->valid = 1;
2772231200Smm	lastrm->code = compression_code_bzip2;
2773231200Smm	lastrm->end = compression_end_bzip2;
2774231200Smm	return (ARCHIVE_OK);
2775231200Smm}
2776231200Smm
2777231200Smmstatic int
2778231200Smmcompression_code_bzip2(struct archive *a,
2779231200Smm    struct la_zstream *lastrm, enum la_zaction action)
2780231200Smm{
2781231200Smm	bz_stream *strm;
2782231200Smm	int r;
2783231200Smm
2784231200Smm	strm = (bz_stream *)lastrm->real_stream;
2785231200Smm	/* bzlib.h is not const-correct, so we need this one bit
2786231200Smm	 * of ugly hackery to convert a const * pointer to
2787231200Smm	 * a non-const pointer. */
2788231200Smm	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
2789231200Smm	strm->avail_in = lastrm->avail_in;
2790231200Smm	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
2791231200Smm	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
2792231200Smm	strm->next_out = (char *)lastrm->next_out;
2793231200Smm	strm->avail_out = lastrm->avail_out;
2794231200Smm	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
2795231200Smm	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
2796231200Smm	r = BZ2_bzCompress(strm,
2797231200Smm	    (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);
2798231200Smm	lastrm->next_in = (const unsigned char *)strm->next_in;
2799231200Smm	lastrm->avail_in = strm->avail_in;
2800231200Smm	lastrm->total_in =
2801231200Smm	    (((uint64_t)(uint32_t)strm->total_in_hi32) << 32)
2802231200Smm	    + (uint64_t)(uint32_t)strm->total_in_lo32;
2803231200Smm	lastrm->next_out = (unsigned char *)strm->next_out;
2804231200Smm	lastrm->avail_out = strm->avail_out;
2805231200Smm	lastrm->total_out =
2806231200Smm	    (((uint64_t)(uint32_t)strm->total_out_hi32) << 32)
2807231200Smm	    + (uint64_t)(uint32_t)strm->total_out_lo32;
2808231200Smm	switch (r) {
2809231200Smm	case BZ_RUN_OK:     /* Non-finishing */
2810231200Smm	case BZ_FINISH_OK:  /* Finishing: There's more work to do */
2811231200Smm		return (ARCHIVE_OK);
2812231200Smm	case BZ_STREAM_END: /* Finishing: all done */
2813231200Smm		/* Only occurs in finishing case */
2814231200Smm		return (ARCHIVE_EOF);
2815231200Smm	default:
2816231200Smm		/* Any other return value indicates an error */
2817231200Smm		archive_set_error(a, ARCHIVE_ERRNO_MISC,
2818231200Smm		    "Bzip2 compression failed:"
2819231200Smm		    " BZ2_bzCompress() call returned status %d", r);
2820231200Smm		return (ARCHIVE_FATAL);
2821231200Smm	}
2822231200Smm}
2823231200Smm
2824231200Smmstatic int
2825231200Smmcompression_end_bzip2(struct archive *a, struct la_zstream *lastrm)
2826231200Smm{
2827231200Smm	bz_stream *strm;
2828231200Smm	int r;
2829231200Smm
2830231200Smm	strm = (bz_stream *)lastrm->real_stream;
2831231200Smm	r = BZ2_bzCompressEnd(strm);
2832231200Smm	free(strm);
2833231200Smm	lastrm->real_stream = NULL;
2834231200Smm	lastrm->valid = 0;
2835231200Smm	if (r != BZ_OK) {
2836231200Smm		archive_set_error(a, ARCHIVE_ERRNO_MISC,
2837231200Smm		    "Failed to clean up compressor");
2838231200Smm		return (ARCHIVE_FATAL);
2839231200Smm	}
2840231200Smm	return (ARCHIVE_OK);
2841231200Smm}
2842231200Smm
2843231200Smm#else
2844231200Smmstatic int
2845231200Smmcompression_init_encoder_bzip2(struct archive *a,
2846231200Smm    struct la_zstream *lastrm, int level)
2847231200Smm{
2848231200Smm
2849231200Smm	(void) level; /* UNUSED */
2850231200Smm	if (lastrm->valid)
2851231200Smm		compression_end(a, lastrm);
2852231200Smm	return (compression_unsupported_encoder(a, lastrm, "bzip2"));
2853231200Smm}
2854231200Smm#endif
2855231200Smm
2856231200Smm#if defined(HAVE_LZMA_H)
2857231200Smmstatic int
2858231200Smmcompression_init_encoder_lzma(struct archive *a,
2859231200Smm    struct la_zstream *lastrm, int level)
2860231200Smm{
2861231200Smm	static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
2862231200Smm	lzma_stream *strm;
2863231200Smm	lzma_options_lzma lzma_opt;
2864231200Smm	int r;
2865231200Smm
2866231200Smm	if (lastrm->valid)
2867231200Smm		compression_end(a, lastrm);
2868231200Smm	if (lzma_lzma_preset(&lzma_opt, level)) {
2869231200Smm		lastrm->real_stream = NULL;
2870231200Smm		archive_set_error(a, ENOMEM,
2871231200Smm		    "Internal error initializing compression library");
2872231200Smm		return (ARCHIVE_FATAL);
2873231200Smm	}
2874231200Smm	strm = calloc(1, sizeof(*strm));
2875231200Smm	if (strm == NULL) {
2876231200Smm		archive_set_error(a, ENOMEM,
2877231200Smm		    "Can't allocate memory for lzma stream");
2878231200Smm		return (ARCHIVE_FATAL);
2879231200Smm	}
2880231200Smm	*strm = lzma_init_data;
2881231200Smm	r = lzma_alone_encoder(strm, &lzma_opt);
2882231200Smm	switch (r) {
2883231200Smm	case LZMA_OK:
2884231200Smm		lastrm->real_stream = strm;
2885231200Smm		lastrm->valid = 1;
2886231200Smm		lastrm->code = compression_code_lzma;
2887231200Smm		lastrm->end = compression_end_lzma;
2888231200Smm		r = ARCHIVE_OK;
2889231200Smm		break;
2890231200Smm	case LZMA_MEM_ERROR:
2891231200Smm		free(strm);
2892231200Smm		lastrm->real_stream = NULL;
2893231200Smm		archive_set_error(a, ENOMEM,
2894231200Smm		    "Internal error initializing compression library: "
2895231200Smm		    "Cannot allocate memory");
2896231200Smm		r =  ARCHIVE_FATAL;
2897231200Smm		break;
2898231200Smm        default:
2899231200Smm		free(strm);
2900231200Smm		lastrm->real_stream = NULL;
2901231200Smm		archive_set_error(a, ARCHIVE_ERRNO_MISC,
2902231200Smm		    "Internal error initializing compression library: "
2903231200Smm		    "It's a bug in liblzma");
2904231200Smm		r =  ARCHIVE_FATAL;
2905231200Smm		break;
2906231200Smm	}
2907231200Smm	return (r);
2908231200Smm}
2909231200Smm
2910231200Smmstatic int
2911231200Smmcompression_init_encoder_xz(struct archive *a,
2912302001Smm    struct la_zstream *lastrm, int level, int threads)
2913231200Smm{
2914231200Smm	static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
2915231200Smm	lzma_stream *strm;
2916231200Smm	lzma_filter *lzmafilters;
2917231200Smm	lzma_options_lzma lzma_opt;
2918231200Smm	int r;
2919302001Smm#ifdef HAVE_LZMA_STREAM_ENCODER_MT
2920302001Smm	lzma_mt mt_options;
2921302001Smm#endif
2922231200Smm
2923302001Smm	(void)threads; /* UNUSED (if multi-threaded LZMA library not avail) */
2924302001Smm
2925231200Smm	if (lastrm->valid)
2926231200Smm		compression_end(a, lastrm);
2927231200Smm	strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);
2928231200Smm	if (strm == NULL) {
2929231200Smm		archive_set_error(a, ENOMEM,
2930231200Smm		    "Can't allocate memory for xz stream");
2931231200Smm		return (ARCHIVE_FATAL);
2932231200Smm	}
2933231200Smm	lzmafilters = (lzma_filter *)(strm+1);
2934368708Smm	if (level > 9)
2935368708Smm		level = 9;
2936231200Smm	if (lzma_lzma_preset(&lzma_opt, level)) {
2937238856Smm		free(strm);
2938231200Smm		lastrm->real_stream = NULL;
2939231200Smm		archive_set_error(a, ENOMEM,
2940231200Smm		    "Internal error initializing compression library");
2941231200Smm		return (ARCHIVE_FATAL);
2942231200Smm	}
2943231200Smm	lzmafilters[0].id = LZMA_FILTER_LZMA2;
2944231200Smm	lzmafilters[0].options = &lzma_opt;
2945231200Smm	lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
2946231200Smm
2947231200Smm	*strm = lzma_init_data;
2948302001Smm#ifdef HAVE_LZMA_STREAM_ENCODER_MT
2949302001Smm	if (threads > 1) {
2950313571Smm		memset(&mt_options, 0, sizeof(mt_options));
2951302001Smm		mt_options.threads = threads;
2952302001Smm		mt_options.timeout = 300;
2953302001Smm		mt_options.filters = lzmafilters;
2954302001Smm		mt_options.check = LZMA_CHECK_CRC64;
2955302001Smm		r = lzma_stream_encoder_mt(strm, &mt_options);
2956302001Smm	} else
2957302001Smm#endif
2958302001Smm		r = lzma_stream_encoder(strm, lzmafilters, LZMA_CHECK_CRC64);
2959231200Smm	switch (r) {
2960231200Smm	case LZMA_OK:
2961231200Smm		lastrm->real_stream = strm;
2962231200Smm		lastrm->valid = 1;
2963231200Smm		lastrm->code = compression_code_lzma;
2964231200Smm		lastrm->end = compression_end_lzma;
2965231200Smm		r = ARCHIVE_OK;
2966231200Smm		break;
2967231200Smm	case LZMA_MEM_ERROR:
2968231200Smm		free(strm);
2969231200Smm		lastrm->real_stream = NULL;
2970231200Smm		archive_set_error(a, ENOMEM,
2971231200Smm		    "Internal error initializing compression library: "
2972231200Smm		    "Cannot allocate memory");
2973231200Smm		r =  ARCHIVE_FATAL;
2974231200Smm		break;
2975231200Smm        default:
2976231200Smm		free(strm);
2977231200Smm		lastrm->real_stream = NULL;
2978231200Smm		archive_set_error(a, ARCHIVE_ERRNO_MISC,
2979231200Smm		    "Internal error initializing compression library: "
2980231200Smm		    "It's a bug in liblzma");
2981231200Smm		r =  ARCHIVE_FATAL;
2982231200Smm		break;
2983231200Smm	}
2984231200Smm	return (r);
2985231200Smm}
2986231200Smm
2987231200Smmstatic int
2988231200Smmcompression_code_lzma(struct archive *a,
2989231200Smm    struct la_zstream *lastrm, enum la_zaction action)
2990231200Smm{
2991231200Smm	lzma_stream *strm;
2992231200Smm	int r;
2993231200Smm
2994231200Smm	strm = (lzma_stream *)lastrm->real_stream;
2995231200Smm	strm->next_in = lastrm->next_in;
2996231200Smm	strm->avail_in = lastrm->avail_in;
2997231200Smm	strm->total_in = lastrm->total_in;
2998231200Smm	strm->next_out = lastrm->next_out;
2999231200Smm	strm->avail_out = lastrm->avail_out;
3000231200Smm	strm->total_out = lastrm->total_out;
3001231200Smm	r = lzma_code(strm,
3002231200Smm	    (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);
3003231200Smm	lastrm->next_in = strm->next_in;
3004231200Smm	lastrm->avail_in = strm->avail_in;
3005231200Smm	lastrm->total_in = strm->total_in;
3006231200Smm	lastrm->next_out = strm->next_out;
3007231200Smm	lastrm->avail_out = strm->avail_out;
3008231200Smm	lastrm->total_out = strm->total_out;
3009231200Smm	switch (r) {
3010231200Smm	case LZMA_OK:
3011231200Smm		/* Non-finishing case */
3012231200Smm		return (ARCHIVE_OK);
3013231200Smm	case LZMA_STREAM_END:
3014231200Smm		/* This return can only occur in finishing case. */
3015231200Smm		return (ARCHIVE_EOF);
3016231200Smm	case LZMA_MEMLIMIT_ERROR:
3017231200Smm		archive_set_error(a, ENOMEM,
3018231200Smm		    "lzma compression error:"
3019231200Smm		    " %ju MiB would have been needed",
3020231200Smm		    (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)
3021231200Smm			/ (1024 * 1024)));
3022231200Smm		return (ARCHIVE_FATAL);
3023231200Smm	default:
3024231200Smm		/* Any other return value indicates an error */
3025231200Smm		archive_set_error(a, ARCHIVE_ERRNO_MISC,
3026231200Smm		    "lzma compression failed:"
3027231200Smm		    " lzma_code() call returned status %d", r);
3028231200Smm		return (ARCHIVE_FATAL);
3029231200Smm	}
3030231200Smm}
3031231200Smm
3032231200Smmstatic int
3033231200Smmcompression_end_lzma(struct archive *a, struct la_zstream *lastrm)
3034231200Smm{
3035231200Smm	lzma_stream *strm;
3036231200Smm
3037231200Smm	(void)a; /* UNUSED */
3038231200Smm	strm = (lzma_stream *)lastrm->real_stream;
3039231200Smm	lzma_end(strm);
3040231200Smm	free(strm);
3041231200Smm	lastrm->valid = 0;
3042231200Smm	lastrm->real_stream = NULL;
3043231200Smm	return (ARCHIVE_OK);
3044231200Smm}
3045231200Smm#else
3046231200Smmstatic int
3047231200Smmcompression_init_encoder_lzma(struct archive *a,
3048231200Smm    struct la_zstream *lastrm, int level)
3049231200Smm{
3050231200Smm
3051231200Smm	(void) level; /* UNUSED */
3052231200Smm	if (lastrm->valid)
3053231200Smm		compression_end(a, lastrm);
3054231200Smm	return (compression_unsupported_encoder(a, lastrm, "lzma"));
3055231200Smm}
3056231200Smmstatic int
3057231200Smmcompression_init_encoder_xz(struct archive *a,
3058302001Smm    struct la_zstream *lastrm, int level, int threads)
3059231200Smm{
3060231200Smm
3061231200Smm	(void) level; /* UNUSED */
3062302001Smm	(void) threads; /* UNUSED */
3063231200Smm	if (lastrm->valid)
3064231200Smm		compression_end(a, lastrm);
3065231200Smm	return (compression_unsupported_encoder(a, lastrm, "xz"));
3066231200Smm}
3067231200Smm#endif
3068231200Smm
3069231200Smmstatic int
3070231200Smmxar_compression_init_encoder(struct archive_write *a)
3071231200Smm{
3072231200Smm	struct xar *xar;
3073231200Smm	int r;
3074231200Smm
3075231200Smm	xar = (struct xar *)a->format_data;
3076231200Smm	switch (xar->opt_compression) {
3077231200Smm	case GZIP:
3078231200Smm		r = compression_init_encoder_gzip(
3079231200Smm		    &(a->archive), &(xar->stream),
3080231200Smm		    xar->opt_compression_level, 1);
3081231200Smm		break;
3082231200Smm	case BZIP2:
3083231200Smm		r = compression_init_encoder_bzip2(
3084231200Smm		    &(a->archive), &(xar->stream),
3085231200Smm		    xar->opt_compression_level);
3086231200Smm		break;
3087231200Smm	case LZMA:
3088231200Smm		r = compression_init_encoder_lzma(
3089231200Smm		    &(a->archive), &(xar->stream),
3090231200Smm		    xar->opt_compression_level);
3091231200Smm		break;
3092231200Smm	case XZ:
3093231200Smm		r = compression_init_encoder_xz(
3094231200Smm		    &(a->archive), &(xar->stream),
3095302001Smm		    xar->opt_compression_level, xar->opt_threads);
3096231200Smm		break;
3097231200Smm	default:
3098231200Smm		r = ARCHIVE_OK;
3099231200Smm		break;
3100231200Smm	}
3101231200Smm	if (r == ARCHIVE_OK) {
3102231200Smm		xar->stream.total_in = 0;
3103231200Smm		xar->stream.next_out = xar->wbuff;
3104231200Smm		xar->stream.avail_out = sizeof(xar->wbuff);
3105231200Smm		xar->stream.total_out = 0;
3106231200Smm	}
3107231200Smm
3108231200Smm	return (r);
3109231200Smm}
3110231200Smm
3111231200Smmstatic int
3112231200Smmcompression_code(struct archive *a, struct la_zstream *lastrm,
3113231200Smm    enum la_zaction action)
3114231200Smm{
3115231200Smm	if (lastrm->valid)
3116231200Smm		return (lastrm->code(a, lastrm, action));
3117231200Smm	return (ARCHIVE_OK);
3118231200Smm}
3119231200Smm
3120231200Smmstatic int
3121231200Smmcompression_end(struct archive *a, struct la_zstream *lastrm)
3122231200Smm{
3123231200Smm	if (lastrm->valid)
3124231200Smm		return (lastrm->end(a, lastrm));
3125231200Smm	return (ARCHIVE_OK);
3126231200Smm}
3127231200Smm
3128231200Smm
3129231200Smmstatic int
3130231200Smmsave_xattrs(struct archive_write *a, struct file *file)
3131231200Smm{
3132231200Smm	struct xar *xar;
3133231200Smm	const char *name;
3134231200Smm	const void *value;
3135231200Smm	struct heap_data *heap;
3136231200Smm	size_t size;
3137231200Smm	int count, r;
3138231200Smm
3139231200Smm	xar = (struct xar *)a->format_data;
3140231200Smm	count = archive_entry_xattr_reset(file->entry);
3141231200Smm	if (count == 0)
3142231200Smm		return (ARCHIVE_OK);
3143231200Smm	while (count--) {
3144231200Smm		archive_entry_xattr_next(file->entry,
3145231200Smm		    &name, &value, &size);
3146231200Smm		checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
3147231200Smm		checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
3148231200Smm
3149231200Smm		heap = calloc(1, sizeof(*heap));
3150231200Smm		if (heap == NULL) {
3151231200Smm			archive_set_error(&a->archive, ENOMEM,
3152231200Smm			    "Can't allocate memory for xattr");
3153231200Smm			return (ARCHIVE_FATAL);
3154231200Smm		}
3155231200Smm		heap->id = file->ea_idx++;
3156231200Smm		heap->temp_offset = xar->temp_offset;
3157231200Smm		heap->size = size;/* save a extracted size */
3158231200Smm		heap->compression = xar->opt_compression;
3159231200Smm		/* Get a extracted sumcheck value. */
3160231200Smm		checksum_update(&(xar->e_sumwrk), value, size);
3161231200Smm		checksum_final(&(xar->e_sumwrk), &(heap->e_sum));
3162231200Smm
3163231200Smm		/*
3164231200Smm		 * Not compression to xattr is simple way.
3165231200Smm		 */
3166231200Smm		if (heap->compression == NONE) {
3167231200Smm			checksum_update(&(xar->a_sumwrk), value, size);
3168231200Smm			checksum_final(&(xar->a_sumwrk), &(heap->a_sum));
3169231200Smm			if (write_to_temp(a, value, size)
3170248616Smm			    != ARCHIVE_OK) {
3171248616Smm				free(heap);
3172231200Smm				return (ARCHIVE_FATAL);
3173248616Smm			}
3174231200Smm			heap->length = size;
3175231200Smm			/* Add heap to the tail of file->xattr. */
3176231200Smm			heap->next = NULL;
3177231200Smm			*file->xattr.last = heap;
3178231200Smm			file->xattr.last = &(heap->next);
3179231200Smm			/* Next xattr */
3180231200Smm			continue;
3181231200Smm		}
3182231200Smm
3183231200Smm		/*
3184231200Smm		 * Init compression library.
3185231200Smm		 */
3186231200Smm		r = xar_compression_init_encoder(a);
3187231200Smm		if (r != ARCHIVE_OK) {
3188231200Smm			free(heap);
3189231200Smm			return (ARCHIVE_FATAL);
3190231200Smm		}
3191231200Smm
3192231200Smm		xar->stream.next_in = (const unsigned char *)value;
3193231200Smm		xar->stream.avail_in = size;
3194231200Smm		for (;;) {
3195231200Smm			r = compression_code(&(a->archive),
3196231200Smm			    &(xar->stream), ARCHIVE_Z_FINISH);
3197231200Smm			if (r != ARCHIVE_OK && r != ARCHIVE_EOF) {
3198231200Smm				free(heap);
3199231200Smm				return (ARCHIVE_FATAL);
3200231200Smm			}
3201231200Smm			size = sizeof(xar->wbuff) - xar->stream.avail_out;
3202231200Smm			checksum_update(&(xar->a_sumwrk),
3203231200Smm			    xar->wbuff, size);
3204231200Smm			if (write_to_temp(a, xar->wbuff, size)
3205231200Smm			    != ARCHIVE_OK)
3206231200Smm				return (ARCHIVE_FATAL);
3207231200Smm			if (r == ARCHIVE_OK) {
3208231200Smm				xar->stream.next_out = xar->wbuff;
3209231200Smm				xar->stream.avail_out = sizeof(xar->wbuff);
3210231200Smm			} else {
3211231200Smm				checksum_final(&(xar->a_sumwrk),
3212231200Smm				    &(heap->a_sum));
3213231200Smm				heap->length = xar->stream.total_out;
3214231200Smm				/* Add heap to the tail of file->xattr. */
3215231200Smm				heap->next = NULL;
3216231200Smm				*file->xattr.last = heap;
3217231200Smm				file->xattr.last = &(heap->next);
3218231200Smm				break;
3219231200Smm			}
3220231200Smm		}
3221231200Smm		/* Clean up compression library. */
3222231200Smm		r = compression_end(&(a->archive), &(xar->stream));
3223231200Smm		if (r != ARCHIVE_OK)
3224231200Smm			return (ARCHIVE_FATAL);
3225231200Smm	}
3226231200Smm	return (ARCHIVE_OK);
3227231200Smm}
3228231200Smm
3229231200Smmstatic int
3230231200Smmgetalgsize(enum sumalg sumalg)
3231231200Smm{
3232231200Smm	switch (sumalg) {
3233231200Smm	default:
3234231200Smm	case CKSUM_NONE:
3235231200Smm		return (0);
3236231200Smm	case CKSUM_SHA1:
3237231200Smm		return (SHA1_SIZE);
3238231200Smm	case CKSUM_MD5:
3239231200Smm		return (MD5_SIZE);
3240231200Smm	}
3241231200Smm}
3242231200Smm
3243231200Smmstatic const char *
3244231200Smmgetalgname(enum sumalg sumalg)
3245231200Smm{
3246231200Smm	switch (sumalg) {
3247231200Smm	default:
3248231200Smm	case CKSUM_NONE:
3249231200Smm		return (NULL);
3250231200Smm	case CKSUM_SHA1:
3251231200Smm		return (SHA1_NAME);
3252231200Smm	case CKSUM_MD5:
3253231200Smm		return (MD5_NAME);
3254231200Smm	}
3255231200Smm}
3256231200Smm
3257231200Smm#endif /* Support xar format */
3258