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