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: stable/10/contrib/libarchive/libarchive/archive_write_set_format_mtree.c 368708 2020-12-16 22:25:40Z mm $");
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"
38302001Smm#include "archive_digest_private.h"
39228753Smm#include "archive_entry.h"
40368708Smm#include "archive_entry_private.h"
41228753Smm#include "archive_private.h"
42248616Smm#include "archive_rb.h"
43248616Smm#include "archive_string.h"
44228753Smm#include "archive_write_private.h"
45228753Smm
46228753Smm#define INDENTNAMELEN	15
47228753Smm#define MAXLINELEN	80
48232153Smm#define SET_KEYS	\
49232153Smm	(F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME)
50228753Smm
51248616Smmstruct attr_counter {
52248616Smm	struct attr_counter *prev;
53248616Smm	struct attr_counter *next;
54248616Smm	struct mtree_entry *m_entry;
55248616Smm	int count;
56248616Smm};
57232153Smm
58248616Smmstruct att_counter_set {
59248616Smm	struct attr_counter *uid_list;
60248616Smm	struct attr_counter *gid_list;
61248616Smm	struct attr_counter *mode_list;
62248616Smm	struct attr_counter *flags_list;
63248616Smm};
64232153Smm
65248616Smmstruct mtree_chain {
66248616Smm	struct mtree_entry *first;
67248616Smm	struct mtree_entry **last;
68248616Smm};
69248616Smm
70248616Smm/*
71248616Smm * The Data only for a directory file.
72248616Smm */
73248616Smmstruct dir_info {
74248616Smm	struct archive_rb_tree rbtree;
75248616Smm	struct mtree_chain children;
76248616Smm	struct mtree_entry *chnext;
77248616Smm	int virtual;
78248616Smm};
79248616Smm
80248616Smm/*
81248616Smm * The Data only for a regular file.
82248616Smm */
83248616Smmstruct reg_info {
84232153Smm	int compute_sum;
85232153Smm	uint32_t crc;
86368708Smm	struct ae_digest digest;
87232153Smm};
88232153Smm
89248616Smmstruct mtree_entry {
90248616Smm	struct archive_rb_node rbnode;
91248616Smm	struct mtree_entry *next;
92248616Smm	struct mtree_entry *parent;
93248616Smm	struct dir_info *dir_info;
94248616Smm	struct reg_info *reg_info;
95248616Smm
96248616Smm	struct archive_string parentdir;
97248616Smm	struct archive_string basename;
98248616Smm	struct archive_string pathname;
99248616Smm	struct archive_string symlink;
100248616Smm	struct archive_string uname;
101248616Smm	struct archive_string gname;
102248616Smm	struct archive_string fflags_text;
103248616Smm	unsigned int nlink;
104248616Smm	mode_t filetype;
105248616Smm	mode_t mode;
106248616Smm	int64_t size;
107248616Smm	int64_t uid;
108248616Smm	int64_t gid;
109248616Smm	time_t mtime;
110248616Smm	long mtime_nsec;
111248616Smm	unsigned long fflags_set;
112248616Smm	unsigned long fflags_clear;
113248616Smm	dev_t rdevmajor;
114248616Smm	dev_t rdevminor;
115302001Smm	dev_t devmajor;
116302001Smm	dev_t devminor;
117302001Smm	int64_t ino;
118232153Smm};
119232153Smm
120228753Smmstruct mtree_writer {
121232153Smm	struct mtree_entry *mtree_entry;
122248616Smm	struct mtree_entry *root;
123248616Smm	struct mtree_entry *cur_dirent;
124248616Smm	struct archive_string cur_dirstr;
125248616Smm	struct mtree_chain file_list;
126248616Smm
127228753Smm	struct archive_string ebuf;
128228753Smm	struct archive_string buf;
129228753Smm	int first;
130228753Smm	uint64_t entry_bytes_remaining;
131248616Smm
132248616Smm	/*
133248616Smm	 * Set global value.
134248616Smm	 */
135228753Smm	struct {
136248616Smm		int		processing;
137228753Smm		mode_t		type;
138228753Smm		int		keys;
139232153Smm		int64_t		uid;
140232153Smm		int64_t		gid;
141228753Smm		mode_t		mode;
142228753Smm		unsigned long	fflags_set;
143228753Smm		unsigned long	fflags_clear;
144248616Smm	} set;
145248616Smm	struct att_counter_set	acs;
146248616Smm	int classic;
147248616Smm	int depth;
148232153Smm
149232153Smm	/* check sum */
150228753Smm	int compute_sum;
151228753Smm	uint32_t crc;
152228753Smm	uint64_t crc_len;
153228753Smm#ifdef ARCHIVE_HAS_MD5
154228753Smm	archive_md5_ctx md5ctx;
155228753Smm#endif
156228753Smm#ifdef ARCHIVE_HAS_RMD160
157228753Smm	archive_rmd160_ctx rmd160ctx;
158228753Smm#endif
159228753Smm#ifdef ARCHIVE_HAS_SHA1
160228753Smm	archive_sha1_ctx sha1ctx;
161228753Smm#endif
162228753Smm#ifdef ARCHIVE_HAS_SHA256
163228753Smm	archive_sha256_ctx sha256ctx;
164228753Smm#endif
165228753Smm#ifdef ARCHIVE_HAS_SHA384
166228753Smm	archive_sha384_ctx sha384ctx;
167228753Smm#endif
168228753Smm#ifdef ARCHIVE_HAS_SHA512
169228753Smm	archive_sha512_ctx sha512ctx;
170228753Smm#endif
171228753Smm	/* Keyword options */
172228753Smm	int keys;
173353377Smm#define	F_CKSUM		0x00000001		/* checksum */
174228753Smm#define	F_DEV		0x00000002		/* device type */
175228753Smm#define	F_DONE		0x00000004		/* directory done */
176228753Smm#define	F_FLAGS		0x00000008		/* file flags */
177228753Smm#define	F_GID		0x00000010		/* gid */
178228753Smm#define	F_GNAME		0x00000020		/* group name */
179228753Smm#define	F_IGN		0x00000040		/* ignore */
180228753Smm#define	F_MAGIC		0x00000080		/* name has magic chars */
181228753Smm#define	F_MD5		0x00000100		/* MD5 digest */
182228753Smm#define	F_MODE		0x00000200		/* mode */
183228753Smm#define	F_NLINK		0x00000400		/* number of links */
184228753Smm#define	F_NOCHANGE 	0x00000800		/* If owner/mode "wrong", do
185228753Smm						 * not change */
186228753Smm#define	F_OPT		0x00001000		/* existence optional */
187228753Smm#define	F_RMD160 	0x00002000		/* RIPEMD160 digest */
188228753Smm#define	F_SHA1		0x00004000		/* SHA-1 digest */
189228753Smm#define	F_SIZE		0x00008000		/* size */
190228753Smm#define	F_SLINK		0x00010000		/* symbolic link */
191228753Smm#define	F_TAGS		0x00020000		/* tags */
192228753Smm#define	F_TIME		0x00040000		/* modification time */
193228753Smm#define	F_TYPE		0x00080000		/* file type */
194228753Smm#define	F_UID		0x00100000		/* uid */
195228753Smm#define	F_UNAME		0x00200000		/* user name */
196228753Smm#define	F_VISIT		0x00400000		/* file visited */
197228753Smm#define	F_SHA256	0x00800000		/* SHA-256 digest */
198228753Smm#define	F_SHA384	0x01000000		/* SHA-384 digest */
199228753Smm#define	F_SHA512	0x02000000		/* SHA-512 digest */
200302001Smm#define	F_INO		0x04000000		/* inode number */
201302001Smm#define	F_RESDEV	0x08000000		/* device ID on which the
202302001Smm						 * entry resides */
203228753Smm
204228753Smm	/* Options */
205248616Smm	int dironly;		/* If it is set, ignore all files except
206248616Smm				 * directory files, like mtree(8) -d option. */
207248616Smm	int indent;		/* If it is set, indent output data. */
208248616Smm	int output_global_set;	/* If it is set, use /set keyword to set
209248616Smm				 * global values. When generating mtree
210248616Smm				 * classic format, it is set by default. */
211228753Smm};
212228753Smm
213228753Smm#define DEFAULT_KEYS	(F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\
214228753Smm			 | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\
215228753Smm			 | F_UNAME)
216248616Smm#define attr_counter_set_reset	attr_counter_set_free
217228753Smm
218248616Smmstatic void attr_counter_free(struct attr_counter **);
219248616Smmstatic int attr_counter_inc(struct attr_counter **, struct attr_counter *,
220248616Smm	struct attr_counter *, struct mtree_entry *);
221248616Smmstatic struct attr_counter * attr_counter_new(struct mtree_entry *,
222232153Smm	struct attr_counter *);
223248616Smmstatic int attr_counter_set_collect(struct mtree_writer *,
224248616Smm	struct mtree_entry *);
225248616Smmstatic void attr_counter_set_free(struct mtree_writer *);
226248616Smmstatic int get_global_set_keys(struct mtree_writer *, struct mtree_entry *);
227248616Smmstatic int mtree_entry_add_child_tail(struct mtree_entry *,
228248616Smm	struct mtree_entry *);
229248616Smmstatic int mtree_entry_create_virtual_dir(struct archive_write *, const char *,
230248616Smm	struct mtree_entry **);
231248616Smmstatic int mtree_entry_cmp_node(const struct archive_rb_node *,
232248616Smm	const struct archive_rb_node *);
233248616Smmstatic int mtree_entry_cmp_key(const struct archive_rb_node *, const void *);
234248616Smmstatic int mtree_entry_exchange_same_entry(struct archive_write *,
235248616Smm    struct mtree_entry *, struct mtree_entry *);
236248616Smmstatic void mtree_entry_free(struct mtree_entry *);
237248616Smmstatic int mtree_entry_new(struct archive_write *, struct archive_entry *,
238248616Smm	struct mtree_entry **);
239248616Smmstatic void mtree_entry_register_free(struct mtree_writer *);
240248616Smmstatic void mtree_entry_register_init(struct mtree_writer *);
241248616Smmstatic int mtree_entry_setup_filenames(struct archive_write *,
242248616Smm	struct mtree_entry *, struct archive_entry *);
243248616Smmstatic int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **);
244232153Smmstatic void sum_init(struct mtree_writer *);
245232153Smmstatic void sum_update(struct mtree_writer *, const void *, size_t);
246248616Smmstatic void sum_final(struct mtree_writer *, struct reg_info *);
247248616Smmstatic void sum_write(struct archive_string *, struct reg_info *);
248248616Smmstatic int write_mtree_entry(struct archive_write *, struct mtree_entry *);
249248616Smmstatic int write_dot_dot_entry(struct archive_write *, struct mtree_entry *);
250232153Smm
251228753Smm#define	COMPUTE_CRC(var, ch)	(var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
252228753Smmstatic const uint32_t crctab[] = {
253228753Smm	0x0,
254228753Smm	0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
255228753Smm	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
256228753Smm	0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
257228753Smm	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
258228753Smm	0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
259228753Smm	0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
260228753Smm	0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
261228753Smm	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
262228753Smm	0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
263228753Smm	0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
264228753Smm	0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
265228753Smm	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
266228753Smm	0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
267228753Smm	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
268228753Smm	0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
269228753Smm	0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
270228753Smm	0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
271228753Smm	0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
272228753Smm	0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
273228753Smm	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
274228753Smm	0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
275228753Smm	0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
276228753Smm	0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
277228753Smm	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
278228753Smm	0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
279228753Smm	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
280228753Smm	0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
281228753Smm	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
282228753Smm	0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
283228753Smm	0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
284228753Smm	0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
285228753Smm	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
286228753Smm	0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
287228753Smm	0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
288228753Smm	0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
289228753Smm	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
290228753Smm	0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
291228753Smm	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
292228753Smm	0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
293228753Smm	0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
294228753Smm	0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
295228753Smm	0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
296228753Smm	0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
297228753Smm	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
298228753Smm	0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
299228753Smm	0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
300228753Smm	0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
301228753Smm	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
302228753Smm	0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
303228753Smm	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
304228753Smm	0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
305228753Smm};
306228753Smm
307248616Smmstatic const unsigned char safe_char[256] = {
308248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
309248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
310248616Smm	/* !"$%&'()*+,-./  EXCLUSION:0x20( ) 0x23(#) */
311248616Smm	0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
312248616Smm	/* 0123456789:;<>?  EXCLUSION:0x3d(=) */
313248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
314248616Smm	/* @ABCDEFGHIJKLMNO */
315248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
316248616Smm	/* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\)  */
317248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */
318248616Smm	/* `abcdefghijklmno */
319248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
320248616Smm	/* pqrstuvwxyz{|}~ */
321248616Smm	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
322248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
323248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
324248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
325248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
326248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
327248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
328248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
329248616Smm	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
330248616Smm};
331228753Smm
332228753Smmstatic void
333228753Smmmtree_quote(struct archive_string *s, const char *str)
334228753Smm{
335228753Smm	const char *start;
336228753Smm	char buf[4];
337228753Smm	unsigned char c;
338228753Smm
339228753Smm	for (start = str; *str != '\0'; ++str) {
340248616Smm		if (safe_char[*(const unsigned char *)str])
341228753Smm			continue;
342228753Smm		if (start != str)
343228753Smm			archive_strncat(s, start, str - start);
344228753Smm		c = (unsigned char)*str;
345228753Smm		buf[0] = '\\';
346228753Smm		buf[1] = (c / 64) + '0';
347228753Smm		buf[2] = (c / 8 % 8) + '0';
348228753Smm		buf[3] = (c % 8) + '0';
349228753Smm		archive_strncat(s, buf, 4);
350228753Smm		start = str + 1;
351228753Smm	}
352228753Smm
353228753Smm	if (start != str)
354228753Smm		archive_strncat(s, start, str - start);
355228753Smm}
356228753Smm
357232153Smm/*
358353377Smm * Indent a line as the mtree utility does so it is readable for people.
359232153Smm */
360228753Smmstatic void
361228753Smmmtree_indent(struct mtree_writer *mtree)
362228753Smm{
363248616Smm	int i, fn, nd, pd;
364228753Smm	const char *r, *s, *x;
365228753Smm
366248616Smm	if (mtree->classic) {
367248616Smm		if (mtree->indent) {
368248616Smm			nd = 0;
369248616Smm			pd = mtree->depth * 4;
370248616Smm		} else {
371248616Smm			nd = mtree->depth?4:0;
372248616Smm			pd = 0;
373248616Smm		}
374248616Smm	} else
375248616Smm		nd = pd = 0;
376228753Smm	fn = 1;
377228753Smm	s = r = mtree->ebuf.s;
378228753Smm	x = NULL;
379228753Smm	while (*r == ' ')
380228753Smm		r++;
381228753Smm	while ((r = strchr(r, ' ')) != NULL) {
382228753Smm		if (fn) {
383228753Smm			fn = 0;
384248616Smm			for (i = 0; i < nd + pd; i++)
385248616Smm				archive_strappend_char(&mtree->buf, ' ');
386228753Smm			archive_strncat(&mtree->buf, s, r - s);
387248616Smm			if (nd + (r -s) > INDENTNAMELEN) {
388228753Smm				archive_strncat(&mtree->buf, " \\\n", 3);
389248616Smm				for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
390228753Smm					archive_strappend_char(&mtree->buf, ' ');
391228753Smm			} else {
392248616Smm				for (i = (int)(r -s + nd);
393248616Smm				    i < (INDENTNAMELEN + 1); i++)
394228753Smm					archive_strappend_char(&mtree->buf, ' ');
395228753Smm			}
396228753Smm			s = ++r;
397228753Smm			x = NULL;
398228753Smm			continue;
399228753Smm		}
400248616Smm		if (pd + (r - s) <= MAXLINELEN - 3 - INDENTNAMELEN)
401228753Smm			x = r++;
402228753Smm		else {
403228753Smm			if (x == NULL)
404228753Smm				x = r;
405228753Smm			archive_strncat(&mtree->buf, s, x - s);
406228753Smm			archive_strncat(&mtree->buf, " \\\n", 3);
407248616Smm			for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
408228753Smm				archive_strappend_char(&mtree->buf, ' ');
409228753Smm			s = r = ++x;
410228753Smm			x = NULL;
411228753Smm		}
412228753Smm	}
413248616Smm	if (fn) {
414248616Smm		for (i = 0; i < nd + pd; i++)
415248616Smm			archive_strappend_char(&mtree->buf, ' ');
416248616Smm		archive_strcat(&mtree->buf, s);
417248616Smm		s += strlen(s);
418248616Smm	}
419248616Smm	if (x != NULL && pd + strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) {
420228753Smm		/* Last keyword is longer. */
421228753Smm		archive_strncat(&mtree->buf, s, x - s);
422228753Smm		archive_strncat(&mtree->buf, " \\\n", 3);
423248616Smm		for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
424228753Smm			archive_strappend_char(&mtree->buf, ' ');
425228753Smm		s = ++x;
426228753Smm	}
427228753Smm	archive_strcat(&mtree->buf, s);
428228753Smm	archive_string_empty(&mtree->ebuf);
429228753Smm}
430228753Smm
431228753Smm/*
432232153Smm * Write /set keyword.
433353377Smm * Set the most used value of uid, gid, mode and fflags, which are
434353377Smm * collected by the attr_counter_set_collect() function.
435228753Smm */
436228753Smmstatic void
437232153Smmwrite_global(struct mtree_writer *mtree)
438228753Smm{
439228753Smm	struct archive_string setstr;
440228753Smm	struct archive_string unsetstr;
441248616Smm	struct att_counter_set *acs;
442228753Smm	int keys, oldkeys, effkeys;
443228753Smm
444228753Smm	archive_string_init(&setstr);
445228753Smm	archive_string_init(&unsetstr);
446232153Smm	keys = mtree->keys & SET_KEYS;
447228753Smm	oldkeys = mtree->set.keys;
448228753Smm	effkeys = keys;
449248616Smm	acs = &mtree->acs;
450248616Smm	if (mtree->set.processing) {
451228753Smm		/*
452232153Smm		 * Check if the global data needs updating.
453228753Smm		 */
454228753Smm		effkeys &= ~F_TYPE;
455248616Smm		if (acs->uid_list == NULL)
456248616Smm			effkeys &= ~(F_UNAME | F_UID);
457248616Smm		else if (oldkeys & (F_UNAME | F_UID)) {
458248616Smm			if (acs->uid_list->count < 2 ||
459248616Smm			    mtree->set.uid == acs->uid_list->m_entry->uid)
460248616Smm				effkeys &= ~(F_UNAME | F_UID);
461232153Smm		}
462248616Smm		if (acs->gid_list == NULL)
463248616Smm			effkeys &= ~(F_GNAME | F_GID);
464248616Smm		else if (oldkeys & (F_GNAME | F_GID)) {
465248616Smm			if (acs->gid_list->count < 2 ||
466248616Smm			    mtree->set.gid == acs->gid_list->m_entry->gid)
467248616Smm				effkeys &= ~(F_GNAME | F_GID);
468232153Smm		}
469248616Smm		if (acs->mode_list == NULL)
470248616Smm			effkeys &= ~F_MODE;
471248616Smm		else if (oldkeys & F_MODE) {
472248616Smm			if (acs->mode_list->count < 2 ||
473248616Smm			    mtree->set.mode == acs->mode_list->m_entry->mode)
474248616Smm				effkeys &= ~F_MODE;
475232153Smm		}
476248616Smm		if (acs->flags_list == NULL)
477248616Smm			effkeys &= ~F_FLAGS;
478248616Smm		else if ((oldkeys & F_FLAGS) != 0) {
479248616Smm			if (acs->flags_list->count < 2 ||
480248616Smm			    (acs->flags_list->m_entry->fflags_set ==
481248616Smm				mtree->set.fflags_set &&
482248616Smm			     acs->flags_list->m_entry->fflags_clear ==
483248616Smm				mtree->set.fflags_clear))
484248616Smm				effkeys &= ~F_FLAGS;
485228753Smm		}
486248616Smm	} else {
487248616Smm		if (acs->uid_list == NULL)
488248616Smm			keys &= ~(F_UNAME | F_UID);
489248616Smm		if (acs->gid_list == NULL)
490248616Smm			keys &= ~(F_GNAME | F_GID);
491248616Smm		if (acs->mode_list == NULL)
492248616Smm			keys &= ~F_MODE;
493248616Smm		if (acs->flags_list == NULL)
494248616Smm			keys &= ~F_FLAGS;
495228753Smm	}
496228753Smm	if ((keys & effkeys & F_TYPE) != 0) {
497232153Smm		if (mtree->dironly) {
498228753Smm			archive_strcat(&setstr, " type=dir");
499232153Smm			mtree->set.type = AE_IFDIR;
500232153Smm		} else {
501228753Smm			archive_strcat(&setstr, " type=file");
502232153Smm			mtree->set.type = AE_IFREG;
503232153Smm		}
504228753Smm	}
505228753Smm	if ((keys & effkeys & F_UNAME) != 0) {
506248616Smm		if (archive_strlen(&(acs->uid_list->m_entry->uname)) > 0) {
507228753Smm			archive_strcat(&setstr, " uname=");
508248616Smm			mtree_quote(&setstr, acs->uid_list->m_entry->uname.s);
509232153Smm		} else {
510228753Smm			keys &= ~F_UNAME;
511232153Smm			if ((oldkeys & F_UNAME) != 0)
512232153Smm				archive_strcat(&unsetstr, " uname");
513232153Smm		}
514228753Smm	}
515228753Smm	if ((keys & effkeys & F_UID) != 0) {
516248616Smm		mtree->set.uid = acs->uid_list->m_entry->uid;
517228753Smm		archive_string_sprintf(&setstr, " uid=%jd",
518228753Smm		    (intmax_t)mtree->set.uid);
519228753Smm	}
520228753Smm	if ((keys & effkeys & F_GNAME) != 0) {
521248616Smm		if (archive_strlen(&(acs->gid_list->m_entry->gname)) > 0) {
522228753Smm			archive_strcat(&setstr, " gname=");
523248616Smm			mtree_quote(&setstr, acs->gid_list->m_entry->gname.s);
524232153Smm		} else {
525228753Smm			keys &= ~F_GNAME;
526232153Smm			if ((oldkeys & F_GNAME) != 0)
527232153Smm				archive_strcat(&unsetstr, " gname");
528232153Smm		}
529228753Smm	}
530228753Smm	if ((keys & effkeys & F_GID) != 0) {
531248616Smm		mtree->set.gid = acs->gid_list->m_entry->gid;
532228753Smm		archive_string_sprintf(&setstr, " gid=%jd",
533228753Smm		    (intmax_t)mtree->set.gid);
534228753Smm	}
535228753Smm	if ((keys & effkeys & F_MODE) != 0) {
536248616Smm		mtree->set.mode = acs->mode_list->m_entry->mode;
537232153Smm		archive_string_sprintf(&setstr, " mode=%o",
538232153Smm		    (unsigned int)mtree->set.mode);
539228753Smm	}
540228753Smm	if ((keys & effkeys & F_FLAGS) != 0) {
541248616Smm		if (archive_strlen(
542248616Smm		    &(acs->flags_list->m_entry->fflags_text)) > 0) {
543228753Smm			archive_strcat(&setstr, " flags=");
544248616Smm			mtree_quote(&setstr,
545248616Smm			    acs->flags_list->m_entry->fflags_text.s);
546232153Smm			mtree->set.fflags_set =
547248616Smm			    acs->flags_list->m_entry->fflags_set;
548232153Smm			mtree->set.fflags_clear =
549248616Smm			    acs->flags_list->m_entry->fflags_clear;
550232153Smm		} else {
551228753Smm			keys &= ~F_FLAGS;
552232153Smm			if ((oldkeys & F_FLAGS) != 0)
553232153Smm				archive_strcat(&unsetstr, " flags");
554232153Smm		}
555228753Smm	}
556228753Smm	if (unsetstr.length > 0)
557228753Smm		archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s);
558228753Smm	archive_string_free(&unsetstr);
559228753Smm	if (setstr.length > 0)
560228753Smm		archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s);
561228753Smm	archive_string_free(&setstr);
562228753Smm	mtree->set.keys = keys;
563248616Smm	mtree->set.processing = 1;
564228753Smm}
565228753Smm
566232153Smmstatic struct attr_counter *
567248616Smmattr_counter_new(struct mtree_entry *me, struct attr_counter *prev)
568232153Smm{
569232153Smm	struct attr_counter *ac;
570232153Smm
571232153Smm	ac = malloc(sizeof(*ac));
572232153Smm	if (ac != NULL) {
573232153Smm		ac->prev = prev;
574232153Smm		ac->next = NULL;
575232153Smm		ac->count = 1;
576232153Smm		ac->m_entry = me;
577232153Smm	}
578232153Smm	return (ac);
579232153Smm}
580232153Smm
581232153Smmstatic void
582248616Smmattr_counter_free(struct attr_counter **top)
583232153Smm{
584232153Smm	struct attr_counter *ac, *tac;
585232153Smm
586232153Smm	if (*top == NULL)
587232153Smm		return;
588232153Smm	ac = *top;
589232153Smm        while (ac != NULL) {
590232153Smm		tac = ac->next;
591232153Smm		free(ac);
592232153Smm		ac = tac;
593232153Smm	}
594232153Smm	*top = NULL;
595232153Smm}
596232153Smm
597228753Smmstatic int
598248616Smmattr_counter_inc(struct attr_counter **top, struct attr_counter *ac,
599232153Smm    struct attr_counter *last, struct mtree_entry *me)
600228753Smm{
601232153Smm	struct attr_counter *pac;
602232153Smm
603232153Smm	if (ac != NULL) {
604232153Smm		ac->count++;
605232153Smm		if (*top == ac || ac->prev->count >= ac->count)
606232153Smm			return (0);
607232153Smm		for (pac = ac->prev; pac; pac = pac->prev) {
608232153Smm			if (pac->count >= ac->count)
609232153Smm				break;
610232153Smm		}
611232153Smm		ac->prev->next = ac->next;
612232153Smm		if (ac->next != NULL)
613232153Smm			ac->next->prev = ac->prev;
614232153Smm		if (pac != NULL) {
615232153Smm			ac->prev = pac;
616232153Smm			ac->next = pac->next;
617232153Smm			pac->next = ac;
618232153Smm			if (ac->next != NULL)
619232153Smm				ac->next->prev = ac;
620232153Smm		} else {
621232153Smm			ac->prev = NULL;
622232153Smm			ac->next = *top;
623232153Smm			*top = ac;
624232153Smm			ac->next->prev = ac;
625232153Smm		}
626302001Smm	} else if (last != NULL) {
627248616Smm		ac = attr_counter_new(me, last);
628232153Smm		if (ac == NULL)
629232153Smm			return (-1);
630232153Smm		last->next = ac;
631232153Smm	}
632232153Smm	return (0);
633232153Smm}
634232153Smm
635248616Smm/*
636353377Smm * Tabulate uid, gid, mode and fflags of a entry in order to be used for /set.
637248616Smm */
638232153Smmstatic int
639248616Smmattr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)
640232153Smm{
641248616Smm	struct attr_counter *ac, *last;
642248616Smm	struct att_counter_set *acs = &mtree->acs;
643232153Smm	int keys = mtree->keys;
644232153Smm
645232153Smm	if (keys & (F_UNAME | F_UID)) {
646248616Smm		if (acs->uid_list == NULL) {
647248616Smm			acs->uid_list = attr_counter_new(me, NULL);
648248616Smm			if (acs->uid_list == NULL)
649232153Smm				return (-1);
650232153Smm		} else {
651232153Smm			last = NULL;
652248616Smm			for (ac = acs->uid_list; ac; ac = ac->next) {
653232153Smm				if (ac->m_entry->uid == me->uid)
654232153Smm					break;
655232153Smm				last = ac;
656232153Smm			}
657248616Smm			if (attr_counter_inc(&acs->uid_list, ac, last, me) < 0)
658232153Smm				return (-1);
659232153Smm		}
660232153Smm	}
661232153Smm	if (keys & (F_GNAME | F_GID)) {
662248616Smm		if (acs->gid_list == NULL) {
663248616Smm			acs->gid_list = attr_counter_new(me, NULL);
664248616Smm			if (acs->gid_list == NULL)
665232153Smm				return (-1);
666232153Smm		} else {
667232153Smm			last = NULL;
668248616Smm			for (ac = acs->gid_list; ac; ac = ac->next) {
669232153Smm				if (ac->m_entry->gid == me->gid)
670232153Smm					break;
671232153Smm				last = ac;
672232153Smm			}
673248616Smm			if (attr_counter_inc(&acs->gid_list, ac, last, me) < 0)
674232153Smm				return (-1);
675232153Smm		}
676232153Smm	}
677232153Smm	if (keys & F_MODE) {
678248616Smm		if (acs->mode_list == NULL) {
679248616Smm			acs->mode_list = attr_counter_new(me, NULL);
680248616Smm			if (acs->mode_list == NULL)
681232153Smm				return (-1);
682232153Smm		} else {
683232153Smm			last = NULL;
684248616Smm			for (ac = acs->mode_list; ac; ac = ac->next) {
685232153Smm				if (ac->m_entry->mode == me->mode)
686232153Smm					break;
687232153Smm				last = ac;
688232153Smm			}
689248616Smm			if (attr_counter_inc(&acs->mode_list, ac, last, me) < 0)
690232153Smm				return (-1);
691232153Smm		}
692232153Smm	}
693232153Smm	if (keys & F_FLAGS) {
694248616Smm		if (acs->flags_list == NULL) {
695248616Smm			acs->flags_list = attr_counter_new(me, NULL);
696248616Smm			if (acs->flags_list == NULL)
697232153Smm				return (-1);
698232153Smm		} else {
699232153Smm			last = NULL;
700248616Smm			for (ac = acs->flags_list; ac; ac = ac->next) {
701232153Smm				if (ac->m_entry->fflags_set == me->fflags_set &&
702248616Smm				    ac->m_entry->fflags_clear ==
703248616Smm							me->fflags_clear)
704232153Smm					break;
705232153Smm				last = ac;
706232153Smm			}
707248616Smm			if (attr_counter_inc(&acs->flags_list, ac, last, me) < 0)
708232153Smm				return (-1);
709232153Smm		}
710232153Smm	}
711232153Smm
712232153Smm	return (0);
713232153Smm}
714232153Smm
715248616Smmstatic void
716248616Smmattr_counter_set_free(struct mtree_writer *mtree)
717248616Smm{
718248616Smm	struct att_counter_set *acs = &mtree->acs;
719248616Smm
720248616Smm	attr_counter_free(&acs->uid_list);
721248616Smm	attr_counter_free(&acs->gid_list);
722248616Smm	attr_counter_free(&acs->mode_list);
723248616Smm	attr_counter_free(&acs->flags_list);
724248616Smm}
725248616Smm
726232153Smmstatic int
727248616Smmget_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me)
728232153Smm{
729228753Smm	int keys;
730228753Smm
731228753Smm	keys = mtree->keys;
732232153Smm
733232153Smm	/*
734232153Smm	 * If a keyword has been set by /set, we do not need to
735232153Smm	 * output it.
736232153Smm	 */
737228753Smm	if (mtree->set.keys == 0)
738232153Smm		return (keys);/* /set is not used. */
739232153Smm
740228753Smm	if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 &&
741232153Smm	     mtree->set.gid == me->gid)
742228753Smm		keys &= ~(F_GNAME | F_GID);
743228753Smm	if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 &&
744232153Smm	     mtree->set.uid == me->uid)
745228753Smm		keys &= ~(F_UNAME | F_UID);
746228753Smm	if (mtree->set.keys & F_FLAGS) {
747232153Smm		if (mtree->set.fflags_set == me->fflags_set &&
748232153Smm		    mtree->set.fflags_clear == me->fflags_clear)
749228753Smm			keys &= ~F_FLAGS;
750228753Smm	}
751232153Smm	if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == me->mode)
752228753Smm		keys &= ~F_MODE;
753228753Smm
754232153Smm	switch (me->filetype) {
755228753Smm	case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
756228753Smm	case AE_IFBLK: case AE_IFIFO:
757228753Smm		break;
758228753Smm	case AE_IFDIR:
759228753Smm		if ((mtree->set.keys & F_TYPE) != 0 &&
760228753Smm		    mtree->set.type == AE_IFDIR)
761228753Smm			keys &= ~F_TYPE;
762228753Smm		break;
763228753Smm	case AE_IFREG:
764228753Smm	default:	/* Handle unknown file types as regular files. */
765228753Smm		if ((mtree->set.keys & F_TYPE) != 0 &&
766228753Smm		    mtree->set.type == AE_IFREG)
767228753Smm			keys &= ~F_TYPE;
768228753Smm		break;
769228753Smm	}
770228753Smm
771228753Smm	return (keys);
772228753Smm}
773228753Smm
774248616Smmstatic int
775248616Smmmtree_entry_new(struct archive_write *a, struct archive_entry *entry,
776248616Smm    struct mtree_entry **m_entry)
777232153Smm{
778232153Smm	struct mtree_entry *me;
779232153Smm	const char *s;
780248616Smm	int r;
781248616Smm	static const struct archive_rb_tree_ops rb_ops = {
782248616Smm		mtree_entry_cmp_node, mtree_entry_cmp_key
783248616Smm	};
784232153Smm
785232153Smm	me = calloc(1, sizeof(*me));
786248616Smm	if (me == NULL) {
787248616Smm		archive_set_error(&a->archive, ENOMEM,
788248616Smm		    "Can't allocate memory for a mtree entry");
789248616Smm		*m_entry = NULL;
790248616Smm		return (ARCHIVE_FATAL);
791248616Smm	}
792248616Smm
793248616Smm	r = mtree_entry_setup_filenames(a, me, entry);
794248616Smm	if (r < ARCHIVE_WARN) {
795248616Smm		mtree_entry_free(me);
796248616Smm		*m_entry = NULL;
797248616Smm		return (r);
798248616Smm	}
799248616Smm
800232153Smm	if ((s = archive_entry_symlink(entry)) != NULL)
801248616Smm		archive_strcpy(&me->symlink, s);
802232153Smm	me->nlink = archive_entry_nlink(entry);
803232153Smm	me->filetype = archive_entry_filetype(entry);
804232153Smm	me->mode = archive_entry_mode(entry) & 07777;
805232153Smm	me->uid = archive_entry_uid(entry);
806232153Smm	me->gid = archive_entry_gid(entry);
807232153Smm	if ((s = archive_entry_uname(entry)) != NULL)
808248616Smm		archive_strcpy(&me->uname, s);
809232153Smm	if ((s = archive_entry_gname(entry)) != NULL)
810248616Smm		archive_strcpy(&me->gname, s);
811232153Smm	if ((s = archive_entry_fflags_text(entry)) != NULL)
812248616Smm		archive_strcpy(&me->fflags_text, s);
813232153Smm	archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear);
814232153Smm	me->mtime = archive_entry_mtime(entry);
815232153Smm	me->mtime_nsec = archive_entry_mtime_nsec(entry);
816302001Smm	me->rdevmajor = archive_entry_rdevmajor(entry);
817232153Smm	me->rdevminor = archive_entry_rdevminor(entry);
818302001Smm	me->devmajor = archive_entry_devmajor(entry);
819302001Smm	me->devminor = archive_entry_devminor(entry);
820302001Smm	me->ino = archive_entry_ino(entry);
821232153Smm	me->size = archive_entry_size(entry);
822248616Smm	if (me->filetype == AE_IFDIR) {
823248616Smm		me->dir_info = calloc(1, sizeof(*me->dir_info));
824248616Smm		if (me->dir_info == NULL) {
825248616Smm			mtree_entry_free(me);
826248616Smm			archive_set_error(&a->archive, ENOMEM,
827248616Smm			    "Can't allocate memory for a mtree entry");
828248616Smm			*m_entry = NULL;
829248616Smm			return (ARCHIVE_FATAL);
830248616Smm		}
831248616Smm		__archive_rb_tree_init(&me->dir_info->rbtree, &rb_ops);
832248616Smm		me->dir_info->children.first = NULL;
833248616Smm		me->dir_info->children.last = &(me->dir_info->children.first);
834248616Smm		me->dir_info->chnext = NULL;
835248616Smm	} else if (me->filetype == AE_IFREG) {
836248616Smm		me->reg_info = calloc(1, sizeof(*me->reg_info));
837248616Smm		if (me->reg_info == NULL) {
838248616Smm			mtree_entry_free(me);
839248616Smm			archive_set_error(&a->archive, ENOMEM,
840248616Smm			    "Can't allocate memory for a mtree entry");
841248616Smm			*m_entry = NULL;
842248616Smm			return (ARCHIVE_FATAL);
843248616Smm		}
844248616Smm		me->reg_info->compute_sum = 0;
845248616Smm	}
846232153Smm
847248616Smm	*m_entry = me;
848248616Smm	return (ARCHIVE_OK);
849232153Smm}
850232153Smm
851232153Smmstatic void
852248616Smmmtree_entry_free(struct mtree_entry *me)
853232153Smm{
854248616Smm	archive_string_free(&me->parentdir);
855248616Smm	archive_string_free(&me->basename);
856248616Smm	archive_string_free(&me->pathname);
857248616Smm	archive_string_free(&me->symlink);
858248616Smm	archive_string_free(&me->uname);
859248616Smm	archive_string_free(&me->gname);
860248616Smm	archive_string_free(&me->fflags_text);
861248616Smm	free(me->dir_info);
862248616Smm	free(me->reg_info);
863232153Smm	free(me);
864232153Smm}
865232153Smm
866228753Smmstatic int
867228753Smmarchive_write_mtree_header(struct archive_write *a,
868228753Smm    struct archive_entry *entry)
869228753Smm{
870228753Smm	struct mtree_writer *mtree= a->format_data;
871248616Smm	struct mtree_entry *mtree_entry;
872248616Smm	int r, r2;
873228753Smm
874228753Smm	if (mtree->first) {
875228753Smm		mtree->first = 0;
876228753Smm		archive_strcat(&mtree->buf, "#mtree\n");
877232153Smm		if ((mtree->keys & SET_KEYS) == 0)
878302001Smm			mtree->output_global_set = 0;/* Disabled. */
879228753Smm	}
880228753Smm
881228753Smm	mtree->entry_bytes_remaining = archive_entry_size(entry);
882248616Smm
883248616Smm	/* While directory only mode, we do not handle non directory files. */
884232153Smm	if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR)
885232153Smm		return (ARCHIVE_OK);
886228753Smm
887248616Smm	r2 = mtree_entry_new(a, entry, &mtree_entry);
888248616Smm	if (r2 < ARCHIVE_WARN)
889248616Smm		return (r2);
890248616Smm	r = mtree_entry_tree_add(a, &mtree_entry);
891248616Smm	if (r < ARCHIVE_WARN) {
892248616Smm		mtree_entry_free(mtree_entry);
893248616Smm		return (r);
894232153Smm	}
895248616Smm	mtree->mtree_entry = mtree_entry;
896228753Smm
897248616Smm	/* If the current file is a regular file, we have to
898248616Smm	 * compute the sum of its content.
899353377Smm	 * Initialize a bunch of checksum context. */
900248616Smm	if (mtree_entry->reg_info)
901248616Smm		sum_init(mtree);
902228753Smm
903248616Smm	return (r2);
904228753Smm}
905228753Smm
906228753Smmstatic int
907248616Smmwrite_mtree_entry(struct archive_write *a, struct mtree_entry *me)
908228753Smm{
909228753Smm	struct mtree_writer *mtree = a->format_data;
910228753Smm	struct archive_string *str;
911228753Smm	int keys, ret;
912228753Smm
913248616Smm	if (me->dir_info) {
914248616Smm		if (mtree->classic) {
915248616Smm			/*
916248616Smm			 * Output a comment line to describe the full
917248616Smm			 * pathname of the entry as mtree utility does
918248616Smm			 * while generating classic format.
919248616Smm			 */
920248616Smm			if (!mtree->dironly)
921248616Smm				archive_strappend_char(&mtree->buf, '\n');
922248616Smm			if (me->parentdir.s)
923248616Smm				archive_string_sprintf(&mtree->buf,
924248616Smm				    "# %s/%s\n",
925248616Smm				    me->parentdir.s, me->basename.s);
926248616Smm			else
927248616Smm				archive_string_sprintf(&mtree->buf,
928248616Smm				    "# %s\n",
929248616Smm				    me->basename.s);
930248616Smm		}
931248616Smm		if (mtree->output_global_set)
932248616Smm			write_global(mtree);
933248616Smm	}
934232153Smm	archive_string_empty(&mtree->ebuf);
935248616Smm	str = (mtree->indent || mtree->classic)? &mtree->ebuf : &mtree->buf;
936248616Smm
937248616Smm	if (!mtree->classic && me->parentdir.s) {
938248616Smm		/*
939248616Smm		 * If generating format is not classic one(v1), output
940248616Smm		 * a full pathname.
941248616Smm		 */
942248616Smm		mtree_quote(str, me->parentdir.s);
943248616Smm		archive_strappend_char(str, '/');
944248616Smm	}
945248616Smm	mtree_quote(str, me->basename.s);
946248616Smm
947248616Smm	keys = get_global_set_keys(mtree, me);
948228753Smm	if ((keys & F_NLINK) != 0 &&
949232153Smm	    me->nlink != 1 && me->filetype != AE_IFDIR)
950232153Smm		archive_string_sprintf(str, " nlink=%u", me->nlink);
951228753Smm
952248616Smm	if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) {
953228753Smm		archive_strcat(str, " gname=");
954248616Smm		mtree_quote(str, me->gname.s);
955228753Smm	}
956248616Smm	if ((keys & F_UNAME) != 0 && archive_strlen(&me->uname) > 0) {
957228753Smm		archive_strcat(str, " uname=");
958248616Smm		mtree_quote(str, me->uname.s);
959228753Smm	}
960232153Smm	if ((keys & F_FLAGS) != 0) {
961248616Smm		if (archive_strlen(&me->fflags_text) > 0) {
962232153Smm			archive_strcat(str, " flags=");
963248616Smm			mtree_quote(str, me->fflags_text.s);
964248616Smm		} else if (mtree->set.processing &&
965232153Smm		    (mtree->set.keys & F_FLAGS) != 0)
966232153Smm			/* Overwrite the global parameter. */
967232153Smm			archive_strcat(str, " flags=none");
968228753Smm	}
969228753Smm	if ((keys & F_TIME) != 0)
970228753Smm		archive_string_sprintf(str, " time=%jd.%jd",
971232153Smm		    (intmax_t)me->mtime, (intmax_t)me->mtime_nsec);
972228753Smm	if ((keys & F_MODE) != 0)
973232153Smm		archive_string_sprintf(str, " mode=%o", (unsigned int)me->mode);
974228753Smm	if ((keys & F_GID) != 0)
975232153Smm		archive_string_sprintf(str, " gid=%jd", (intmax_t)me->gid);
976228753Smm	if ((keys & F_UID) != 0)
977232153Smm		archive_string_sprintf(str, " uid=%jd", (intmax_t)me->uid);
978228753Smm
979302001Smm	if ((keys & F_INO) != 0)
980302001Smm		archive_string_sprintf(str, " inode=%jd", (intmax_t)me->ino);
981302001Smm	if ((keys & F_RESDEV) != 0) {
982302001Smm		archive_string_sprintf(str,
983302001Smm		    " resdevice=native,%ju,%ju",
984302001Smm		    (uintmax_t)me->devmajor,
985302001Smm		    (uintmax_t)me->devminor);
986302001Smm	}
987302001Smm
988232153Smm	switch (me->filetype) {
989228753Smm	case AE_IFLNK:
990228753Smm		if ((keys & F_TYPE) != 0)
991228753Smm			archive_strcat(str, " type=link");
992228753Smm		if ((keys & F_SLINK) != 0) {
993228753Smm			archive_strcat(str, " link=");
994248616Smm			mtree_quote(str, me->symlink.s);
995228753Smm		}
996228753Smm		break;
997228753Smm	case AE_IFSOCK:
998228753Smm		if ((keys & F_TYPE) != 0)
999228753Smm			archive_strcat(str, " type=socket");
1000228753Smm		break;
1001228753Smm	case AE_IFCHR:
1002228753Smm		if ((keys & F_TYPE) != 0)
1003228753Smm			archive_strcat(str, " type=char");
1004228753Smm		if ((keys & F_DEV) != 0) {
1005228753Smm			archive_string_sprintf(str,
1006232153Smm			    " device=native,%ju,%ju",
1007232153Smm			    (uintmax_t)me->rdevmajor,
1008232153Smm			    (uintmax_t)me->rdevminor);
1009228753Smm		}
1010228753Smm		break;
1011228753Smm	case AE_IFBLK:
1012228753Smm		if ((keys & F_TYPE) != 0)
1013228753Smm			archive_strcat(str, " type=block");
1014228753Smm		if ((keys & F_DEV) != 0) {
1015228753Smm			archive_string_sprintf(str,
1016232153Smm			    " device=native,%ju,%ju",
1017232153Smm			    (uintmax_t)me->rdevmajor,
1018232153Smm			    (uintmax_t)me->rdevminor);
1019228753Smm		}
1020228753Smm		break;
1021228753Smm	case AE_IFDIR:
1022228753Smm		if ((keys & F_TYPE) != 0)
1023228753Smm			archive_strcat(str, " type=dir");
1024228753Smm		break;
1025228753Smm	case AE_IFIFO:
1026228753Smm		if ((keys & F_TYPE) != 0)
1027228753Smm			archive_strcat(str, " type=fifo");
1028228753Smm		break;
1029228753Smm	case AE_IFREG:
1030228753Smm	default:	/* Handle unknown file types as regular files. */
1031228753Smm		if ((keys & F_TYPE) != 0)
1032228753Smm			archive_strcat(str, " type=file");
1033228753Smm		if ((keys & F_SIZE) != 0)
1034228753Smm			archive_string_sprintf(str, " size=%jd",
1035232153Smm			    (intmax_t)me->size);
1036228753Smm		break;
1037228753Smm	}
1038228753Smm
1039232153Smm	/* Write a bunch of sum. */
1040248616Smm	if (me->reg_info)
1041248616Smm		sum_write(str, me->reg_info);
1042228753Smm
1043248616Smm	archive_strappend_char(str, '\n');
1044248616Smm	if (mtree->indent || mtree->classic)
1045232153Smm		mtree_indent(mtree);
1046228753Smm
1047232153Smm	if (mtree->buf.length > 32768) {
1048248616Smm		ret = __archive_write_output(
1049248616Smm			a, mtree->buf.s, mtree->buf.length);
1050232153Smm		archive_string_empty(&mtree->buf);
1051232153Smm	} else
1052232153Smm		ret = ARCHIVE_OK;
1053232153Smm	return (ret);
1054232153Smm}
1055228753Smm
1056248616Smmstatic int
1057248616Smmwrite_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)
1058248616Smm{
1059248616Smm	struct mtree_writer *mtree = a->format_data;
1060248616Smm	int ret;
1061248616Smm
1062248616Smm	if (n->parentdir.s) {
1063248616Smm		if (mtree->indent) {
1064248616Smm			int i, pd = mtree->depth * 4;
1065248616Smm			for (i = 0; i < pd; i++)
1066248616Smm				archive_strappend_char(&mtree->buf, ' ');
1067248616Smm		}
1068248616Smm		archive_string_sprintf(&mtree->buf, "# %s/%s\n",
1069248616Smm			n->parentdir.s, n->basename.s);
1070248616Smm	}
1071248616Smm
1072248616Smm	if (mtree->indent) {
1073248616Smm		archive_string_empty(&mtree->ebuf);
1074248616Smm		archive_strncat(&mtree->ebuf, "..\n\n", (mtree->dironly)?3:4);
1075248616Smm		mtree_indent(mtree);
1076248616Smm	} else
1077248616Smm		archive_strncat(&mtree->buf, "..\n\n", (mtree->dironly)?3:4);
1078248616Smm
1079248616Smm	if (mtree->buf.length > 32768) {
1080248616Smm		ret = __archive_write_output(
1081248616Smm			a, mtree->buf.s, mtree->buf.length);
1082248616Smm		archive_string_empty(&mtree->buf);
1083248616Smm	} else
1084248616Smm		ret = ARCHIVE_OK;
1085248616Smm	return (ret);
1086248616Smm}
1087248616Smm
1088232153Smm/*
1089248616Smm * Write mtree entries saved at attr_counter_set_collect() function.
1090232153Smm */
1091232153Smmstatic int
1092248616Smmwrite_mtree_entry_tree(struct archive_write *a)
1093232153Smm{
1094232153Smm	struct mtree_writer *mtree = a->format_data;
1095248616Smm	struct mtree_entry *np = mtree->root;
1096248616Smm	struct archive_rb_node *n;
1097232153Smm	int ret;
1098228753Smm
1099248616Smm	do {
1100248616Smm		if (mtree->output_global_set) {
1101248616Smm			/*
1102302001Smm			 * Collect attribute information to know which value
1103248616Smm			 * is frequently used among the children.
1104248616Smm			 */
1105248616Smm			attr_counter_set_reset(mtree);
1106248616Smm			ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
1107248616Smm				struct mtree_entry *e = (struct mtree_entry *)n;
1108248616Smm				if (attr_counter_set_collect(mtree, e) < 0) {
1109248616Smm					archive_set_error(&a->archive, ENOMEM,
1110248616Smm					    "Can't allocate memory");
1111248616Smm					return (ARCHIVE_FATAL);
1112248616Smm				}
1113248616Smm			}
1114248616Smm		}
1115248616Smm		if (!np->dir_info->virtual || mtree->classic) {
1116248616Smm			ret = write_mtree_entry(a, np);
1117248616Smm			if (ret != ARCHIVE_OK)
1118248616Smm				return (ARCHIVE_FATAL);
1119248616Smm		} else {
1120248616Smm			/* Whenever output_global_set is enabled
1121248616Smm			 * output global value(/set keywords)
1122302001Smm			 * even if the directory entry is not allowed
1123248616Smm			 * to be written because the global values
1124248616Smm			 * can be used for the children. */
1125248616Smm			if (mtree->output_global_set)
1126248616Smm				write_global(mtree);
1127248616Smm		}
1128248616Smm		/*
1129248616Smm		 * Output the attribute of all files except directory files.
1130248616Smm		 */
1131248616Smm		mtree->depth++;
1132248616Smm		ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
1133248616Smm			struct mtree_entry *e = (struct mtree_entry *)n;
1134228753Smm
1135248616Smm			if (e->dir_info)
1136248616Smm				mtree_entry_add_child_tail(np, e);
1137248616Smm			else {
1138248616Smm				ret = write_mtree_entry(a, e);
1139248616Smm				if (ret != ARCHIVE_OK)
1140248616Smm					return (ARCHIVE_FATAL);
1141248616Smm			}
1142248616Smm		}
1143248616Smm		mtree->depth--;
1144248616Smm
1145248616Smm		if (np->dir_info->children.first != NULL) {
1146248616Smm			/*
1147248616Smm			 * Descend the tree.
1148248616Smm			 */
1149248616Smm			np = np->dir_info->children.first;
1150248616Smm			if (mtree->indent)
1151248616Smm				mtree->depth++;
1152248616Smm			continue;
1153248616Smm		} else if (mtree->classic) {
1154248616Smm			/*
1155248616Smm			 * While printing mtree classic, if there are not
1156248616Smm			 * any directory files(except "." and "..") in the
1157248616Smm			 * directory, output two dots ".." as returning
1158248616Smm			 * the parent directory.
1159248616Smm			 */
1160248616Smm			ret = write_dot_dot_entry(a, np);
1161248616Smm			if (ret != ARCHIVE_OK)
1162248616Smm				return (ARCHIVE_FATAL);
1163248616Smm		}
1164248616Smm
1165248616Smm		while (np != np->parent) {
1166248616Smm			if (np->dir_info->chnext == NULL) {
1167248616Smm				/*
1168248616Smm				 * Ascend the tree; go back to the parent.
1169248616Smm				 */
1170248616Smm				if (mtree->indent)
1171248616Smm					mtree->depth--;
1172248616Smm				if (mtree->classic) {
1173248616Smm					ret = write_dot_dot_entry(a,
1174248616Smm						np->parent);
1175248616Smm					if (ret != ARCHIVE_OK)
1176248616Smm						return (ARCHIVE_FATAL);
1177248616Smm				}
1178248616Smm				np = np->parent;
1179248616Smm			} else {
1180248616Smm				/*
1181248616Smm				 * Switch to next mtree entry in the directory.
1182248616Smm				 */
1183248616Smm				np = np->dir_info->chnext;
1184248616Smm				break;
1185248616Smm			}
1186248616Smm		}
1187248616Smm	} while (np != np->parent);
1188248616Smm
1189232153Smm	return (ARCHIVE_OK);
1190232153Smm}
1191228753Smm
1192232153Smmstatic int
1193232153Smmarchive_write_mtree_finish_entry(struct archive_write *a)
1194232153Smm{
1195232153Smm	struct mtree_writer *mtree = a->format_data;
1196232153Smm	struct mtree_entry *me;
1197228753Smm
1198232153Smm	if ((me = mtree->mtree_entry) == NULL)
1199232153Smm		return (ARCHIVE_OK);
1200232153Smm	mtree->mtree_entry = NULL;
1201228753Smm
1202248616Smm	if (me->reg_info)
1203248616Smm		sum_final(mtree, me->reg_info);
1204228753Smm
1205248616Smm	return (ARCHIVE_OK);
1206228753Smm}
1207228753Smm
1208228753Smmstatic int
1209232153Smmarchive_write_mtree_close(struct archive_write *a)
1210228753Smm{
1211228753Smm	struct mtree_writer *mtree= a->format_data;
1212232153Smm	int ret;
1213228753Smm
1214248616Smm	if (mtree->root != NULL) {
1215248616Smm		ret = write_mtree_entry_tree(a);
1216232153Smm		if (ret != ARCHIVE_OK)
1217232153Smm			return (ARCHIVE_FATAL);
1218232153Smm	}
1219232153Smm
1220228753Smm	archive_write_set_bytes_in_last_block(&a->archive, 1);
1221228753Smm
1222232153Smm	return __archive_write_output(a, mtree->buf.s, mtree->buf.length);
1223228753Smm}
1224228753Smm
1225228753Smmstatic ssize_t
1226228753Smmarchive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
1227228753Smm{
1228228753Smm	struct mtree_writer *mtree= a->format_data;
1229228753Smm
1230228753Smm	if (n > mtree->entry_bytes_remaining)
1231238856Smm		n = (size_t)mtree->entry_bytes_remaining;
1232232153Smm	mtree->entry_bytes_remaining -= n;
1233232153Smm
1234232153Smm	/* We don't need to compute a regular file sum */
1235232153Smm	if (mtree->mtree_entry == NULL)
1236228753Smm		return (n);
1237228753Smm
1238232153Smm	if (mtree->mtree_entry->filetype == AE_IFREG)
1239232153Smm		sum_update(mtree, buff, n);
1240232153Smm
1241228753Smm	return (n);
1242228753Smm}
1243228753Smm
1244228753Smmstatic int
1245232153Smmarchive_write_mtree_free(struct archive_write *a)
1246228753Smm{
1247228753Smm	struct mtree_writer *mtree= a->format_data;
1248228753Smm
1249228753Smm	if (mtree == NULL)
1250228753Smm		return (ARCHIVE_OK);
1251228753Smm
1252353377Smm	/* Make sure we do not leave any entries. */
1253248616Smm	mtree_entry_register_free(mtree);
1254248616Smm	archive_string_free(&mtree->cur_dirstr);
1255228753Smm	archive_string_free(&mtree->ebuf);
1256228753Smm	archive_string_free(&mtree->buf);
1257248616Smm	attr_counter_set_free(mtree);
1258228753Smm	free(mtree);
1259228753Smm	a->format_data = NULL;
1260228753Smm	return (ARCHIVE_OK);
1261228753Smm}
1262228753Smm
1263228753Smmstatic int
1264228753Smmarchive_write_mtree_options(struct archive_write *a, const char *key,
1265228753Smm    const char *value)
1266228753Smm{
1267228753Smm	struct mtree_writer *mtree= a->format_data;
1268228753Smm	int keybit = 0;
1269228753Smm
1270228753Smm	switch (key[0]) {
1271228753Smm	case 'a':
1272228753Smm		if (strcmp(key, "all") == 0)
1273228753Smm			keybit = ~0;
1274228753Smm		break;
1275228753Smm	case 'c':
1276228753Smm		if (strcmp(key, "cksum") == 0)
1277228753Smm			keybit = F_CKSUM;
1278228753Smm		break;
1279228753Smm	case 'd':
1280228753Smm		if (strcmp(key, "device") == 0)
1281228753Smm			keybit = F_DEV;
1282228753Smm		else if (strcmp(key, "dironly") == 0) {
1283228753Smm			mtree->dironly = (value != NULL)? 1: 0;
1284228753Smm			return (ARCHIVE_OK);
1285228753Smm		}
1286228753Smm		break;
1287228753Smm	case 'f':
1288228753Smm		if (strcmp(key, "flags") == 0)
1289228753Smm			keybit = F_FLAGS;
1290228753Smm		break;
1291228753Smm	case 'g':
1292228753Smm		if (strcmp(key, "gid") == 0)
1293228753Smm			keybit = F_GID;
1294228753Smm		else if (strcmp(key, "gname") == 0)
1295228753Smm			keybit = F_GNAME;
1296228753Smm		break;
1297228753Smm	case 'i':
1298228753Smm		if (strcmp(key, "indent") == 0) {
1299228753Smm			mtree->indent = (value != NULL)? 1: 0;
1300228753Smm			return (ARCHIVE_OK);
1301302001Smm		} else if (strcmp(key, "inode") == 0) {
1302302001Smm			keybit = F_INO;
1303228753Smm		}
1304228753Smm		break;
1305228753Smm	case 'l':
1306228753Smm		if (strcmp(key, "link") == 0)
1307228753Smm			keybit = F_SLINK;
1308228753Smm		break;
1309228753Smm	case 'm':
1310228753Smm		if (strcmp(key, "md5") == 0 ||
1311228753Smm		    strcmp(key, "md5digest") == 0)
1312228753Smm			keybit = F_MD5;
1313228753Smm		if (strcmp(key, "mode") == 0)
1314228753Smm			keybit = F_MODE;
1315228753Smm		break;
1316228753Smm	case 'n':
1317228753Smm		if (strcmp(key, "nlink") == 0)
1318228753Smm			keybit = F_NLINK;
1319228753Smm		break;
1320228753Smm	case 'r':
1321302001Smm		if (strcmp(key, "resdevice") == 0) {
1322302001Smm			keybit = F_RESDEV;
1323302001Smm		} else if (strcmp(key, "ripemd160digest") == 0 ||
1324228753Smm		    strcmp(key, "rmd160") == 0 ||
1325228753Smm		    strcmp(key, "rmd160digest") == 0)
1326228753Smm			keybit = F_RMD160;
1327228753Smm		break;
1328228753Smm	case 's':
1329228753Smm		if (strcmp(key, "sha1") == 0 ||
1330228753Smm		    strcmp(key, "sha1digest") == 0)
1331228753Smm			keybit = F_SHA1;
1332228753Smm		if (strcmp(key, "sha256") == 0 ||
1333228753Smm		    strcmp(key, "sha256digest") == 0)
1334228753Smm			keybit = F_SHA256;
1335228753Smm		if (strcmp(key, "sha384") == 0 ||
1336228753Smm		    strcmp(key, "sha384digest") == 0)
1337228753Smm			keybit = F_SHA384;
1338228753Smm		if (strcmp(key, "sha512") == 0 ||
1339228753Smm		    strcmp(key, "sha512digest") == 0)
1340228753Smm			keybit = F_SHA512;
1341228753Smm		if (strcmp(key, "size") == 0)
1342228753Smm			keybit = F_SIZE;
1343228753Smm		break;
1344228753Smm	case 't':
1345228753Smm		if (strcmp(key, "time") == 0)
1346228753Smm			keybit = F_TIME;
1347228753Smm		else if (strcmp(key, "type") == 0)
1348228753Smm			keybit = F_TYPE;
1349228753Smm		break;
1350228753Smm	case 'u':
1351228753Smm		if (strcmp(key, "uid") == 0)
1352228753Smm			keybit = F_UID;
1353228753Smm		else if (strcmp(key, "uname") == 0)
1354228753Smm			keybit = F_UNAME;
1355228753Smm		else if (strcmp(key, "use-set") == 0) {
1356248616Smm			mtree->output_global_set = (value != NULL)? 1: 0;
1357228753Smm			return (ARCHIVE_OK);
1358228753Smm		}
1359228753Smm		break;
1360228753Smm	}
1361228753Smm	if (keybit != 0) {
1362228753Smm		if (value != NULL)
1363228753Smm			mtree->keys |= keybit;
1364228753Smm		else
1365228753Smm			mtree->keys &= ~keybit;
1366228753Smm		return (ARCHIVE_OK);
1367228753Smm	}
1368228753Smm
1369232153Smm	/* Note: The "warn" return is just to inform the options
1370232153Smm	 * supervisor that we didn't handle it.  It will generate
1371232153Smm	 * a suitable error if no one used this option. */
1372228753Smm	return (ARCHIVE_WARN);
1373228753Smm}
1374228753Smm
1375248616Smmstatic int
1376248616Smmarchive_write_set_format_mtree_default(struct archive *_a, const char *fn)
1377228753Smm{
1378228753Smm	struct archive_write *a = (struct archive_write *)_a;
1379228753Smm	struct mtree_writer *mtree;
1380228753Smm
1381248616Smm	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn);
1382228753Smm
1383232153Smm	if (a->format_free != NULL)
1384232153Smm		(a->format_free)(a);
1385232153Smm
1386232153Smm	if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {
1387228753Smm		archive_set_error(&a->archive, ENOMEM,
1388228753Smm		    "Can't allocate mtree data");
1389228753Smm		return (ARCHIVE_FATAL);
1390228753Smm	}
1391228753Smm
1392232153Smm	mtree->mtree_entry = NULL;
1393228753Smm	mtree->first = 1;
1394228753Smm	memset(&(mtree->set), 0, sizeof(mtree->set));
1395228753Smm	mtree->keys = DEFAULT_KEYS;
1396228753Smm	mtree->dironly = 0;
1397228753Smm	mtree->indent = 0;
1398228753Smm	archive_string_init(&mtree->ebuf);
1399228753Smm	archive_string_init(&mtree->buf);
1400248616Smm	mtree_entry_register_init(mtree);
1401228753Smm	a->format_data = mtree;
1402232153Smm	a->format_free = archive_write_mtree_free;
1403228753Smm	a->format_name = "mtree";
1404228753Smm	a->format_options = archive_write_mtree_options;
1405228753Smm	a->format_write_header = archive_write_mtree_header;
1406232153Smm	a->format_close = archive_write_mtree_close;
1407228753Smm	a->format_write_data = archive_write_mtree_data;
1408228753Smm	a->format_finish_entry = archive_write_mtree_finish_entry;
1409228753Smm	a->archive.archive_format = ARCHIVE_FORMAT_MTREE;
1410228753Smm	a->archive.archive_format_name = "mtree";
1411228753Smm
1412228753Smm	return (ARCHIVE_OK);
1413228753Smm}
1414232153Smm
1415248616Smmint
1416248616Smmarchive_write_set_format_mtree(struct archive *_a)
1417248616Smm{
1418248616Smm	return archive_write_set_format_mtree_default(_a,
1419248616Smm		"archive_write_set_format_mtree");
1420248616Smm}
1421248616Smm
1422248616Smmint
1423248616Smmarchive_write_set_format_mtree_classic(struct archive *_a)
1424248616Smm{
1425248616Smm	int r;
1426248616Smm
1427248616Smm	r = archive_write_set_format_mtree_default(_a,
1428248616Smm		"archive_write_set_format_mtree_classic");
1429248616Smm	if (r == ARCHIVE_OK) {
1430248616Smm		struct archive_write *a = (struct archive_write *)_a;
1431248616Smm		struct mtree_writer *mtree;
1432248616Smm
1433248616Smm		mtree = (struct mtree_writer *)a->format_data;
1434248616Smm
1435248616Smm		/* Set to output a mtree archive in classic format. */
1436248616Smm		mtree->classic = 1;
1437248616Smm		/* Basically, mtree classic format uses '/set' global
1438248616Smm		 * value. */
1439248616Smm		mtree->output_global_set = 1;
1440248616Smm	}
1441248616Smm	return (r);
1442248616Smm}
1443248616Smm
1444232153Smmstatic void
1445232153Smmsum_init(struct mtree_writer *mtree)
1446232153Smm{
1447248616Smm
1448248616Smm	mtree->compute_sum = 0;
1449248616Smm
1450232153Smm	if (mtree->keys & F_CKSUM) {
1451232153Smm		mtree->compute_sum |= F_CKSUM;
1452232153Smm		mtree->crc = 0;
1453232153Smm		mtree->crc_len = 0;
1454232153Smm	}
1455232153Smm#ifdef ARCHIVE_HAS_MD5
1456232153Smm	if (mtree->keys & F_MD5) {
1457232153Smm		if (archive_md5_init(&mtree->md5ctx) == ARCHIVE_OK)
1458232153Smm			mtree->compute_sum |= F_MD5;
1459232153Smm		else
1460232153Smm			mtree->keys &= ~F_MD5;/* Not supported. */
1461232153Smm	}
1462232153Smm#endif
1463232153Smm#ifdef ARCHIVE_HAS_RMD160
1464232153Smm	if (mtree->keys & F_RMD160) {
1465232153Smm		if (archive_rmd160_init(&mtree->rmd160ctx) == ARCHIVE_OK)
1466232153Smm			mtree->compute_sum |= F_RMD160;
1467232153Smm		else
1468232153Smm			mtree->keys &= ~F_RMD160;/* Not supported. */
1469232153Smm	}
1470232153Smm#endif
1471232153Smm#ifdef ARCHIVE_HAS_SHA1
1472232153Smm	if (mtree->keys & F_SHA1) {
1473232153Smm		if (archive_sha1_init(&mtree->sha1ctx) == ARCHIVE_OK)
1474232153Smm			mtree->compute_sum |= F_SHA1;
1475232153Smm		else
1476232153Smm			mtree->keys &= ~F_SHA1;/* Not supported. */
1477232153Smm	}
1478232153Smm#endif
1479232153Smm#ifdef ARCHIVE_HAS_SHA256
1480232153Smm	if (mtree->keys & F_SHA256) {
1481232153Smm		if (archive_sha256_init(&mtree->sha256ctx) == ARCHIVE_OK)
1482232153Smm			mtree->compute_sum |= F_SHA256;
1483232153Smm		else
1484232153Smm			mtree->keys &= ~F_SHA256;/* Not supported. */
1485232153Smm	}
1486232153Smm#endif
1487232153Smm#ifdef ARCHIVE_HAS_SHA384
1488232153Smm	if (mtree->keys & F_SHA384) {
1489232153Smm		if (archive_sha384_init(&mtree->sha384ctx) == ARCHIVE_OK)
1490232153Smm			mtree->compute_sum |= F_SHA384;
1491232153Smm		else
1492232153Smm			mtree->keys &= ~F_SHA384;/* Not supported. */
1493232153Smm	}
1494232153Smm#endif
1495232153Smm#ifdef ARCHIVE_HAS_SHA512
1496232153Smm	if (mtree->keys & F_SHA512) {
1497232153Smm		if (archive_sha512_init(&mtree->sha512ctx) == ARCHIVE_OK)
1498232153Smm			mtree->compute_sum |= F_SHA512;
1499232153Smm		else
1500232153Smm			mtree->keys &= ~F_SHA512;/* Not supported. */
1501232153Smm	}
1502232153Smm#endif
1503232153Smm}
1504232153Smm
1505232153Smmstatic void
1506232153Smmsum_update(struct mtree_writer *mtree, const void *buff, size_t n)
1507232153Smm{
1508232153Smm	if (mtree->compute_sum & F_CKSUM) {
1509232153Smm		/*
1510232153Smm		 * Compute a POSIX 1003.2 checksum
1511232153Smm		 */
1512232153Smm		const unsigned char *p;
1513232153Smm		size_t nn;
1514232153Smm
1515232153Smm		for (nn = n, p = buff; nn--; ++p)
1516232153Smm			COMPUTE_CRC(mtree->crc, *p);
1517232153Smm		mtree->crc_len += n;
1518232153Smm	}
1519232153Smm#ifdef ARCHIVE_HAS_MD5
1520232153Smm	if (mtree->compute_sum & F_MD5)
1521232153Smm		archive_md5_update(&mtree->md5ctx, buff, n);
1522232153Smm#endif
1523232153Smm#ifdef ARCHIVE_HAS_RMD160
1524232153Smm	if (mtree->compute_sum & F_RMD160)
1525232153Smm		archive_rmd160_update(&mtree->rmd160ctx, buff, n);
1526232153Smm#endif
1527232153Smm#ifdef ARCHIVE_HAS_SHA1
1528232153Smm	if (mtree->compute_sum & F_SHA1)
1529232153Smm		archive_sha1_update(&mtree->sha1ctx, buff, n);
1530232153Smm#endif
1531232153Smm#ifdef ARCHIVE_HAS_SHA256
1532232153Smm	if (mtree->compute_sum & F_SHA256)
1533232153Smm		archive_sha256_update(&mtree->sha256ctx, buff, n);
1534232153Smm#endif
1535232153Smm#ifdef ARCHIVE_HAS_SHA384
1536232153Smm	if (mtree->compute_sum & F_SHA384)
1537232153Smm		archive_sha384_update(&mtree->sha384ctx, buff, n);
1538232153Smm#endif
1539232153Smm#ifdef ARCHIVE_HAS_SHA512
1540232153Smm	if (mtree->compute_sum & F_SHA512)
1541232153Smm		archive_sha512_update(&mtree->sha512ctx, buff, n);
1542232153Smm#endif
1543232153Smm}
1544232153Smm
1545232153Smmstatic void
1546248616Smmsum_final(struct mtree_writer *mtree, struct reg_info *reg)
1547232153Smm{
1548232153Smm
1549232153Smm	if (mtree->compute_sum & F_CKSUM) {
1550232153Smm		uint64_t len;
1551232153Smm		/* Include the length of the file. */
1552232153Smm		for (len = mtree->crc_len; len != 0; len >>= 8)
1553232153Smm			COMPUTE_CRC(mtree->crc, len & 0xff);
1554248616Smm		reg->crc = ~mtree->crc;
1555232153Smm	}
1556232153Smm#ifdef ARCHIVE_HAS_MD5
1557232153Smm	if (mtree->compute_sum & F_MD5)
1558368708Smm		archive_md5_final(&mtree->md5ctx, reg->digest.md5);
1559232153Smm#endif
1560232153Smm#ifdef ARCHIVE_HAS_RMD160
1561232153Smm	if (mtree->compute_sum & F_RMD160)
1562368708Smm		archive_rmd160_final(&mtree->rmd160ctx, reg->digest.rmd160);
1563232153Smm#endif
1564232153Smm#ifdef ARCHIVE_HAS_SHA1
1565232153Smm	if (mtree->compute_sum & F_SHA1)
1566368708Smm		archive_sha1_final(&mtree->sha1ctx, reg->digest.sha1);
1567232153Smm#endif
1568232153Smm#ifdef ARCHIVE_HAS_SHA256
1569232153Smm	if (mtree->compute_sum & F_SHA256)
1570368708Smm		archive_sha256_final(&mtree->sha256ctx, reg->digest.sha256);
1571232153Smm#endif
1572232153Smm#ifdef ARCHIVE_HAS_SHA384
1573232153Smm	if (mtree->compute_sum & F_SHA384)
1574368708Smm		archive_sha384_final(&mtree->sha384ctx, reg->digest.sha384);
1575232153Smm#endif
1576232153Smm#ifdef ARCHIVE_HAS_SHA512
1577232153Smm	if (mtree->compute_sum & F_SHA512)
1578368708Smm		archive_sha512_final(&mtree->sha512ctx, reg->digest.sha512);
1579232153Smm#endif
1580232153Smm	/* Save what types of sum are computed. */
1581248616Smm	reg->compute_sum = mtree->compute_sum;
1582232153Smm}
1583232153Smm
1584232153Smm#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \
1585232153Smm    defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \
1586232153Smm    defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)
1587232153Smmstatic void
1588232153Smmstrappend_bin(struct archive_string *s, const unsigned char *bin, int n)
1589232153Smm{
1590232153Smm	static const char hex[] = "0123456789abcdef";
1591232153Smm	int i;
1592232153Smm
1593232153Smm	for (i = 0; i < n; i++) {
1594232153Smm		archive_strappend_char(s, hex[bin[i] >> 4]);
1595232153Smm		archive_strappend_char(s, hex[bin[i] & 0x0f]);
1596232153Smm	}
1597232153Smm}
1598232153Smm#endif
1599232153Smm
1600232153Smmstatic void
1601248616Smmsum_write(struct archive_string *str, struct reg_info *reg)
1602232153Smm{
1603232153Smm
1604248616Smm	if (reg->compute_sum & F_CKSUM) {
1605232153Smm		archive_string_sprintf(str, " cksum=%ju",
1606248616Smm		    (uintmax_t)reg->crc);
1607232153Smm	}
1608368708Smm
1609368708Smm#define append_digest(_s, _r, _t) \
1610368708Smm	strappend_bin(_s, _r->digest._t, sizeof(_r->digest._t))
1611368708Smm
1612232153Smm#ifdef ARCHIVE_HAS_MD5
1613248616Smm	if (reg->compute_sum & F_MD5) {
1614232153Smm		archive_strcat(str, " md5digest=");
1615368708Smm		append_digest(str, reg, md5);
1616232153Smm	}
1617232153Smm#endif
1618232153Smm#ifdef ARCHIVE_HAS_RMD160
1619248616Smm	if (reg->compute_sum & F_RMD160) {
1620232153Smm		archive_strcat(str, " rmd160digest=");
1621368708Smm		append_digest(str, reg, rmd160);
1622232153Smm	}
1623232153Smm#endif
1624232153Smm#ifdef ARCHIVE_HAS_SHA1
1625248616Smm	if (reg->compute_sum & F_SHA1) {
1626232153Smm		archive_strcat(str, " sha1digest=");
1627368708Smm		append_digest(str, reg, sha1);
1628232153Smm	}
1629232153Smm#endif
1630232153Smm#ifdef ARCHIVE_HAS_SHA256
1631248616Smm	if (reg->compute_sum & F_SHA256) {
1632232153Smm		archive_strcat(str, " sha256digest=");
1633368708Smm		append_digest(str, reg, sha256);
1634232153Smm	}
1635232153Smm#endif
1636232153Smm#ifdef ARCHIVE_HAS_SHA384
1637248616Smm	if (reg->compute_sum & F_SHA384) {
1638232153Smm		archive_strcat(str, " sha384digest=");
1639368708Smm		append_digest(str, reg, sha384);
1640232153Smm	}
1641232153Smm#endif
1642232153Smm#ifdef ARCHIVE_HAS_SHA512
1643248616Smm	if (reg->compute_sum & F_SHA512) {
1644232153Smm		archive_strcat(str, " sha512digest=");
1645368708Smm		append_digest(str, reg, sha512);
1646232153Smm	}
1647232153Smm#endif
1648368708Smm#undef append_digest
1649232153Smm}
1650248616Smm
1651248616Smmstatic int
1652248616Smmmtree_entry_cmp_node(const struct archive_rb_node *n1,
1653248616Smm    const struct archive_rb_node *n2)
1654248616Smm{
1655248616Smm	const struct mtree_entry *e1 = (const struct mtree_entry *)n1;
1656248616Smm	const struct mtree_entry *e2 = (const struct mtree_entry *)n2;
1657248616Smm
1658248616Smm	return (strcmp(e2->basename.s, e1->basename.s));
1659248616Smm}
1660248616Smm
1661248616Smmstatic int
1662248616Smmmtree_entry_cmp_key(const struct archive_rb_node *n, const void *key)
1663248616Smm{
1664248616Smm	const struct mtree_entry *e = (const struct mtree_entry *)n;
1665248616Smm
1666248616Smm	return (strcmp((const char *)key, e->basename.s));
1667248616Smm}
1668248616Smm
1669248616Smm#if defined(_WIN32) || defined(__CYGWIN__)
1670248616Smmstatic int
1671248616Smmcleanup_backslash_1(char *p)
1672248616Smm{
1673248616Smm	int mb, dos;
1674248616Smm
1675248616Smm	mb = dos = 0;
1676248616Smm	while (*p) {
1677248616Smm		if (*(unsigned char *)p > 127)
1678248616Smm			mb = 1;
1679248616Smm		if (*p == '\\') {
1680248616Smm			/* If we have not met any multi-byte characters,
1681248616Smm			 * we can replace '\' with '/'. */
1682248616Smm			if (!mb)
1683248616Smm				*p = '/';
1684248616Smm			dos = 1;
1685248616Smm		}
1686248616Smm		p++;
1687248616Smm	}
1688248616Smm	if (!mb || !dos)
1689248616Smm		return (0);
1690248616Smm	return (-1);
1691248616Smm}
1692248616Smm
1693248616Smmstatic void
1694248616Smmcleanup_backslash_2(wchar_t *p)
1695248616Smm{
1696248616Smm
1697248616Smm	/* Convert a path-separator from '\' to  '/' */
1698248616Smm	while (*p != L'\0') {
1699248616Smm		if (*p == L'\\')
1700248616Smm			*p = L'/';
1701248616Smm		p++;
1702248616Smm	}
1703248616Smm}
1704248616Smm#endif
1705248616Smm
1706248616Smm/*
1707248616Smm * Generate a parent directory name and a base name from a pathname.
1708248616Smm */
1709248616Smmstatic int
1710248616Smmmtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file,
1711248616Smm    struct archive_entry *entry)
1712248616Smm{
1713248616Smm	const char *pathname;
1714248616Smm	char *p, *dirname, *slash;
1715248616Smm	size_t len;
1716248616Smm	int ret = ARCHIVE_OK;
1717248616Smm
1718248616Smm	archive_strcpy(&file->pathname, archive_entry_pathname(entry));
1719248616Smm#if defined(_WIN32) || defined(__CYGWIN__)
1720248616Smm	/*
1721248616Smm	 * Convert a path-separator from '\' to  '/'
1722248616Smm	 */
1723248616Smm	if (cleanup_backslash_1(file->pathname.s) != 0) {
1724248616Smm		const wchar_t *wp = archive_entry_pathname_w(entry);
1725248616Smm		struct archive_wstring ws;
1726248616Smm
1727248616Smm		if (wp != NULL) {
1728248616Smm			int r;
1729248616Smm			archive_string_init(&ws);
1730248616Smm			archive_wstrcpy(&ws, wp);
1731248616Smm			cleanup_backslash_2(ws.s);
1732248616Smm			archive_string_empty(&(file->pathname));
1733248616Smm			r = archive_string_append_from_wcs(&(file->pathname),
1734248616Smm			    ws.s, ws.length);
1735248616Smm			archive_wstring_free(&ws);
1736248616Smm			if (r < 0 && errno == ENOMEM) {
1737248616Smm				archive_set_error(&a->archive, ENOMEM,
1738248616Smm				    "Can't allocate memory");
1739248616Smm				return (ARCHIVE_FATAL);
1740248616Smm			}
1741248616Smm		}
1742248616Smm	}
1743248616Smm#else
1744248616Smm	(void)a; /* UNUSED */
1745248616Smm#endif
1746248616Smm	pathname =  file->pathname.s;
1747248616Smm	if (strcmp(pathname, ".") == 0) {
1748248616Smm		archive_strcpy(&file->basename, ".");
1749248616Smm		return (ARCHIVE_OK);
1750248616Smm	}
1751248616Smm
1752248616Smm	archive_strcpy(&(file->parentdir), pathname);
1753248616Smm
1754248616Smm	len = file->parentdir.length;
1755248616Smm	p = dirname = file->parentdir.s;
1756248616Smm
1757248616Smm	/*
1758248616Smm	 * Remove leading '/' and '../' elements
1759248616Smm	 */
1760248616Smm	while (*p) {
1761248616Smm		if (p[0] == '/') {
1762248616Smm			p++;
1763248616Smm			len--;
1764248616Smm		} else if (p[0] != '.')
1765248616Smm			break;
1766248616Smm		else if (p[1] == '.' && p[2] == '/') {
1767248616Smm			p += 3;
1768248616Smm			len -= 3;
1769248616Smm		} else
1770248616Smm			break;
1771248616Smm	}
1772248616Smm	if (p != dirname) {
1773248616Smm		memmove(dirname, p, len+1);
1774248616Smm		p = dirname;
1775248616Smm	}
1776248616Smm	/*
1777248616Smm	 * Remove "/","/." and "/.." elements from tail.
1778248616Smm	 */
1779248616Smm	while (len > 0) {
1780248616Smm		size_t ll = len;
1781248616Smm
1782248616Smm		if (len > 0 && p[len-1] == '/') {
1783248616Smm			p[len-1] = '\0';
1784248616Smm			len--;
1785248616Smm		}
1786248616Smm		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
1787248616Smm			p[len-2] = '\0';
1788248616Smm			len -= 2;
1789248616Smm		}
1790248616Smm		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
1791248616Smm		    p[len-1] == '.') {
1792248616Smm			p[len-3] = '\0';
1793248616Smm			len -= 3;
1794248616Smm		}
1795248616Smm		if (ll == len)
1796248616Smm			break;
1797248616Smm	}
1798248616Smm	while (*p) {
1799248616Smm		if (p[0] == '/') {
1800248616Smm			if (p[1] == '/')
1801248616Smm				/* Convert '//' --> '/' */
1802248616Smm				strcpy(p, p+1);
1803248616Smm			else if (p[1] == '.' && p[2] == '/')
1804248616Smm				/* Convert '/./' --> '/' */
1805248616Smm				strcpy(p, p+2);
1806248616Smm			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
1807248616Smm				/* Convert 'dir/dir1/../dir2/'
1808248616Smm				 *     --> 'dir/dir2/'
1809248616Smm				 */
1810248616Smm				char *rp = p -1;
1811248616Smm				while (rp >= dirname) {
1812248616Smm					if (*rp == '/')
1813248616Smm						break;
1814248616Smm					--rp;
1815248616Smm				}
1816248616Smm				if (rp > dirname) {
1817248616Smm					strcpy(rp, p+3);
1818248616Smm					p = rp;
1819248616Smm				} else {
1820248616Smm					strcpy(dirname, p+4);
1821248616Smm					p = dirname;
1822248616Smm				}
1823248616Smm			} else
1824248616Smm				p++;
1825248616Smm		} else
1826248616Smm			p++;
1827248616Smm	}
1828248616Smm	p = dirname;
1829248616Smm	len = strlen(p);
1830248616Smm
1831248616Smm	/*
1832311042Smm	 * Add "./" prefix.
1833248616Smm	 * NOTE: If the pathname does not have a path separator, we have
1834311042Smm	 * to add "./" to the head of the pathname because mtree reader
1835248616Smm	 * will suppose that it is v1(a.k.a classic) mtree format and
1836248616Smm	 * change the directory unexpectedly and so it will make a wrong
1837248616Smm	 * path.
1838248616Smm	 */
1839248616Smm	if (strcmp(p, ".") != 0 && strncmp(p, "./", 2) != 0) {
1840248616Smm		struct archive_string as;
1841248616Smm		archive_string_init(&as);
1842248616Smm		archive_strcpy(&as, "./");
1843248616Smm		archive_strncat(&as, p, len);
1844248616Smm		archive_string_empty(&file->parentdir);
1845248616Smm		archive_string_concat(&file->parentdir, &as);
1846248616Smm		archive_string_free(&as);
1847248616Smm		p = file->parentdir.s;
1848248616Smm		len = archive_strlen(&file->parentdir);
1849248616Smm	}
1850248616Smm
1851248616Smm	/*
1852248616Smm	 * Find out the position which points the last position of
1853248616Smm	 * path separator('/').
1854248616Smm	 */
1855248616Smm	slash = NULL;
1856248616Smm	for (; *p != '\0'; p++) {
1857248616Smm		if (*p == '/')
1858248616Smm			slash = p;
1859248616Smm	}
1860248616Smm	if (slash == NULL) {
1861248616Smm		/* The pathname doesn't have a parent directory. */
1862248616Smm		file->parentdir.length = len;
1863248616Smm		archive_string_copy(&(file->basename), &(file->parentdir));
1864248616Smm		archive_string_empty(&(file->parentdir));
1865248616Smm		*file->parentdir.s = '\0';
1866248616Smm		return (ret);
1867248616Smm	}
1868248616Smm
1869302001Smm	/* Make a basename from file->parentdir.s and slash */
1870248616Smm	*slash  = '\0';
1871302001Smm	file->parentdir.length = slash - file->parentdir.s;
1872248616Smm	archive_strcpy(&(file->basename),  slash + 1);
1873248616Smm	return (ret);
1874248616Smm}
1875248616Smm
1876248616Smmstatic int
1877248616Smmmtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname,
1878248616Smm    struct mtree_entry **m_entry)
1879248616Smm{
1880248616Smm	struct archive_entry *entry;
1881248616Smm	struct mtree_entry *file;
1882248616Smm	int r;
1883248616Smm
1884248616Smm	entry = archive_entry_new();
1885248616Smm	if (entry == NULL) {
1886248616Smm		*m_entry = NULL;
1887248616Smm		archive_set_error(&a->archive, ENOMEM,
1888248616Smm		    "Can't allocate memory");
1889248616Smm		return (ARCHIVE_FATAL);
1890248616Smm	}
1891248616Smm	archive_entry_copy_pathname(entry, pathname);
1892248616Smm	archive_entry_set_mode(entry, AE_IFDIR | 0755);
1893248616Smm	archive_entry_set_mtime(entry, time(NULL), 0);
1894248616Smm
1895248616Smm	r = mtree_entry_new(a, entry, &file);
1896248616Smm	archive_entry_free(entry);
1897248616Smm	if (r < ARCHIVE_WARN) {
1898248616Smm		*m_entry = NULL;
1899248616Smm		archive_set_error(&a->archive, ENOMEM,
1900248616Smm		    "Can't allocate memory");
1901248616Smm		return (ARCHIVE_FATAL);
1902248616Smm	}
1903248616Smm
1904248616Smm	file->dir_info->virtual = 1;
1905248616Smm
1906248616Smm	*m_entry = file;
1907248616Smm	return (ARCHIVE_OK);
1908248616Smm}
1909248616Smm
1910248616Smmstatic void
1911248616Smmmtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)
1912248616Smm{
1913248616Smm        file->next = NULL;
1914248616Smm        *mtree->file_list.last = file;
1915248616Smm        mtree->file_list.last = &(file->next);
1916248616Smm}
1917248616Smm
1918248616Smmstatic void
1919248616Smmmtree_entry_register_init(struct mtree_writer *mtree)
1920248616Smm{
1921248616Smm	mtree->file_list.first = NULL;
1922248616Smm	mtree->file_list.last = &(mtree->file_list.first);
1923248616Smm}
1924248616Smm
1925248616Smmstatic void
1926248616Smmmtree_entry_register_free(struct mtree_writer *mtree)
1927248616Smm{
1928248616Smm	struct mtree_entry *file, *file_next;
1929248616Smm
1930248616Smm	file = mtree->file_list.first;
1931248616Smm	while (file != NULL) {
1932248616Smm		file_next = file->next;
1933248616Smm		mtree_entry_free(file);
1934248616Smm		file = file_next;
1935248616Smm	}
1936248616Smm}
1937248616Smm
1938248616Smmstatic int
1939248616Smmmtree_entry_add_child_tail(struct mtree_entry *parent,
1940248616Smm    struct mtree_entry *child)
1941248616Smm{
1942248616Smm	child->dir_info->chnext = NULL;
1943248616Smm	*parent->dir_info->children.last = child;
1944248616Smm	parent->dir_info->children.last = &(child->dir_info->chnext);
1945248616Smm	return (1);
1946248616Smm}
1947248616Smm
1948248616Smm/*
1949248616Smm * Find a entry from a parent entry with the name.
1950248616Smm */
1951248616Smmstatic struct mtree_entry *
1952248616Smmmtree_entry_find_child(struct mtree_entry *parent, const char *child_name)
1953248616Smm{
1954248616Smm	struct mtree_entry *np;
1955248616Smm
1956248616Smm	if (parent == NULL)
1957248616Smm		return (NULL);
1958248616Smm	np = (struct mtree_entry *)__archive_rb_tree_find_node(
1959248616Smm	    &(parent->dir_info->rbtree), child_name);
1960248616Smm	return (np);
1961248616Smm}
1962248616Smm
1963248616Smmstatic int
1964248616Smmget_path_component(char *name, size_t n, const char *fn)
1965248616Smm{
1966248616Smm	char *p;
1967248616Smm	size_t l;
1968248616Smm
1969248616Smm	p = strchr(fn, '/');
1970248616Smm	if (p == NULL) {
1971248616Smm		if ((l = strlen(fn)) == 0)
1972248616Smm			return (0);
1973248616Smm	} else
1974248616Smm		l = p - fn;
1975248616Smm	if (l > n -1)
1976248616Smm		return (-1);
1977248616Smm	memcpy(name, fn, l);
1978248616Smm	name[l] = '\0';
1979248616Smm
1980248616Smm	return ((int)l);
1981248616Smm}
1982248616Smm
1983248616Smm/*
1984248616Smm * Add a new entry into the tree.
1985248616Smm */
1986248616Smmstatic int
1987248616Smmmtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
1988248616Smm{
1989248616Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1990248616Smm	char name[_MAX_FNAME];/* Included null terminator size. */
1991248616Smm#elif defined(NAME_MAX) && NAME_MAX >= 255
1992248616Smm	char name[NAME_MAX+1];
1993248616Smm#else
1994248616Smm	char name[256];
1995248616Smm#endif
1996248616Smm	struct mtree_writer *mtree = (struct mtree_writer *)a->format_data;
1997248616Smm	struct mtree_entry *dent, *file, *np;
1998248616Smm	const char *fn, *p;
1999248616Smm	int l, r;
2000248616Smm
2001248616Smm	file = *filep;
2002248616Smm	if (file->parentdir.length == 0 && file->basename.length == 1 &&
2003248616Smm	    file->basename.s[0] == '.') {
2004248616Smm		file->parent = file;
2005248616Smm		if (mtree->root != NULL) {
2006248616Smm			np = mtree->root;
2007248616Smm			goto same_entry;
2008248616Smm		}
2009248616Smm		mtree->root = file;
2010248616Smm		mtree_entry_register_add(mtree, file);
2011248616Smm		return (ARCHIVE_OK);
2012248616Smm	}
2013248616Smm
2014248616Smm	if (file->parentdir.length == 0) {
2015248616Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
2016353377Smm		    "Internal programming error "
2017248616Smm		    "in generating canonical name for %s",
2018248616Smm		    file->pathname.s);
2019248616Smm		return (ARCHIVE_FAILED);
2020248616Smm	}
2021248616Smm
2022248616Smm	fn = p = file->parentdir.s;
2023248616Smm
2024248616Smm	/*
2025248616Smm	 * If the path of the parent directory of `file' entry is
2026248616Smm	 * the same as the path of `cur_dirent', add `file' entry to
2027248616Smm	 * `cur_dirent'.
2028248616Smm	 */
2029248616Smm	if (archive_strlen(&(mtree->cur_dirstr))
2030248616Smm	      == archive_strlen(&(file->parentdir)) &&
2031248616Smm	    strcmp(mtree->cur_dirstr.s, fn) == 0) {
2032248616Smm		if (!__archive_rb_tree_insert_node(
2033248616Smm		    &(mtree->cur_dirent->dir_info->rbtree),
2034248616Smm		    (struct archive_rb_node *)file)) {
2035248616Smm			/* There is the same name in the tree. */
2036248616Smm			np = (struct mtree_entry *)__archive_rb_tree_find_node(
2037248616Smm			    &(mtree->cur_dirent->dir_info->rbtree),
2038248616Smm			    file->basename.s);
2039248616Smm			goto same_entry;
2040248616Smm		}
2041248616Smm		file->parent = mtree->cur_dirent;
2042248616Smm		mtree_entry_register_add(mtree, file);
2043248616Smm		return (ARCHIVE_OK);
2044248616Smm	}
2045248616Smm
2046248616Smm	dent = mtree->root;
2047248616Smm	for (;;) {
2048248616Smm		l = get_path_component(name, sizeof(name), fn);
2049248616Smm		if (l == 0) {
2050248616Smm			np = NULL;
2051248616Smm			break;
2052248616Smm		}
2053248616Smm		if (l < 0) {
2054248616Smm			archive_set_error(&a->archive,
2055248616Smm			    ARCHIVE_ERRNO_MISC,
2056248616Smm			    "A name buffer is too small");
2057248616Smm			return (ARCHIVE_FATAL);
2058248616Smm		}
2059248616Smm		if (l == 1 && name[0] == '.' && dent != NULL &&
2060248616Smm		    dent == mtree->root) {
2061248616Smm			fn += l;
2062248616Smm			if (fn[0] == '/')
2063248616Smm				fn++;
2064248616Smm			continue;
2065248616Smm		}
2066248616Smm
2067248616Smm		np = mtree_entry_find_child(dent, name);
2068248616Smm		if (np == NULL || fn[0] == '\0')
2069248616Smm			break;
2070248616Smm
2071248616Smm		/* Find next sub directory. */
2072248616Smm		if (!np->dir_info) {
2073248616Smm			/* NOT Directory! */
2074248616Smm			archive_set_error(&a->archive,
2075248616Smm			    ARCHIVE_ERRNO_MISC,
2076248616Smm			    "`%s' is not directory, we cannot insert `%s' ",
2077248616Smm			    np->pathname.s, file->pathname.s);
2078248616Smm			return (ARCHIVE_FAILED);
2079248616Smm		}
2080248616Smm		fn += l;
2081248616Smm		if (fn[0] == '/')
2082248616Smm			fn++;
2083248616Smm		dent = np;
2084248616Smm	}
2085248616Smm	if (np == NULL) {
2086248616Smm		/*
2087248616Smm		 * Create virtual parent directories.
2088248616Smm		 */
2089248616Smm		while (fn[0] != '\0') {
2090248616Smm			struct mtree_entry *vp;
2091248616Smm			struct archive_string as;
2092248616Smm
2093248616Smm			archive_string_init(&as);
2094248616Smm			archive_strncat(&as, p, fn - p + l);
2095248616Smm			if (as.s[as.length-1] == '/') {
2096248616Smm				as.s[as.length-1] = '\0';
2097248616Smm				as.length--;
2098248616Smm			}
2099248616Smm			r = mtree_entry_create_virtual_dir(a, as.s, &vp);
2100248616Smm			archive_string_free(&as);
2101248616Smm			if (r < ARCHIVE_WARN)
2102248616Smm				return (r);
2103248616Smm
2104248616Smm			if (strcmp(vp->pathname.s, ".") == 0) {
2105248616Smm				vp->parent = vp;
2106248616Smm				mtree->root = vp;
2107248616Smm			} else {
2108248616Smm				__archive_rb_tree_insert_node(
2109248616Smm				    &(dent->dir_info->rbtree),
2110248616Smm				    (struct archive_rb_node *)vp);
2111248616Smm				vp->parent = dent;
2112248616Smm			}
2113248616Smm			mtree_entry_register_add(mtree, vp);
2114248616Smm			np = vp;
2115248616Smm
2116248616Smm			fn += l;
2117248616Smm			if (fn[0] == '/')
2118248616Smm				fn++;
2119248616Smm			l = get_path_component(name, sizeof(name), fn);
2120248616Smm			if (l < 0) {
2121248616Smm				archive_string_free(&as);
2122248616Smm				archive_set_error(&a->archive,
2123248616Smm				    ARCHIVE_ERRNO_MISC,
2124248616Smm				    "A name buffer is too small");
2125248616Smm				return (ARCHIVE_FATAL);
2126248616Smm			}
2127248616Smm			dent = np;
2128248616Smm		}
2129248616Smm
2130248616Smm		/* Found out the parent directory where `file' can be
2131248616Smm		 * inserted. */
2132248616Smm		mtree->cur_dirent = dent;
2133248616Smm		archive_string_empty(&(mtree->cur_dirstr));
2134248616Smm		archive_string_ensure(&(mtree->cur_dirstr),
2135248616Smm		    archive_strlen(&(dent->parentdir)) +
2136248616Smm		    archive_strlen(&(dent->basename)) + 2);
2137248616Smm		if (archive_strlen(&(dent->parentdir)) +
2138248616Smm		    archive_strlen(&(dent->basename)) == 0)
2139248616Smm			mtree->cur_dirstr.s[0] = 0;
2140248616Smm		else {
2141248616Smm			if (archive_strlen(&(dent->parentdir)) > 0) {
2142248616Smm				archive_string_copy(&(mtree->cur_dirstr),
2143248616Smm				    &(dent->parentdir));
2144248616Smm				archive_strappend_char(
2145248616Smm				    &(mtree->cur_dirstr), '/');
2146248616Smm			}
2147248616Smm			archive_string_concat(&(mtree->cur_dirstr),
2148248616Smm			    &(dent->basename));
2149248616Smm		}
2150248616Smm
2151248616Smm		if (!__archive_rb_tree_insert_node(
2152248616Smm		    &(dent->dir_info->rbtree),
2153248616Smm		    (struct archive_rb_node *)file)) {
2154248616Smm			np = (struct mtree_entry *)__archive_rb_tree_find_node(
2155248616Smm			    &(dent->dir_info->rbtree), file->basename.s);
2156248616Smm			goto same_entry;
2157248616Smm		}
2158248616Smm		file->parent = dent;
2159248616Smm		mtree_entry_register_add(mtree, file);
2160248616Smm		return (ARCHIVE_OK);
2161248616Smm	}
2162248616Smm
2163248616Smmsame_entry:
2164248616Smm	/*
2165248616Smm	 * We have already has the entry the filename of which is
2166248616Smm	 * the same.
2167248616Smm	 */
2168248616Smm	r = mtree_entry_exchange_same_entry(a, np, file);
2169248616Smm	if (r < ARCHIVE_WARN)
2170248616Smm		return (r);
2171248616Smm	if (np->dir_info)
2172248616Smm		np->dir_info->virtual = 0;
2173248616Smm	*filep = np;
2174248616Smm	mtree_entry_free(file);
2175248616Smm	return (ARCHIVE_WARN);
2176248616Smm}
2177248616Smm
2178248616Smmstatic int
2179248616Smmmtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np,
2180248616Smm    struct mtree_entry *file)
2181248616Smm{
2182248616Smm
2183248616Smm	if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) {
2184248616Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
2185248616Smm		    "Found duplicate entries `%s' and its file type is "
2186248616Smm		    "different",
2187248616Smm		    np->pathname.s);
2188248616Smm		return (ARCHIVE_FAILED);
2189248616Smm	}
2190248616Smm
2191248616Smm	/* Update the existent mtree entry's attributes by the new one's. */
2192248616Smm	archive_string_empty(&np->symlink);
2193248616Smm	archive_string_concat(&np->symlink, &file->symlink);
2194248616Smm	archive_string_empty(&np->uname);
2195248616Smm	archive_string_concat(&np->uname, &file->uname);
2196248616Smm	archive_string_empty(&np->gname);
2197248616Smm	archive_string_concat(&np->gname, &file->gname);
2198248616Smm	archive_string_empty(&np->fflags_text);
2199248616Smm	archive_string_concat(&np->fflags_text, &file->fflags_text);
2200248616Smm	np->nlink = file->nlink;
2201248616Smm	np->filetype = file->filetype;
2202248616Smm	np->mode = file->mode;
2203248616Smm	np->size = file->size;
2204248616Smm	np->uid = file->uid;
2205248616Smm	np->gid = file->gid;
2206248616Smm	np->fflags_set = file->fflags_set;
2207248616Smm	np->fflags_clear = file->fflags_clear;
2208248616Smm	np->mtime = file->mtime;
2209248616Smm	np->mtime_nsec = file->mtime_nsec;
2210248616Smm	np->rdevmajor = file->rdevmajor;
2211248616Smm	np->rdevminor = file->rdevminor;
2212302001Smm	np->devmajor = file->devmajor;
2213302001Smm	np->devminor = file->devminor;
2214302001Smm	np->ino = file->ino;
2215248616Smm
2216248616Smm	return (ARCHIVE_WARN);
2217248616Smm}
2218