1228753Smm/*-
2228753Smm * Copyright (c) 2008 Joerg Sonnenberger
3232153Smm * Copyright (c) 2009-2012 Michihiro NAKAJIMA
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"
28228763Smm__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"
38232153Smm#include "archive_crypto_private.h"
39228753Smm#include "archive_entry.h"
40228753Smm#include "archive_private.h"
41248616Smm#include "archive_rb.h"
42248616Smm#include "archive_string.h"
43228753Smm#include "archive_write_private.h"
44228753Smm
45228753Smm#define INDENTNAMELEN	15
46228753Smm#define MAXLINELEN	80
47232153Smm#define SET_KEYS	\
48232153Smm	(F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME)
49228753Smm
50248616Smmstruct attr_counter {
51248616Smm	struct attr_counter *prev;
52248616Smm	struct attr_counter *next;
53248616Smm	struct mtree_entry *m_entry;
54248616Smm	int count;
55248616Smm};
56232153Smm
57248616Smmstruct att_counter_set {
58248616Smm	struct attr_counter *uid_list;
59248616Smm	struct attr_counter *gid_list;
60248616Smm	struct attr_counter *mode_list;
61248616Smm	struct attr_counter *flags_list;
62248616Smm};
63232153Smm
64248616Smmstruct mtree_chain {
65248616Smm	struct mtree_entry *first;
66248616Smm	struct mtree_entry **last;
67248616Smm};
68248616Smm
69248616Smm/*
70248616Smm * The Data only for a directory file.
71248616Smm */
72248616Smmstruct dir_info {
73248616Smm	struct archive_rb_tree rbtree;
74248616Smm	struct mtree_chain children;
75248616Smm	struct mtree_entry *chnext;
76248616Smm	int virtual;
77248616Smm};
78248616Smm
79248616Smm/*
80248616Smm * The Data only for a regular file.
81248616Smm */
82248616Smmstruct reg_info {
83232153Smm	int compute_sum;
84232153Smm	uint32_t crc;
85232153Smm#ifdef ARCHIVE_HAS_MD5
86232153Smm	unsigned char buf_md5[16];
87232153Smm#endif
88232153Smm#ifdef ARCHIVE_HAS_RMD160
89232153Smm	unsigned char buf_rmd160[20];
90232153Smm#endif
91232153Smm#ifdef ARCHIVE_HAS_SHA1
92232153Smm	unsigned char buf_sha1[20];
93232153Smm#endif
94232153Smm#ifdef ARCHIVE_HAS_SHA256
95232153Smm	unsigned char buf_sha256[32];
96232153Smm#endif
97232153Smm#ifdef ARCHIVE_HAS_SHA384
98232153Smm	unsigned char buf_sha384[48];
99232153Smm#endif
100232153Smm#ifdef ARCHIVE_HAS_SHA512
101232153Smm	unsigned char buf_sha512[64];
102232153Smm#endif
103232153Smm};
104232153Smm
105248616Smmstruct mtree_entry {
106248616Smm	struct archive_rb_node rbnode;
107248616Smm	struct mtree_entry *next;
108248616Smm	struct mtree_entry *parent;
109248616Smm	struct dir_info *dir_info;
110248616Smm	struct reg_info *reg_info;
111248616Smm
112248616Smm	struct archive_string parentdir;
113248616Smm	struct archive_string basename;
114248616Smm	struct archive_string pathname;
115248616Smm	struct archive_string symlink;
116248616Smm	struct archive_string uname;
117248616Smm	struct archive_string gname;
118248616Smm	struct archive_string fflags_text;
119248616Smm	unsigned int nlink;
120248616Smm	mode_t filetype;
121248616Smm	mode_t mode;
122248616Smm	int64_t size;
123248616Smm	int64_t uid;
124248616Smm	int64_t gid;
125248616Smm	time_t mtime;
126248616Smm	long mtime_nsec;
127248616Smm	unsigned long fflags_set;
128248616Smm	unsigned long fflags_clear;
129248616Smm	dev_t rdevmajor;
130248616Smm	dev_t rdevminor;
131232153Smm};
132232153Smm
133228753Smmstruct mtree_writer {
134232153Smm	struct mtree_entry *mtree_entry;
135248616Smm	struct mtree_entry *root;
136248616Smm	struct mtree_entry *cur_dirent;
137248616Smm	struct archive_string cur_dirstr;
138248616Smm	struct mtree_chain file_list;
139248616Smm
140228753Smm	struct archive_string ebuf;
141228753Smm	struct archive_string buf;
142228753Smm	int first;
143228753Smm	uint64_t entry_bytes_remaining;
144248616Smm
145248616Smm	/*
146248616Smm	 * Set global value.
147248616Smm	 */
148228753Smm	struct {
149248616Smm		int		processing;
150228753Smm		mode_t		type;
151228753Smm		int		keys;
152232153Smm		int64_t		uid;
153232153Smm		int64_t		gid;
154228753Smm		mode_t		mode;
155228753Smm		unsigned long	fflags_set;
156228753Smm		unsigned long	fflags_clear;
157248616Smm	} set;
158248616Smm	struct att_counter_set	acs;
159248616Smm	int classic;
160248616Smm	int depth;
161232153Smm
162232153Smm	/* check sum */
163228753Smm	int compute_sum;
164228753Smm	uint32_t crc;
165228753Smm	uint64_t crc_len;
166228753Smm#ifdef ARCHIVE_HAS_MD5
167228753Smm	archive_md5_ctx md5ctx;
168228753Smm#endif
169228753Smm#ifdef ARCHIVE_HAS_RMD160
170228753Smm	archive_rmd160_ctx rmd160ctx;
171228753Smm#endif
172228753Smm#ifdef ARCHIVE_HAS_SHA1
173228753Smm	archive_sha1_ctx sha1ctx;
174228753Smm#endif
175228753Smm#ifdef ARCHIVE_HAS_SHA256
176228753Smm	archive_sha256_ctx sha256ctx;
177228753Smm#endif
178228753Smm#ifdef ARCHIVE_HAS_SHA384
179228753Smm	archive_sha384_ctx sha384ctx;
180228753Smm#endif
181228753Smm#ifdef ARCHIVE_HAS_SHA512
182228753Smm	archive_sha512_ctx sha512ctx;
183228753Smm#endif
184228753Smm	/* Keyword options */
185228753Smm	int keys;
186228753Smm#define	F_CKSUM		0x00000001		/* check sum */
187228753Smm#define	F_DEV		0x00000002		/* device type */
188228753Smm#define	F_DONE		0x00000004		/* directory done */
189228753Smm#define	F_FLAGS		0x00000008		/* file flags */
190228753Smm#define	F_GID		0x00000010		/* gid */
191228753Smm#define	F_GNAME		0x00000020		/* group name */
192228753Smm#define	F_IGN		0x00000040		/* ignore */
193228753Smm#define	F_MAGIC		0x00000080		/* name has magic chars */
194228753Smm#define	F_MD5		0x00000100		/* MD5 digest */
195228753Smm#define	F_MODE		0x00000200		/* mode */
196228753Smm#define	F_NLINK		0x00000400		/* number of links */
197228753Smm#define	F_NOCHANGE 	0x00000800		/* If owner/mode "wrong", do
198228753Smm						 * not change */
199228753Smm#define	F_OPT		0x00001000		/* existence optional */
200228753Smm#define	F_RMD160 	0x00002000		/* RIPEMD160 digest */
201228753Smm#define	F_SHA1		0x00004000		/* SHA-1 digest */
202228753Smm#define	F_SIZE		0x00008000		/* size */
203228753Smm#define	F_SLINK		0x00010000		/* symbolic link */
204228753Smm#define	F_TAGS		0x00020000		/* tags */
205228753Smm#define	F_TIME		0x00040000		/* modification time */
206228753Smm#define	F_TYPE		0x00080000		/* file type */
207228753Smm#define	F_UID		0x00100000		/* uid */
208228753Smm#define	F_UNAME		0x00200000		/* user name */
209228753Smm#define	F_VISIT		0x00400000		/* file visited */
210228753Smm#define	F_SHA256	0x00800000		/* SHA-256 digest */
211228753Smm#define	F_SHA384	0x01000000		/* SHA-384 digest */
212228753Smm#define	F_SHA512	0x02000000		/* SHA-512 digest */
213228753Smm
214228753Smm	/* Options */
215248616Smm	int dironly;		/* If it is set, ignore all files except
216248616Smm				 * directory files, like mtree(8) -d option. */
217248616Smm	int indent;		/* If it is set, indent output data. */
218248616Smm	int output_global_set;	/* If it is set, use /set keyword to set
219248616Smm				 * global values. When generating mtree
220248616Smm				 * classic format, it is set by default. */
221228753Smm};
222228753Smm
223228753Smm#define DEFAULT_KEYS	(F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\
224228753Smm			 | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\
225228753Smm			 | F_UNAME)
226248616Smm#define attr_counter_set_reset	attr_counter_set_free
227228753Smm
228248616Smmstatic void attr_counter_free(struct attr_counter **);
229248616Smmstatic int attr_counter_inc(struct attr_counter **, struct attr_counter *,
230248616Smm	struct attr_counter *, struct mtree_entry *);
231248616Smmstatic struct attr_counter * attr_counter_new(struct mtree_entry *,
232232153Smm	struct attr_counter *);
233248616Smmstatic int attr_counter_set_collect(struct mtree_writer *,
234248616Smm	struct mtree_entry *);
235248616Smmstatic void attr_counter_set_free(struct mtree_writer *);
236248616Smmstatic int get_global_set_keys(struct mtree_writer *, struct mtree_entry *);
237248616Smmstatic int mtree_entry_add_child_tail(struct mtree_entry *,
238248616Smm	struct mtree_entry *);
239248616Smmstatic int mtree_entry_create_virtual_dir(struct archive_write *, const char *,
240248616Smm	struct mtree_entry **);
241248616Smmstatic int mtree_entry_cmp_node(const struct archive_rb_node *,
242248616Smm	const struct archive_rb_node *);
243248616Smmstatic int mtree_entry_cmp_key(const struct archive_rb_node *, const void *);
244248616Smmstatic int mtree_entry_exchange_same_entry(struct archive_write *,
245248616Smm    struct mtree_entry *, struct mtree_entry *);
246248616Smmstatic void mtree_entry_free(struct mtree_entry *);
247248616Smmstatic int mtree_entry_new(struct archive_write *, struct archive_entry *,
248248616Smm	struct mtree_entry **);
249248616Smmstatic void mtree_entry_register_free(struct mtree_writer *);
250248616Smmstatic void mtree_entry_register_init(struct mtree_writer *);
251248616Smmstatic int mtree_entry_setup_filenames(struct archive_write *,
252248616Smm	struct mtree_entry *, struct archive_entry *);
253248616Smmstatic int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **);
254232153Smmstatic void sum_init(struct mtree_writer *);
255232153Smmstatic void sum_update(struct mtree_writer *, const void *, size_t);
256248616Smmstatic void sum_final(struct mtree_writer *, struct reg_info *);
257248616Smmstatic void sum_write(struct archive_string *, struct reg_info *);
258248616Smmstatic int write_mtree_entry(struct archive_write *, struct mtree_entry *);
259248616Smmstatic int write_dot_dot_entry(struct archive_write *, struct mtree_entry *);
260232153Smm
261228753Smm#define	COMPUTE_CRC(var, ch)	(var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
262228753Smmstatic const uint32_t crctab[] = {
263228753Smm	0x0,
264228753Smm	0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
265228753Smm	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
266228753Smm	0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
267228753Smm	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
268228753Smm	0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
269228753Smm	0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
270228753Smm	0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
271228753Smm	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
272228753Smm	0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
273228753Smm	0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
274228753Smm	0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
275228753Smm	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
276228753Smm	0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
277228753Smm	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
278228753Smm	0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
279228753Smm	0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
280228753Smm	0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
281228753Smm	0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
282228753Smm	0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
283228753Smm	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
284228753Smm	0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
285228753Smm	0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
286228753Smm	0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
287228753Smm	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
288228753Smm	0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
289228753Smm	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
290228753Smm	0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
291228753Smm	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
292228753Smm	0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
293228753Smm	0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
294228753Smm	0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
295228753Smm	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
296228753Smm	0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
297228753Smm	0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
298228753Smm	0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
299228753Smm	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
300228753Smm	0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
301228753Smm	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
302228753Smm	0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
303228753Smm	0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
304228753Smm	0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
305228753Smm	0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
306228753Smm	0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
307228753Smm	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
308228753Smm	0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
309228753Smm	0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
310228753Smm	0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
311228753Smm	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
312228753Smm	0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
313228753Smm	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
314228753Smm	0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
315228753Smm};
316228753Smm
317248616Smmstatic const unsigned char safe_char[256] = {
318248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
319248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
320248616Smm	/* !"$%&'()*+,-./  EXCLUSION:0x20( ) 0x23(#) */
321248616Smm	0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
322248616Smm	/* 0123456789:;<>?  EXCLUSION:0x3d(=) */
323248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
324248616Smm	/* @ABCDEFGHIJKLMNO */
325248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
326248616Smm	/* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\)  */
327248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */
328248616Smm	/* `abcdefghijklmno */
329248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
330248616Smm	/* pqrstuvwxyz{|}~ */
331248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
332248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
333248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
334248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
335248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
336248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
337248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
338248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
339248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
340248616Smm};
341228753Smm
342228753Smmstatic void
343228753Smmmtree_quote(struct archive_string *s, const char *str)
344228753Smm{
345228753Smm	const char *start;
346228753Smm	char buf[4];
347228753Smm	unsigned char c;
348228753Smm
349228753Smm	for (start = str; *str != '\0'; ++str) {
350248616Smm		if (safe_char[*(const unsigned char *)str])
351228753Smm			continue;
352228753Smm		if (start != str)
353228753Smm			archive_strncat(s, start, str - start);
354228753Smm		c = (unsigned char)*str;
355228753Smm		buf[0] = '\\';
356228753Smm		buf[1] = (c / 64) + '0';
357228753Smm		buf[2] = (c / 8 % 8) + '0';
358228753Smm		buf[3] = (c % 8) + '0';
359228753Smm		archive_strncat(s, buf, 4);
360228753Smm		start = str + 1;
361228753Smm	}
362228753Smm
363228753Smm	if (start != str)
364228753Smm		archive_strncat(s, start, str - start);
365228753Smm}
366228753Smm
367232153Smm/*
368232153Smm * Indent a line as mtree utility to be readable for people.
369232153Smm */
370228753Smmstatic void
371228753Smmmtree_indent(struct mtree_writer *mtree)
372228753Smm{
373248616Smm	int i, fn, nd, pd;
374228753Smm	const char *r, *s, *x;
375228753Smm
376248616Smm	if (mtree->classic) {
377248616Smm		if (mtree->indent) {
378248616Smm			nd = 0;
379248616Smm			pd = mtree->depth * 4;
380248616Smm		} else {
381248616Smm			nd = mtree->depth?4:0;
382248616Smm			pd = 0;
383248616Smm		}
384248616Smm	} else
385248616Smm		nd = pd = 0;
386228753Smm	fn = 1;
387228753Smm	s = r = mtree->ebuf.s;
388228753Smm	x = NULL;
389228753Smm	while (*r == ' ')
390228753Smm		r++;
391228753Smm	while ((r = strchr(r, ' ')) != NULL) {
392228753Smm		if (fn) {
393228753Smm			fn = 0;
394248616Smm			for (i = 0; i < nd + pd; i++)
395248616Smm				archive_strappend_char(&mtree->buf, ' ');
396228753Smm			archive_strncat(&mtree->buf, s, r - s);
397248616Smm			if (nd + (r -s) > INDENTNAMELEN) {
398228753Smm				archive_strncat(&mtree->buf, " \\\n", 3);
399248616Smm				for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
400228753Smm					archive_strappend_char(&mtree->buf, ' ');
401228753Smm			} else {
402248616Smm				for (i = (int)(r -s + nd);
403248616Smm				    i < (INDENTNAMELEN + 1); i++)
404228753Smm					archive_strappend_char(&mtree->buf, ' ');
405228753Smm			}
406228753Smm			s = ++r;
407228753Smm			x = NULL;
408228753Smm			continue;
409228753Smm		}
410248616Smm		if (pd + (r - s) <= MAXLINELEN - 3 - INDENTNAMELEN)
411228753Smm			x = r++;
412228753Smm		else {
413228753Smm			if (x == NULL)
414228753Smm				x = r;
415228753Smm			archive_strncat(&mtree->buf, s, x - s);
416228753Smm			archive_strncat(&mtree->buf, " \\\n", 3);
417248616Smm			for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
418228753Smm				archive_strappend_char(&mtree->buf, ' ');
419228753Smm			s = r = ++x;
420228753Smm			x = NULL;
421228753Smm		}
422228753Smm	}
423248616Smm	if (fn) {
424248616Smm		for (i = 0; i < nd + pd; i++)
425248616Smm			archive_strappend_char(&mtree->buf, ' ');
426248616Smm		archive_strcat(&mtree->buf, s);
427248616Smm		s += strlen(s);
428248616Smm	}
429248616Smm	if (x != NULL && pd + strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) {
430228753Smm		/* Last keyword is longer. */
431228753Smm		archive_strncat(&mtree->buf, s, x - s);
432228753Smm		archive_strncat(&mtree->buf, " \\\n", 3);
433248616Smm		for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
434228753Smm			archive_strappend_char(&mtree->buf, ' ');
435228753Smm		s = ++x;
436228753Smm	}
437228753Smm	archive_strcat(&mtree->buf, s);
438228753Smm	archive_string_empty(&mtree->ebuf);
439228753Smm}
440228753Smm
441228753Smm/*
442232153Smm * Write /set keyword.
443232153Smm * Set most used value of uid,gid,mode and fflags, which are
444248616Smm * collected by attr_counter_set_collect() function.
445228753Smm */
446228753Smmstatic void
447232153Smmwrite_global(struct mtree_writer *mtree)
448228753Smm{
449228753Smm	struct archive_string setstr;
450228753Smm	struct archive_string unsetstr;
451248616Smm	struct att_counter_set *acs;
452228753Smm	int keys, oldkeys, effkeys;
453228753Smm
454228753Smm	archive_string_init(&setstr);
455228753Smm	archive_string_init(&unsetstr);
456232153Smm	keys = mtree->keys & SET_KEYS;
457228753Smm	oldkeys = mtree->set.keys;
458228753Smm	effkeys = keys;
459248616Smm	acs = &mtree->acs;
460248616Smm	if (mtree->set.processing) {
461228753Smm		/*
462232153Smm		 * Check if the global data needs updating.
463228753Smm		 */
464228753Smm		effkeys &= ~F_TYPE;
465248616Smm		if (acs->uid_list == NULL)
466248616Smm			effkeys &= ~(F_UNAME | F_UID);
467248616Smm		else if (oldkeys & (F_UNAME | F_UID)) {
468248616Smm			if (acs->uid_list->count < 2 ||
469248616Smm			    mtree->set.uid == acs->uid_list->m_entry->uid)
470248616Smm				effkeys &= ~(F_UNAME | F_UID);
471232153Smm		}
472248616Smm		if (acs->gid_list == NULL)
473248616Smm			effkeys &= ~(F_GNAME | F_GID);
474248616Smm		else if (oldkeys & (F_GNAME | F_GID)) {
475248616Smm			if (acs->gid_list->count < 2 ||
476248616Smm			    mtree->set.gid == acs->gid_list->m_entry->gid)
477248616Smm				effkeys &= ~(F_GNAME | F_GID);
478232153Smm		}
479248616Smm		if (acs->mode_list == NULL)
480248616Smm			effkeys &= ~F_MODE;
481248616Smm		else if (oldkeys & F_MODE) {
482248616Smm			if (acs->mode_list->count < 2 ||
483248616Smm			    mtree->set.mode == acs->mode_list->m_entry->mode)
484248616Smm				effkeys &= ~F_MODE;
485232153Smm		}
486248616Smm		if (acs->flags_list == NULL)
487248616Smm			effkeys &= ~F_FLAGS;
488248616Smm		else if ((oldkeys & F_FLAGS) != 0) {
489248616Smm			if (acs->flags_list->count < 2 ||
490248616Smm			    (acs->flags_list->m_entry->fflags_set ==
491248616Smm				mtree->set.fflags_set &&
492248616Smm			     acs->flags_list->m_entry->fflags_clear ==
493248616Smm				mtree->set.fflags_clear))
494248616Smm				effkeys &= ~F_FLAGS;
495228753Smm		}
496248616Smm	} else {
497248616Smm		if (acs->uid_list == NULL)
498248616Smm			keys &= ~(F_UNAME | F_UID);
499248616Smm		if (acs->gid_list == NULL)
500248616Smm			keys &= ~(F_GNAME | F_GID);
501248616Smm		if (acs->mode_list == NULL)
502248616Smm			keys &= ~F_MODE;
503248616Smm		if (acs->flags_list == NULL)
504248616Smm			keys &= ~F_FLAGS;
505228753Smm	}
506228753Smm	if ((keys & effkeys & F_TYPE) != 0) {
507232153Smm		if (mtree->dironly) {
508228753Smm			archive_strcat(&setstr, " type=dir");
509232153Smm			mtree->set.type = AE_IFDIR;
510232153Smm		} else {
511228753Smm			archive_strcat(&setstr, " type=file");
512232153Smm			mtree->set.type = AE_IFREG;
513232153Smm		}
514228753Smm	}
515228753Smm	if ((keys & effkeys & F_UNAME) != 0) {
516248616Smm		if (archive_strlen(&(acs->uid_list->m_entry->uname)) > 0) {
517228753Smm			archive_strcat(&setstr, " uname=");
518248616Smm			mtree_quote(&setstr, acs->uid_list->m_entry->uname.s);
519232153Smm		} else {
520228753Smm			keys &= ~F_UNAME;
521232153Smm			if ((oldkeys & F_UNAME) != 0)
522232153Smm				archive_strcat(&unsetstr, " uname");
523232153Smm		}
524228753Smm	}
525228753Smm	if ((keys & effkeys & F_UID) != 0) {
526248616Smm		mtree->set.uid = acs->uid_list->m_entry->uid;
527228753Smm		archive_string_sprintf(&setstr, " uid=%jd",
528228753Smm		    (intmax_t)mtree->set.uid);
529228753Smm	}
530228753Smm	if ((keys & effkeys & F_GNAME) != 0) {
531248616Smm		if (archive_strlen(&(acs->gid_list->m_entry->gname)) > 0) {
532228753Smm			archive_strcat(&setstr, " gname=");
533248616Smm			mtree_quote(&setstr, acs->gid_list->m_entry->gname.s);
534232153Smm		} else {
535228753Smm			keys &= ~F_GNAME;
536232153Smm			if ((oldkeys & F_GNAME) != 0)
537232153Smm				archive_strcat(&unsetstr, " gname");
538232153Smm		}
539228753Smm	}
540228753Smm	if ((keys & effkeys & F_GID) != 0) {
541248616Smm		mtree->set.gid = acs->gid_list->m_entry->gid;
542228753Smm		archive_string_sprintf(&setstr, " gid=%jd",
543228753Smm		    (intmax_t)mtree->set.gid);
544228753Smm	}
545228753Smm	if ((keys & effkeys & F_MODE) != 0) {
546248616Smm		mtree->set.mode = acs->mode_list->m_entry->mode;
547232153Smm		archive_string_sprintf(&setstr, " mode=%o",
548232153Smm		    (unsigned int)mtree->set.mode);
549228753Smm	}
550228753Smm	if ((keys & effkeys & F_FLAGS) != 0) {
551248616Smm		if (archive_strlen(
552248616Smm		    &(acs->flags_list->m_entry->fflags_text)) > 0) {
553228753Smm			archive_strcat(&setstr, " flags=");
554248616Smm			mtree_quote(&setstr,
555248616Smm			    acs->flags_list->m_entry->fflags_text.s);
556232153Smm			mtree->set.fflags_set =
557248616Smm			    acs->flags_list->m_entry->fflags_set;
558232153Smm			mtree->set.fflags_clear =
559248616Smm			    acs->flags_list->m_entry->fflags_clear;
560232153Smm		} else {
561228753Smm			keys &= ~F_FLAGS;
562232153Smm			if ((oldkeys & F_FLAGS) != 0)
563232153Smm				archive_strcat(&unsetstr, " flags");
564232153Smm		}
565228753Smm	}
566228753Smm	if (unsetstr.length > 0)
567228753Smm		archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s);
568228753Smm	archive_string_free(&unsetstr);
569228753Smm	if (setstr.length > 0)
570228753Smm		archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s);
571228753Smm	archive_string_free(&setstr);
572228753Smm	mtree->set.keys = keys;
573248616Smm	mtree->set.processing = 1;
574228753Smm}
575228753Smm
576232153Smmstatic struct attr_counter *
577248616Smmattr_counter_new(struct mtree_entry *me, struct attr_counter *prev)
578232153Smm{
579232153Smm	struct attr_counter *ac;
580232153Smm
581232153Smm	ac = malloc(sizeof(*ac));
582232153Smm	if (ac != NULL) {
583232153Smm		ac->prev = prev;
584232153Smm		ac->next = NULL;
585232153Smm		ac->count = 1;
586232153Smm		ac->m_entry = me;
587232153Smm	}
588232153Smm	return (ac);
589232153Smm}
590232153Smm
591232153Smmstatic void
592248616Smmattr_counter_free(struct attr_counter **top)
593232153Smm{
594232153Smm	struct attr_counter *ac, *tac;
595232153Smm
596232153Smm	if (*top == NULL)
597232153Smm		return;
598232153Smm	ac = *top;
599232153Smm        while (ac != NULL) {
600232153Smm		tac = ac->next;
601232153Smm		free(ac);
602232153Smm		ac = tac;
603232153Smm	}
604232153Smm	*top = NULL;
605232153Smm}
606232153Smm
607228753Smmstatic int
608248616Smmattr_counter_inc(struct attr_counter **top, struct attr_counter *ac,
609232153Smm    struct attr_counter *last, struct mtree_entry *me)
610228753Smm{
611232153Smm	struct attr_counter *pac;
612232153Smm
613232153Smm	if (ac != NULL) {
614232153Smm		ac->count++;
615232153Smm		if (*top == ac || ac->prev->count >= ac->count)
616232153Smm			return (0);
617232153Smm		for (pac = ac->prev; pac; pac = pac->prev) {
618232153Smm			if (pac->count >= ac->count)
619232153Smm				break;
620232153Smm		}
621232153Smm		ac->prev->next = ac->next;
622232153Smm		if (ac->next != NULL)
623232153Smm			ac->next->prev = ac->prev;
624232153Smm		if (pac != NULL) {
625232153Smm			ac->prev = pac;
626232153Smm			ac->next = pac->next;
627232153Smm			pac->next = ac;
628232153Smm			if (ac->next != NULL)
629232153Smm				ac->next->prev = ac;
630232153Smm		} else {
631232153Smm			ac->prev = NULL;
632232153Smm			ac->next = *top;
633232153Smm			*top = ac;
634232153Smm			ac->next->prev = ac;
635232153Smm		}
636232153Smm	} else {
637248616Smm		ac = attr_counter_new(me, last);
638232153Smm		if (ac == NULL)
639232153Smm			return (-1);
640232153Smm		last->next = ac;
641232153Smm	}
642232153Smm	return (0);
643232153Smm}
644232153Smm
645248616Smm/*
646248616Smm * Tabulate uid,gid,mode and fflags of a entry in order to be used for /set.
647248616Smm */
648232153Smmstatic int
649248616Smmattr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)
650232153Smm{
651248616Smm	struct attr_counter *ac, *last;
652248616Smm	struct att_counter_set *acs = &mtree->acs;
653232153Smm	int keys = mtree->keys;
654232153Smm
655232153Smm	if (keys & (F_UNAME | F_UID)) {
656248616Smm		if (acs->uid_list == NULL) {
657248616Smm			acs->uid_list = attr_counter_new(me, NULL);
658248616Smm			if (acs->uid_list == NULL)
659232153Smm				return (-1);
660232153Smm		} else {
661232153Smm			last = NULL;
662248616Smm			for (ac = acs->uid_list; ac; ac = ac->next) {
663232153Smm				if (ac->m_entry->uid == me->uid)
664232153Smm					break;
665232153Smm				last = ac;
666232153Smm			}
667248616Smm			if (attr_counter_inc(&acs->uid_list, ac, last, me) < 0)
668232153Smm				return (-1);
669232153Smm		}
670232153Smm	}
671232153Smm	if (keys & (F_GNAME | F_GID)) {
672248616Smm		if (acs->gid_list == NULL) {
673248616Smm			acs->gid_list = attr_counter_new(me, NULL);
674248616Smm			if (acs->gid_list == NULL)
675232153Smm				return (-1);
676232153Smm		} else {
677232153Smm			last = NULL;
678248616Smm			for (ac = acs->gid_list; ac; ac = ac->next) {
679232153Smm				if (ac->m_entry->gid == me->gid)
680232153Smm					break;
681232153Smm				last = ac;
682232153Smm			}
683248616Smm			if (attr_counter_inc(&acs->gid_list, ac, last, me) < 0)
684232153Smm				return (-1);
685232153Smm		}
686232153Smm	}
687232153Smm	if (keys & F_MODE) {
688248616Smm		if (acs->mode_list == NULL) {
689248616Smm			acs->mode_list = attr_counter_new(me, NULL);
690248616Smm			if (acs->mode_list == NULL)
691232153Smm				return (-1);
692232153Smm		} else {
693232153Smm			last = NULL;
694248616Smm			for (ac = acs->mode_list; ac; ac = ac->next) {
695232153Smm				if (ac->m_entry->mode == me->mode)
696232153Smm					break;
697232153Smm				last = ac;
698232153Smm			}
699248616Smm			if (attr_counter_inc(&acs->mode_list, ac, last, me) < 0)
700232153Smm				return (-1);
701232153Smm		}
702232153Smm	}
703232153Smm	if (keys & F_FLAGS) {
704248616Smm		if (acs->flags_list == NULL) {
705248616Smm			acs->flags_list = attr_counter_new(me, NULL);
706248616Smm			if (acs->flags_list == NULL)
707232153Smm				return (-1);
708232153Smm		} else {
709232153Smm			last = NULL;
710248616Smm			for (ac = acs->flags_list; ac; ac = ac->next) {
711232153Smm				if (ac->m_entry->fflags_set == me->fflags_set &&
712248616Smm				    ac->m_entry->fflags_clear ==
713248616Smm							me->fflags_clear)
714232153Smm					break;
715232153Smm				last = ac;
716232153Smm			}
717248616Smm			if (attr_counter_inc(&acs->flags_list, ac, last, me) < 0)
718232153Smm				return (-1);
719232153Smm		}
720232153Smm	}
721232153Smm
722232153Smm	return (0);
723232153Smm}
724232153Smm
725248616Smmstatic void
726248616Smmattr_counter_set_free(struct mtree_writer *mtree)
727248616Smm{
728248616Smm	struct att_counter_set *acs = &mtree->acs;
729248616Smm
730248616Smm	attr_counter_free(&acs->uid_list);
731248616Smm	attr_counter_free(&acs->gid_list);
732248616Smm	attr_counter_free(&acs->mode_list);
733248616Smm	attr_counter_free(&acs->flags_list);
734248616Smm}
735248616Smm
736232153Smmstatic int
737248616Smmget_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me)
738232153Smm{
739228753Smm	int keys;
740228753Smm
741228753Smm	keys = mtree->keys;
742232153Smm
743232153Smm	/*
744232153Smm	 * If a keyword has been set by /set, we do not need to
745232153Smm	 * output it.
746232153Smm	 */
747228753Smm	if (mtree->set.keys == 0)
748232153Smm		return (keys);/* /set is not used. */
749232153Smm
750228753Smm	if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 &&
751232153Smm	     mtree->set.gid == me->gid)
752228753Smm		keys &= ~(F_GNAME | F_GID);
753228753Smm	if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 &&
754232153Smm	     mtree->set.uid == me->uid)
755228753Smm		keys &= ~(F_UNAME | F_UID);
756228753Smm	if (mtree->set.keys & F_FLAGS) {
757232153Smm		if (mtree->set.fflags_set == me->fflags_set &&
758232153Smm		    mtree->set.fflags_clear == me->fflags_clear)
759228753Smm			keys &= ~F_FLAGS;
760228753Smm	}
761232153Smm	if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == me->mode)
762228753Smm		keys &= ~F_MODE;
763228753Smm
764232153Smm	switch (me->filetype) {
765228753Smm	case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
766228753Smm	case AE_IFBLK: case AE_IFIFO:
767228753Smm		break;
768228753Smm	case AE_IFDIR:
769228753Smm		if ((mtree->set.keys & F_TYPE) != 0 &&
770228753Smm		    mtree->set.type == AE_IFDIR)
771228753Smm			keys &= ~F_TYPE;
772228753Smm		break;
773228753Smm	case AE_IFREG:
774228753Smm	default:	/* Handle unknown file types as regular files. */
775228753Smm		if ((mtree->set.keys & F_TYPE) != 0 &&
776228753Smm		    mtree->set.type == AE_IFREG)
777228753Smm			keys &= ~F_TYPE;
778228753Smm		break;
779228753Smm	}
780228753Smm
781228753Smm	return (keys);
782228753Smm}
783228753Smm
784248616Smmstatic int
785248616Smmmtree_entry_new(struct archive_write *a, struct archive_entry *entry,
786248616Smm    struct mtree_entry **m_entry)
787232153Smm{
788232153Smm	struct mtree_entry *me;
789232153Smm	const char *s;
790248616Smm	int r;
791248616Smm	static const struct archive_rb_tree_ops rb_ops = {
792248616Smm		mtree_entry_cmp_node, mtree_entry_cmp_key
793248616Smm	};
794232153Smm
795232153Smm	me = calloc(1, sizeof(*me));
796248616Smm	if (me == NULL) {
797248616Smm		archive_set_error(&a->archive, ENOMEM,
798248616Smm		    "Can't allocate memory for a mtree entry");
799248616Smm		*m_entry = NULL;
800248616Smm		return (ARCHIVE_FATAL);
801248616Smm	}
802248616Smm
803248616Smm	r = mtree_entry_setup_filenames(a, me, entry);
804248616Smm	if (r < ARCHIVE_WARN) {
805248616Smm		mtree_entry_free(me);
806248616Smm		*m_entry = NULL;
807248616Smm		return (r);
808248616Smm	}
809248616Smm
810232153Smm	if ((s = archive_entry_symlink(entry)) != NULL)
811248616Smm		archive_strcpy(&me->symlink, s);
812232153Smm	me->nlink = archive_entry_nlink(entry);
813232153Smm	me->filetype = archive_entry_filetype(entry);
814232153Smm	me->mode = archive_entry_mode(entry) & 07777;
815232153Smm	me->uid = archive_entry_uid(entry);
816232153Smm	me->gid = archive_entry_gid(entry);
817232153Smm	if ((s = archive_entry_uname(entry)) != NULL)
818248616Smm		archive_strcpy(&me->uname, s);
819232153Smm	if ((s = archive_entry_gname(entry)) != NULL)
820248616Smm		archive_strcpy(&me->gname, s);
821232153Smm	if ((s = archive_entry_fflags_text(entry)) != NULL)
822248616Smm		archive_strcpy(&me->fflags_text, s);
823232153Smm	archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear);
824232153Smm	me->mtime = archive_entry_mtime(entry);
825232153Smm	me->mtime_nsec = archive_entry_mtime_nsec(entry);
826232153Smm	me->rdevmajor =	archive_entry_rdevmajor(entry);
827232153Smm	me->rdevminor = archive_entry_rdevminor(entry);
828232153Smm	me->size = archive_entry_size(entry);
829248616Smm	if (me->filetype == AE_IFDIR) {
830248616Smm		me->dir_info = calloc(1, sizeof(*me->dir_info));
831248616Smm		if (me->dir_info == NULL) {
832248616Smm			mtree_entry_free(me);
833248616Smm			archive_set_error(&a->archive, ENOMEM,
834248616Smm			    "Can't allocate memory for a mtree entry");
835248616Smm			*m_entry = NULL;
836248616Smm			return (ARCHIVE_FATAL);
837248616Smm		}
838248616Smm		__archive_rb_tree_init(&me->dir_info->rbtree, &rb_ops);
839248616Smm		me->dir_info->children.first = NULL;
840248616Smm		me->dir_info->children.last = &(me->dir_info->children.first);
841248616Smm		me->dir_info->chnext = NULL;
842248616Smm	} else if (me->filetype == AE_IFREG) {
843248616Smm		me->reg_info = calloc(1, sizeof(*me->reg_info));
844248616Smm		if (me->reg_info == NULL) {
845248616Smm			mtree_entry_free(me);
846248616Smm			archive_set_error(&a->archive, ENOMEM,
847248616Smm			    "Can't allocate memory for a mtree entry");
848248616Smm			*m_entry = NULL;
849248616Smm			return (ARCHIVE_FATAL);
850248616Smm		}
851248616Smm		me->reg_info->compute_sum = 0;
852248616Smm	}
853232153Smm
854248616Smm	*m_entry = me;
855248616Smm	return (ARCHIVE_OK);
856232153Smm}
857232153Smm
858232153Smmstatic void
859248616Smmmtree_entry_free(struct mtree_entry *me)
860232153Smm{
861248616Smm	archive_string_free(&me->parentdir);
862248616Smm	archive_string_free(&me->basename);
863248616Smm	archive_string_free(&me->pathname);
864248616Smm	archive_string_free(&me->symlink);
865248616Smm	archive_string_free(&me->uname);
866248616Smm	archive_string_free(&me->gname);
867248616Smm	archive_string_free(&me->fflags_text);
868248616Smm	free(me->dir_info);
869248616Smm	free(me->reg_info);
870232153Smm	free(me);
871232153Smm}
872232153Smm
873228753Smmstatic int
874228753Smmarchive_write_mtree_header(struct archive_write *a,
875228753Smm    struct archive_entry *entry)
876228753Smm{
877228753Smm	struct mtree_writer *mtree= a->format_data;
878248616Smm	struct mtree_entry *mtree_entry;
879248616Smm	int r, r2;
880228753Smm
881228753Smm	if (mtree->first) {
882228753Smm		mtree->first = 0;
883228753Smm		archive_strcat(&mtree->buf, "#mtree\n");
884232153Smm		if ((mtree->keys & SET_KEYS) == 0)
885248616Smm			mtree->output_global_set = 0;/* Disalbed. */
886228753Smm	}
887228753Smm
888228753Smm	mtree->entry_bytes_remaining = archive_entry_size(entry);
889248616Smm
890248616Smm	/* While directory only mode, we do not handle non directory files. */
891232153Smm	if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR)
892232153Smm		return (ARCHIVE_OK);
893228753Smm
894248616Smm	r2 = mtree_entry_new(a, entry, &mtree_entry);
895248616Smm	if (r2 < ARCHIVE_WARN)
896248616Smm		return (r2);
897248616Smm	r = mtree_entry_tree_add(a, &mtree_entry);
898248616Smm	if (r < ARCHIVE_WARN) {
899248616Smm		mtree_entry_free(mtree_entry);
900248616Smm		return (r);
901232153Smm	}
902248616Smm	mtree->mtree_entry = mtree_entry;
903228753Smm
904248616Smm	/* If the current file is a regular file, we have to
905248616Smm	 * compute the sum of its content.
906248616Smm	 * Initialize a bunch of sum check context. */
907248616Smm	if (mtree_entry->reg_info)
908248616Smm		sum_init(mtree);
909228753Smm
910248616Smm	return (r2);
911228753Smm}
912228753Smm
913228753Smmstatic int
914248616Smmwrite_mtree_entry(struct archive_write *a, struct mtree_entry *me)
915228753Smm{
916228753Smm	struct mtree_writer *mtree = a->format_data;
917228753Smm	struct archive_string *str;
918228753Smm	int keys, ret;
919228753Smm
920248616Smm	if (me->dir_info) {
921248616Smm		if (mtree->classic) {
922248616Smm			/*
923248616Smm			 * Output a comment line to describe the full
924248616Smm			 * pathname of the entry as mtree utility does
925248616Smm			 * while generating classic format.
926248616Smm			 */
927248616Smm			if (!mtree->dironly)
928248616Smm				archive_strappend_char(&mtree->buf, '\n');
929248616Smm			if (me->parentdir.s)
930248616Smm				archive_string_sprintf(&mtree->buf,
931248616Smm				    "# %s/%s\n",
932248616Smm				    me->parentdir.s, me->basename.s);
933248616Smm			else
934248616Smm				archive_string_sprintf(&mtree->buf,
935248616Smm				    "# %s\n",
936248616Smm				    me->basename.s);
937248616Smm		}
938248616Smm		if (mtree->output_global_set)
939248616Smm			write_global(mtree);
940248616Smm	}
941232153Smm	archive_string_empty(&mtree->ebuf);
942248616Smm	str = (mtree->indent || mtree->classic)? &mtree->ebuf : &mtree->buf;
943248616Smm
944248616Smm	if (!mtree->classic && me->parentdir.s) {
945248616Smm		/*
946248616Smm		 * If generating format is not classic one(v1), output
947248616Smm		 * a full pathname.
948248616Smm		 */
949248616Smm		mtree_quote(str, me->parentdir.s);
950248616Smm		archive_strappend_char(str, '/');
951248616Smm	}
952248616Smm	mtree_quote(str, me->basename.s);
953248616Smm
954248616Smm	keys = get_global_set_keys(mtree, me);
955228753Smm	if ((keys & F_NLINK) != 0 &&
956232153Smm	    me->nlink != 1 && me->filetype != AE_IFDIR)
957232153Smm		archive_string_sprintf(str, " nlink=%u", me->nlink);
958228753Smm
959248616Smm	if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) {
960228753Smm		archive_strcat(str, " gname=");
961248616Smm		mtree_quote(str, me->gname.s);
962228753Smm	}
963248616Smm	if ((keys & F_UNAME) != 0 && archive_strlen(&me->uname) > 0) {
964228753Smm		archive_strcat(str, " uname=");
965248616Smm		mtree_quote(str, me->uname.s);
966228753Smm	}
967232153Smm	if ((keys & F_FLAGS) != 0) {
968248616Smm		if (archive_strlen(&me->fflags_text) > 0) {
969232153Smm			archive_strcat(str, " flags=");
970248616Smm			mtree_quote(str, me->fflags_text.s);
971248616Smm		} else if (mtree->set.processing &&
972232153Smm		    (mtree->set.keys & F_FLAGS) != 0)
973232153Smm			/* Overwrite the global parameter. */
974232153Smm			archive_strcat(str, " flags=none");
975228753Smm	}
976228753Smm	if ((keys & F_TIME) != 0)
977228753Smm		archive_string_sprintf(str, " time=%jd.%jd",
978232153Smm		    (intmax_t)me->mtime, (intmax_t)me->mtime_nsec);
979228753Smm	if ((keys & F_MODE) != 0)
980232153Smm		archive_string_sprintf(str, " mode=%o", (unsigned int)me->mode);
981228753Smm	if ((keys & F_GID) != 0)
982232153Smm		archive_string_sprintf(str, " gid=%jd", (intmax_t)me->gid);
983228753Smm	if ((keys & F_UID) != 0)
984232153Smm		archive_string_sprintf(str, " uid=%jd", (intmax_t)me->uid);
985228753Smm
986232153Smm	switch (me->filetype) {
987228753Smm	case AE_IFLNK:
988228753Smm		if ((keys & F_TYPE) != 0)
989228753Smm			archive_strcat(str, " type=link");
990228753Smm		if ((keys & F_SLINK) != 0) {
991228753Smm			archive_strcat(str, " link=");
992248616Smm			mtree_quote(str, me->symlink.s);
993228753Smm		}
994228753Smm		break;
995228753Smm	case AE_IFSOCK:
996228753Smm		if ((keys & F_TYPE) != 0)
997228753Smm			archive_strcat(str, " type=socket");
998228753Smm		break;
999228753Smm	case AE_IFCHR:
1000228753Smm		if ((keys & F_TYPE) != 0)
1001228753Smm			archive_strcat(str, " type=char");
1002228753Smm		if ((keys & F_DEV) != 0) {
1003228753Smm			archive_string_sprintf(str,
1004232153Smm			    " device=native,%ju,%ju",
1005232153Smm			    (uintmax_t)me->rdevmajor,
1006232153Smm			    (uintmax_t)me->rdevminor);
1007228753Smm		}
1008228753Smm		break;
1009228753Smm	case AE_IFBLK:
1010228753Smm		if ((keys & F_TYPE) != 0)
1011228753Smm			archive_strcat(str, " type=block");
1012228753Smm		if ((keys & F_DEV) != 0) {
1013228753Smm			archive_string_sprintf(str,
1014232153Smm			    " device=native,%ju,%ju",
1015232153Smm			    (uintmax_t)me->rdevmajor,
1016232153Smm			    (uintmax_t)me->rdevminor);
1017228753Smm		}
1018228753Smm		break;
1019228753Smm	case AE_IFDIR:
1020228753Smm		if ((keys & F_TYPE) != 0)
1021228753Smm			archive_strcat(str, " type=dir");
1022228753Smm		break;
1023228753Smm	case AE_IFIFO:
1024228753Smm		if ((keys & F_TYPE) != 0)
1025228753Smm			archive_strcat(str, " type=fifo");
1026228753Smm		break;
1027228753Smm	case AE_IFREG:
1028228753Smm	default:	/* Handle unknown file types as regular files. */
1029228753Smm		if ((keys & F_TYPE) != 0)
1030228753Smm			archive_strcat(str, " type=file");
1031228753Smm		if ((keys & F_SIZE) != 0)
1032228753Smm			archive_string_sprintf(str, " size=%jd",
1033232153Smm			    (intmax_t)me->size);
1034228753Smm		break;
1035228753Smm	}
1036228753Smm
1037232153Smm	/* Write a bunch of sum. */
1038248616Smm	if (me->reg_info)
1039248616Smm		sum_write(str, me->reg_info);
1040228753Smm
1041248616Smm	archive_strappend_char(str, '\n');
1042248616Smm	if (mtree->indent || mtree->classic)
1043232153Smm		mtree_indent(mtree);
1044228753Smm
1045232153Smm	if (mtree->buf.length > 32768) {
1046248616Smm		ret = __archive_write_output(
1047248616Smm			a, mtree->buf.s, mtree->buf.length);
1048232153Smm		archive_string_empty(&mtree->buf);
1049232153Smm	} else
1050232153Smm		ret = ARCHIVE_OK;
1051232153Smm	return (ret);
1052232153Smm}
1053228753Smm
1054248616Smmstatic int
1055248616Smmwrite_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)
1056248616Smm{
1057248616Smm	struct mtree_writer *mtree = a->format_data;
1058248616Smm	int ret;
1059248616Smm
1060248616Smm	if (n->parentdir.s) {
1061248616Smm		if (mtree->indent) {
1062248616Smm			int i, pd = mtree->depth * 4;
1063248616Smm			for (i = 0; i < pd; i++)
1064248616Smm				archive_strappend_char(&mtree->buf, ' ');
1065248616Smm		}
1066248616Smm		archive_string_sprintf(&mtree->buf, "# %s/%s\n",
1067248616Smm			n->parentdir.s, n->basename.s);
1068248616Smm	}
1069248616Smm
1070248616Smm	if (mtree->indent) {
1071248616Smm		archive_string_empty(&mtree->ebuf);
1072248616Smm		archive_strncat(&mtree->ebuf, "..\n\n", (mtree->dironly)?3:4);
1073248616Smm		mtree_indent(mtree);
1074248616Smm	} else
1075248616Smm		archive_strncat(&mtree->buf, "..\n\n", (mtree->dironly)?3:4);
1076248616Smm
1077248616Smm	if (mtree->buf.length > 32768) {
1078248616Smm		ret = __archive_write_output(
1079248616Smm			a, mtree->buf.s, mtree->buf.length);
1080248616Smm		archive_string_empty(&mtree->buf);
1081248616Smm	} else
1082248616Smm		ret = ARCHIVE_OK;
1083248616Smm	return (ret);
1084248616Smm}
1085248616Smm
1086232153Smm/*
1087248616Smm * Write mtree entries saved at attr_counter_set_collect() function.
1088232153Smm */
1089232153Smmstatic int
1090248616Smmwrite_mtree_entry_tree(struct archive_write *a)
1091232153Smm{
1092232153Smm	struct mtree_writer *mtree = a->format_data;
1093248616Smm	struct mtree_entry *np = mtree->root;
1094248616Smm	struct archive_rb_node *n;
1095232153Smm	int ret;
1096228753Smm
1097248616Smm	do {
1098248616Smm		if (mtree->output_global_set) {
1099248616Smm			/*
1100248616Smm			 * Collect attribute infomation to know which value
1101248616Smm			 * is frequently used among the children.
1102248616Smm			 */
1103248616Smm			attr_counter_set_reset(mtree);
1104248616Smm			ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
1105248616Smm				struct mtree_entry *e = (struct mtree_entry *)n;
1106248616Smm				if (attr_counter_set_collect(mtree, e) < 0) {
1107248616Smm					archive_set_error(&a->archive, ENOMEM,
1108248616Smm					    "Can't allocate memory");
1109248616Smm					return (ARCHIVE_FATAL);
1110248616Smm				}
1111248616Smm			}
1112248616Smm		}
1113248616Smm		if (!np->dir_info->virtual || mtree->classic) {
1114248616Smm			ret = write_mtree_entry(a, np);
1115248616Smm			if (ret != ARCHIVE_OK)
1116248616Smm				return (ARCHIVE_FATAL);
1117248616Smm		} else {
1118248616Smm			/* Whenever output_global_set is enabled
1119248616Smm			 * output global value(/set keywords)
1120248616Smm			 * even if the directory entry is not allowd
1121248616Smm			 * to be written because the global values
1122248616Smm			 * can be used for the children. */
1123248616Smm			if (mtree->output_global_set)
1124248616Smm				write_global(mtree);
1125248616Smm		}
1126248616Smm		/*
1127248616Smm		 * Output the attribute of all files except directory files.
1128248616Smm		 */
1129248616Smm		mtree->depth++;
1130248616Smm		ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
1131248616Smm			struct mtree_entry *e = (struct mtree_entry *)n;
1132228753Smm
1133248616Smm			if (e->dir_info)
1134248616Smm				mtree_entry_add_child_tail(np, e);
1135248616Smm			else {
1136248616Smm				ret = write_mtree_entry(a, e);
1137248616Smm				if (ret != ARCHIVE_OK)
1138248616Smm					return (ARCHIVE_FATAL);
1139248616Smm			}
1140248616Smm		}
1141248616Smm		mtree->depth--;
1142248616Smm
1143248616Smm		if (np->dir_info->children.first != NULL) {
1144248616Smm			/*
1145248616Smm			 * Descend the tree.
1146248616Smm			 */
1147248616Smm			np = np->dir_info->children.first;
1148248616Smm			if (mtree->indent)
1149248616Smm				mtree->depth++;
1150248616Smm			continue;
1151248616Smm		} else if (mtree->classic) {
1152248616Smm			/*
1153248616Smm			 * While printing mtree classic, if there are not
1154248616Smm			 * any directory files(except "." and "..") in the
1155248616Smm			 * directory, output two dots ".." as returning
1156248616Smm			 * the parent directory.
1157248616Smm			 */
1158248616Smm			ret = write_dot_dot_entry(a, np);
1159248616Smm			if (ret != ARCHIVE_OK)
1160248616Smm				return (ARCHIVE_FATAL);
1161248616Smm		}
1162248616Smm
1163248616Smm		while (np != np->parent) {
1164248616Smm			if (np->dir_info->chnext == NULL) {
1165248616Smm				/*
1166248616Smm				 * Ascend the tree; go back to the parent.
1167248616Smm				 */
1168248616Smm				if (mtree->indent)
1169248616Smm					mtree->depth--;
1170248616Smm				if (mtree->classic) {
1171248616Smm					ret = write_dot_dot_entry(a,
1172248616Smm						np->parent);
1173248616Smm					if (ret != ARCHIVE_OK)
1174248616Smm						return (ARCHIVE_FATAL);
1175248616Smm				}
1176248616Smm				np = np->parent;
1177248616Smm			} else {
1178248616Smm				/*
1179248616Smm				 * Switch to next mtree entry in the directory.
1180248616Smm				 */
1181248616Smm				np = np->dir_info->chnext;
1182248616Smm				break;
1183248616Smm			}
1184248616Smm		}
1185248616Smm	} while (np != np->parent);
1186248616Smm
1187232153Smm	return (ARCHIVE_OK);
1188232153Smm}
1189228753Smm
1190232153Smmstatic int
1191232153Smmarchive_write_mtree_finish_entry(struct archive_write *a)
1192232153Smm{
1193232153Smm	struct mtree_writer *mtree = a->format_data;
1194232153Smm	struct mtree_entry *me;
1195228753Smm
1196232153Smm	if ((me = mtree->mtree_entry) == NULL)
1197232153Smm		return (ARCHIVE_OK);
1198232153Smm	mtree->mtree_entry = NULL;
1199228753Smm
1200248616Smm	if (me->reg_info)
1201248616Smm		sum_final(mtree, me->reg_info);
1202228753Smm
1203248616Smm	return (ARCHIVE_OK);
1204228753Smm}
1205228753Smm
1206228753Smmstatic int
1207232153Smmarchive_write_mtree_close(struct archive_write *a)
1208228753Smm{
1209228753Smm	struct mtree_writer *mtree= a->format_data;
1210232153Smm	int ret;
1211228753Smm
1212248616Smm	if (mtree->root != NULL) {
1213248616Smm		ret = write_mtree_entry_tree(a);
1214232153Smm		if (ret != ARCHIVE_OK)
1215232153Smm			return (ARCHIVE_FATAL);
1216232153Smm	}
1217232153Smm
1218228753Smm	archive_write_set_bytes_in_last_block(&a->archive, 1);
1219228753Smm
1220232153Smm	return __archive_write_output(a, mtree->buf.s, mtree->buf.length);
1221228753Smm}
1222228753Smm
1223228753Smmstatic ssize_t
1224228753Smmarchive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
1225228753Smm{
1226228753Smm	struct mtree_writer *mtree= a->format_data;
1227228753Smm
1228228753Smm	if (n > mtree->entry_bytes_remaining)
1229238856Smm		n = (size_t)mtree->entry_bytes_remaining;
1230232153Smm	mtree->entry_bytes_remaining -= n;
1231232153Smm
1232232153Smm	/* We don't need to compute a regular file sum */
1233232153Smm	if (mtree->mtree_entry == NULL)
1234228753Smm		return (n);
1235228753Smm
1236232153Smm	if (mtree->mtree_entry->filetype == AE_IFREG)
1237232153Smm		sum_update(mtree, buff, n);
1238232153Smm
1239228753Smm	return (n);
1240228753Smm}
1241228753Smm
1242228753Smmstatic int
1243232153Smmarchive_write_mtree_free(struct archive_write *a)
1244228753Smm{
1245228753Smm	struct mtree_writer *mtree= a->format_data;
1246228753Smm
1247228753Smm	if (mtree == NULL)
1248228753Smm		return (ARCHIVE_OK);
1249228753Smm
1250232153Smm	/* Make sure we dot not leave any entries. */
1251248616Smm	mtree_entry_register_free(mtree);
1252248616Smm	archive_string_free(&mtree->cur_dirstr);
1253228753Smm	archive_string_free(&mtree->ebuf);
1254228753Smm	archive_string_free(&mtree->buf);
1255248616Smm	attr_counter_set_free(mtree);
1256228753Smm	free(mtree);
1257228753Smm	a->format_data = NULL;
1258228753Smm	return (ARCHIVE_OK);
1259228753Smm}
1260228753Smm
1261228753Smmstatic int
1262228753Smmarchive_write_mtree_options(struct archive_write *a, const char *key,
1263228753Smm    const char *value)
1264228753Smm{
1265228753Smm	struct mtree_writer *mtree= a->format_data;
1266228753Smm	int keybit = 0;
1267228753Smm
1268228753Smm	switch (key[0]) {
1269228753Smm	case 'a':
1270228753Smm		if (strcmp(key, "all") == 0)
1271228753Smm			keybit = ~0;
1272228753Smm		break;
1273228753Smm	case 'c':
1274228753Smm		if (strcmp(key, "cksum") == 0)
1275228753Smm			keybit = F_CKSUM;
1276228753Smm		break;
1277228753Smm	case 'd':
1278228753Smm		if (strcmp(key, "device") == 0)
1279228753Smm			keybit = F_DEV;
1280228753Smm		else if (strcmp(key, "dironly") == 0) {
1281228753Smm			mtree->dironly = (value != NULL)? 1: 0;
1282228753Smm			return (ARCHIVE_OK);
1283228753Smm		}
1284228753Smm		break;
1285228753Smm	case 'f':
1286228753Smm		if (strcmp(key, "flags") == 0)
1287228753Smm			keybit = F_FLAGS;
1288228753Smm		break;
1289228753Smm	case 'g':
1290228753Smm		if (strcmp(key, "gid") == 0)
1291228753Smm			keybit = F_GID;
1292228753Smm		else if (strcmp(key, "gname") == 0)
1293228753Smm			keybit = F_GNAME;
1294228753Smm		break;
1295228753Smm	case 'i':
1296228753Smm		if (strcmp(key, "indent") == 0) {
1297228753Smm			mtree->indent = (value != NULL)? 1: 0;
1298228753Smm			return (ARCHIVE_OK);
1299228753Smm		}
1300228753Smm		break;
1301228753Smm	case 'l':
1302228753Smm		if (strcmp(key, "link") == 0)
1303228753Smm			keybit = F_SLINK;
1304228753Smm		break;
1305228753Smm	case 'm':
1306228753Smm		if (strcmp(key, "md5") == 0 ||
1307228753Smm		    strcmp(key, "md5digest") == 0)
1308228753Smm			keybit = F_MD5;
1309228753Smm		if (strcmp(key, "mode") == 0)
1310228753Smm			keybit = F_MODE;
1311228753Smm		break;
1312228753Smm	case 'n':
1313228753Smm		if (strcmp(key, "nlink") == 0)
1314228753Smm			keybit = F_NLINK;
1315228753Smm		break;
1316228753Smm	case 'r':
1317228753Smm		if (strcmp(key, "ripemd160digest") == 0 ||
1318228753Smm		    strcmp(key, "rmd160") == 0 ||
1319228753Smm		    strcmp(key, "rmd160digest") == 0)
1320228753Smm			keybit = F_RMD160;
1321228753Smm		break;
1322228753Smm	case 's':
1323228753Smm		if (strcmp(key, "sha1") == 0 ||
1324228753Smm		    strcmp(key, "sha1digest") == 0)
1325228753Smm			keybit = F_SHA1;
1326228753Smm		if (strcmp(key, "sha256") == 0 ||
1327228753Smm		    strcmp(key, "sha256digest") == 0)
1328228753Smm			keybit = F_SHA256;
1329228753Smm		if (strcmp(key, "sha384") == 0 ||
1330228753Smm		    strcmp(key, "sha384digest") == 0)
1331228753Smm			keybit = F_SHA384;
1332228753Smm		if (strcmp(key, "sha512") == 0 ||
1333228753Smm		    strcmp(key, "sha512digest") == 0)
1334228753Smm			keybit = F_SHA512;
1335228753Smm		if (strcmp(key, "size") == 0)
1336228753Smm			keybit = F_SIZE;
1337228753Smm		break;
1338228753Smm	case 't':
1339228753Smm		if (strcmp(key, "time") == 0)
1340228753Smm			keybit = F_TIME;
1341228753Smm		else if (strcmp(key, "type") == 0)
1342228753Smm			keybit = F_TYPE;
1343228753Smm		break;
1344228753Smm	case 'u':
1345228753Smm		if (strcmp(key, "uid") == 0)
1346228753Smm			keybit = F_UID;
1347228753Smm		else if (strcmp(key, "uname") == 0)
1348228753Smm			keybit = F_UNAME;
1349228753Smm		else if (strcmp(key, "use-set") == 0) {
1350248616Smm			mtree->output_global_set = (value != NULL)? 1: 0;
1351228753Smm			return (ARCHIVE_OK);
1352228753Smm		}
1353228753Smm		break;
1354228753Smm	}
1355228753Smm	if (keybit != 0) {
1356228753Smm		if (value != NULL)
1357228753Smm			mtree->keys |= keybit;
1358228753Smm		else
1359228753Smm			mtree->keys &= ~keybit;
1360228753Smm		return (ARCHIVE_OK);
1361228753Smm	}
1362228753Smm
1363232153Smm	/* Note: The "warn" return is just to inform the options
1364232153Smm	 * supervisor that we didn't handle it.  It will generate
1365232153Smm	 * a suitable error if no one used this option. */
1366228753Smm	return (ARCHIVE_WARN);
1367228753Smm}
1368228753Smm
1369248616Smmstatic int
1370248616Smmarchive_write_set_format_mtree_default(struct archive *_a, const char *fn)
1371228753Smm{
1372228753Smm	struct archive_write *a = (struct archive_write *)_a;
1373228753Smm	struct mtree_writer *mtree;
1374228753Smm
1375248616Smm	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn);
1376228753Smm
1377232153Smm	if (a->format_free != NULL)
1378232153Smm		(a->format_free)(a);
1379232153Smm
1380232153Smm	if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {
1381228753Smm		archive_set_error(&a->archive, ENOMEM,
1382228753Smm		    "Can't allocate mtree data");
1383228753Smm		return (ARCHIVE_FATAL);
1384228753Smm	}
1385228753Smm
1386232153Smm	mtree->mtree_entry = NULL;
1387228753Smm	mtree->first = 1;
1388228753Smm	memset(&(mtree->set), 0, sizeof(mtree->set));
1389228753Smm	mtree->keys = DEFAULT_KEYS;
1390228753Smm	mtree->dironly = 0;
1391228753Smm	mtree->indent = 0;
1392228753Smm	archive_string_init(&mtree->ebuf);
1393228753Smm	archive_string_init(&mtree->buf);
1394248616Smm	mtree_entry_register_init(mtree);
1395228753Smm	a->format_data = mtree;
1396232153Smm	a->format_free = archive_write_mtree_free;
1397228753Smm	a->format_name = "mtree";
1398228753Smm	a->format_options = archive_write_mtree_options;
1399228753Smm	a->format_write_header = archive_write_mtree_header;
1400232153Smm	a->format_close = archive_write_mtree_close;
1401228753Smm	a->format_write_data = archive_write_mtree_data;
1402228753Smm	a->format_finish_entry = archive_write_mtree_finish_entry;
1403228753Smm	a->archive.archive_format = ARCHIVE_FORMAT_MTREE;
1404228753Smm	a->archive.archive_format_name = "mtree";
1405228753Smm
1406228753Smm	return (ARCHIVE_OK);
1407228753Smm}
1408232153Smm
1409248616Smmint
1410248616Smmarchive_write_set_format_mtree(struct archive *_a)
1411248616Smm{
1412248616Smm	return archive_write_set_format_mtree_default(_a,
1413248616Smm		"archive_write_set_format_mtree");
1414248616Smm}
1415248616Smm
1416248616Smmint
1417248616Smmarchive_write_set_format_mtree_classic(struct archive *_a)
1418248616Smm{
1419248616Smm	int r;
1420248616Smm
1421248616Smm	r = archive_write_set_format_mtree_default(_a,
1422248616Smm		"archive_write_set_format_mtree_classic");
1423248616Smm	if (r == ARCHIVE_OK) {
1424248616Smm		struct archive_write *a = (struct archive_write *)_a;
1425248616Smm		struct mtree_writer *mtree;
1426248616Smm
1427248616Smm		mtree = (struct mtree_writer *)a->format_data;
1428248616Smm
1429248616Smm		/* Set to output a mtree archive in classic format. */
1430248616Smm		mtree->classic = 1;
1431248616Smm		/* Basically, mtree classic format uses '/set' global
1432248616Smm		 * value. */
1433248616Smm		mtree->output_global_set = 1;
1434248616Smm	}
1435248616Smm	return (r);
1436248616Smm}
1437248616Smm
1438232153Smmstatic void
1439232153Smmsum_init(struct mtree_writer *mtree)
1440232153Smm{
1441248616Smm
1442248616Smm	mtree->compute_sum = 0;
1443248616Smm
1444232153Smm	if (mtree->keys & F_CKSUM) {
1445232153Smm		mtree->compute_sum |= F_CKSUM;
1446232153Smm		mtree->crc = 0;
1447232153Smm		mtree->crc_len = 0;
1448232153Smm	}
1449232153Smm#ifdef ARCHIVE_HAS_MD5
1450232153Smm	if (mtree->keys & F_MD5) {
1451232153Smm		if (archive_md5_init(&mtree->md5ctx) == ARCHIVE_OK)
1452232153Smm			mtree->compute_sum |= F_MD5;
1453232153Smm		else
1454232153Smm			mtree->keys &= ~F_MD5;/* Not supported. */
1455232153Smm	}
1456232153Smm#endif
1457232153Smm#ifdef ARCHIVE_HAS_RMD160
1458232153Smm	if (mtree->keys & F_RMD160) {
1459232153Smm		if (archive_rmd160_init(&mtree->rmd160ctx) == ARCHIVE_OK)
1460232153Smm			mtree->compute_sum |= F_RMD160;
1461232153Smm		else
1462232153Smm			mtree->keys &= ~F_RMD160;/* Not supported. */
1463232153Smm	}
1464232153Smm#endif
1465232153Smm#ifdef ARCHIVE_HAS_SHA1
1466232153Smm	if (mtree->keys & F_SHA1) {
1467232153Smm		if (archive_sha1_init(&mtree->sha1ctx) == ARCHIVE_OK)
1468232153Smm			mtree->compute_sum |= F_SHA1;
1469232153Smm		else
1470232153Smm			mtree->keys &= ~F_SHA1;/* Not supported. */
1471232153Smm	}
1472232153Smm#endif
1473232153Smm#ifdef ARCHIVE_HAS_SHA256
1474232153Smm	if (mtree->keys & F_SHA256) {
1475232153Smm		if (archive_sha256_init(&mtree->sha256ctx) == ARCHIVE_OK)
1476232153Smm			mtree->compute_sum |= F_SHA256;
1477232153Smm		else
1478232153Smm			mtree->keys &= ~F_SHA256;/* Not supported. */
1479232153Smm	}
1480232153Smm#endif
1481232153Smm#ifdef ARCHIVE_HAS_SHA384
1482232153Smm	if (mtree->keys & F_SHA384) {
1483232153Smm		if (archive_sha384_init(&mtree->sha384ctx) == ARCHIVE_OK)
1484232153Smm			mtree->compute_sum |= F_SHA384;
1485232153Smm		else
1486232153Smm			mtree->keys &= ~F_SHA384;/* Not supported. */
1487232153Smm	}
1488232153Smm#endif
1489232153Smm#ifdef ARCHIVE_HAS_SHA512
1490232153Smm	if (mtree->keys & F_SHA512) {
1491232153Smm		if (archive_sha512_init(&mtree->sha512ctx) == ARCHIVE_OK)
1492232153Smm			mtree->compute_sum |= F_SHA512;
1493232153Smm		else
1494232153Smm			mtree->keys &= ~F_SHA512;/* Not supported. */
1495232153Smm	}
1496232153Smm#endif
1497232153Smm}
1498232153Smm
1499232153Smmstatic void
1500232153Smmsum_update(struct mtree_writer *mtree, const void *buff, size_t n)
1501232153Smm{
1502232153Smm	if (mtree->compute_sum & F_CKSUM) {
1503232153Smm		/*
1504232153Smm		 * Compute a POSIX 1003.2 checksum
1505232153Smm		 */
1506232153Smm		const unsigned char *p;
1507232153Smm		size_t nn;
1508232153Smm
1509232153Smm		for (nn = n, p = buff; nn--; ++p)
1510232153Smm			COMPUTE_CRC(mtree->crc, *p);
1511232153Smm		mtree->crc_len += n;
1512232153Smm	}
1513232153Smm#ifdef ARCHIVE_HAS_MD5
1514232153Smm	if (mtree->compute_sum & F_MD5)
1515232153Smm		archive_md5_update(&mtree->md5ctx, buff, n);
1516232153Smm#endif
1517232153Smm#ifdef ARCHIVE_HAS_RMD160
1518232153Smm	if (mtree->compute_sum & F_RMD160)
1519232153Smm		archive_rmd160_update(&mtree->rmd160ctx, buff, n);
1520232153Smm#endif
1521232153Smm#ifdef ARCHIVE_HAS_SHA1
1522232153Smm	if (mtree->compute_sum & F_SHA1)
1523232153Smm		archive_sha1_update(&mtree->sha1ctx, buff, n);
1524232153Smm#endif
1525232153Smm#ifdef ARCHIVE_HAS_SHA256
1526232153Smm	if (mtree->compute_sum & F_SHA256)
1527232153Smm		archive_sha256_update(&mtree->sha256ctx, buff, n);
1528232153Smm#endif
1529232153Smm#ifdef ARCHIVE_HAS_SHA384
1530232153Smm	if (mtree->compute_sum & F_SHA384)
1531232153Smm		archive_sha384_update(&mtree->sha384ctx, buff, n);
1532232153Smm#endif
1533232153Smm#ifdef ARCHIVE_HAS_SHA512
1534232153Smm	if (mtree->compute_sum & F_SHA512)
1535232153Smm		archive_sha512_update(&mtree->sha512ctx, buff, n);
1536232153Smm#endif
1537232153Smm}
1538232153Smm
1539232153Smmstatic void
1540248616Smmsum_final(struct mtree_writer *mtree, struct reg_info *reg)
1541232153Smm{
1542232153Smm
1543232153Smm	if (mtree->compute_sum & F_CKSUM) {
1544232153Smm		uint64_t len;
1545232153Smm		/* Include the length of the file. */
1546232153Smm		for (len = mtree->crc_len; len != 0; len >>= 8)
1547232153Smm			COMPUTE_CRC(mtree->crc, len & 0xff);
1548248616Smm		reg->crc = ~mtree->crc;
1549232153Smm	}
1550232153Smm#ifdef ARCHIVE_HAS_MD5
1551232153Smm	if (mtree->compute_sum & F_MD5)
1552248616Smm		archive_md5_final(&mtree->md5ctx, reg->buf_md5);
1553232153Smm#endif
1554232153Smm#ifdef ARCHIVE_HAS_RMD160
1555232153Smm	if (mtree->compute_sum & F_RMD160)
1556248616Smm		archive_rmd160_final(&mtree->rmd160ctx, reg->buf_rmd160);
1557232153Smm#endif
1558232153Smm#ifdef ARCHIVE_HAS_SHA1
1559232153Smm	if (mtree->compute_sum & F_SHA1)
1560248616Smm		archive_sha1_final(&mtree->sha1ctx, reg->buf_sha1);
1561232153Smm#endif
1562232153Smm#ifdef ARCHIVE_HAS_SHA256
1563232153Smm	if (mtree->compute_sum & F_SHA256)
1564248616Smm		archive_sha256_final(&mtree->sha256ctx, reg->buf_sha256);
1565232153Smm#endif
1566232153Smm#ifdef ARCHIVE_HAS_SHA384
1567232153Smm	if (mtree->compute_sum & F_SHA384)
1568248616Smm		archive_sha384_final(&mtree->sha384ctx, reg->buf_sha384);
1569232153Smm#endif
1570232153Smm#ifdef ARCHIVE_HAS_SHA512
1571232153Smm	if (mtree->compute_sum & F_SHA512)
1572248616Smm		archive_sha512_final(&mtree->sha512ctx, reg->buf_sha512);
1573232153Smm#endif
1574232153Smm	/* Save what types of sum are computed. */
1575248616Smm	reg->compute_sum = mtree->compute_sum;
1576232153Smm}
1577232153Smm
1578232153Smm#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \
1579232153Smm    defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \
1580232153Smm    defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)
1581232153Smmstatic void
1582232153Smmstrappend_bin(struct archive_string *s, const unsigned char *bin, int n)
1583232153Smm{
1584232153Smm	static const char hex[] = "0123456789abcdef";
1585232153Smm	int i;
1586232153Smm
1587232153Smm	for (i = 0; i < n; i++) {
1588232153Smm		archive_strappend_char(s, hex[bin[i] >> 4]);
1589232153Smm		archive_strappend_char(s, hex[bin[i] & 0x0f]);
1590232153Smm	}
1591232153Smm}
1592232153Smm#endif
1593232153Smm
1594232153Smmstatic void
1595248616Smmsum_write(struct archive_string *str, struct reg_info *reg)
1596232153Smm{
1597232153Smm
1598248616Smm	if (reg->compute_sum & F_CKSUM) {
1599232153Smm		archive_string_sprintf(str, " cksum=%ju",
1600248616Smm		    (uintmax_t)reg->crc);
1601232153Smm	}
1602232153Smm#ifdef ARCHIVE_HAS_MD5
1603248616Smm	if (reg->compute_sum & F_MD5) {
1604232153Smm		archive_strcat(str, " md5digest=");
1605248616Smm		strappend_bin(str, reg->buf_md5, sizeof(reg->buf_md5));
1606232153Smm	}
1607232153Smm#endif
1608232153Smm#ifdef ARCHIVE_HAS_RMD160
1609248616Smm	if (reg->compute_sum & F_RMD160) {
1610232153Smm		archive_strcat(str, " rmd160digest=");
1611248616Smm		strappend_bin(str, reg->buf_rmd160, sizeof(reg->buf_rmd160));
1612232153Smm	}
1613232153Smm#endif
1614232153Smm#ifdef ARCHIVE_HAS_SHA1
1615248616Smm	if (reg->compute_sum & F_SHA1) {
1616232153Smm		archive_strcat(str, " sha1digest=");
1617248616Smm		strappend_bin(str, reg->buf_sha1, sizeof(reg->buf_sha1));
1618232153Smm	}
1619232153Smm#endif
1620232153Smm#ifdef ARCHIVE_HAS_SHA256
1621248616Smm	if (reg->compute_sum & F_SHA256) {
1622232153Smm		archive_strcat(str, " sha256digest=");
1623248616Smm		strappend_bin(str, reg->buf_sha256, sizeof(reg->buf_sha256));
1624232153Smm	}
1625232153Smm#endif
1626232153Smm#ifdef ARCHIVE_HAS_SHA384
1627248616Smm	if (reg->compute_sum & F_SHA384) {
1628232153Smm		archive_strcat(str, " sha384digest=");
1629248616Smm		strappend_bin(str, reg->buf_sha384, sizeof(reg->buf_sha384));
1630232153Smm	}
1631232153Smm#endif
1632232153Smm#ifdef ARCHIVE_HAS_SHA512
1633248616Smm	if (reg->compute_sum & F_SHA512) {
1634232153Smm		archive_strcat(str, " sha512digest=");
1635248616Smm		strappend_bin(str, reg->buf_sha512, sizeof(reg->buf_sha512));
1636232153Smm	}
1637232153Smm#endif
1638232153Smm}
1639248616Smm
1640248616Smmstatic int
1641248616Smmmtree_entry_cmp_node(const struct archive_rb_node *n1,
1642248616Smm    const struct archive_rb_node *n2)
1643248616Smm{
1644248616Smm	const struct mtree_entry *e1 = (const struct mtree_entry *)n1;
1645248616Smm	const struct mtree_entry *e2 = (const struct mtree_entry *)n2;
1646248616Smm
1647248616Smm	return (strcmp(e2->basename.s, e1->basename.s));
1648248616Smm}
1649248616Smm
1650248616Smmstatic int
1651248616Smmmtree_entry_cmp_key(const struct archive_rb_node *n, const void *key)
1652248616Smm{
1653248616Smm	const struct mtree_entry *e = (const struct mtree_entry *)n;
1654248616Smm
1655248616Smm	return (strcmp((const char *)key, e->basename.s));
1656248616Smm}
1657248616Smm
1658248616Smm#if defined(_WIN32) || defined(__CYGWIN__)
1659248616Smmstatic int
1660248616Smmcleanup_backslash_1(char *p)
1661248616Smm{
1662248616Smm	int mb, dos;
1663248616Smm
1664248616Smm	mb = dos = 0;
1665248616Smm	while (*p) {
1666248616Smm		if (*(unsigned char *)p > 127)
1667248616Smm			mb = 1;
1668248616Smm		if (*p == '\\') {
1669248616Smm			/* If we have not met any multi-byte characters,
1670248616Smm			 * we can replace '\' with '/'. */
1671248616Smm			if (!mb)
1672248616Smm				*p = '/';
1673248616Smm			dos = 1;
1674248616Smm		}
1675248616Smm		p++;
1676248616Smm	}
1677248616Smm	if (!mb || !dos)
1678248616Smm		return (0);
1679248616Smm	return (-1);
1680248616Smm}
1681248616Smm
1682248616Smmstatic void
1683248616Smmcleanup_backslash_2(wchar_t *p)
1684248616Smm{
1685248616Smm
1686248616Smm	/* Convert a path-separator from '\' to  '/' */
1687248616Smm	while (*p != L'\0') {
1688248616Smm		if (*p == L'\\')
1689248616Smm			*p = L'/';
1690248616Smm		p++;
1691248616Smm	}
1692248616Smm}
1693248616Smm#endif
1694248616Smm
1695248616Smm/*
1696248616Smm * Generate a parent directory name and a base name from a pathname.
1697248616Smm */
1698248616Smmstatic int
1699248616Smmmtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file,
1700248616Smm    struct archive_entry *entry)
1701248616Smm{
1702248616Smm	const char *pathname;
1703248616Smm	char *p, *dirname, *slash;
1704248616Smm	size_t len;
1705248616Smm	int ret = ARCHIVE_OK;
1706248616Smm
1707248616Smm	archive_strcpy(&file->pathname, archive_entry_pathname(entry));
1708248616Smm#if defined(_WIN32) || defined(__CYGWIN__)
1709248616Smm	/*
1710248616Smm	 * Convert a path-separator from '\' to  '/'
1711248616Smm	 */
1712248616Smm	if (cleanup_backslash_1(file->pathname.s) != 0) {
1713248616Smm		const wchar_t *wp = archive_entry_pathname_w(entry);
1714248616Smm		struct archive_wstring ws;
1715248616Smm
1716248616Smm		if (wp != NULL) {
1717248616Smm			int r;
1718248616Smm			archive_string_init(&ws);
1719248616Smm			archive_wstrcpy(&ws, wp);
1720248616Smm			cleanup_backslash_2(ws.s);
1721248616Smm			archive_string_empty(&(file->pathname));
1722248616Smm			r = archive_string_append_from_wcs(&(file->pathname),
1723248616Smm			    ws.s, ws.length);
1724248616Smm			archive_wstring_free(&ws);
1725248616Smm			if (r < 0 && errno == ENOMEM) {
1726248616Smm				archive_set_error(&a->archive, ENOMEM,
1727248616Smm				    "Can't allocate memory");
1728248616Smm				return (ARCHIVE_FATAL);
1729248616Smm			}
1730248616Smm		}
1731248616Smm	}
1732248616Smm#else
1733248616Smm	(void)a; /* UNUSED */
1734248616Smm#endif
1735248616Smm	pathname =  file->pathname.s;
1736248616Smm	if (strcmp(pathname, ".") == 0) {
1737248616Smm		archive_strcpy(&file->basename, ".");
1738248616Smm		return (ARCHIVE_OK);
1739248616Smm	}
1740248616Smm
1741248616Smm	archive_strcpy(&(file->parentdir), pathname);
1742248616Smm
1743248616Smm	len = file->parentdir.length;
1744248616Smm	p = dirname = file->parentdir.s;
1745248616Smm
1746248616Smm	/*
1747248616Smm	 * Remove leading '/' and '../' elements
1748248616Smm	 */
1749248616Smm	while (*p) {
1750248616Smm		if (p[0] == '/') {
1751248616Smm			p++;
1752248616Smm			len--;
1753248616Smm		} else if (p[0] != '.')
1754248616Smm			break;
1755248616Smm		else if (p[1] == '.' && p[2] == '/') {
1756248616Smm			p += 3;
1757248616Smm			len -= 3;
1758248616Smm		} else
1759248616Smm			break;
1760248616Smm	}
1761248616Smm	if (p != dirname) {
1762248616Smm		memmove(dirname, p, len+1);
1763248616Smm		p = dirname;
1764248616Smm	}
1765248616Smm	/*
1766248616Smm	 * Remove "/","/." and "/.." elements from tail.
1767248616Smm	 */
1768248616Smm	while (len > 0) {
1769248616Smm		size_t ll = len;
1770248616Smm
1771248616Smm		if (len > 0 && p[len-1] == '/') {
1772248616Smm			p[len-1] = '\0';
1773248616Smm			len--;
1774248616Smm		}
1775248616Smm		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
1776248616Smm			p[len-2] = '\0';
1777248616Smm			len -= 2;
1778248616Smm		}
1779248616Smm		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
1780248616Smm		    p[len-1] == '.') {
1781248616Smm			p[len-3] = '\0';
1782248616Smm			len -= 3;
1783248616Smm		}
1784248616Smm		if (ll == len)
1785248616Smm			break;
1786248616Smm	}
1787248616Smm	while (*p) {
1788248616Smm		if (p[0] == '/') {
1789248616Smm			if (p[1] == '/')
1790248616Smm				/* Convert '//' --> '/' */
1791248616Smm				strcpy(p, p+1);
1792248616Smm			else if (p[1] == '.' && p[2] == '/')
1793248616Smm				/* Convert '/./' --> '/' */
1794248616Smm				strcpy(p, p+2);
1795248616Smm			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
1796248616Smm				/* Convert 'dir/dir1/../dir2/'
1797248616Smm				 *     --> 'dir/dir2/'
1798248616Smm				 */
1799248616Smm				char *rp = p -1;
1800248616Smm				while (rp >= dirname) {
1801248616Smm					if (*rp == '/')
1802248616Smm						break;
1803248616Smm					--rp;
1804248616Smm				}
1805248616Smm				if (rp > dirname) {
1806248616Smm					strcpy(rp, p+3);
1807248616Smm					p = rp;
1808248616Smm				} else {
1809248616Smm					strcpy(dirname, p+4);
1810248616Smm					p = dirname;
1811248616Smm				}
1812248616Smm			} else
1813248616Smm				p++;
1814248616Smm		} else
1815248616Smm			p++;
1816248616Smm	}
1817248616Smm	p = dirname;
1818248616Smm	len = strlen(p);
1819248616Smm
1820248616Smm	/*
1821248616Smm	 * Add "./" prefiex.
1822248616Smm	 * NOTE: If the pathname does not have a path separator, we have
1823248616Smm	 * to add "./" to the head of the pathename because mtree reader
1824248616Smm	 * will suppose that it is v1(a.k.a classic) mtree format and
1825248616Smm	 * change the directory unexpectedly and so it will make a wrong
1826248616Smm	 * path.
1827248616Smm	 */
1828248616Smm	if (strcmp(p, ".") != 0 && strncmp(p, "./", 2) != 0) {
1829248616Smm		struct archive_string as;
1830248616Smm		archive_string_init(&as);
1831248616Smm		archive_strcpy(&as, "./");
1832248616Smm		archive_strncat(&as, p, len);
1833248616Smm		archive_string_empty(&file->parentdir);
1834248616Smm		archive_string_concat(&file->parentdir, &as);
1835248616Smm		archive_string_free(&as);
1836248616Smm		p = file->parentdir.s;
1837248616Smm		len = archive_strlen(&file->parentdir);
1838248616Smm	}
1839248616Smm
1840248616Smm	/*
1841248616Smm	 * Find out the position which points the last position of
1842248616Smm	 * path separator('/').
1843248616Smm	 */
1844248616Smm	slash = NULL;
1845248616Smm	for (; *p != '\0'; p++) {
1846248616Smm		if (*p == '/')
1847248616Smm			slash = p;
1848248616Smm	}
1849248616Smm	if (slash == NULL) {
1850248616Smm		/* The pathname doesn't have a parent directory. */
1851248616Smm		file->parentdir.length = len;
1852248616Smm		archive_string_copy(&(file->basename), &(file->parentdir));
1853248616Smm		archive_string_empty(&(file->parentdir));
1854248616Smm		*file->parentdir.s = '\0';
1855248616Smm		return (ret);
1856248616Smm	}
1857248616Smm
1858248616Smm	/* Make a basename from dirname and slash */
1859248616Smm	*slash  = '\0';
1860248616Smm	file->parentdir.length = slash - dirname;
1861248616Smm	archive_strcpy(&(file->basename),  slash + 1);
1862248616Smm	return (ret);
1863248616Smm}
1864248616Smm
1865248616Smmstatic int
1866248616Smmmtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname,
1867248616Smm    struct mtree_entry **m_entry)
1868248616Smm{
1869248616Smm	struct archive_entry *entry;
1870248616Smm	struct mtree_entry *file;
1871248616Smm	int r;
1872248616Smm
1873248616Smm	entry = archive_entry_new();
1874248616Smm	if (entry == NULL) {
1875248616Smm		*m_entry = NULL;
1876248616Smm		archive_set_error(&a->archive, ENOMEM,
1877248616Smm		    "Can't allocate memory");
1878248616Smm		return (ARCHIVE_FATAL);
1879248616Smm	}
1880248616Smm	archive_entry_copy_pathname(entry, pathname);
1881248616Smm	archive_entry_set_mode(entry, AE_IFDIR | 0755);
1882248616Smm	archive_entry_set_mtime(entry, time(NULL), 0);
1883248616Smm
1884248616Smm	r = mtree_entry_new(a, entry, &file);
1885248616Smm	archive_entry_free(entry);
1886248616Smm	if (r < ARCHIVE_WARN) {
1887248616Smm		*m_entry = NULL;
1888248616Smm		archive_set_error(&a->archive, ENOMEM,
1889248616Smm		    "Can't allocate memory");
1890248616Smm		return (ARCHIVE_FATAL);
1891248616Smm	}
1892248616Smm
1893248616Smm	file->dir_info->virtual = 1;
1894248616Smm
1895248616Smm	*m_entry = file;
1896248616Smm	return (ARCHIVE_OK);
1897248616Smm}
1898248616Smm
1899248616Smmstatic void
1900248616Smmmtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)
1901248616Smm{
1902248616Smm        file->next = NULL;
1903248616Smm        *mtree->file_list.last = file;
1904248616Smm        mtree->file_list.last = &(file->next);
1905248616Smm}
1906248616Smm
1907248616Smmstatic void
1908248616Smmmtree_entry_register_init(struct mtree_writer *mtree)
1909248616Smm{
1910248616Smm	mtree->file_list.first = NULL;
1911248616Smm	mtree->file_list.last = &(mtree->file_list.first);
1912248616Smm}
1913248616Smm
1914248616Smmstatic void
1915248616Smmmtree_entry_register_free(struct mtree_writer *mtree)
1916248616Smm{
1917248616Smm	struct mtree_entry *file, *file_next;
1918248616Smm
1919248616Smm	file = mtree->file_list.first;
1920248616Smm	while (file != NULL) {
1921248616Smm		file_next = file->next;
1922248616Smm		mtree_entry_free(file);
1923248616Smm		file = file_next;
1924248616Smm	}
1925248616Smm}
1926248616Smm
1927248616Smmstatic int
1928248616Smmmtree_entry_add_child_tail(struct mtree_entry *parent,
1929248616Smm    struct mtree_entry *child)
1930248616Smm{
1931248616Smm	child->dir_info->chnext = NULL;
1932248616Smm	*parent->dir_info->children.last = child;
1933248616Smm	parent->dir_info->children.last = &(child->dir_info->chnext);
1934248616Smm	return (1);
1935248616Smm}
1936248616Smm
1937248616Smm/*
1938248616Smm * Find a entry from a parent entry with the name.
1939248616Smm */
1940248616Smmstatic struct mtree_entry *
1941248616Smmmtree_entry_find_child(struct mtree_entry *parent, const char *child_name)
1942248616Smm{
1943248616Smm	struct mtree_entry *np;
1944248616Smm
1945248616Smm	if (parent == NULL)
1946248616Smm		return (NULL);
1947248616Smm	np = (struct mtree_entry *)__archive_rb_tree_find_node(
1948248616Smm	    &(parent->dir_info->rbtree), child_name);
1949248616Smm	return (np);
1950248616Smm}
1951248616Smm
1952248616Smmstatic int
1953248616Smmget_path_component(char *name, size_t n, const char *fn)
1954248616Smm{
1955248616Smm	char *p;
1956248616Smm	size_t l;
1957248616Smm
1958248616Smm	p = strchr(fn, '/');
1959248616Smm	if (p == NULL) {
1960248616Smm		if ((l = strlen(fn)) == 0)
1961248616Smm			return (0);
1962248616Smm	} else
1963248616Smm		l = p - fn;
1964248616Smm	if (l > n -1)
1965248616Smm		return (-1);
1966248616Smm	memcpy(name, fn, l);
1967248616Smm	name[l] = '\0';
1968248616Smm
1969248616Smm	return ((int)l);
1970248616Smm}
1971248616Smm
1972248616Smm/*
1973248616Smm * Add a new entry into the tree.
1974248616Smm */
1975248616Smmstatic int
1976248616Smmmtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
1977248616Smm{
1978248616Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1979248616Smm	char name[_MAX_FNAME];/* Included null terminator size. */
1980248616Smm#elif defined(NAME_MAX) && NAME_MAX >= 255
1981248616Smm	char name[NAME_MAX+1];
1982248616Smm#else
1983248616Smm	char name[256];
1984248616Smm#endif
1985248616Smm	struct mtree_writer *mtree = (struct mtree_writer *)a->format_data;
1986248616Smm	struct mtree_entry *dent, *file, *np;
1987248616Smm	const char *fn, *p;
1988248616Smm	int l, r;
1989248616Smm
1990248616Smm	file = *filep;
1991248616Smm	if (file->parentdir.length == 0 && file->basename.length == 1 &&
1992248616Smm	    file->basename.s[0] == '.') {
1993248616Smm		file->parent = file;
1994248616Smm		if (mtree->root != NULL) {
1995248616Smm			np = mtree->root;
1996248616Smm			goto same_entry;
1997248616Smm		}
1998248616Smm		mtree->root = file;
1999248616Smm		mtree_entry_register_add(mtree, file);
2000248616Smm		return (ARCHIVE_OK);
2001248616Smm	}
2002248616Smm
2003248616Smm	if (file->parentdir.length == 0) {
2004248616Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
2005248616Smm		    "Internal programing error "
2006248616Smm		    "in generating canonical name for %s",
2007248616Smm		    file->pathname.s);
2008248616Smm		return (ARCHIVE_FAILED);
2009248616Smm	}
2010248616Smm
2011248616Smm	fn = p = file->parentdir.s;
2012248616Smm
2013248616Smm	/*
2014248616Smm	 * If the path of the parent directory of `file' entry is
2015248616Smm	 * the same as the path of `cur_dirent', add `file' entry to
2016248616Smm	 * `cur_dirent'.
2017248616Smm	 */
2018248616Smm	if (archive_strlen(&(mtree->cur_dirstr))
2019248616Smm	      == archive_strlen(&(file->parentdir)) &&
2020248616Smm	    strcmp(mtree->cur_dirstr.s, fn) == 0) {
2021248616Smm		if (!__archive_rb_tree_insert_node(
2022248616Smm		    &(mtree->cur_dirent->dir_info->rbtree),
2023248616Smm		    (struct archive_rb_node *)file)) {
2024248616Smm			/* There is the same name in the tree. */
2025248616Smm			np = (struct mtree_entry *)__archive_rb_tree_find_node(
2026248616Smm			    &(mtree->cur_dirent->dir_info->rbtree),
2027248616Smm			    file->basename.s);
2028248616Smm			goto same_entry;
2029248616Smm		}
2030248616Smm		file->parent = mtree->cur_dirent;
2031248616Smm		mtree_entry_register_add(mtree, file);
2032248616Smm		return (ARCHIVE_OK);
2033248616Smm	}
2034248616Smm
2035248616Smm	dent = mtree->root;
2036248616Smm	for (;;) {
2037248616Smm		l = get_path_component(name, sizeof(name), fn);
2038248616Smm		if (l == 0) {
2039248616Smm			np = NULL;
2040248616Smm			break;
2041248616Smm		}
2042248616Smm		if (l < 0) {
2043248616Smm			archive_set_error(&a->archive,
2044248616Smm			    ARCHIVE_ERRNO_MISC,
2045248616Smm			    "A name buffer is too small");
2046248616Smm			return (ARCHIVE_FATAL);
2047248616Smm		}
2048248616Smm		if (l == 1 && name[0] == '.' && dent != NULL &&
2049248616Smm		    dent == mtree->root) {
2050248616Smm			fn += l;
2051248616Smm			if (fn[0] == '/')
2052248616Smm				fn++;
2053248616Smm			continue;
2054248616Smm		}
2055248616Smm
2056248616Smm		np = mtree_entry_find_child(dent, name);
2057248616Smm		if (np == NULL || fn[0] == '\0')
2058248616Smm			break;
2059248616Smm
2060248616Smm		/* Find next sub directory. */
2061248616Smm		if (!np->dir_info) {
2062248616Smm			/* NOT Directory! */
2063248616Smm			archive_set_error(&a->archive,
2064248616Smm			    ARCHIVE_ERRNO_MISC,
2065248616Smm			    "`%s' is not directory, we cannot insert `%s' ",
2066248616Smm			    np->pathname.s, file->pathname.s);
2067248616Smm			return (ARCHIVE_FAILED);
2068248616Smm		}
2069248616Smm		fn += l;
2070248616Smm		if (fn[0] == '/')
2071248616Smm			fn++;
2072248616Smm		dent = np;
2073248616Smm	}
2074248616Smm	if (np == NULL) {
2075248616Smm		/*
2076248616Smm		 * Create virtual parent directories.
2077248616Smm		 */
2078248616Smm		while (fn[0] != '\0') {
2079248616Smm			struct mtree_entry *vp;
2080248616Smm			struct archive_string as;
2081248616Smm
2082248616Smm			archive_string_init(&as);
2083248616Smm			archive_strncat(&as, p, fn - p + l);
2084248616Smm			if (as.s[as.length-1] == '/') {
2085248616Smm				as.s[as.length-1] = '\0';
2086248616Smm				as.length--;
2087248616Smm			}
2088248616Smm			r = mtree_entry_create_virtual_dir(a, as.s, &vp);
2089248616Smm			archive_string_free(&as);
2090248616Smm			if (r < ARCHIVE_WARN)
2091248616Smm				return (r);
2092248616Smm
2093248616Smm			if (strcmp(vp->pathname.s, ".") == 0) {
2094248616Smm				vp->parent = vp;
2095248616Smm				mtree->root = vp;
2096248616Smm			} else {
2097248616Smm				__archive_rb_tree_insert_node(
2098248616Smm				    &(dent->dir_info->rbtree),
2099248616Smm				    (struct archive_rb_node *)vp);
2100248616Smm				vp->parent = dent;
2101248616Smm			}
2102248616Smm			mtree_entry_register_add(mtree, vp);
2103248616Smm			np = vp;
2104248616Smm
2105248616Smm			fn += l;
2106248616Smm			if (fn[0] == '/')
2107248616Smm				fn++;
2108248616Smm			l = get_path_component(name, sizeof(name), fn);
2109248616Smm			if (l < 0) {
2110248616Smm				archive_string_free(&as);
2111248616Smm				archive_set_error(&a->archive,
2112248616Smm				    ARCHIVE_ERRNO_MISC,
2113248616Smm				    "A name buffer is too small");
2114248616Smm				return (ARCHIVE_FATAL);
2115248616Smm			}
2116248616Smm			dent = np;
2117248616Smm		}
2118248616Smm
2119248616Smm		/* Found out the parent directory where `file' can be
2120248616Smm		 * inserted. */
2121248616Smm		mtree->cur_dirent = dent;
2122248616Smm		archive_string_empty(&(mtree->cur_dirstr));
2123248616Smm		archive_string_ensure(&(mtree->cur_dirstr),
2124248616Smm		    archive_strlen(&(dent->parentdir)) +
2125248616Smm		    archive_strlen(&(dent->basename)) + 2);
2126248616Smm		if (archive_strlen(&(dent->parentdir)) +
2127248616Smm		    archive_strlen(&(dent->basename)) == 0)
2128248616Smm			mtree->cur_dirstr.s[0] = 0;
2129248616Smm		else {
2130248616Smm			if (archive_strlen(&(dent->parentdir)) > 0) {
2131248616Smm				archive_string_copy(&(mtree->cur_dirstr),
2132248616Smm				    &(dent->parentdir));
2133248616Smm				archive_strappend_char(
2134248616Smm				    &(mtree->cur_dirstr), '/');
2135248616Smm			}
2136248616Smm			archive_string_concat(&(mtree->cur_dirstr),
2137248616Smm			    &(dent->basename));
2138248616Smm		}
2139248616Smm
2140248616Smm		if (!__archive_rb_tree_insert_node(
2141248616Smm		    &(dent->dir_info->rbtree),
2142248616Smm		    (struct archive_rb_node *)file)) {
2143248616Smm			np = (struct mtree_entry *)__archive_rb_tree_find_node(
2144248616Smm			    &(dent->dir_info->rbtree), file->basename.s);
2145248616Smm			goto same_entry;
2146248616Smm		}
2147248616Smm		file->parent = dent;
2148248616Smm		mtree_entry_register_add(mtree, file);
2149248616Smm		return (ARCHIVE_OK);
2150248616Smm	}
2151248616Smm
2152248616Smmsame_entry:
2153248616Smm	/*
2154248616Smm	 * We have already has the entry the filename of which is
2155248616Smm	 * the same.
2156248616Smm	 */
2157248616Smm	r = mtree_entry_exchange_same_entry(a, np, file);
2158248616Smm	if (r < ARCHIVE_WARN)
2159248616Smm		return (r);
2160248616Smm	if (np->dir_info)
2161248616Smm		np->dir_info->virtual = 0;
2162248616Smm	*filep = np;
2163248616Smm	mtree_entry_free(file);
2164248616Smm	return (ARCHIVE_WARN);
2165248616Smm}
2166248616Smm
2167248616Smmstatic int
2168248616Smmmtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np,
2169248616Smm    struct mtree_entry *file)
2170248616Smm{
2171248616Smm
2172248616Smm	if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) {
2173248616Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
2174248616Smm		    "Found duplicate entries `%s' and its file type is "
2175248616Smm		    "different",
2176248616Smm		    np->pathname.s);
2177248616Smm		return (ARCHIVE_FAILED);
2178248616Smm	}
2179248616Smm
2180248616Smm	/* Update the existent mtree entry's attributes by the new one's. */
2181248616Smm	archive_string_empty(&np->symlink);
2182248616Smm	archive_string_concat(&np->symlink, &file->symlink);
2183248616Smm	archive_string_empty(&np->uname);
2184248616Smm	archive_string_concat(&np->uname, &file->uname);
2185248616Smm	archive_string_empty(&np->gname);
2186248616Smm	archive_string_concat(&np->gname, &file->gname);
2187248616Smm	archive_string_empty(&np->fflags_text);
2188248616Smm	archive_string_concat(&np->fflags_text, &file->fflags_text);
2189248616Smm	np->nlink = file->nlink;
2190248616Smm	np->filetype = file->filetype;
2191248616Smm	np->mode = file->mode;
2192248616Smm	np->size = file->size;
2193248616Smm	np->uid = file->uid;
2194248616Smm	np->gid = file->gid;
2195248616Smm	np->fflags_set = file->fflags_set;
2196248616Smm	np->fflags_clear = file->fflags_clear;
2197248616Smm	np->mtime = file->mtime;
2198248616Smm	np->mtime_nsec = file->mtime_nsec;
2199248616Smm	np->rdevmajor = file->rdevmajor;
2200248616Smm	np->rdevminor = file->rdevminor;
2201248616Smm
2202248616Smm	return (ARCHIVE_WARN);
2203248616Smm}
2204