1228753Smm/*-
2228753Smm * Copyright (c) 2009 Michihiro NAKAJIMA
3228753Smm * Copyright (c) 2008 Joerg Sonnenberger
4228753Smm * All rights reserved.
5228753Smm *
6228753Smm * Redistribution and use in source and binary forms, with or without
7228753Smm * modification, are permitted provided that the following conditions
8228753Smm * are met:
9228753Smm * 1. Redistributions of source code must retain the above copyright
10228753Smm *    notice, this list of conditions and the following disclaimer.
11228753Smm * 2. Redistributions in binary form must reproduce the above copyright
12228753Smm *    notice, this list of conditions and the following disclaimer in the
13228753Smm *    documentation and/or other materials provided with the distribution.
14228753Smm *
15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25228753Smm */
26228753Smm
27228753Smm#include "archive_platform.h"
28229592Smm__FBSDID("$FreeBSD$");
29228753Smm
30228753Smm#ifdef HAVE_SYS_TYPES_H
31228753Smm#include <sys/types.h>
32228753Smm#endif
33228753Smm#include <errno.h>
34228753Smm#include <stdlib.h>
35228753Smm#include <string.h>
36228753Smm
37228753Smm#include "archive.h"
38228753Smm#include "archive_entry.h"
39228753Smm#include "archive_private.h"
40228753Smm#include "archive_write_private.h"
41228753Smm
42228753Smm#include "archive_hash.h"
43228753Smm
44228753Smm#define INDENTNAMELEN	15
45228753Smm#define MAXLINELEN	80
46228753Smm
47228753Smmstruct mtree_writer {
48228753Smm	struct archive_entry *entry;
49228753Smm	struct archive_string ebuf;
50228753Smm	struct archive_string buf;
51228753Smm	int first;
52228753Smm	uint64_t entry_bytes_remaining;
53228753Smm	struct {
54228753Smm		int		output;
55228753Smm		int		processed;
56228753Smm		struct archive_string parent;
57228753Smm		mode_t		type;
58228753Smm		int		keys;
59228753Smm		uid_t		uid;
60228753Smm		gid_t		gid;
61228753Smm		mode_t		mode;
62228753Smm		unsigned long	fflags_set;
63228753Smm		unsigned long	fflags_clear;
64228753Smm	} set;
65228753Smm	/* chekc sum */
66228753Smm	int compute_sum;
67228753Smm	uint32_t crc;
68228753Smm	uint64_t crc_len;
69228753Smm#ifdef ARCHIVE_HAS_MD5
70228753Smm	archive_md5_ctx md5ctx;
71228753Smm#endif
72228753Smm#ifdef ARCHIVE_HAS_RMD160
73228753Smm	archive_rmd160_ctx rmd160ctx;
74228753Smm#endif
75228753Smm#ifdef ARCHIVE_HAS_SHA1
76228753Smm	archive_sha1_ctx sha1ctx;
77228753Smm#endif
78228753Smm#ifdef ARCHIVE_HAS_SHA256
79228753Smm	archive_sha256_ctx sha256ctx;
80228753Smm#endif
81228753Smm#ifdef ARCHIVE_HAS_SHA384
82228753Smm	archive_sha384_ctx sha384ctx;
83228753Smm#endif
84228753Smm#ifdef ARCHIVE_HAS_SHA512
85228753Smm	archive_sha512_ctx sha512ctx;
86228753Smm#endif
87228753Smm	/* Keyword options */
88228753Smm	int keys;
89228753Smm#define	F_CKSUM		0x00000001		/* check sum */
90228753Smm#define	F_DEV		0x00000002		/* device type */
91228753Smm#define	F_DONE		0x00000004		/* directory done */
92228753Smm#define	F_FLAGS		0x00000008		/* file flags */
93228753Smm#define	F_GID		0x00000010		/* gid */
94228753Smm#define	F_GNAME		0x00000020		/* group name */
95228753Smm#define	F_IGN		0x00000040		/* ignore */
96228753Smm#define	F_MAGIC		0x00000080		/* name has magic chars */
97228753Smm#define	F_MD5		0x00000100		/* MD5 digest */
98228753Smm#define	F_MODE		0x00000200		/* mode */
99228753Smm#define	F_NLINK		0x00000400		/* number of links */
100228753Smm#define	F_NOCHANGE 	0x00000800		/* If owner/mode "wrong", do
101228753Smm						 * not change */
102228753Smm#define	F_OPT		0x00001000		/* existence optional */
103228753Smm#define	F_RMD160 	0x00002000		/* RIPEMD160 digest */
104228753Smm#define	F_SHA1		0x00004000		/* SHA-1 digest */
105228753Smm#define	F_SIZE		0x00008000		/* size */
106228753Smm#define	F_SLINK		0x00010000		/* symbolic link */
107228753Smm#define	F_TAGS		0x00020000		/* tags */
108228753Smm#define	F_TIME		0x00040000		/* modification time */
109228753Smm#define	F_TYPE		0x00080000		/* file type */
110228753Smm#define	F_UID		0x00100000		/* uid */
111228753Smm#define	F_UNAME		0x00200000		/* user name */
112228753Smm#define	F_VISIT		0x00400000		/* file visited */
113228753Smm#define	F_SHA256	0x00800000		/* SHA-256 digest */
114228753Smm#define	F_SHA384	0x01000000		/* SHA-384 digest */
115228753Smm#define	F_SHA512	0x02000000		/* SHA-512 digest */
116228753Smm
117228753Smm	/* Options */
118228753Smm	int dironly;		/* if the dironly is 1, ignore everything except
119228753Smm				 * directory type files. like mtree(8) -d option.
120228753Smm				 */
121228753Smm	int indent;		/* if the indent is 1, indent writing data. */
122228753Smm};
123228753Smm
124228753Smm#define DEFAULT_KEYS	(F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\
125228753Smm			 | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\
126228753Smm			 | F_UNAME)
127228753Smm
128228753Smm#define	COMPUTE_CRC(var, ch)	(var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
129228753Smmstatic const uint32_t crctab[] = {
130228753Smm	0x0,
131228753Smm	0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
132228753Smm	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
133228753Smm	0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
134228753Smm	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
135228753Smm	0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
136228753Smm	0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
137228753Smm	0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
138228753Smm	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
139228753Smm	0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
140228753Smm	0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
141228753Smm	0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
142228753Smm	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
143228753Smm	0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
144228753Smm	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
145228753Smm	0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
146228753Smm	0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
147228753Smm	0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
148228753Smm	0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
149228753Smm	0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
150228753Smm	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
151228753Smm	0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
152228753Smm	0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
153228753Smm	0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
154228753Smm	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
155228753Smm	0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
156228753Smm	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
157228753Smm	0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
158228753Smm	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
159228753Smm	0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
160228753Smm	0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
161228753Smm	0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
162228753Smm	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
163228753Smm	0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
164228753Smm	0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
165228753Smm	0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
166228753Smm	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
167228753Smm	0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
168228753Smm	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
169228753Smm	0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
170228753Smm	0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
171228753Smm	0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
172228753Smm	0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
173228753Smm	0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
174228753Smm	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
175228753Smm	0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
176228753Smm	0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
177228753Smm	0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
178228753Smm	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
179228753Smm	0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
180228753Smm	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
181228753Smm	0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
182228753Smm};
183228753Smm
184228753Smmstatic int
185228753Smmmtree_safe_char(char c)
186228753Smm{
187228753Smm	if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
188228753Smm		return 1;
189228753Smm	if (c >= '0' && c <= '9')
190228753Smm		return 1;
191228753Smm	if (c == 35 || c == 61 || c == 92)
192228753Smm		return 0; /* #, = and \ are always quoted */
193228753Smm
194228753Smm	if (c >= 33 && c <= 47) /* !"$%&'()*+,-./ */
195228753Smm		return 1;
196228753Smm	if (c >= 58 && c <= 64) /* :;<>?@ */
197228753Smm		return 1;
198228753Smm	if (c >= 91 && c <= 96) /* []^_` */
199228753Smm		return 1;
200228753Smm	if (c >= 123 && c <= 126) /* {|}~ */
201228753Smm		return 1;
202228753Smm	return 0;
203228753Smm}
204228753Smm
205228753Smmstatic void
206228753Smmmtree_quote(struct archive_string *s, const char *str)
207228753Smm{
208228753Smm	const char *start;
209228753Smm	char buf[4];
210228753Smm	unsigned char c;
211228753Smm
212228753Smm	for (start = str; *str != '\0'; ++str) {
213228753Smm		if (mtree_safe_char(*str))
214228753Smm			continue;
215228753Smm		if (start != str)
216228753Smm			archive_strncat(s, start, str - start);
217228753Smm		c = (unsigned char)*str;
218228753Smm		buf[0] = '\\';
219228753Smm		buf[1] = (c / 64) + '0';
220228753Smm		buf[2] = (c / 8 % 8) + '0';
221228753Smm		buf[3] = (c % 8) + '0';
222228753Smm		archive_strncat(s, buf, 4);
223228753Smm		start = str + 1;
224228753Smm	}
225228753Smm
226228753Smm	if (start != str)
227228753Smm		archive_strncat(s, start, str - start);
228228753Smm}
229228753Smm
230228753Smmstatic void
231228753Smmmtree_indent(struct mtree_writer *mtree)
232228753Smm{
233228753Smm	int i, fn;
234228753Smm	const char *r, *s, *x;
235228753Smm
236228753Smm	fn = 1;
237228753Smm	s = r = mtree->ebuf.s;
238228753Smm	x = NULL;
239228753Smm	while (*r == ' ')
240228753Smm		r++;
241228753Smm	while ((r = strchr(r, ' ')) != NULL) {
242228753Smm		if (fn) {
243228753Smm			fn = 0;
244228753Smm			archive_strncat(&mtree->buf, s, r - s);
245228753Smm			if (r -s > INDENTNAMELEN) {
246228753Smm				archive_strncat(&mtree->buf, " \\\n", 3);
247228753Smm				for (i = 0; i < (INDENTNAMELEN + 1); i++)
248228753Smm					archive_strappend_char(&mtree->buf, ' ');
249228753Smm			} else {
250228753Smm				for (i = r -s; i < (INDENTNAMELEN + 1); i++)
251228753Smm					archive_strappend_char(&mtree->buf, ' ');
252228753Smm			}
253228753Smm			s = ++r;
254228753Smm			x = NULL;
255228753Smm			continue;
256228753Smm		}
257228753Smm		if (r - s <= MAXLINELEN - 3 - INDENTNAMELEN)
258228753Smm			x = r++;
259228753Smm		else {
260228753Smm			if (x == NULL)
261228753Smm				x = r;
262228753Smm			archive_strncat(&mtree->buf, s, x - s);
263228753Smm			archive_strncat(&mtree->buf, " \\\n", 3);
264228753Smm			for (i = 0; i < (INDENTNAMELEN + 1); i++)
265228753Smm				archive_strappend_char(&mtree->buf, ' ');
266228753Smm			s = r = ++x;
267228753Smm			x = NULL;
268228753Smm		}
269228753Smm	}
270228753Smm	if (x != NULL && strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) {
271228753Smm		/* Last keyword is longer. */
272228753Smm		archive_strncat(&mtree->buf, s, x - s);
273228753Smm		archive_strncat(&mtree->buf, " \\\n", 3);
274228753Smm		for (i = 0; i < (INDENTNAMELEN + 1); i++)
275228753Smm			archive_strappend_char(&mtree->buf, ' ');
276228753Smm		s = ++x;
277228753Smm	}
278228753Smm	archive_strcat(&mtree->buf, s);
279228753Smm	archive_string_empty(&mtree->ebuf);
280228753Smm}
281228753Smm
282228753Smm#if !defined(_WIN32) || defined(__CYGWIN__)
283228753Smmstatic size_t
284228753Smmdir_len(struct archive_entry *entry)
285228753Smm{
286228753Smm	const char *path, *r;
287228753Smm
288228753Smm	path = archive_entry_pathname(entry);
289228753Smm	r = strrchr(path, '/');
290228753Smm	if (r == NULL)
291228753Smm		return (0);
292228753Smm	/* Include a separator size */
293228753Smm	return (r - path + 1);
294228753Smm}
295228753Smm
296228753Smm#else /* _WIN32 && !__CYGWIN__ */
297228753Smm/*
298228753Smm * Note: We should use wide-character for findng '\' character,
299228753Smm * a directory separator on Windows, because some character-set have
300228753Smm * been using the '\' character for a part of its multibyte character
301228753Smm * code.
302228753Smm */
303228753Smmstatic size_t
304228753Smmdir_len(struct archive_entry *entry)
305228753Smm{
306228753Smm	wchar_t wc;
307228753Smm	const char *path;
308228753Smm	const char *p, *rp;
309228753Smm	size_t al, l, size;
310228753Smm
311228753Smm	path = archive_entry_pathname(entry);
312228753Smm	al = l = -1;
313228753Smm	for (p = path; *p != '\0'; ++p) {
314228753Smm		if (*p == '\\')
315228753Smm			al = l = p - path;
316228753Smm		else if (*p == '/')
317228753Smm			al = p - path;
318228753Smm	}
319228753Smm	if (l == -1)
320228753Smm		goto alen;
321228753Smm	size = p - path;
322228753Smm	rp = p = path;
323228753Smm	while (*p != '\0') {
324228753Smm		l = mbtowc(&wc, p, size);
325228753Smm		if (l == -1)
326228753Smm			goto alen;
327228753Smm		if (l == 1 && (wc == L'/' || wc == L'\\'))
328228753Smm			rp = p;
329228753Smm		p += l;
330228753Smm		size -= l;
331228753Smm	}
332228753Smm	return (rp - path + 1);
333228753Smmalen:
334228753Smm	if (al == -1)
335228753Smm		return (0);
336228753Smm	return (al + 1);
337228753Smm}
338228753Smm#endif /* _WIN32 && !__CYGWIN__ */
339228753Smm
340228753Smmstatic int
341228753Smmparent_dir_changed(struct archive_string *dir, struct archive_entry *entry)
342228753Smm{
343228753Smm	const char *path;
344228753Smm	size_t l;
345228753Smm
346228753Smm	l = dir_len(entry);
347228753Smm	path = archive_entry_pathname(entry);
348228753Smm	if (archive_strlen(dir) > 0) {
349228753Smm		if (l == 0) {
350228753Smm			archive_string_empty(dir);
351228753Smm			return (1);
352228753Smm		}
353228753Smm		if (strncmp(dir->s, path, l) == 0)
354228753Smm			return (0); /* The parent directory is the same. */
355228753Smm	} else if (l == 0)
356228753Smm		return (0);	    /* The parent directory is the same. */
357228753Smm	archive_strncpy(dir, path, l);
358228753Smm	return (1);
359228753Smm}
360228753Smm
361228753Smm/*
362228753Smm * Write /set keyword. It means set global datas.
363228753Smm * [directory-only mode]
364228753Smm *   - It is only once to write /set keyword. It is using values of the
365228753Smm *     first entry.
366228753Smm * [normal mode]
367228753Smm *   - Write /set keyword. It is using values of the first entry whose
368228753Smm *     filetype is a regular file.
369228753Smm *   - When a parent directory of the entry whose filetype is the regular
370228753Smm *     file is changed, check the global datas and write it again if its
371228753Smm *     values are different from the entry's.
372228753Smm */
373228753Smmstatic void
374228753Smmset_global(struct mtree_writer *mtree, struct archive_entry *entry)
375228753Smm{
376228753Smm	struct archive_string setstr;
377228753Smm	struct archive_string unsetstr;
378228753Smm	const char *name;
379228753Smm	int keys, oldkeys, effkeys;
380228753Smm	mode_t set_type = 0;
381228753Smm
382228753Smm	switch (archive_entry_filetype(entry)) {
383228753Smm	case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
384228753Smm	case AE_IFBLK: case AE_IFIFO:
385228753Smm		break;
386228753Smm	case AE_IFDIR:
387228753Smm		if (mtree->dironly)
388228753Smm			set_type = AE_IFDIR;
389228753Smm		break;
390228753Smm	case AE_IFREG:
391228753Smm	default:	/* Handle unknown file types as regular files. */
392228753Smm		if (!mtree->dironly)
393228753Smm			set_type = AE_IFREG;
394228753Smm		break;
395228753Smm	}
396228753Smm	if (set_type == 0)
397228753Smm		return;
398228753Smm	if (mtree->set.processed &&
399228753Smm	    !parent_dir_changed(&mtree->set.parent, entry))
400228753Smm		return;
401228753Smm	/* At first, save a parent directory of the entry for following
402228753Smm	 * entries. */
403228753Smm	if (!mtree->set.processed && set_type == AE_IFREG)
404228753Smm		parent_dir_changed(&mtree->set.parent, entry);
405228753Smm
406228753Smm	archive_string_init(&setstr);
407228753Smm	archive_string_init(&unsetstr);
408228753Smm	keys = mtree->keys & (F_FLAGS | F_GID | F_GNAME | F_NLINK | F_MODE
409228753Smm	    | F_TYPE | F_UID | F_UNAME);
410228753Smm	oldkeys = mtree->set.keys;
411228753Smm	effkeys = keys;
412228753Smm	if (mtree->set.processed) {
413228753Smm		/*
414228753Smm		 * Check the global datas for whether it needs updating.
415228753Smm		 */
416228753Smm		effkeys &= ~F_TYPE;
417228753Smm		if ((oldkeys & (F_UNAME | F_UID)) != 0 &&
418228753Smm		    mtree->set.uid == archive_entry_uid(entry))
419228753Smm			effkeys &= ~(F_UNAME | F_UID);
420228753Smm		if ((oldkeys & (F_GNAME | F_GID)) != 0 &&
421228753Smm		    mtree->set.gid == archive_entry_gid(entry))
422228753Smm			effkeys &= ~(F_GNAME | F_GID);
423228753Smm		if ((oldkeys & F_MODE) != 0 &&
424228753Smm		    mtree->set.mode == (archive_entry_mode(entry) & 07777))
425228753Smm			effkeys &= ~F_MODE;
426228753Smm		if ((oldkeys & F_FLAGS) != 0) {
427228753Smm			unsigned long	fflags_set;
428228753Smm			unsigned long	fflags_clear;
429228753Smm
430228753Smm			archive_entry_fflags(entry, &fflags_set, &fflags_clear);
431228753Smm			if (fflags_set == mtree->set.fflags_set &&
432228753Smm			    fflags_clear == mtree->set.fflags_clear)
433228753Smm				effkeys &= ~F_FLAGS;
434228753Smm		}
435228753Smm	}
436228753Smm	if ((keys & effkeys & F_TYPE) != 0) {
437228753Smm		mtree->set.type = set_type;
438228753Smm		if (set_type == AE_IFDIR)
439228753Smm			archive_strcat(&setstr, " type=dir");
440228753Smm		else
441228753Smm			archive_strcat(&setstr, " type=file");
442228753Smm	}
443228753Smm	if ((keys & effkeys & F_UNAME) != 0) {
444228753Smm		if ((name = archive_entry_uname(entry)) != NULL) {
445228753Smm			archive_strcat(&setstr, " uname=");
446228753Smm			mtree_quote(&setstr, name);
447228753Smm		} else if ((oldkeys & F_UNAME) != 0)
448228753Smm			archive_strcat(&unsetstr, " uname");
449228753Smm		else
450228753Smm			keys &= ~F_UNAME;
451228753Smm	}
452228753Smm	if ((keys & effkeys & F_UID) != 0) {
453228753Smm		mtree->set.uid = archive_entry_uid(entry);
454228753Smm		archive_string_sprintf(&setstr, " uid=%jd",
455228753Smm		    (intmax_t)mtree->set.uid);
456228753Smm	}
457228753Smm	if ((keys & effkeys & F_GNAME) != 0) {
458228753Smm		if ((name = archive_entry_gname(entry)) != NULL) {
459228753Smm			archive_strcat(&setstr, " gname=");
460228753Smm			mtree_quote(&setstr, name);
461228753Smm		} else if ((oldkeys & F_GNAME) != 0)
462228753Smm			archive_strcat(&unsetstr, " gname");
463228753Smm		else
464228753Smm			keys &= ~F_GNAME;
465228753Smm	}
466228753Smm	if ((keys & effkeys & F_GID) != 0) {
467228753Smm		mtree->set.gid = archive_entry_gid(entry);
468228753Smm		archive_string_sprintf(&setstr, " gid=%jd",
469228753Smm		    (intmax_t)mtree->set.gid);
470228753Smm	}
471228753Smm	if ((keys & effkeys & F_MODE) != 0) {
472228753Smm		mtree->set.mode = archive_entry_mode(entry) & 07777;
473228753Smm		archive_string_sprintf(&setstr, " mode=%o", mtree->set.mode);
474228753Smm	}
475228753Smm	if ((keys & effkeys & F_FLAGS) != 0) {
476228753Smm		if ((name = archive_entry_fflags_text(entry)) != NULL) {
477228753Smm			archive_strcat(&setstr, " flags=");
478228753Smm			mtree_quote(&setstr, name);
479228753Smm			archive_entry_fflags(entry, &mtree->set.fflags_set,
480228753Smm			    &mtree->set.fflags_clear);
481228753Smm		} else if ((oldkeys & F_FLAGS) != 0)
482228753Smm			archive_strcat(&unsetstr, " flags");
483228753Smm		else
484228753Smm			keys &= ~F_FLAGS;
485228753Smm	}
486228753Smm	if (unsetstr.length > 0)
487228753Smm		archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s);
488228753Smm	archive_string_free(&unsetstr);
489228753Smm	if (setstr.length > 0)
490228753Smm		archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s);
491228753Smm	archive_string_free(&setstr);
492228753Smm	mtree->set.keys = keys;
493228753Smm	mtree->set.processed = 1;
494228753Smm	/* On directory-only mode, it is only once to write /set keyword. */
495228753Smm	if (mtree->dironly)
496228753Smm		mtree->set.output = 0;
497228753Smm}
498228753Smm
499228753Smmstatic int
500228753Smmget_keys(struct mtree_writer *mtree, struct archive_entry *entry)
501228753Smm{
502228753Smm	int keys;
503228753Smm
504228753Smm	keys = mtree->keys;
505228753Smm	if (mtree->set.keys == 0)
506228753Smm		return (keys);
507228753Smm	if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 &&
508228753Smm	     mtree->set.gid == archive_entry_gid(entry))
509228753Smm		keys &= ~(F_GNAME | F_GID);
510228753Smm	if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 &&
511228753Smm	     mtree->set.uid == archive_entry_uid(entry))
512228753Smm		keys &= ~(F_UNAME | F_UID);
513228753Smm	if (mtree->set.keys & F_FLAGS) {
514228753Smm		unsigned long set, clear;
515228753Smm
516228753Smm		archive_entry_fflags(entry, &set, &clear);
517228753Smm		if (mtree->set.fflags_set == set &&
518228753Smm		    mtree->set.fflags_clear == clear)
519228753Smm			keys &= ~F_FLAGS;
520228753Smm	}
521228753Smm	if ((mtree->set.keys & F_MODE) != 0 &&
522228753Smm	     mtree->set.mode == (archive_entry_mode(entry) & 07777))
523228753Smm		keys &= ~F_MODE;
524228753Smm
525228753Smm	switch (archive_entry_filetype(entry)) {
526228753Smm	case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
527228753Smm	case AE_IFBLK: case AE_IFIFO:
528228753Smm		break;
529228753Smm	case AE_IFDIR:
530228753Smm		if ((mtree->set.keys & F_TYPE) != 0 &&
531228753Smm		    mtree->set.type == AE_IFDIR)
532228753Smm			keys &= ~F_TYPE;
533228753Smm		break;
534228753Smm	case AE_IFREG:
535228753Smm	default:	/* Handle unknown file types as regular files. */
536228753Smm		if ((mtree->set.keys & F_TYPE) != 0 &&
537228753Smm		    mtree->set.type == AE_IFREG)
538228753Smm			keys &= ~F_TYPE;
539228753Smm		break;
540228753Smm	}
541228753Smm
542228753Smm	return (keys);
543228753Smm}
544228753Smm
545228753Smmstatic int
546228753Smmarchive_write_mtree_header(struct archive_write *a,
547228753Smm    struct archive_entry *entry)
548228753Smm{
549228753Smm	struct mtree_writer *mtree= a->format_data;
550228753Smm	struct archive_string *str;
551228753Smm	const char *path;
552228753Smm
553228753Smm	mtree->entry = archive_entry_clone(entry);
554228753Smm	path = archive_entry_pathname(mtree->entry);
555228753Smm
556228753Smm	if (mtree->first) {
557228753Smm		mtree->first = 0;
558228753Smm		archive_strcat(&mtree->buf, "#mtree\n");
559228753Smm	}
560228753Smm	if (mtree->set.output)
561228753Smm		set_global(mtree, entry);
562228753Smm
563228753Smm	archive_string_empty(&mtree->ebuf);
564228753Smm	str = (mtree->indent)? &mtree->ebuf : &mtree->buf;
565228753Smm	if (!mtree->dironly || archive_entry_filetype(entry) == AE_IFDIR)
566228753Smm		mtree_quote(str, path);
567228753Smm
568228753Smm	mtree->entry_bytes_remaining = archive_entry_size(entry);
569228753Smm	if ((mtree->keys & F_CKSUM) != 0 &&
570228753Smm	    archive_entry_filetype(entry) == AE_IFREG) {
571228753Smm		mtree->compute_sum |= F_CKSUM;
572228753Smm		mtree->crc = 0;
573228753Smm		mtree->crc_len = 0;
574228753Smm	} else
575228753Smm		mtree->compute_sum &= ~F_CKSUM;
576228753Smm#ifdef ARCHIVE_HAS_MD5
577228753Smm	if ((mtree->keys & F_MD5) != 0 &&
578228753Smm	    archive_entry_filetype(entry) == AE_IFREG) {
579228753Smm		mtree->compute_sum |= F_MD5;
580228753Smm		archive_md5_init(&mtree->md5ctx);
581228753Smm	} else
582228753Smm		mtree->compute_sum &= ~F_MD5;
583228753Smm#endif
584228753Smm#ifdef ARCHIVE_HAS_RMD160
585228753Smm	if ((mtree->keys & F_RMD160) != 0 &&
586228753Smm	    archive_entry_filetype(entry) == AE_IFREG) {
587228753Smm		mtree->compute_sum |= F_RMD160;
588228753Smm		archive_rmd160_init(&mtree->rmd160ctx);
589228753Smm	} else
590228753Smm		mtree->compute_sum &= ~F_RMD160;
591228753Smm#endif
592228753Smm#ifdef ARCHIVE_HAS_SHA1
593228753Smm	if ((mtree->keys & F_SHA1) != 0 &&
594228753Smm	    archive_entry_filetype(entry) == AE_IFREG) {
595228753Smm		mtree->compute_sum |= F_SHA1;
596228753Smm		archive_sha1_init(&mtree->sha1ctx);
597228753Smm	} else
598228753Smm		mtree->compute_sum &= ~F_SHA1;
599228753Smm#endif
600228753Smm#ifdef ARCHIVE_HAS_SHA256
601228753Smm	if ((mtree->keys & F_SHA256) != 0 &&
602228753Smm	    archive_entry_filetype(entry) == AE_IFREG) {
603228753Smm		mtree->compute_sum |= F_SHA256;
604228753Smm		archive_sha256_init(&mtree->sha256ctx);
605228753Smm	} else
606228753Smm		mtree->compute_sum &= ~F_SHA256;
607228753Smm#endif
608228753Smm#ifdef ARCHIVE_HAS_SHA384
609228753Smm	if ((mtree->keys & F_SHA384) != 0 &&
610228753Smm	    archive_entry_filetype(entry) == AE_IFREG) {
611228753Smm		mtree->compute_sum |= F_SHA384;
612228753Smm		archive_sha384_init(&mtree->sha384ctx);
613228753Smm	} else
614228753Smm		mtree->compute_sum &= ~F_SHA384;
615228753Smm#endif
616228753Smm#ifdef ARCHIVE_HAS_SHA512
617228753Smm	if ((mtree->keys & F_SHA512) != 0 &&
618228753Smm	    archive_entry_filetype(entry) == AE_IFREG) {
619228753Smm		mtree->compute_sum |= F_SHA512;
620228753Smm		archive_sha512_init(&mtree->sha512ctx);
621228753Smm	} else
622228753Smm		mtree->compute_sum &= ~F_SHA512;
623228753Smm#endif
624228753Smm
625228753Smm	return (ARCHIVE_OK);
626228753Smm}
627228753Smm
628228753Smm#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \
629228753Smm    defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \
630228753Smm    defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)
631228753Smmstatic void
632228753Smmstrappend_bin(struct archive_string *s, const unsigned char *bin, int n)
633228753Smm{
634228753Smm	static const char hex[] = "0123456789abcdef";
635228753Smm	int i;
636228753Smm
637228753Smm	for (i = 0; i < n; i++) {
638228753Smm		archive_strappend_char(s, hex[bin[i] >> 4]);
639228753Smm		archive_strappend_char(s, hex[bin[i] & 0x0f]);
640228753Smm	}
641228753Smm}
642228753Smm#endif
643228753Smm
644228753Smmstatic int
645228753Smmarchive_write_mtree_finish_entry(struct archive_write *a)
646228753Smm{
647228753Smm	struct mtree_writer *mtree = a->format_data;
648228753Smm	struct archive_entry *entry;
649228753Smm	struct archive_string *str;
650228753Smm	const char *name;
651228753Smm	int keys, ret;
652228753Smm
653228753Smm	entry = mtree->entry;
654228753Smm	if (entry == NULL) {
655228753Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
656228753Smm		    "Finished entry without being open first.");
657228753Smm		return (ARCHIVE_FATAL);
658228753Smm	}
659228753Smm	mtree->entry = NULL;
660228753Smm
661228753Smm	if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR) {
662228753Smm		archive_entry_free(entry);
663228753Smm		return (ARCHIVE_OK);
664228753Smm	}
665228753Smm
666228753Smm	str = (mtree->indent)? &mtree->ebuf : &mtree->buf;
667228753Smm	keys = get_keys(mtree, entry);
668228753Smm	if ((keys & F_NLINK) != 0 &&
669228753Smm	    archive_entry_nlink(entry) != 1 &&
670228753Smm	    archive_entry_filetype(entry) != AE_IFDIR)
671228753Smm		archive_string_sprintf(str,
672228753Smm		    " nlink=%u", archive_entry_nlink(entry));
673228753Smm
674228753Smm	if ((keys & F_GNAME) != 0 &&
675228753Smm	    (name = archive_entry_gname(entry)) != NULL) {
676228753Smm		archive_strcat(str, " gname=");
677228753Smm		mtree_quote(str, name);
678228753Smm	}
679228753Smm	if ((keys & F_UNAME) != 0 &&
680228753Smm	    (name = archive_entry_uname(entry)) != NULL) {
681228753Smm		archive_strcat(str, " uname=");
682228753Smm		mtree_quote(str, name);
683228753Smm	}
684228753Smm	if ((keys & F_FLAGS) != 0 &&
685228753Smm	    (name = archive_entry_fflags_text(entry)) != NULL) {
686228753Smm		archive_strcat(str, " flags=");
687228753Smm		mtree_quote(str, name);
688228753Smm	}
689228753Smm	if ((keys & F_TIME) != 0)
690228753Smm		archive_string_sprintf(str, " time=%jd.%jd",
691228753Smm		    (intmax_t)archive_entry_mtime(entry),
692228753Smm		    (intmax_t)archive_entry_mtime_nsec(entry));
693228753Smm	if ((keys & F_MODE) != 0)
694228753Smm		archive_string_sprintf(str, " mode=%o",
695228753Smm		    archive_entry_mode(entry) & 07777);
696228753Smm	if ((keys & F_GID) != 0)
697228753Smm		archive_string_sprintf(str, " gid=%jd",
698228753Smm		    (intmax_t)archive_entry_gid(entry));
699228753Smm	if ((keys & F_UID) != 0)
700228753Smm		archive_string_sprintf(str, " uid=%jd",
701228753Smm		    (intmax_t)archive_entry_uid(entry));
702228753Smm
703228753Smm	switch (archive_entry_filetype(entry)) {
704228753Smm	case AE_IFLNK:
705228753Smm		if ((keys & F_TYPE) != 0)
706228753Smm			archive_strcat(str, " type=link");
707228753Smm		if ((keys & F_SLINK) != 0) {
708228753Smm			archive_strcat(str, " link=");
709228753Smm			mtree_quote(str, archive_entry_symlink(entry));
710228753Smm		}
711228753Smm		break;
712228753Smm	case AE_IFSOCK:
713228753Smm		if ((keys & F_TYPE) != 0)
714228753Smm			archive_strcat(str, " type=socket");
715228753Smm		break;
716228753Smm	case AE_IFCHR:
717228753Smm		if ((keys & F_TYPE) != 0)
718228753Smm			archive_strcat(str, " type=char");
719228753Smm		if ((keys & F_DEV) != 0) {
720228753Smm			archive_string_sprintf(str,
721228753Smm			    " device=native,%d,%d",
722228753Smm			    archive_entry_rdevmajor(entry),
723228753Smm			    archive_entry_rdevminor(entry));
724228753Smm		}
725228753Smm		break;
726228753Smm	case AE_IFBLK:
727228753Smm		if ((keys & F_TYPE) != 0)
728228753Smm			archive_strcat(str, " type=block");
729228753Smm		if ((keys & F_DEV) != 0) {
730228753Smm			archive_string_sprintf(str,
731228753Smm			    " device=native,%d,%d",
732228753Smm			    archive_entry_rdevmajor(entry),
733228753Smm			    archive_entry_rdevminor(entry));
734228753Smm		}
735228753Smm		break;
736228753Smm	case AE_IFDIR:
737228753Smm		if ((keys & F_TYPE) != 0)
738228753Smm			archive_strcat(str, " type=dir");
739228753Smm		break;
740228753Smm	case AE_IFIFO:
741228753Smm		if ((keys & F_TYPE) != 0)
742228753Smm			archive_strcat(str, " type=fifo");
743228753Smm		break;
744228753Smm	case AE_IFREG:
745228753Smm	default:	/* Handle unknown file types as regular files. */
746228753Smm		if ((keys & F_TYPE) != 0)
747228753Smm			archive_strcat(str, " type=file");
748228753Smm		if ((keys & F_SIZE) != 0)
749228753Smm			archive_string_sprintf(str, " size=%jd",
750228753Smm			    (intmax_t)archive_entry_size(entry));
751228753Smm		break;
752228753Smm	}
753228753Smm
754228753Smm	if (mtree->compute_sum & F_CKSUM) {
755228753Smm		uint64_t len;
756228753Smm		/* Include the length of the file. */
757228753Smm		for (len = mtree->crc_len; len != 0; len >>= 8)
758228753Smm			COMPUTE_CRC(mtree->crc, len & 0xff);
759228753Smm		mtree->crc = ~mtree->crc;
760228753Smm		archive_string_sprintf(str, " cksum=%ju",
761228753Smm		    (uintmax_t)mtree->crc);
762228753Smm	}
763228753Smm#ifdef ARCHIVE_HAS_MD5
764228753Smm	if (mtree->compute_sum & F_MD5) {
765228753Smm		unsigned char buf[16];
766228753Smm
767228753Smm		archive_md5_final(&mtree->md5ctx, buf);
768228753Smm		archive_strcat(str, " md5digest=");
769228753Smm		strappend_bin(str, buf, sizeof(buf));
770228753Smm	}
771228753Smm#endif
772228753Smm#ifdef ARCHIVE_HAS_RMD160
773228753Smm	if (mtree->compute_sum & F_RMD160) {
774228753Smm		unsigned char buf[20];
775228753Smm
776228753Smm		archive_rmd160_final(&mtree->rmd160ctx, buf);
777228753Smm		archive_strcat(str, " rmd160digest=");
778228753Smm		strappend_bin(str, buf, sizeof(buf));
779228753Smm	}
780228753Smm#endif
781228753Smm#ifdef ARCHIVE_HAS_SHA1
782228753Smm	if (mtree->compute_sum & F_SHA1) {
783228753Smm		unsigned char buf[20];
784228753Smm
785228753Smm		archive_sha1_final(&mtree->sha1ctx, buf);
786228753Smm		archive_strcat(str, " sha1digest=");
787228753Smm		strappend_bin(str, buf, sizeof(buf));
788228753Smm	}
789228753Smm#endif
790228753Smm#ifdef ARCHIVE_HAS_SHA256
791228753Smm	if (mtree->compute_sum & F_SHA256) {
792228753Smm		unsigned char buf[32];
793228753Smm
794228753Smm		archive_sha256_final(&mtree->sha256ctx, buf);
795228753Smm		archive_strcat(str, " sha256digest=");
796228753Smm		strappend_bin(str, buf, sizeof(buf));
797228753Smm	}
798228753Smm#endif
799228753Smm#ifdef ARCHIVE_HAS_SHA384
800228753Smm	if (mtree->compute_sum & F_SHA384) {
801228753Smm		unsigned char buf[48];
802228753Smm
803228753Smm		archive_sha384_final(&mtree->sha384ctx, buf);
804228753Smm		archive_strcat(str, " sha384digest=");
805228753Smm		strappend_bin(str, buf, sizeof(buf));
806228753Smm	}
807228753Smm#endif
808228753Smm#ifdef ARCHIVE_HAS_SHA512
809228753Smm	if (mtree->compute_sum & F_SHA512) {
810228753Smm		unsigned char buf[64];
811228753Smm
812228753Smm		archive_sha512_final(&mtree->sha512ctx, buf);
813228753Smm		archive_strcat(str, " sha512digest=");
814228753Smm		strappend_bin(str, buf, sizeof(buf));
815228753Smm	}
816228753Smm#endif
817228753Smm	archive_strcat(str, "\n");
818228753Smm	if (mtree->indent)
819228753Smm		mtree_indent(mtree);
820228753Smm
821228753Smm	archive_entry_free(entry);
822228753Smm
823228753Smm	if (mtree->buf.length > 32768) {
824228753Smm		ret = (a->compressor.write)(a, mtree->buf.s, mtree->buf.length);
825228753Smm		archive_string_empty(&mtree->buf);
826228753Smm	} else
827228753Smm		ret = ARCHIVE_OK;
828228753Smm
829228753Smm	return (ret == ARCHIVE_OK ? ret : ARCHIVE_FATAL);
830228753Smm}
831228753Smm
832228753Smmstatic int
833228753Smmarchive_write_mtree_finish(struct archive_write *a)
834228753Smm{
835228753Smm	struct mtree_writer *mtree= a->format_data;
836228753Smm
837228753Smm	archive_write_set_bytes_in_last_block(&a->archive, 1);
838228753Smm
839228753Smm	return (a->compressor.write)(a, mtree->buf.s, mtree->buf.length);
840228753Smm}
841228753Smm
842228753Smmstatic ssize_t
843228753Smmarchive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
844228753Smm{
845228753Smm	struct mtree_writer *mtree= a->format_data;
846228753Smm
847228753Smm	if (n > mtree->entry_bytes_remaining)
848228753Smm		n = mtree->entry_bytes_remaining;
849228753Smm	if (mtree->dironly)
850228753Smm		/* We don't need compute a regular file sum */
851228753Smm		return (n);
852228753Smm	if (mtree->compute_sum & F_CKSUM) {
853228753Smm		/*
854228753Smm		 * Compute a POSIX 1003.2 checksum
855228753Smm		 */
856228753Smm		const unsigned char *p;
857228753Smm		size_t nn;
858228753Smm
859228753Smm		for (nn = n, p = buff; nn--; ++p)
860228753Smm			COMPUTE_CRC(mtree->crc, *p);
861228753Smm		mtree->crc_len += n;
862228753Smm	}
863228753Smm#ifdef ARCHIVE_HAS_MD5
864228753Smm	if (mtree->compute_sum & F_MD5)
865228753Smm		archive_md5_update(&mtree->md5ctx, buff, n);
866228753Smm#endif
867228753Smm#ifdef ARCHIVE_HAS_RMD160
868228753Smm	if (mtree->compute_sum & F_RMD160)
869228753Smm		archive_rmd160_update(&mtree->rmd160ctx, buff, n);
870228753Smm#endif
871228753Smm#ifdef ARCHIVE_HAS_SHA1
872228753Smm	if (mtree->compute_sum & F_SHA1)
873228753Smm		archive_sha1_update(&mtree->sha1ctx, buff, n);
874228753Smm#endif
875228753Smm#ifdef ARCHIVE_HAS_SHA256
876228753Smm	if (mtree->compute_sum & F_SHA256)
877228753Smm		archive_sha256_update(&mtree->sha256ctx, buff, n);
878228753Smm#endif
879228753Smm#ifdef ARCHIVE_HAS_SHA384
880228753Smm	if (mtree->compute_sum & F_SHA384)
881228753Smm		archive_sha384_update(&mtree->sha384ctx, buff, n);
882228753Smm#endif
883228753Smm#ifdef ARCHIVE_HAS_SHA512
884228753Smm	if (mtree->compute_sum & F_SHA512)
885228753Smm		archive_sha512_update(&mtree->sha512ctx, buff, n);
886228753Smm#endif
887228753Smm	return (n);
888228753Smm}
889228753Smm
890228753Smmstatic int
891228753Smmarchive_write_mtree_destroy(struct archive_write *a)
892228753Smm{
893228753Smm	struct mtree_writer *mtree= a->format_data;
894228753Smm
895228753Smm	if (mtree == NULL)
896228753Smm		return (ARCHIVE_OK);
897228753Smm
898228753Smm	archive_entry_free(mtree->entry);
899228753Smm	archive_string_free(&mtree->ebuf);
900228753Smm	archive_string_free(&mtree->buf);
901228753Smm	archive_string_free(&mtree->set.parent);
902228753Smm	free(mtree);
903228753Smm	a->format_data = NULL;
904228753Smm	return (ARCHIVE_OK);
905228753Smm}
906228753Smm
907228753Smmstatic int
908228753Smmarchive_write_mtree_options(struct archive_write *a, const char *key,
909228753Smm    const char *value)
910228753Smm{
911228753Smm	struct mtree_writer *mtree= a->format_data;
912228753Smm	int keybit = 0;
913228753Smm
914228753Smm	switch (key[0]) {
915228753Smm	case 'a':
916228753Smm		if (strcmp(key, "all") == 0)
917228753Smm			keybit = ~0;
918228753Smm		break;
919228753Smm	case 'c':
920228753Smm		if (strcmp(key, "cksum") == 0)
921228753Smm			keybit = F_CKSUM;
922228753Smm		break;
923228753Smm	case 'd':
924228753Smm		if (strcmp(key, "device") == 0)
925228753Smm			keybit = F_DEV;
926228753Smm		else if (strcmp(key, "dironly") == 0) {
927228753Smm			mtree->dironly = (value != NULL)? 1: 0;
928228753Smm			return (ARCHIVE_OK);
929228753Smm		}
930228753Smm		break;
931228753Smm	case 'f':
932228753Smm		if (strcmp(key, "flags") == 0)
933228753Smm			keybit = F_FLAGS;
934228753Smm		break;
935228753Smm	case 'g':
936228753Smm		if (strcmp(key, "gid") == 0)
937228753Smm			keybit = F_GID;
938228753Smm		else if (strcmp(key, "gname") == 0)
939228753Smm			keybit = F_GNAME;
940228753Smm		break;
941228753Smm	case 'i':
942228753Smm		if (strcmp(key, "indent") == 0) {
943228753Smm			mtree->indent = (value != NULL)? 1: 0;
944228753Smm			return (ARCHIVE_OK);
945228753Smm		}
946228753Smm		break;
947228753Smm	case 'l':
948228753Smm		if (strcmp(key, "link") == 0)
949228753Smm			keybit = F_SLINK;
950228753Smm		break;
951228753Smm	case 'm':
952228753Smm		if (strcmp(key, "md5") == 0 ||
953228753Smm		    strcmp(key, "md5digest") == 0)
954228753Smm			keybit = F_MD5;
955228753Smm		if (strcmp(key, "mode") == 0)
956228753Smm			keybit = F_MODE;
957228753Smm		break;
958228753Smm	case 'n':
959228753Smm		if (strcmp(key, "nlink") == 0)
960228753Smm			keybit = F_NLINK;
961228753Smm		break;
962228753Smm	case 'r':
963228753Smm		if (strcmp(key, "ripemd160digest") == 0 ||
964228753Smm		    strcmp(key, "rmd160") == 0 ||
965228753Smm		    strcmp(key, "rmd160digest") == 0)
966228753Smm			keybit = F_RMD160;
967228753Smm		break;
968228753Smm	case 's':
969228753Smm		if (strcmp(key, "sha1") == 0 ||
970228753Smm		    strcmp(key, "sha1digest") == 0)
971228753Smm			keybit = F_SHA1;
972228753Smm		if (strcmp(key, "sha256") == 0 ||
973228753Smm		    strcmp(key, "sha256digest") == 0)
974228753Smm			keybit = F_SHA256;
975228753Smm		if (strcmp(key, "sha384") == 0 ||
976228753Smm		    strcmp(key, "sha384digest") == 0)
977228753Smm			keybit = F_SHA384;
978228753Smm		if (strcmp(key, "sha512") == 0 ||
979228753Smm		    strcmp(key, "sha512digest") == 0)
980228753Smm			keybit = F_SHA512;
981228753Smm		if (strcmp(key, "size") == 0)
982228753Smm			keybit = F_SIZE;
983228753Smm		break;
984228753Smm	case 't':
985228753Smm		if (strcmp(key, "time") == 0)
986228753Smm			keybit = F_TIME;
987228753Smm		else if (strcmp(key, "type") == 0)
988228753Smm			keybit = F_TYPE;
989228753Smm		break;
990228753Smm	case 'u':
991228753Smm		if (strcmp(key, "uid") == 0)
992228753Smm			keybit = F_UID;
993228753Smm		else if (strcmp(key, "uname") == 0)
994228753Smm			keybit = F_UNAME;
995228753Smm		else if (strcmp(key, "use-set") == 0) {
996228753Smm			mtree->set.output = (value != NULL)? 1: 0;
997228753Smm			return (ARCHIVE_OK);
998228753Smm		}
999228753Smm		break;
1000228753Smm	}
1001228753Smm	if (keybit != 0) {
1002228753Smm		if (value != NULL)
1003228753Smm			mtree->keys |= keybit;
1004228753Smm		else
1005228753Smm			mtree->keys &= ~keybit;
1006228753Smm		return (ARCHIVE_OK);
1007228753Smm	}
1008228753Smm
1009228753Smm	return (ARCHIVE_WARN);
1010228753Smm}
1011228753Smm
1012228753Smmint
1013228753Smmarchive_write_set_format_mtree(struct archive *_a)
1014228753Smm{
1015228753Smm	struct archive_write *a = (struct archive_write *)_a;
1016228753Smm	struct mtree_writer *mtree;
1017228753Smm
1018228753Smm	if (a->format_destroy != NULL)
1019228753Smm		(a->format_destroy)(a);
1020228753Smm
1021228753Smm	if ((mtree = malloc(sizeof(*mtree))) == NULL) {
1022228753Smm		archive_set_error(&a->archive, ENOMEM,
1023228753Smm		    "Can't allocate mtree data");
1024228753Smm		return (ARCHIVE_FATAL);
1025228753Smm	}
1026228753Smm
1027228753Smm	mtree->entry = NULL;
1028228753Smm	mtree->first = 1;
1029228753Smm	memset(&(mtree->set), 0, sizeof(mtree->set));
1030228753Smm	archive_string_init(&mtree->set.parent);
1031228753Smm	mtree->keys = DEFAULT_KEYS;
1032228753Smm	mtree->dironly = 0;
1033228753Smm	mtree->indent = 0;
1034228753Smm	archive_string_init(&mtree->ebuf);
1035228753Smm	archive_string_init(&mtree->buf);
1036228753Smm	a->format_data = mtree;
1037228753Smm	a->format_destroy = archive_write_mtree_destroy;
1038228753Smm
1039228753Smm	a->pad_uncompressed = 0;
1040228753Smm	a->format_name = "mtree";
1041228753Smm	a->format_options = archive_write_mtree_options;
1042228753Smm	a->format_write_header = archive_write_mtree_header;
1043228753Smm	a->format_finish = archive_write_mtree_finish;
1044228753Smm	a->format_write_data = archive_write_mtree_data;
1045228753Smm	a->format_finish_entry = archive_write_mtree_finish_entry;
1046228753Smm	a->archive.archive_format = ARCHIVE_FORMAT_MTREE;
1047228753Smm	a->archive.archive_format_name = "mtree";
1048228753Smm
1049228753Smm	return (ARCHIVE_OK);
1050228753Smm}
1051