Blame libarchive/archive_write_set_format_mtree.c

Packit 08bd4c
/*-
Packit 08bd4c
 * Copyright (c) 2008 Joerg Sonnenberger
Packit 08bd4c
 * Copyright (c) 2009-2012 Michihiro NAKAJIMA
Packit 08bd4c
 * All rights reserved.
Packit 08bd4c
 *
Packit 08bd4c
 * Redistribution and use in source and binary forms, with or without
Packit 08bd4c
 * modification, are permitted provided that the following conditions
Packit 08bd4c
 * are met:
Packit 08bd4c
 * 1. Redistributions of source code must retain the above copyright
Packit 08bd4c
 *    notice, this list of conditions and the following disclaimer.
Packit 08bd4c
 * 2. Redistributions in binary form must reproduce the above copyright
Packit 08bd4c
 *    notice, this list of conditions and the following disclaimer in the
Packit 08bd4c
 *    documentation and/or other materials provided with the distribution.
Packit 08bd4c
 *
Packit 08bd4c
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
Packit 08bd4c
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Packit 08bd4c
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Packit 08bd4c
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit 08bd4c
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Packit 08bd4c
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit 08bd4c
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit 08bd4c
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit 08bd4c
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Packit 08bd4c
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit 08bd4c
 */
Packit 08bd4c
Packit 08bd4c
#include "archive_platform.h"
Packit 08bd4c
__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_mtree.c 201171 2009-12-29 06:39:07Z kientzle $");
Packit 08bd4c
Packit 08bd4c
#ifdef HAVE_SYS_TYPES_H
Packit 08bd4c
#include <sys/types.h>
Packit 08bd4c
#endif
Packit 08bd4c
#include <errno.h>
Packit 08bd4c
#include <stdlib.h>
Packit 08bd4c
#include <string.h>
Packit 08bd4c
Packit 08bd4c
#include "archive.h"
Packit 08bd4c
#include "archive_digest_private.h"
Packit 08bd4c
#include "archive_entry.h"
Packit 08bd4c
#include "archive_private.h"
Packit 08bd4c
#include "archive_rb.h"
Packit 08bd4c
#include "archive_string.h"
Packit 08bd4c
#include "archive_write_private.h"
Packit 08bd4c
Packit 08bd4c
#define INDENTNAMELEN	15
Packit 08bd4c
#define MAXLINELEN	80
Packit 08bd4c
#define SET_KEYS	\
Packit 08bd4c
	(F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME)
Packit 08bd4c
Packit 08bd4c
struct attr_counter {
Packit 08bd4c
	struct attr_counter *prev;
Packit 08bd4c
	struct attr_counter *next;
Packit 08bd4c
	struct mtree_entry *m_entry;
Packit 08bd4c
	int count;
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
struct att_counter_set {
Packit 08bd4c
	struct attr_counter *uid_list;
Packit 08bd4c
	struct attr_counter *gid_list;
Packit 08bd4c
	struct attr_counter *mode_list;
Packit 08bd4c
	struct attr_counter *flags_list;
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
struct mtree_chain {
Packit 08bd4c
	struct mtree_entry *first;
Packit 08bd4c
	struct mtree_entry **last;
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * The Data only for a directory file.
Packit 08bd4c
 */
Packit 08bd4c
struct dir_info {
Packit 08bd4c
	struct archive_rb_tree rbtree;
Packit 08bd4c
	struct mtree_chain children;
Packit 08bd4c
	struct mtree_entry *chnext;
Packit 08bd4c
	int virtual;
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * The Data only for a regular file.
Packit 08bd4c
 */
Packit 08bd4c
struct reg_info {
Packit 08bd4c
	int compute_sum;
Packit 08bd4c
	uint32_t crc;
Packit 08bd4c
#ifdef ARCHIVE_HAS_MD5
Packit 08bd4c
	unsigned char buf_md5[16];
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_RMD160
Packit 08bd4c
	unsigned char buf_rmd160[20];
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA1
Packit 08bd4c
	unsigned char buf_sha1[20];
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA256
Packit 08bd4c
	unsigned char buf_sha256[32];
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA384
Packit 08bd4c
	unsigned char buf_sha384[48];
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA512
Packit 08bd4c
	unsigned char buf_sha512[64];
Packit 08bd4c
#endif
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
struct mtree_entry {
Packit 08bd4c
	struct archive_rb_node rbnode;
Packit 08bd4c
	struct mtree_entry *next;
Packit 08bd4c
	struct mtree_entry *parent;
Packit 08bd4c
	struct dir_info *dir_info;
Packit 08bd4c
	struct reg_info *reg_info;
Packit 08bd4c
Packit 08bd4c
	struct archive_string parentdir;
Packit 08bd4c
	struct archive_string basename;
Packit 08bd4c
	struct archive_string pathname;
Packit 08bd4c
	struct archive_string symlink;
Packit 08bd4c
	struct archive_string uname;
Packit 08bd4c
	struct archive_string gname;
Packit 08bd4c
	struct archive_string fflags_text;
Packit 08bd4c
	unsigned int nlink;
Packit 08bd4c
	mode_t filetype;
Packit 08bd4c
	mode_t mode;
Packit 08bd4c
	int64_t size;
Packit 08bd4c
	int64_t uid;
Packit 08bd4c
	int64_t gid;
Packit 08bd4c
	time_t mtime;
Packit 08bd4c
	long mtime_nsec;
Packit 08bd4c
	unsigned long fflags_set;
Packit 08bd4c
	unsigned long fflags_clear;
Packit 08bd4c
	dev_t rdevmajor;
Packit 08bd4c
	dev_t rdevminor;
Packit 08bd4c
	dev_t devmajor;
Packit 08bd4c
	dev_t devminor;
Packit 08bd4c
	int64_t ino;
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
struct mtree_writer {
Packit 08bd4c
	struct mtree_entry *mtree_entry;
Packit 08bd4c
	struct mtree_entry *root;
Packit 08bd4c
	struct mtree_entry *cur_dirent;
Packit 08bd4c
	struct archive_string cur_dirstr;
Packit 08bd4c
	struct mtree_chain file_list;
Packit 08bd4c
Packit 08bd4c
	struct archive_string ebuf;
Packit 08bd4c
	struct archive_string buf;
Packit 08bd4c
	int first;
Packit 08bd4c
	uint64_t entry_bytes_remaining;
Packit 08bd4c
Packit 08bd4c
	/*
Packit 08bd4c
	 * Set global value.
Packit 08bd4c
	 */
Packit 08bd4c
	struct {
Packit 08bd4c
		int		processing;
Packit 08bd4c
		mode_t		type;
Packit 08bd4c
		int		keys;
Packit 08bd4c
		int64_t		uid;
Packit 08bd4c
		int64_t		gid;
Packit 08bd4c
		mode_t		mode;
Packit 08bd4c
		unsigned long	fflags_set;
Packit 08bd4c
		unsigned long	fflags_clear;
Packit 08bd4c
	} set;
Packit 08bd4c
	struct att_counter_set	acs;
Packit 08bd4c
	int classic;
Packit 08bd4c
	int depth;
Packit 08bd4c
Packit 08bd4c
	/* check sum */
Packit 08bd4c
	int compute_sum;
Packit 08bd4c
	uint32_t crc;
Packit 08bd4c
	uint64_t crc_len;
Packit 08bd4c
#ifdef ARCHIVE_HAS_MD5
Packit 08bd4c
	archive_md5_ctx md5ctx;
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_RMD160
Packit 08bd4c
	archive_rmd160_ctx rmd160ctx;
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA1
Packit 08bd4c
	archive_sha1_ctx sha1ctx;
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA256
Packit 08bd4c
	archive_sha256_ctx sha256ctx;
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA384
Packit 08bd4c
	archive_sha384_ctx sha384ctx;
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA512
Packit 08bd4c
	archive_sha512_ctx sha512ctx;
Packit 08bd4c
#endif
Packit 08bd4c
	/* Keyword options */
Packit 08bd4c
	int keys;
Packit 08bd4c
#define	F_CKSUM		0x00000001		/* check sum */
Packit 08bd4c
#define	F_DEV		0x00000002		/* device type */
Packit 08bd4c
#define	F_DONE		0x00000004		/* directory done */
Packit 08bd4c
#define	F_FLAGS		0x00000008		/* file flags */
Packit 08bd4c
#define	F_GID		0x00000010		/* gid */
Packit 08bd4c
#define	F_GNAME		0x00000020		/* group name */
Packit 08bd4c
#define	F_IGN		0x00000040		/* ignore */
Packit 08bd4c
#define	F_MAGIC		0x00000080		/* name has magic chars */
Packit 08bd4c
#define	F_MD5		0x00000100		/* MD5 digest */
Packit 08bd4c
#define	F_MODE		0x00000200		/* mode */
Packit 08bd4c
#define	F_NLINK		0x00000400		/* number of links */
Packit 08bd4c
#define	F_NOCHANGE 	0x00000800		/* If owner/mode "wrong", do
Packit 08bd4c
						 * not change */
Packit 08bd4c
#define	F_OPT		0x00001000		/* existence optional */
Packit 08bd4c
#define	F_RMD160 	0x00002000		/* RIPEMD160 digest */
Packit 08bd4c
#define	F_SHA1		0x00004000		/* SHA-1 digest */
Packit 08bd4c
#define	F_SIZE		0x00008000		/* size */
Packit 08bd4c
#define	F_SLINK		0x00010000		/* symbolic link */
Packit 08bd4c
#define	F_TAGS		0x00020000		/* tags */
Packit 08bd4c
#define	F_TIME		0x00040000		/* modification time */
Packit 08bd4c
#define	F_TYPE		0x00080000		/* file type */
Packit 08bd4c
#define	F_UID		0x00100000		/* uid */
Packit 08bd4c
#define	F_UNAME		0x00200000		/* user name */
Packit 08bd4c
#define	F_VISIT		0x00400000		/* file visited */
Packit 08bd4c
#define	F_SHA256	0x00800000		/* SHA-256 digest */
Packit 08bd4c
#define	F_SHA384	0x01000000		/* SHA-384 digest */
Packit 08bd4c
#define	F_SHA512	0x02000000		/* SHA-512 digest */
Packit 08bd4c
#define	F_INO		0x04000000		/* inode number */
Packit 08bd4c
#define	F_RESDEV	0x08000000		/* device ID on which the
Packit 08bd4c
						 * entry resides */
Packit 08bd4c
Packit 08bd4c
	/* Options */
Packit 08bd4c
	int dironly;		/* If it is set, ignore all files except
Packit 08bd4c
				 * directory files, like mtree(8) -d option. */
Packit 08bd4c
	int indent;		/* If it is set, indent output data. */
Packit 08bd4c
	int output_global_set;	/* If it is set, use /set keyword to set
Packit 08bd4c
				 * global values. When generating mtree
Packit 08bd4c
				 * classic format, it is set by default. */
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
#define DEFAULT_KEYS	(F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\
Packit 08bd4c
			 | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\
Packit 08bd4c
			 | F_UNAME)
Packit 08bd4c
#define attr_counter_set_reset	attr_counter_set_free
Packit 08bd4c
Packit 08bd4c
static void attr_counter_free(struct attr_counter **);
Packit 08bd4c
static int attr_counter_inc(struct attr_counter **, struct attr_counter *,
Packit 08bd4c
	struct attr_counter *, struct mtree_entry *);
Packit 08bd4c
static struct attr_counter * attr_counter_new(struct mtree_entry *,
Packit 08bd4c
	struct attr_counter *);
Packit 08bd4c
static int attr_counter_set_collect(struct mtree_writer *,
Packit 08bd4c
	struct mtree_entry *);
Packit 08bd4c
static void attr_counter_set_free(struct mtree_writer *);
Packit 08bd4c
static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *);
Packit 08bd4c
static int mtree_entry_add_child_tail(struct mtree_entry *,
Packit 08bd4c
	struct mtree_entry *);
Packit 08bd4c
static int mtree_entry_create_virtual_dir(struct archive_write *, const char *,
Packit 08bd4c
	struct mtree_entry **);
Packit 08bd4c
static int mtree_entry_cmp_node(const struct archive_rb_node *,
Packit 08bd4c
	const struct archive_rb_node *);
Packit 08bd4c
static int mtree_entry_cmp_key(const struct archive_rb_node *, const void *);
Packit 08bd4c
static int mtree_entry_exchange_same_entry(struct archive_write *,
Packit 08bd4c
    struct mtree_entry *, struct mtree_entry *);
Packit 08bd4c
static void mtree_entry_free(struct mtree_entry *);
Packit 08bd4c
static int mtree_entry_new(struct archive_write *, struct archive_entry *,
Packit 08bd4c
	struct mtree_entry **);
Packit 08bd4c
static void mtree_entry_register_free(struct mtree_writer *);
Packit 08bd4c
static void mtree_entry_register_init(struct mtree_writer *);
Packit 08bd4c
static int mtree_entry_setup_filenames(struct archive_write *,
Packit 08bd4c
	struct mtree_entry *, struct archive_entry *);
Packit 08bd4c
static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **);
Packit 08bd4c
static void sum_init(struct mtree_writer *);
Packit 08bd4c
static void sum_update(struct mtree_writer *, const void *, size_t);
Packit 08bd4c
static void sum_final(struct mtree_writer *, struct reg_info *);
Packit 08bd4c
static void sum_write(struct archive_string *, struct reg_info *);
Packit 08bd4c
static int write_mtree_entry(struct archive_write *, struct mtree_entry *);
Packit 08bd4c
static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *);
Packit 08bd4c
Packit 08bd4c
#define	COMPUTE_CRC(var, ch)	(var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
Packit 08bd4c
static const uint32_t crctab[] = {
Packit 08bd4c
	0x0,
Packit 08bd4c
	0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
Packit 08bd4c
	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
Packit 08bd4c
	0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
Packit 08bd4c
	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
Packit 08bd4c
	0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
Packit 08bd4c
	0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
Packit 08bd4c
	0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
Packit 08bd4c
	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
Packit 08bd4c
	0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
Packit 08bd4c
	0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
Packit 08bd4c
	0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
Packit 08bd4c
	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
Packit 08bd4c
	0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
Packit 08bd4c
	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
Packit 08bd4c
	0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
Packit 08bd4c
	0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
Packit 08bd4c
	0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
Packit 08bd4c
	0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
Packit 08bd4c
	0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
Packit 08bd4c
	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
Packit 08bd4c
	0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
Packit 08bd4c
	0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
Packit 08bd4c
	0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
Packit 08bd4c
	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
Packit 08bd4c
	0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
Packit 08bd4c
	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
Packit 08bd4c
	0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
Packit 08bd4c
	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
Packit 08bd4c
	0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
Packit 08bd4c
	0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
Packit 08bd4c
	0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
Packit 08bd4c
	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
Packit 08bd4c
	0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
Packit 08bd4c
	0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
Packit 08bd4c
	0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
Packit 08bd4c
	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
Packit 08bd4c
	0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
Packit 08bd4c
	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
Packit 08bd4c
	0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
Packit 08bd4c
	0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
Packit 08bd4c
	0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
Packit 08bd4c
	0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
Packit 08bd4c
	0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
Packit 08bd4c
	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
Packit 08bd4c
	0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
Packit 08bd4c
	0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
Packit 08bd4c
	0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
Packit 08bd4c
	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
Packit 08bd4c
	0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
Packit 08bd4c
	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
Packit 08bd4c
	0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
static const unsigned char safe_char[256] = {
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
Packit 08bd4c
	/* !"$%&'()*+,-./  EXCLUSION:0x20( ) 0x23(#) */
Packit 08bd4c
	0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
Packit 08bd4c
	/* 0123456789:;<>?  EXCLUSION:0x3d(=) */
Packit 08bd4c
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
Packit 08bd4c
	/* @ABCDEFGHIJKLMNO */
Packit 08bd4c
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
Packit 08bd4c
	/* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\)  */
Packit 08bd4c
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */
Packit 08bd4c
	/* `abcdefghijklmno */
Packit 08bd4c
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
Packit 08bd4c
	/* pqrstuvwxyz{|}~ */
Packit 08bd4c
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
Packit 08bd4c
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
Packit 08bd4c
};
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
mtree_quote(struct archive_string *s, const char *str)
Packit 08bd4c
{
Packit 08bd4c
	const char *start;
Packit 08bd4c
	char buf[4];
Packit 08bd4c
	unsigned char c;
Packit 08bd4c
Packit 08bd4c
	for (start = str; *str != '\0'; ++str) {
Packit 08bd4c
		if (safe_char[*(const unsigned char *)str])
Packit 08bd4c
			continue;
Packit 08bd4c
		if (start != str)
Packit 08bd4c
			archive_strncat(s, start, str - start);
Packit 08bd4c
		c = (unsigned char)*str;
Packit 08bd4c
		buf[0] = '\\';
Packit 08bd4c
		buf[1] = (c / 64) + '0';
Packit 08bd4c
		buf[2] = (c / 8 % 8) + '0';
Packit 08bd4c
		buf[3] = (c % 8) + '0';
Packit 08bd4c
		archive_strncat(s, buf, 4);
Packit 08bd4c
		start = str + 1;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (start != str)
Packit 08bd4c
		archive_strncat(s, start, str - start);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Indent a line as mtree utility to be readable for people.
Packit 08bd4c
 */
Packit 08bd4c
static void
Packit 08bd4c
mtree_indent(struct mtree_writer *mtree)
Packit 08bd4c
{
Packit 08bd4c
	int i, fn, nd, pd;
Packit 08bd4c
	const char *r, *s, *x;
Packit 08bd4c
Packit 08bd4c
	if (mtree->classic) {
Packit 08bd4c
		if (mtree->indent) {
Packit 08bd4c
			nd = 0;
Packit 08bd4c
			pd = mtree->depth * 4;
Packit 08bd4c
		} else {
Packit 08bd4c
			nd = mtree->depth?4:0;
Packit 08bd4c
			pd = 0;
Packit 08bd4c
		}
Packit 08bd4c
	} else
Packit 08bd4c
		nd = pd = 0;
Packit 08bd4c
	fn = 1;
Packit 08bd4c
	s = r = mtree->ebuf.s;
Packit 08bd4c
	x = NULL;
Packit 08bd4c
	while (*r == ' ')
Packit 08bd4c
		r++;
Packit 08bd4c
	while ((r = strchr(r, ' ')) != NULL) {
Packit 08bd4c
		if (fn) {
Packit 08bd4c
			fn = 0;
Packit 08bd4c
			for (i = 0; i < nd + pd; i++)
Packit 08bd4c
				archive_strappend_char(&mtree->buf, ' ');
Packit 08bd4c
			archive_strncat(&mtree->buf, s, r - s);
Packit 08bd4c
			if (nd + (r -s) > INDENTNAMELEN) {
Packit 08bd4c
				archive_strncat(&mtree->buf, " \\\n", 3);
Packit 08bd4c
				for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
Packit 08bd4c
					archive_strappend_char(&mtree->buf, ' ');
Packit 08bd4c
			} else {
Packit 08bd4c
				for (i = (int)(r -s + nd);
Packit 08bd4c
				    i < (INDENTNAMELEN + 1); i++)
Packit 08bd4c
					archive_strappend_char(&mtree->buf, ' ');
Packit 08bd4c
			}
Packit 08bd4c
			s = ++r;
Packit 08bd4c
			x = NULL;
Packit 08bd4c
			continue;
Packit 08bd4c
		}
Packit 08bd4c
		if (pd + (r - s) <= MAXLINELEN - 3 - INDENTNAMELEN)
Packit 08bd4c
			x = r++;
Packit 08bd4c
		else {
Packit 08bd4c
			if (x == NULL)
Packit 08bd4c
				x = r;
Packit 08bd4c
			archive_strncat(&mtree->buf, s, x - s);
Packit 08bd4c
			archive_strncat(&mtree->buf, " \\\n", 3);
Packit 08bd4c
			for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
Packit 08bd4c
				archive_strappend_char(&mtree->buf, ' ');
Packit 08bd4c
			s = r = ++x;
Packit 08bd4c
			x = NULL;
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if (fn) {
Packit 08bd4c
		for (i = 0; i < nd + pd; i++)
Packit 08bd4c
			archive_strappend_char(&mtree->buf, ' ');
Packit 08bd4c
		archive_strcat(&mtree->buf, s);
Packit 08bd4c
		s += strlen(s);
Packit 08bd4c
	}
Packit 08bd4c
	if (x != NULL && pd + strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) {
Packit 08bd4c
		/* Last keyword is longer. */
Packit 08bd4c
		archive_strncat(&mtree->buf, s, x - s);
Packit 08bd4c
		archive_strncat(&mtree->buf, " \\\n", 3);
Packit 08bd4c
		for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
Packit 08bd4c
			archive_strappend_char(&mtree->buf, ' ');
Packit 08bd4c
		s = ++x;
Packit 08bd4c
	}
Packit 08bd4c
	archive_strcat(&mtree->buf, s);
Packit 08bd4c
	archive_string_empty(&mtree->ebuf);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Write /set keyword.
Packit 08bd4c
 * Set most used value of uid,gid,mode and fflags, which are
Packit 08bd4c
 * collected by attr_counter_set_collect() function.
Packit 08bd4c
 */
Packit 08bd4c
static void
Packit 08bd4c
write_global(struct mtree_writer *mtree)
Packit 08bd4c
{
Packit 08bd4c
	struct archive_string setstr;
Packit 08bd4c
	struct archive_string unsetstr;
Packit 08bd4c
	struct att_counter_set *acs;
Packit 08bd4c
	int keys, oldkeys, effkeys;
Packit 08bd4c
Packit 08bd4c
	archive_string_init(&setstr);
Packit 08bd4c
	archive_string_init(&unsetstr);
Packit 08bd4c
	keys = mtree->keys & SET_KEYS;
Packit 08bd4c
	oldkeys = mtree->set.keys;
Packit 08bd4c
	effkeys = keys;
Packit 08bd4c
	acs = &mtree->acs;
Packit 08bd4c
	if (mtree->set.processing) {
Packit 08bd4c
		/*
Packit 08bd4c
		 * Check if the global data needs updating.
Packit 08bd4c
		 */
Packit 08bd4c
		effkeys &= ~F_TYPE;
Packit 08bd4c
		if (acs->uid_list == NULL)
Packit 08bd4c
			effkeys &= ~(F_UNAME | F_UID);
Packit 08bd4c
		else if (oldkeys & (F_UNAME | F_UID)) {
Packit 08bd4c
			if (acs->uid_list->count < 2 ||
Packit 08bd4c
			    mtree->set.uid == acs->uid_list->m_entry->uid)
Packit 08bd4c
				effkeys &= ~(F_UNAME | F_UID);
Packit 08bd4c
		}
Packit 08bd4c
		if (acs->gid_list == NULL)
Packit 08bd4c
			effkeys &= ~(F_GNAME | F_GID);
Packit 08bd4c
		else if (oldkeys & (F_GNAME | F_GID)) {
Packit 08bd4c
			if (acs->gid_list->count < 2 ||
Packit 08bd4c
			    mtree->set.gid == acs->gid_list->m_entry->gid)
Packit 08bd4c
				effkeys &= ~(F_GNAME | F_GID);
Packit 08bd4c
		}
Packit 08bd4c
		if (acs->mode_list == NULL)
Packit 08bd4c
			effkeys &= ~F_MODE;
Packit 08bd4c
		else if (oldkeys & F_MODE) {
Packit 08bd4c
			if (acs->mode_list->count < 2 ||
Packit 08bd4c
			    mtree->set.mode == acs->mode_list->m_entry->mode)
Packit 08bd4c
				effkeys &= ~F_MODE;
Packit 08bd4c
		}
Packit 08bd4c
		if (acs->flags_list == NULL)
Packit 08bd4c
			effkeys &= ~F_FLAGS;
Packit 08bd4c
		else if ((oldkeys & F_FLAGS) != 0) {
Packit 08bd4c
			if (acs->flags_list->count < 2 ||
Packit 08bd4c
			    (acs->flags_list->m_entry->fflags_set ==
Packit 08bd4c
				mtree->set.fflags_set &&
Packit 08bd4c
			     acs->flags_list->m_entry->fflags_clear ==
Packit 08bd4c
				mtree->set.fflags_clear))
Packit 08bd4c
				effkeys &= ~F_FLAGS;
Packit 08bd4c
		}
Packit 08bd4c
	} else {
Packit 08bd4c
		if (acs->uid_list == NULL)
Packit 08bd4c
			keys &= ~(F_UNAME | F_UID);
Packit 08bd4c
		if (acs->gid_list == NULL)
Packit 08bd4c
			keys &= ~(F_GNAME | F_GID);
Packit 08bd4c
		if (acs->mode_list == NULL)
Packit 08bd4c
			keys &= ~F_MODE;
Packit 08bd4c
		if (acs->flags_list == NULL)
Packit 08bd4c
			keys &= ~F_FLAGS;
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & effkeys & F_TYPE) != 0) {
Packit 08bd4c
		if (mtree->dironly) {
Packit 08bd4c
			archive_strcat(&setstr, " type=dir");
Packit 08bd4c
			mtree->set.type = AE_IFDIR;
Packit 08bd4c
		} else {
Packit 08bd4c
			archive_strcat(&setstr, " type=file");
Packit 08bd4c
			mtree->set.type = AE_IFREG;
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & effkeys & F_UNAME) != 0) {
Packit 08bd4c
		if (archive_strlen(&(acs->uid_list->m_entry->uname)) > 0) {
Packit 08bd4c
			archive_strcat(&setstr, " uname=");
Packit 08bd4c
			mtree_quote(&setstr, acs->uid_list->m_entry->uname.s);
Packit 08bd4c
		} else {
Packit 08bd4c
			keys &= ~F_UNAME;
Packit 08bd4c
			if ((oldkeys & F_UNAME) != 0)
Packit 08bd4c
				archive_strcat(&unsetstr, " uname");
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & effkeys & F_UID) != 0) {
Packit 08bd4c
		mtree->set.uid = acs->uid_list->m_entry->uid;
Packit 08bd4c
		archive_string_sprintf(&setstr, " uid=%jd",
Packit 08bd4c
		    (intmax_t)mtree->set.uid);
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & effkeys & F_GNAME) != 0) {
Packit 08bd4c
		if (archive_strlen(&(acs->gid_list->m_entry->gname)) > 0) {
Packit 08bd4c
			archive_strcat(&setstr, " gname=");
Packit 08bd4c
			mtree_quote(&setstr, acs->gid_list->m_entry->gname.s);
Packit 08bd4c
		} else {
Packit 08bd4c
			keys &= ~F_GNAME;
Packit 08bd4c
			if ((oldkeys & F_GNAME) != 0)
Packit 08bd4c
				archive_strcat(&unsetstr, " gname");
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & effkeys & F_GID) != 0) {
Packit 08bd4c
		mtree->set.gid = acs->gid_list->m_entry->gid;
Packit 08bd4c
		archive_string_sprintf(&setstr, " gid=%jd",
Packit 08bd4c
		    (intmax_t)mtree->set.gid);
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & effkeys & F_MODE) != 0) {
Packit 08bd4c
		mtree->set.mode = acs->mode_list->m_entry->mode;
Packit 08bd4c
		archive_string_sprintf(&setstr, " mode=%o",
Packit 08bd4c
		    (unsigned int)mtree->set.mode);
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & effkeys & F_FLAGS) != 0) {
Packit 08bd4c
		if (archive_strlen(
Packit 08bd4c
		    &(acs->flags_list->m_entry->fflags_text)) > 0) {
Packit 08bd4c
			archive_strcat(&setstr, " flags=");
Packit 08bd4c
			mtree_quote(&setstr,
Packit 08bd4c
			    acs->flags_list->m_entry->fflags_text.s);
Packit 08bd4c
			mtree->set.fflags_set =
Packit 08bd4c
			    acs->flags_list->m_entry->fflags_set;
Packit 08bd4c
			mtree->set.fflags_clear =
Packit 08bd4c
			    acs->flags_list->m_entry->fflags_clear;
Packit 08bd4c
		} else {
Packit 08bd4c
			keys &= ~F_FLAGS;
Packit 08bd4c
			if ((oldkeys & F_FLAGS) != 0)
Packit 08bd4c
				archive_strcat(&unsetstr, " flags");
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if (unsetstr.length > 0)
Packit 08bd4c
		archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s);
Packit 08bd4c
	archive_string_free(&unsetstr);
Packit 08bd4c
	if (setstr.length > 0)
Packit 08bd4c
		archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s);
Packit 08bd4c
	archive_string_free(&setstr);
Packit 08bd4c
	mtree->set.keys = keys;
Packit 08bd4c
	mtree->set.processing = 1;
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static struct attr_counter *
Packit 08bd4c
attr_counter_new(struct mtree_entry *me, struct attr_counter *prev)
Packit 08bd4c
{
Packit 08bd4c
	struct attr_counter *ac;
Packit 08bd4c
Packit 08bd4c
	ac = malloc(sizeof(*ac));
Packit 08bd4c
	if (ac != NULL) {
Packit 08bd4c
		ac->prev = prev;
Packit 08bd4c
		ac->next = NULL;
Packit 08bd4c
		ac->count = 1;
Packit 08bd4c
		ac->m_entry = me;
Packit 08bd4c
	}
Packit 08bd4c
	return (ac);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
attr_counter_free(struct attr_counter **top)
Packit 08bd4c
{
Packit 08bd4c
	struct attr_counter *ac, *tac;
Packit 08bd4c
Packit 08bd4c
	if (*top == NULL)
Packit 08bd4c
		return;
Packit 08bd4c
	ac = *top;
Packit 08bd4c
        while (ac != NULL) {
Packit 08bd4c
		tac = ac->next;
Packit 08bd4c
		free(ac);
Packit 08bd4c
		ac = tac;
Packit 08bd4c
	}
Packit 08bd4c
	*top = NULL;
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
attr_counter_inc(struct attr_counter **top, struct attr_counter *ac,
Packit 08bd4c
    struct attr_counter *last, struct mtree_entry *me)
Packit 08bd4c
{
Packit 08bd4c
	struct attr_counter *pac;
Packit 08bd4c
Packit 08bd4c
	if (ac != NULL) {
Packit 08bd4c
		ac->count++;
Packit 08bd4c
		if (*top == ac || ac->prev->count >= ac->count)
Packit 08bd4c
			return (0);
Packit 08bd4c
		for (pac = ac->prev; pac; pac = pac->prev) {
Packit 08bd4c
			if (pac->count >= ac->count)
Packit 08bd4c
				break;
Packit 08bd4c
		}
Packit 08bd4c
		ac->prev->next = ac->next;
Packit 08bd4c
		if (ac->next != NULL)
Packit 08bd4c
			ac->next->prev = ac->prev;
Packit 08bd4c
		if (pac != NULL) {
Packit 08bd4c
			ac->prev = pac;
Packit 08bd4c
			ac->next = pac->next;
Packit 08bd4c
			pac->next = ac;
Packit 08bd4c
			if (ac->next != NULL)
Packit 08bd4c
				ac->next->prev = ac;
Packit 08bd4c
		} else {
Packit 08bd4c
			ac->prev = NULL;
Packit 08bd4c
			ac->next = *top;
Packit 08bd4c
			*top = ac;
Packit 08bd4c
			ac->next->prev = ac;
Packit 08bd4c
		}
Packit 08bd4c
	} else if (last != NULL) {
Packit 08bd4c
		ac = attr_counter_new(me, last);
Packit 08bd4c
		if (ac == NULL)
Packit 08bd4c
			return (-1);
Packit 08bd4c
		last->next = ac;
Packit 08bd4c
	}
Packit 08bd4c
	return (0);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Tabulate uid,gid,mode and fflags of a entry in order to be used for /set.
Packit 08bd4c
 */
Packit 08bd4c
static int
Packit 08bd4c
attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)
Packit 08bd4c
{
Packit 08bd4c
	struct attr_counter *ac, *last;
Packit 08bd4c
	struct att_counter_set *acs = &mtree->acs;
Packit 08bd4c
	int keys = mtree->keys;
Packit 08bd4c
Packit 08bd4c
	if (keys & (F_UNAME | F_UID)) {
Packit 08bd4c
		if (acs->uid_list == NULL) {
Packit 08bd4c
			acs->uid_list = attr_counter_new(me, NULL);
Packit 08bd4c
			if (acs->uid_list == NULL)
Packit 08bd4c
				return (-1);
Packit 08bd4c
		} else {
Packit 08bd4c
			last = NULL;
Packit 08bd4c
			for (ac = acs->uid_list; ac; ac = ac->next) {
Packit 08bd4c
				if (ac->m_entry->uid == me->uid)
Packit 08bd4c
					break;
Packit 08bd4c
				last = ac;
Packit 08bd4c
			}
Packit 08bd4c
			if (attr_counter_inc(&acs->uid_list, ac, last, me) < 0)
Packit 08bd4c
				return (-1);
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if (keys & (F_GNAME | F_GID)) {
Packit 08bd4c
		if (acs->gid_list == NULL) {
Packit 08bd4c
			acs->gid_list = attr_counter_new(me, NULL);
Packit 08bd4c
			if (acs->gid_list == NULL)
Packit 08bd4c
				return (-1);
Packit 08bd4c
		} else {
Packit 08bd4c
			last = NULL;
Packit 08bd4c
			for (ac = acs->gid_list; ac; ac = ac->next) {
Packit 08bd4c
				if (ac->m_entry->gid == me->gid)
Packit 08bd4c
					break;
Packit 08bd4c
				last = ac;
Packit 08bd4c
			}
Packit 08bd4c
			if (attr_counter_inc(&acs->gid_list, ac, last, me) < 0)
Packit 08bd4c
				return (-1);
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if (keys & F_MODE) {
Packit 08bd4c
		if (acs->mode_list == NULL) {
Packit 08bd4c
			acs->mode_list = attr_counter_new(me, NULL);
Packit 08bd4c
			if (acs->mode_list == NULL)
Packit 08bd4c
				return (-1);
Packit 08bd4c
		} else {
Packit 08bd4c
			last = NULL;
Packit 08bd4c
			for (ac = acs->mode_list; ac; ac = ac->next) {
Packit 08bd4c
				if (ac->m_entry->mode == me->mode)
Packit 08bd4c
					break;
Packit 08bd4c
				last = ac;
Packit 08bd4c
			}
Packit 08bd4c
			if (attr_counter_inc(&acs->mode_list, ac, last, me) < 0)
Packit 08bd4c
				return (-1);
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
	if (keys & F_FLAGS) {
Packit 08bd4c
		if (acs->flags_list == NULL) {
Packit 08bd4c
			acs->flags_list = attr_counter_new(me, NULL);
Packit 08bd4c
			if (acs->flags_list == NULL)
Packit 08bd4c
				return (-1);
Packit 08bd4c
		} else {
Packit 08bd4c
			last = NULL;
Packit 08bd4c
			for (ac = acs->flags_list; ac; ac = ac->next) {
Packit 08bd4c
				if (ac->m_entry->fflags_set == me->fflags_set &&
Packit 08bd4c
				    ac->m_entry->fflags_clear ==
Packit 08bd4c
							me->fflags_clear)
Packit 08bd4c
					break;
Packit 08bd4c
				last = ac;
Packit 08bd4c
			}
Packit 08bd4c
			if (attr_counter_inc(&acs->flags_list, ac, last, me) < 0)
Packit 08bd4c
				return (-1);
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	return (0);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
attr_counter_set_free(struct mtree_writer *mtree)
Packit 08bd4c
{
Packit 08bd4c
	struct att_counter_set *acs = &mtree->acs;
Packit 08bd4c
Packit 08bd4c
	attr_counter_free(&acs->uid_list);
Packit 08bd4c
	attr_counter_free(&acs->gid_list);
Packit 08bd4c
	attr_counter_free(&acs->mode_list);
Packit 08bd4c
	attr_counter_free(&acs->flags_list);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me)
Packit 08bd4c
{
Packit 08bd4c
	int keys;
Packit 08bd4c
Packit 08bd4c
	keys = mtree->keys;
Packit 08bd4c
Packit 08bd4c
	/*
Packit 08bd4c
	 * If a keyword has been set by /set, we do not need to
Packit 08bd4c
	 * output it.
Packit 08bd4c
	 */
Packit 08bd4c
	if (mtree->set.keys == 0)
Packit 08bd4c
		return (keys);/* /set is not used. */
Packit 08bd4c
Packit 08bd4c
	if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 &&
Packit 08bd4c
	     mtree->set.gid == me->gid)
Packit 08bd4c
		keys &= ~(F_GNAME | F_GID);
Packit 08bd4c
	if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 &&
Packit 08bd4c
	     mtree->set.uid == me->uid)
Packit 08bd4c
		keys &= ~(F_UNAME | F_UID);
Packit 08bd4c
	if (mtree->set.keys & F_FLAGS) {
Packit 08bd4c
		if (mtree->set.fflags_set == me->fflags_set &&
Packit 08bd4c
		    mtree->set.fflags_clear == me->fflags_clear)
Packit 08bd4c
			keys &= ~F_FLAGS;
Packit 08bd4c
	}
Packit 08bd4c
	if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == me->mode)
Packit 08bd4c
		keys &= ~F_MODE;
Packit 08bd4c
Packit 08bd4c
	switch (me->filetype) {
Packit 08bd4c
	case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
Packit 08bd4c
	case AE_IFBLK: case AE_IFIFO:
Packit 08bd4c
		break;
Packit 08bd4c
	case AE_IFDIR:
Packit 08bd4c
		if ((mtree->set.keys & F_TYPE) != 0 &&
Packit 08bd4c
		    mtree->set.type == AE_IFDIR)
Packit 08bd4c
			keys &= ~F_TYPE;
Packit 08bd4c
		break;
Packit 08bd4c
	case AE_IFREG:
Packit 08bd4c
	default:	/* Handle unknown file types as regular files. */
Packit 08bd4c
		if ((mtree->set.keys & F_TYPE) != 0 &&
Packit 08bd4c
		    mtree->set.type == AE_IFREG)
Packit 08bd4c
			keys &= ~F_TYPE;
Packit 08bd4c
		break;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	return (keys);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
mtree_entry_new(struct archive_write *a, struct archive_entry *entry,
Packit 08bd4c
    struct mtree_entry **m_entry)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_entry *me;
Packit 08bd4c
	const char *s;
Packit 08bd4c
	int r;
Packit 08bd4c
	static const struct archive_rb_tree_ops rb_ops = {
Packit 08bd4c
		mtree_entry_cmp_node, mtree_entry_cmp_key
Packit 08bd4c
	};
Packit 08bd4c
Packit 08bd4c
	me = calloc(1, sizeof(*me));
Packit 08bd4c
	if (me == NULL) {
Packit 08bd4c
		archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
		    "Can't allocate memory for a mtree entry");
Packit 08bd4c
		*m_entry = NULL;
Packit 08bd4c
		return (ARCHIVE_FATAL);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	r = mtree_entry_setup_filenames(a, me, entry);
Packit 08bd4c
	if (r < ARCHIVE_WARN) {
Packit 08bd4c
		mtree_entry_free(me);
Packit 08bd4c
		*m_entry = NULL;
Packit 08bd4c
		return (r);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if ((s = archive_entry_symlink(entry)) != NULL)
Packit 08bd4c
		archive_strcpy(&me->symlink, s);
Packit 08bd4c
	me->nlink = archive_entry_nlink(entry);
Packit 08bd4c
	me->filetype = archive_entry_filetype(entry);
Packit 08bd4c
	me->mode = archive_entry_mode(entry) & 07777;
Packit 08bd4c
	me->uid = archive_entry_uid(entry);
Packit 08bd4c
	me->gid = archive_entry_gid(entry);
Packit 08bd4c
	if ((s = archive_entry_uname(entry)) != NULL)
Packit 08bd4c
		archive_strcpy(&me->uname, s);
Packit 08bd4c
	if ((s = archive_entry_gname(entry)) != NULL)
Packit 08bd4c
		archive_strcpy(&me->gname, s);
Packit 08bd4c
	if ((s = archive_entry_fflags_text(entry)) != NULL)
Packit 08bd4c
		archive_strcpy(&me->fflags_text, s);
Packit 08bd4c
	archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear);
Packit 08bd4c
	me->mtime = archive_entry_mtime(entry);
Packit 08bd4c
	me->mtime_nsec = archive_entry_mtime_nsec(entry);
Packit 08bd4c
	me->rdevmajor = archive_entry_rdevmajor(entry);
Packit 08bd4c
	me->rdevminor = archive_entry_rdevminor(entry);
Packit 08bd4c
	me->devmajor = archive_entry_devmajor(entry);
Packit 08bd4c
	me->devminor = archive_entry_devminor(entry);
Packit 08bd4c
	me->ino = archive_entry_ino(entry);
Packit 08bd4c
	me->size = archive_entry_size(entry);
Packit 08bd4c
	if (me->filetype == AE_IFDIR) {
Packit 08bd4c
		me->dir_info = calloc(1, sizeof(*me->dir_info));
Packit 08bd4c
		if (me->dir_info == NULL) {
Packit 08bd4c
			mtree_entry_free(me);
Packit 08bd4c
			archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
			    "Can't allocate memory for a mtree entry");
Packit 08bd4c
			*m_entry = NULL;
Packit 08bd4c
			return (ARCHIVE_FATAL);
Packit 08bd4c
		}
Packit 08bd4c
		__archive_rb_tree_init(&me->dir_info->rbtree, &rb_ops);
Packit 08bd4c
		me->dir_info->children.first = NULL;
Packit 08bd4c
		me->dir_info->children.last = &(me->dir_info->children.first);
Packit 08bd4c
		me->dir_info->chnext = NULL;
Packit 08bd4c
	} else if (me->filetype == AE_IFREG) {
Packit 08bd4c
		me->reg_info = calloc(1, sizeof(*me->reg_info));
Packit 08bd4c
		if (me->reg_info == NULL) {
Packit 08bd4c
			mtree_entry_free(me);
Packit 08bd4c
			archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
			    "Can't allocate memory for a mtree entry");
Packit 08bd4c
			*m_entry = NULL;
Packit 08bd4c
			return (ARCHIVE_FATAL);
Packit 08bd4c
		}
Packit 08bd4c
		me->reg_info->compute_sum = 0;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	*m_entry = me;
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
mtree_entry_free(struct mtree_entry *me)
Packit 08bd4c
{
Packit 08bd4c
	archive_string_free(&me->parentdir);
Packit 08bd4c
	archive_string_free(&me->basename);
Packit 08bd4c
	archive_string_free(&me->pathname);
Packit 08bd4c
	archive_string_free(&me->symlink);
Packit 08bd4c
	archive_string_free(&me->uname);
Packit 08bd4c
	archive_string_free(&me->gname);
Packit 08bd4c
	archive_string_free(&me->fflags_text);
Packit 08bd4c
	free(me->dir_info);
Packit 08bd4c
	free(me->reg_info);
Packit 08bd4c
	free(me);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_mtree_header(struct archive_write *a,
Packit 08bd4c
    struct archive_entry *entry)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_writer *mtree= a->format_data;
Packit 08bd4c
	struct mtree_entry *mtree_entry;
Packit 08bd4c
	int r, r2;
Packit 08bd4c
Packit 08bd4c
	if (mtree->first) {
Packit 08bd4c
		mtree->first = 0;
Packit 08bd4c
		archive_strcat(&mtree->buf, "#mtree\n");
Packit 08bd4c
		if ((mtree->keys & SET_KEYS) == 0)
Packit 08bd4c
			mtree->output_global_set = 0;/* Disabled. */
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	mtree->entry_bytes_remaining = archive_entry_size(entry);
Packit 08bd4c
Packit 08bd4c
	/* While directory only mode, we do not handle non directory files. */
Packit 08bd4c
	if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR)
Packit 08bd4c
		return (ARCHIVE_OK);
Packit 08bd4c
Packit 08bd4c
	r2 = mtree_entry_new(a, entry, &mtree_entry);
Packit 08bd4c
	if (r2 < ARCHIVE_WARN)
Packit 08bd4c
		return (r2);
Packit 08bd4c
	r = mtree_entry_tree_add(a, &mtree_entry);
Packit 08bd4c
	if (r < ARCHIVE_WARN) {
Packit 08bd4c
		mtree_entry_free(mtree_entry);
Packit 08bd4c
		return (r);
Packit 08bd4c
	}
Packit 08bd4c
	mtree->mtree_entry = mtree_entry;
Packit 08bd4c
Packit 08bd4c
	/* If the current file is a regular file, we have to
Packit 08bd4c
	 * compute the sum of its content.
Packit 08bd4c
	 * Initialize a bunch of sum check context. */
Packit 08bd4c
	if (mtree_entry->reg_info)
Packit 08bd4c
		sum_init(mtree);
Packit 08bd4c
Packit 08bd4c
	return (r2);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
write_mtree_entry(struct archive_write *a, struct mtree_entry *me)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_writer *mtree = a->format_data;
Packit 08bd4c
	struct archive_string *str;
Packit 08bd4c
	int keys, ret;
Packit 08bd4c
Packit 08bd4c
	if (me->dir_info) {
Packit 08bd4c
		if (mtree->classic) {
Packit 08bd4c
			/*
Packit 08bd4c
			 * Output a comment line to describe the full
Packit 08bd4c
			 * pathname of the entry as mtree utility does
Packit 08bd4c
			 * while generating classic format.
Packit 08bd4c
			 */
Packit 08bd4c
			if (!mtree->dironly)
Packit 08bd4c
				archive_strappend_char(&mtree->buf, '\n');
Packit 08bd4c
			if (me->parentdir.s)
Packit 08bd4c
				archive_string_sprintf(&mtree->buf,
Packit 08bd4c
				    "# %s/%s\n",
Packit 08bd4c
				    me->parentdir.s, me->basename.s);
Packit 08bd4c
			else
Packit 08bd4c
				archive_string_sprintf(&mtree->buf,
Packit 08bd4c
				    "# %s\n",
Packit 08bd4c
				    me->basename.s);
Packit 08bd4c
		}
Packit 08bd4c
		if (mtree->output_global_set)
Packit 08bd4c
			write_global(mtree);
Packit 08bd4c
	}
Packit 08bd4c
	archive_string_empty(&mtree->ebuf);
Packit 08bd4c
	str = (mtree->indent || mtree->classic)? &mtree->ebuf : &mtree->buf;
Packit 08bd4c
Packit 08bd4c
	if (!mtree->classic && me->parentdir.s) {
Packit 08bd4c
		/*
Packit 08bd4c
		 * If generating format is not classic one(v1), output
Packit 08bd4c
		 * a full pathname.
Packit 08bd4c
		 */
Packit 08bd4c
		mtree_quote(str, me->parentdir.s);
Packit 08bd4c
		archive_strappend_char(str, '/');
Packit 08bd4c
	}
Packit 08bd4c
	mtree_quote(str, me->basename.s);
Packit 08bd4c
Packit 08bd4c
	keys = get_global_set_keys(mtree, me);
Packit 08bd4c
	if ((keys & F_NLINK) != 0 &&
Packit 08bd4c
	    me->nlink != 1 && me->filetype != AE_IFDIR)
Packit 08bd4c
		archive_string_sprintf(str, " nlink=%u", me->nlink);
Packit 08bd4c
Packit 08bd4c
	if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) {
Packit 08bd4c
		archive_strcat(str, " gname=");
Packit 08bd4c
		mtree_quote(str, me->gname.s);
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & F_UNAME) != 0 && archive_strlen(&me->uname) > 0) {
Packit 08bd4c
		archive_strcat(str, " uname=");
Packit 08bd4c
		mtree_quote(str, me->uname.s);
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & F_FLAGS) != 0) {
Packit 08bd4c
		if (archive_strlen(&me->fflags_text) > 0) {
Packit 08bd4c
			archive_strcat(str, " flags=");
Packit 08bd4c
			mtree_quote(str, me->fflags_text.s);
Packit 08bd4c
		} else if (mtree->set.processing &&
Packit 08bd4c
		    (mtree->set.keys & F_FLAGS) != 0)
Packit 08bd4c
			/* Overwrite the global parameter. */
Packit 08bd4c
			archive_strcat(str, " flags=none");
Packit 08bd4c
	}
Packit 08bd4c
	if ((keys & F_TIME) != 0)
Packit 08bd4c
		archive_string_sprintf(str, " time=%jd.%jd",
Packit 08bd4c
		    (intmax_t)me->mtime, (intmax_t)me->mtime_nsec);
Packit 08bd4c
	if ((keys & F_MODE) != 0)
Packit 08bd4c
		archive_string_sprintf(str, " mode=%o", (unsigned int)me->mode);
Packit 08bd4c
	if ((keys & F_GID) != 0)
Packit 08bd4c
		archive_string_sprintf(str, " gid=%jd", (intmax_t)me->gid);
Packit 08bd4c
	if ((keys & F_UID) != 0)
Packit 08bd4c
		archive_string_sprintf(str, " uid=%jd", (intmax_t)me->uid);
Packit 08bd4c
Packit 08bd4c
	if ((keys & F_INO) != 0)
Packit 08bd4c
		archive_string_sprintf(str, " inode=%jd", (intmax_t)me->ino);
Packit 08bd4c
	if ((keys & F_RESDEV) != 0) {
Packit 08bd4c
		archive_string_sprintf(str,
Packit 08bd4c
		    " resdevice=native,%ju,%ju",
Packit 08bd4c
		    (uintmax_t)me->devmajor,
Packit 08bd4c
		    (uintmax_t)me->devminor);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	switch (me->filetype) {
Packit 08bd4c
	case AE_IFLNK:
Packit 08bd4c
		if ((keys & F_TYPE) != 0)
Packit 08bd4c
			archive_strcat(str, " type=link");
Packit 08bd4c
		if ((keys & F_SLINK) != 0) {
Packit 08bd4c
			archive_strcat(str, " link=");
Packit 08bd4c
			mtree_quote(str, me->symlink.s);
Packit 08bd4c
		}
Packit 08bd4c
		break;
Packit 08bd4c
	case AE_IFSOCK:
Packit 08bd4c
		if ((keys & F_TYPE) != 0)
Packit 08bd4c
			archive_strcat(str, " type=socket");
Packit 08bd4c
		break;
Packit 08bd4c
	case AE_IFCHR:
Packit 08bd4c
		if ((keys & F_TYPE) != 0)
Packit 08bd4c
			archive_strcat(str, " type=char");
Packit 08bd4c
		if ((keys & F_DEV) != 0) {
Packit 08bd4c
			archive_string_sprintf(str,
Packit 08bd4c
			    " device=native,%ju,%ju",
Packit 08bd4c
			    (uintmax_t)me->rdevmajor,
Packit 08bd4c
			    (uintmax_t)me->rdevminor);
Packit 08bd4c
		}
Packit 08bd4c
		break;
Packit 08bd4c
	case AE_IFBLK:
Packit 08bd4c
		if ((keys & F_TYPE) != 0)
Packit 08bd4c
			archive_strcat(str, " type=block");
Packit 08bd4c
		if ((keys & F_DEV) != 0) {
Packit 08bd4c
			archive_string_sprintf(str,
Packit 08bd4c
			    " device=native,%ju,%ju",
Packit 08bd4c
			    (uintmax_t)me->rdevmajor,
Packit 08bd4c
			    (uintmax_t)me->rdevminor);
Packit 08bd4c
		}
Packit 08bd4c
		break;
Packit 08bd4c
	case AE_IFDIR:
Packit 08bd4c
		if ((keys & F_TYPE) != 0)
Packit 08bd4c
			archive_strcat(str, " type=dir");
Packit 08bd4c
		break;
Packit 08bd4c
	case AE_IFIFO:
Packit 08bd4c
		if ((keys & F_TYPE) != 0)
Packit 08bd4c
			archive_strcat(str, " type=fifo");
Packit 08bd4c
		break;
Packit 08bd4c
	case AE_IFREG:
Packit 08bd4c
	default:	/* Handle unknown file types as regular files. */
Packit 08bd4c
		if ((keys & F_TYPE) != 0)
Packit 08bd4c
			archive_strcat(str, " type=file");
Packit 08bd4c
		if ((keys & F_SIZE) != 0)
Packit 08bd4c
			archive_string_sprintf(str, " size=%jd",
Packit 08bd4c
			    (intmax_t)me->size);
Packit 08bd4c
		break;
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* Write a bunch of sum. */
Packit 08bd4c
	if (me->reg_info)
Packit 08bd4c
		sum_write(str, me->reg_info);
Packit 08bd4c
Packit 08bd4c
	archive_strappend_char(str, '\n');
Packit 08bd4c
	if (mtree->indent || mtree->classic)
Packit 08bd4c
		mtree_indent(mtree);
Packit 08bd4c
Packit 08bd4c
	if (mtree->buf.length > 32768) {
Packit 08bd4c
		ret = __archive_write_output(
Packit 08bd4c
			a, mtree->buf.s, mtree->buf.length);
Packit 08bd4c
		archive_string_empty(&mtree->buf);
Packit 08bd4c
	} else
Packit 08bd4c
		ret = ARCHIVE_OK;
Packit 08bd4c
	return (ret);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_writer *mtree = a->format_data;
Packit 08bd4c
	int ret;
Packit 08bd4c
Packit 08bd4c
	if (n->parentdir.s) {
Packit 08bd4c
		if (mtree->indent) {
Packit 08bd4c
			int i, pd = mtree->depth * 4;
Packit 08bd4c
			for (i = 0; i < pd; i++)
Packit 08bd4c
				archive_strappend_char(&mtree->buf, ' ');
Packit 08bd4c
		}
Packit 08bd4c
		archive_string_sprintf(&mtree->buf, "# %s/%s\n",
Packit 08bd4c
			n->parentdir.s, n->basename.s);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (mtree->indent) {
Packit 08bd4c
		archive_string_empty(&mtree->ebuf);
Packit 08bd4c
		archive_strncat(&mtree->ebuf, "..\n\n", (mtree->dironly)?3:4);
Packit 08bd4c
		mtree_indent(mtree);
Packit 08bd4c
	} else
Packit 08bd4c
		archive_strncat(&mtree->buf, "..\n\n", (mtree->dironly)?3:4);
Packit 08bd4c
Packit 08bd4c
	if (mtree->buf.length > 32768) {
Packit 08bd4c
		ret = __archive_write_output(
Packit 08bd4c
			a, mtree->buf.s, mtree->buf.length);
Packit 08bd4c
		archive_string_empty(&mtree->buf);
Packit 08bd4c
	} else
Packit 08bd4c
		ret = ARCHIVE_OK;
Packit 08bd4c
	return (ret);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Write mtree entries saved at attr_counter_set_collect() function.
Packit 08bd4c
 */
Packit 08bd4c
static int
Packit 08bd4c
write_mtree_entry_tree(struct archive_write *a)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_writer *mtree = a->format_data;
Packit 08bd4c
	struct mtree_entry *np = mtree->root;
Packit 08bd4c
	struct archive_rb_node *n;
Packit 08bd4c
	int ret;
Packit 08bd4c
Packit 08bd4c
	do {
Packit 08bd4c
		if (mtree->output_global_set) {
Packit 08bd4c
			/*
Packit 08bd4c
			 * Collect attribute information to know which value
Packit 08bd4c
			 * is frequently used among the children.
Packit 08bd4c
			 */
Packit 08bd4c
			attr_counter_set_reset(mtree);
Packit 08bd4c
			ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
Packit 08bd4c
				struct mtree_entry *e = (struct mtree_entry *)n;
Packit 08bd4c
				if (attr_counter_set_collect(mtree, e) < 0) {
Packit 08bd4c
					archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
					    "Can't allocate memory");
Packit 08bd4c
					return (ARCHIVE_FATAL);
Packit 08bd4c
				}
Packit 08bd4c
			}
Packit 08bd4c
		}
Packit 08bd4c
		if (!np->dir_info->virtual || mtree->classic) {
Packit 08bd4c
			ret = write_mtree_entry(a, np);
Packit 08bd4c
			if (ret != ARCHIVE_OK)
Packit 08bd4c
				return (ARCHIVE_FATAL);
Packit 08bd4c
		} else {
Packit 08bd4c
			/* Whenever output_global_set is enabled
Packit 08bd4c
			 * output global value(/set keywords)
Packit 08bd4c
			 * even if the directory entry is not allowed
Packit 08bd4c
			 * to be written because the global values
Packit 08bd4c
			 * can be used for the children. */
Packit 08bd4c
			if (mtree->output_global_set)
Packit 08bd4c
				write_global(mtree);
Packit 08bd4c
		}
Packit 08bd4c
		/*
Packit 08bd4c
		 * Output the attribute of all files except directory files.
Packit 08bd4c
		 */
Packit 08bd4c
		mtree->depth++;
Packit 08bd4c
		ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
Packit 08bd4c
			struct mtree_entry *e = (struct mtree_entry *)n;
Packit 08bd4c
Packit 08bd4c
			if (e->dir_info)
Packit 08bd4c
				mtree_entry_add_child_tail(np, e);
Packit 08bd4c
			else {
Packit 08bd4c
				ret = write_mtree_entry(a, e);
Packit 08bd4c
				if (ret != ARCHIVE_OK)
Packit 08bd4c
					return (ARCHIVE_FATAL);
Packit 08bd4c
			}
Packit 08bd4c
		}
Packit 08bd4c
		mtree->depth--;
Packit 08bd4c
Packit 08bd4c
		if (np->dir_info->children.first != NULL) {
Packit 08bd4c
			/*
Packit 08bd4c
			 * Descend the tree.
Packit 08bd4c
			 */
Packit 08bd4c
			np = np->dir_info->children.first;
Packit 08bd4c
			if (mtree->indent)
Packit 08bd4c
				mtree->depth++;
Packit 08bd4c
			continue;
Packit 08bd4c
		} else if (mtree->classic) {
Packit 08bd4c
			/*
Packit 08bd4c
			 * While printing mtree classic, if there are not
Packit 08bd4c
			 * any directory files(except "." and "..") in the
Packit 08bd4c
			 * directory, output two dots ".." as returning
Packit 08bd4c
			 * the parent directory.
Packit 08bd4c
			 */
Packit 08bd4c
			ret = write_dot_dot_entry(a, np);
Packit 08bd4c
			if (ret != ARCHIVE_OK)
Packit 08bd4c
				return (ARCHIVE_FATAL);
Packit 08bd4c
		}
Packit 08bd4c
Packit 08bd4c
		while (np != np->parent) {
Packit 08bd4c
			if (np->dir_info->chnext == NULL) {
Packit 08bd4c
				/*
Packit 08bd4c
				 * Ascend the tree; go back to the parent.
Packit 08bd4c
				 */
Packit 08bd4c
				if (mtree->indent)
Packit 08bd4c
					mtree->depth--;
Packit 08bd4c
				if (mtree->classic) {
Packit 08bd4c
					ret = write_dot_dot_entry(a,
Packit 08bd4c
						np->parent);
Packit 08bd4c
					if (ret != ARCHIVE_OK)
Packit 08bd4c
						return (ARCHIVE_FATAL);
Packit 08bd4c
				}
Packit 08bd4c
				np = np->parent;
Packit 08bd4c
			} else {
Packit 08bd4c
				/*
Packit 08bd4c
				 * Switch to next mtree entry in the directory.
Packit 08bd4c
				 */
Packit 08bd4c
				np = np->dir_info->chnext;
Packit 08bd4c
				break;
Packit 08bd4c
			}
Packit 08bd4c
		}
Packit 08bd4c
	} while (np != np->parent); 
Packit 08bd4c
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_mtree_finish_entry(struct archive_write *a)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_writer *mtree = a->format_data;
Packit 08bd4c
	struct mtree_entry *me;
Packit 08bd4c
Packit 08bd4c
	if ((me = mtree->mtree_entry) == NULL)
Packit 08bd4c
		return (ARCHIVE_OK);
Packit 08bd4c
	mtree->mtree_entry = NULL;
Packit 08bd4c
Packit 08bd4c
	if (me->reg_info)
Packit 08bd4c
		sum_final(mtree, me->reg_info);
Packit 08bd4c
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_mtree_close(struct archive_write *a)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_writer *mtree= a->format_data;
Packit 08bd4c
	int ret;
Packit 08bd4c
Packit 08bd4c
	if (mtree->root != NULL) {
Packit 08bd4c
		ret = write_mtree_entry_tree(a);
Packit 08bd4c
		if (ret != ARCHIVE_OK)
Packit 08bd4c
			return (ARCHIVE_FATAL);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	archive_write_set_bytes_in_last_block(&a->archive, 1);
Packit 08bd4c
Packit 08bd4c
	return __archive_write_output(a, mtree->buf.s, mtree->buf.length);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static ssize_t
Packit 08bd4c
archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_writer *mtree= a->format_data;
Packit 08bd4c
Packit 08bd4c
	if (n > mtree->entry_bytes_remaining)
Packit 08bd4c
		n = (size_t)mtree->entry_bytes_remaining;
Packit 08bd4c
	mtree->entry_bytes_remaining -= n;
Packit 08bd4c
Packit 08bd4c
	/* We don't need to compute a regular file sum */
Packit 08bd4c
	if (mtree->mtree_entry == NULL)
Packit 08bd4c
		return (n);
Packit 08bd4c
Packit 08bd4c
	if (mtree->mtree_entry->filetype == AE_IFREG)
Packit 08bd4c
		sum_update(mtree, buff, n);
Packit 08bd4c
Packit 08bd4c
	return (n);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_mtree_free(struct archive_write *a)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_writer *mtree= a->format_data;
Packit 08bd4c
Packit 08bd4c
	if (mtree == NULL)
Packit 08bd4c
		return (ARCHIVE_OK);
Packit 08bd4c
Packit 08bd4c
	/* Make sure we dot not leave any entries. */
Packit 08bd4c
	mtree_entry_register_free(mtree);
Packit 08bd4c
	archive_string_free(&mtree->cur_dirstr);
Packit 08bd4c
	archive_string_free(&mtree->ebuf);
Packit 08bd4c
	archive_string_free(&mtree->buf);
Packit 08bd4c
	attr_counter_set_free(mtree);
Packit 08bd4c
	free(mtree);
Packit 08bd4c
	a->format_data = NULL;
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_mtree_options(struct archive_write *a, const char *key,
Packit 08bd4c
    const char *value)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_writer *mtree= a->format_data;
Packit 08bd4c
	int keybit = 0;
Packit 08bd4c
Packit 08bd4c
	switch (key[0]) {
Packit 08bd4c
	case 'a':
Packit 08bd4c
		if (strcmp(key, "all") == 0)
Packit 08bd4c
			keybit = ~0;
Packit 08bd4c
		break;
Packit 08bd4c
	case 'c':
Packit 08bd4c
		if (strcmp(key, "cksum") == 0)
Packit 08bd4c
			keybit = F_CKSUM;
Packit 08bd4c
		break;
Packit 08bd4c
	case 'd':
Packit 08bd4c
		if (strcmp(key, "device") == 0)
Packit 08bd4c
			keybit = F_DEV;
Packit 08bd4c
		else if (strcmp(key, "dironly") == 0) {
Packit 08bd4c
			mtree->dironly = (value != NULL)? 1: 0;
Packit 08bd4c
			return (ARCHIVE_OK);
Packit 08bd4c
		}
Packit 08bd4c
		break;
Packit 08bd4c
	case 'f':
Packit 08bd4c
		if (strcmp(key, "flags") == 0)
Packit 08bd4c
			keybit = F_FLAGS;
Packit 08bd4c
		break;
Packit 08bd4c
	case 'g':
Packit 08bd4c
		if (strcmp(key, "gid") == 0)
Packit 08bd4c
			keybit = F_GID;
Packit 08bd4c
		else if (strcmp(key, "gname") == 0)
Packit 08bd4c
			keybit = F_GNAME;
Packit 08bd4c
		break;
Packit 08bd4c
	case 'i':
Packit 08bd4c
		if (strcmp(key, "indent") == 0) {
Packit 08bd4c
			mtree->indent = (value != NULL)? 1: 0;
Packit 08bd4c
			return (ARCHIVE_OK);
Packit 08bd4c
		} else if (strcmp(key, "inode") == 0) {
Packit 08bd4c
			keybit = F_INO;
Packit 08bd4c
		}
Packit 08bd4c
		break;
Packit 08bd4c
	case 'l':
Packit 08bd4c
		if (strcmp(key, "link") == 0)
Packit 08bd4c
			keybit = F_SLINK;
Packit 08bd4c
		break;
Packit 08bd4c
	case 'm':
Packit 08bd4c
		if (strcmp(key, "md5") == 0 ||
Packit 08bd4c
		    strcmp(key, "md5digest") == 0)
Packit 08bd4c
			keybit = F_MD5;
Packit 08bd4c
		if (strcmp(key, "mode") == 0)
Packit 08bd4c
			keybit = F_MODE;
Packit 08bd4c
		break;
Packit 08bd4c
	case 'n':
Packit 08bd4c
		if (strcmp(key, "nlink") == 0)
Packit 08bd4c
			keybit = F_NLINK;
Packit 08bd4c
		break;
Packit 08bd4c
	case 'r':
Packit 08bd4c
		if (strcmp(key, "resdevice") == 0) {
Packit 08bd4c
			keybit = F_RESDEV;
Packit 08bd4c
		} else if (strcmp(key, "ripemd160digest") == 0 ||
Packit 08bd4c
		    strcmp(key, "rmd160") == 0 ||
Packit 08bd4c
		    strcmp(key, "rmd160digest") == 0)
Packit 08bd4c
			keybit = F_RMD160;
Packit 08bd4c
		break;
Packit 08bd4c
	case 's':
Packit 08bd4c
		if (strcmp(key, "sha1") == 0 ||
Packit 08bd4c
		    strcmp(key, "sha1digest") == 0)
Packit 08bd4c
			keybit = F_SHA1;
Packit 08bd4c
		if (strcmp(key, "sha256") == 0 ||
Packit 08bd4c
		    strcmp(key, "sha256digest") == 0)
Packit 08bd4c
			keybit = F_SHA256;
Packit 08bd4c
		if (strcmp(key, "sha384") == 0 ||
Packit 08bd4c
		    strcmp(key, "sha384digest") == 0)
Packit 08bd4c
			keybit = F_SHA384;
Packit 08bd4c
		if (strcmp(key, "sha512") == 0 ||
Packit 08bd4c
		    strcmp(key, "sha512digest") == 0)
Packit 08bd4c
			keybit = F_SHA512;
Packit 08bd4c
		if (strcmp(key, "size") == 0)
Packit 08bd4c
			keybit = F_SIZE;
Packit 08bd4c
		break;
Packit 08bd4c
	case 't':
Packit 08bd4c
		if (strcmp(key, "time") == 0)
Packit 08bd4c
			keybit = F_TIME;
Packit 08bd4c
		else if (strcmp(key, "type") == 0)
Packit 08bd4c
			keybit = F_TYPE;
Packit 08bd4c
		break;
Packit 08bd4c
	case 'u':
Packit 08bd4c
		if (strcmp(key, "uid") == 0)
Packit 08bd4c
			keybit = F_UID;
Packit 08bd4c
		else if (strcmp(key, "uname") == 0)
Packit 08bd4c
			keybit = F_UNAME;
Packit 08bd4c
		else if (strcmp(key, "use-set") == 0) {
Packit 08bd4c
			mtree->output_global_set = (value != NULL)? 1: 0;
Packit 08bd4c
			return (ARCHIVE_OK);
Packit 08bd4c
		}
Packit 08bd4c
		break;
Packit 08bd4c
	}
Packit 08bd4c
	if (keybit != 0) {
Packit 08bd4c
		if (value != NULL)
Packit 08bd4c
			mtree->keys |= keybit;
Packit 08bd4c
		else
Packit 08bd4c
			mtree->keys &= ~keybit;
Packit 08bd4c
		return (ARCHIVE_OK);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* Note: The "warn" return is just to inform the options
Packit 08bd4c
	 * supervisor that we didn't handle it.  It will generate
Packit 08bd4c
	 * a suitable error if no one used this option. */
Packit 08bd4c
	return (ARCHIVE_WARN);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
archive_write_set_format_mtree_default(struct archive *_a, const char *fn)
Packit 08bd4c
{
Packit 08bd4c
	struct archive_write *a = (struct archive_write *)_a;
Packit 08bd4c
	struct mtree_writer *mtree;
Packit 08bd4c
Packit 08bd4c
	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn);
Packit 08bd4c
Packit 08bd4c
	if (a->format_free != NULL)
Packit 08bd4c
		(a->format_free)(a);
Packit 08bd4c
Packit 08bd4c
	if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {
Packit 08bd4c
		archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
		    "Can't allocate mtree data");
Packit 08bd4c
		return (ARCHIVE_FATAL);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	mtree->mtree_entry = NULL;
Packit 08bd4c
	mtree->first = 1;
Packit 08bd4c
	memset(&(mtree->set), 0, sizeof(mtree->set));
Packit 08bd4c
	mtree->keys = DEFAULT_KEYS;
Packit 08bd4c
	mtree->dironly = 0;
Packit 08bd4c
	mtree->indent = 0;
Packit 08bd4c
	archive_string_init(&mtree->ebuf);
Packit 08bd4c
	archive_string_init(&mtree->buf);
Packit 08bd4c
	mtree_entry_register_init(mtree);
Packit 08bd4c
	a->format_data = mtree;
Packit 08bd4c
	a->format_free = archive_write_mtree_free;
Packit 08bd4c
	a->format_name = "mtree";
Packit 08bd4c
	a->format_options = archive_write_mtree_options;
Packit 08bd4c
	a->format_write_header = archive_write_mtree_header;
Packit 08bd4c
	a->format_close = archive_write_mtree_close;
Packit 08bd4c
	a->format_write_data = archive_write_mtree_data;
Packit 08bd4c
	a->format_finish_entry = archive_write_mtree_finish_entry;
Packit 08bd4c
	a->archive.archive_format = ARCHIVE_FORMAT_MTREE;
Packit 08bd4c
	a->archive.archive_format_name = "mtree";
Packit 08bd4c
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
int
Packit 08bd4c
archive_write_set_format_mtree(struct archive *_a)
Packit 08bd4c
{
Packit 08bd4c
	return archive_write_set_format_mtree_default(_a,
Packit 08bd4c
		"archive_write_set_format_mtree");
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
int
Packit 08bd4c
archive_write_set_format_mtree_classic(struct archive *_a)
Packit 08bd4c
{
Packit 08bd4c
	int r;
Packit 08bd4c
Packit 08bd4c
	r = archive_write_set_format_mtree_default(_a,
Packit 08bd4c
		"archive_write_set_format_mtree_classic");
Packit 08bd4c
	if (r == ARCHIVE_OK) {
Packit 08bd4c
		struct archive_write *a = (struct archive_write *)_a;
Packit 08bd4c
		struct mtree_writer *mtree;
Packit 08bd4c
Packit 08bd4c
		mtree = (struct mtree_writer *)a->format_data;
Packit 08bd4c
Packit 08bd4c
		/* Set to output a mtree archive in classic format. */
Packit 08bd4c
		mtree->classic = 1;
Packit 08bd4c
		/* Basically, mtree classic format uses '/set' global
Packit 08bd4c
		 * value. */
Packit 08bd4c
		mtree->output_global_set = 1;
Packit 08bd4c
	}
Packit 08bd4c
	return (r);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
sum_init(struct mtree_writer *mtree)
Packit 08bd4c
{
Packit 08bd4c
Packit 08bd4c
	mtree->compute_sum = 0;
Packit 08bd4c
Packit 08bd4c
	if (mtree->keys & F_CKSUM) {
Packit 08bd4c
		mtree->compute_sum |= F_CKSUM;
Packit 08bd4c
		mtree->crc = 0;
Packit 08bd4c
		mtree->crc_len = 0;
Packit 08bd4c
	}
Packit 08bd4c
#ifdef ARCHIVE_HAS_MD5
Packit 08bd4c
	if (mtree->keys & F_MD5) {
Packit 08bd4c
		if (archive_md5_init(&mtree->md5ctx) == ARCHIVE_OK)
Packit 08bd4c
			mtree->compute_sum |= F_MD5;
Packit 08bd4c
		else
Packit 08bd4c
			mtree->keys &= ~F_MD5;/* Not supported. */
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_RMD160
Packit 08bd4c
	if (mtree->keys & F_RMD160) {
Packit 08bd4c
		if (archive_rmd160_init(&mtree->rmd160ctx) == ARCHIVE_OK)
Packit 08bd4c
			mtree->compute_sum |= F_RMD160;
Packit 08bd4c
		else
Packit 08bd4c
			mtree->keys &= ~F_RMD160;/* Not supported. */
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA1
Packit 08bd4c
	if (mtree->keys & F_SHA1) {
Packit 08bd4c
		if (archive_sha1_init(&mtree->sha1ctx) == ARCHIVE_OK)
Packit 08bd4c
			mtree->compute_sum |= F_SHA1;
Packit 08bd4c
		else
Packit 08bd4c
			mtree->keys &= ~F_SHA1;/* Not supported. */
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA256
Packit 08bd4c
	if (mtree->keys & F_SHA256) {
Packit 08bd4c
		if (archive_sha256_init(&mtree->sha256ctx) == ARCHIVE_OK)
Packit 08bd4c
			mtree->compute_sum |= F_SHA256;
Packit 08bd4c
		else
Packit 08bd4c
			mtree->keys &= ~F_SHA256;/* Not supported. */
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA384
Packit 08bd4c
	if (mtree->keys & F_SHA384) {
Packit 08bd4c
		if (archive_sha384_init(&mtree->sha384ctx) == ARCHIVE_OK)
Packit 08bd4c
			mtree->compute_sum |= F_SHA384;
Packit 08bd4c
		else
Packit 08bd4c
			mtree->keys &= ~F_SHA384;/* Not supported. */
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA512
Packit 08bd4c
	if (mtree->keys & F_SHA512) {
Packit 08bd4c
		if (archive_sha512_init(&mtree->sha512ctx) == ARCHIVE_OK)
Packit 08bd4c
			mtree->compute_sum |= F_SHA512;
Packit 08bd4c
		else
Packit 08bd4c
			mtree->keys &= ~F_SHA512;/* Not supported. */
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
sum_update(struct mtree_writer *mtree, const void *buff, size_t n)
Packit 08bd4c
{
Packit 08bd4c
	if (mtree->compute_sum & F_CKSUM) {
Packit 08bd4c
		/*
Packit 08bd4c
		 * Compute a POSIX 1003.2 checksum
Packit 08bd4c
		 */
Packit 08bd4c
		const unsigned char *p;
Packit 08bd4c
		size_t nn;
Packit 08bd4c
Packit 08bd4c
		for (nn = n, p = buff; nn--; ++p)
Packit 08bd4c
			COMPUTE_CRC(mtree->crc, *p);
Packit 08bd4c
		mtree->crc_len += n;
Packit 08bd4c
	}
Packit 08bd4c
#ifdef ARCHIVE_HAS_MD5
Packit 08bd4c
	if (mtree->compute_sum & F_MD5)
Packit 08bd4c
		archive_md5_update(&mtree->md5ctx, buff, n);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_RMD160
Packit 08bd4c
	if (mtree->compute_sum & F_RMD160)
Packit 08bd4c
		archive_rmd160_update(&mtree->rmd160ctx, buff, n);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA1
Packit 08bd4c
	if (mtree->compute_sum & F_SHA1)
Packit 08bd4c
		archive_sha1_update(&mtree->sha1ctx, buff, n);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA256
Packit 08bd4c
	if (mtree->compute_sum & F_SHA256)
Packit 08bd4c
		archive_sha256_update(&mtree->sha256ctx, buff, n);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA384
Packit 08bd4c
	if (mtree->compute_sum & F_SHA384)
Packit 08bd4c
		archive_sha384_update(&mtree->sha384ctx, buff, n);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA512
Packit 08bd4c
	if (mtree->compute_sum & F_SHA512)
Packit 08bd4c
		archive_sha512_update(&mtree->sha512ctx, buff, n);
Packit 08bd4c
#endif
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
sum_final(struct mtree_writer *mtree, struct reg_info *reg)
Packit 08bd4c
{
Packit 08bd4c
Packit 08bd4c
	if (mtree->compute_sum & F_CKSUM) {
Packit 08bd4c
		uint64_t len;
Packit 08bd4c
		/* Include the length of the file. */
Packit 08bd4c
		for (len = mtree->crc_len; len != 0; len >>= 8)
Packit 08bd4c
			COMPUTE_CRC(mtree->crc, len & 0xff);
Packit 08bd4c
		reg->crc = ~mtree->crc;
Packit 08bd4c
	}
Packit 08bd4c
#ifdef ARCHIVE_HAS_MD5
Packit 08bd4c
	if (mtree->compute_sum & F_MD5)
Packit 08bd4c
		archive_md5_final(&mtree->md5ctx, reg->buf_md5);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_RMD160
Packit 08bd4c
	if (mtree->compute_sum & F_RMD160)
Packit 08bd4c
		archive_rmd160_final(&mtree->rmd160ctx, reg->buf_rmd160);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA1
Packit 08bd4c
	if (mtree->compute_sum & F_SHA1)
Packit 08bd4c
		archive_sha1_final(&mtree->sha1ctx, reg->buf_sha1);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA256
Packit 08bd4c
	if (mtree->compute_sum & F_SHA256)
Packit 08bd4c
		archive_sha256_final(&mtree->sha256ctx, reg->buf_sha256);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA384
Packit 08bd4c
	if (mtree->compute_sum & F_SHA384)
Packit 08bd4c
		archive_sha384_final(&mtree->sha384ctx, reg->buf_sha384);
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA512
Packit 08bd4c
	if (mtree->compute_sum & F_SHA512)
Packit 08bd4c
		archive_sha512_final(&mtree->sha512ctx, reg->buf_sha512);
Packit 08bd4c
#endif
Packit 08bd4c
	/* Save what types of sum are computed. */
Packit 08bd4c
	reg->compute_sum = mtree->compute_sum;
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \
Packit 08bd4c
    defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \
Packit 08bd4c
    defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)
Packit 08bd4c
static void
Packit 08bd4c
strappend_bin(struct archive_string *s, const unsigned char *bin, int n)
Packit 08bd4c
{
Packit 08bd4c
	static const char hex[] = "0123456789abcdef";
Packit 08bd4c
	int i;
Packit 08bd4c
Packit 08bd4c
	for (i = 0; i < n; i++) {
Packit 08bd4c
		archive_strappend_char(s, hex[bin[i] >> 4]);
Packit 08bd4c
		archive_strappend_char(s, hex[bin[i] & 0x0f]);
Packit 08bd4c
	}
Packit 08bd4c
}
Packit 08bd4c
#endif
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
sum_write(struct archive_string *str, struct reg_info *reg)
Packit 08bd4c
{
Packit 08bd4c
Packit 08bd4c
	if (reg->compute_sum & F_CKSUM) {
Packit 08bd4c
		archive_string_sprintf(str, " cksum=%ju",
Packit 08bd4c
		    (uintmax_t)reg->crc);
Packit 08bd4c
	}
Packit 08bd4c
#ifdef ARCHIVE_HAS_MD5
Packit 08bd4c
	if (reg->compute_sum & F_MD5) {
Packit 08bd4c
		archive_strcat(str, " md5digest=");
Packit 08bd4c
		strappend_bin(str, reg->buf_md5, sizeof(reg->buf_md5));
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_RMD160
Packit 08bd4c
	if (reg->compute_sum & F_RMD160) {
Packit 08bd4c
		archive_strcat(str, " rmd160digest=");
Packit 08bd4c
		strappend_bin(str, reg->buf_rmd160, sizeof(reg->buf_rmd160));
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA1
Packit 08bd4c
	if (reg->compute_sum & F_SHA1) {
Packit 08bd4c
		archive_strcat(str, " sha1digest=");
Packit 08bd4c
		strappend_bin(str, reg->buf_sha1, sizeof(reg->buf_sha1));
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA256
Packit 08bd4c
	if (reg->compute_sum & F_SHA256) {
Packit 08bd4c
		archive_strcat(str, " sha256digest=");
Packit 08bd4c
		strappend_bin(str, reg->buf_sha256, sizeof(reg->buf_sha256));
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA384
Packit 08bd4c
	if (reg->compute_sum & F_SHA384) {
Packit 08bd4c
		archive_strcat(str, " sha384digest=");
Packit 08bd4c
		strappend_bin(str, reg->buf_sha384, sizeof(reg->buf_sha384));
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
#ifdef ARCHIVE_HAS_SHA512
Packit 08bd4c
	if (reg->compute_sum & F_SHA512) {
Packit 08bd4c
		archive_strcat(str, " sha512digest=");
Packit 08bd4c
		strappend_bin(str, reg->buf_sha512, sizeof(reg->buf_sha512));
Packit 08bd4c
	}
Packit 08bd4c
#endif
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
mtree_entry_cmp_node(const struct archive_rb_node *n1,
Packit 08bd4c
    const struct archive_rb_node *n2)
Packit 08bd4c
{
Packit 08bd4c
	const struct mtree_entry *e1 = (const struct mtree_entry *)n1;
Packit 08bd4c
	const struct mtree_entry *e2 = (const struct mtree_entry *)n2;
Packit 08bd4c
Packit 08bd4c
	return (strcmp(e2->basename.s, e1->basename.s));
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
mtree_entry_cmp_key(const struct archive_rb_node *n, const void *key)
Packit 08bd4c
{
Packit 08bd4c
	const struct mtree_entry *e = (const struct mtree_entry *)n;
Packit 08bd4c
Packit 08bd4c
	return (strcmp((const char *)key, e->basename.s));
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
#if defined(_WIN32) || defined(__CYGWIN__)
Packit 08bd4c
static int
Packit 08bd4c
cleanup_backslash_1(char *p)
Packit 08bd4c
{
Packit 08bd4c
	int mb, dos;
Packit 08bd4c
Packit 08bd4c
	mb = dos = 0;
Packit 08bd4c
	while (*p) {
Packit 08bd4c
		if (*(unsigned char *)p > 127)
Packit 08bd4c
			mb = 1;
Packit 08bd4c
		if (*p == '\\') {
Packit 08bd4c
			/* If we have not met any multi-byte characters,
Packit 08bd4c
			 * we can replace '\' with '/'. */
Packit 08bd4c
			if (!mb)
Packit 08bd4c
				*p = '/';
Packit 08bd4c
			dos = 1;
Packit 08bd4c
		}
Packit 08bd4c
		p++;
Packit 08bd4c
	}
Packit 08bd4c
	if (!mb || !dos)
Packit 08bd4c
		return (0);
Packit 08bd4c
	return (-1);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
cleanup_backslash_2(wchar_t *p)
Packit 08bd4c
{
Packit 08bd4c
Packit 08bd4c
	/* Convert a path-separator from '\' to  '/' */
Packit 08bd4c
	while (*p != L'\0') {
Packit 08bd4c
		if (*p == L'\\')
Packit 08bd4c
			*p = L'/';
Packit 08bd4c
		p++;
Packit 08bd4c
	}
Packit 08bd4c
}
Packit 08bd4c
#endif
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Generate a parent directory name and a base name from a pathname.
Packit 08bd4c
 */
Packit 08bd4c
static int
Packit 08bd4c
mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file,
Packit 08bd4c
    struct archive_entry *entry)
Packit 08bd4c
{
Packit 08bd4c
	const char *pathname;
Packit 08bd4c
	char *p, *dirname, *slash;
Packit 08bd4c
	size_t len;
Packit 08bd4c
	int ret = ARCHIVE_OK;
Packit 08bd4c
Packit 08bd4c
	archive_strcpy(&file->pathname, archive_entry_pathname(entry));
Packit 08bd4c
#if defined(_WIN32) || defined(__CYGWIN__)
Packit 08bd4c
	/*
Packit 08bd4c
	 * Convert a path-separator from '\' to  '/'
Packit 08bd4c
	 */
Packit 08bd4c
	if (cleanup_backslash_1(file->pathname.s) != 0) {
Packit 08bd4c
		const wchar_t *wp = archive_entry_pathname_w(entry);
Packit 08bd4c
		struct archive_wstring ws;
Packit 08bd4c
Packit 08bd4c
		if (wp != NULL) {
Packit 08bd4c
			int r;
Packit 08bd4c
			archive_string_init(&ws);
Packit 08bd4c
			archive_wstrcpy(&ws, wp);
Packit 08bd4c
			cleanup_backslash_2(ws.s);
Packit 08bd4c
			archive_string_empty(&(file->pathname));
Packit 08bd4c
			r = archive_string_append_from_wcs(&(file->pathname),
Packit 08bd4c
			    ws.s, ws.length);
Packit 08bd4c
			archive_wstring_free(&ws);
Packit 08bd4c
			if (r < 0 && errno == ENOMEM) {
Packit 08bd4c
				archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
				    "Can't allocate memory");
Packit 08bd4c
				return (ARCHIVE_FATAL);
Packit 08bd4c
			}
Packit 08bd4c
		}
Packit 08bd4c
	}
Packit 08bd4c
#else
Packit 08bd4c
	(void)a; /* UNUSED */
Packit 08bd4c
#endif
Packit 08bd4c
	pathname =  file->pathname.s;
Packit 08bd4c
	if (strcmp(pathname, ".") == 0) {
Packit 08bd4c
		archive_strcpy(&file->basename, ".");
Packit 08bd4c
		return (ARCHIVE_OK);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	archive_strcpy(&(file->parentdir), pathname);
Packit 08bd4c
Packit 08bd4c
	len = file->parentdir.length;
Packit 08bd4c
	p = dirname = file->parentdir.s;
Packit 08bd4c
Packit 08bd4c
	/*
Packit 08bd4c
	 * Remove leading '/' and '../' elements
Packit 08bd4c
	 */
Packit 08bd4c
	while (*p) {
Packit 08bd4c
		if (p[0] == '/') {
Packit 08bd4c
			p++;
Packit 08bd4c
			len--;
Packit 08bd4c
		} else if (p[0] != '.')
Packit 08bd4c
			break;
Packit 08bd4c
		else if (p[1] == '.' && p[2] == '/') {
Packit 08bd4c
			p += 3;
Packit 08bd4c
			len -= 3;
Packit 08bd4c
		} else
Packit 08bd4c
			break;
Packit 08bd4c
	}
Packit 08bd4c
	if (p != dirname) {
Packit 08bd4c
		memmove(dirname, p, len+1);
Packit 08bd4c
		p = dirname;
Packit 08bd4c
	}
Packit 08bd4c
	/*
Packit 08bd4c
	 * Remove "/","/." and "/.." elements from tail.
Packit 08bd4c
	 */
Packit 08bd4c
	while (len > 0) {
Packit 08bd4c
		size_t ll = len;
Packit 08bd4c
Packit 08bd4c
		if (len > 0 && p[len-1] == '/') {
Packit 08bd4c
			p[len-1] = '\0';
Packit 08bd4c
			len--;
Packit 08bd4c
		}
Packit 08bd4c
		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
Packit 08bd4c
			p[len-2] = '\0';
Packit 08bd4c
			len -= 2;
Packit 08bd4c
		}
Packit 08bd4c
		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
Packit 08bd4c
		    p[len-1] == '.') {
Packit 08bd4c
			p[len-3] = '\0';
Packit 08bd4c
			len -= 3;
Packit 08bd4c
		}
Packit 08bd4c
		if (ll == len)
Packit 08bd4c
			break;
Packit 08bd4c
	}
Packit 08bd4c
	while (*p) {
Packit 08bd4c
		if (p[0] == '/') {
Packit 08bd4c
			if (p[1] == '/')
Packit 08bd4c
				/* Convert '//' --> '/' */
Packit 08bd4c
				strcpy(p, p+1);
Packit 08bd4c
			else if (p[1] == '.' && p[2] == '/')
Packit 08bd4c
				/* Convert '/./' --> '/' */
Packit 08bd4c
				strcpy(p, p+2);
Packit 08bd4c
			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
Packit 08bd4c
				/* Convert 'dir/dir1/../dir2/'
Packit 08bd4c
				 *     --> 'dir/dir2/'
Packit 08bd4c
				 */
Packit 08bd4c
				char *rp = p -1;
Packit 08bd4c
				while (rp >= dirname) {
Packit 08bd4c
					if (*rp == '/')
Packit 08bd4c
						break;
Packit 08bd4c
					--rp;
Packit 08bd4c
				}
Packit 08bd4c
				if (rp > dirname) {
Packit 08bd4c
					strcpy(rp, p+3);
Packit 08bd4c
					p = rp;
Packit 08bd4c
				} else {
Packit 08bd4c
					strcpy(dirname, p+4);
Packit 08bd4c
					p = dirname;
Packit 08bd4c
				}
Packit 08bd4c
			} else
Packit 08bd4c
				p++;
Packit 08bd4c
		} else
Packit 08bd4c
			p++;
Packit 08bd4c
	}
Packit 08bd4c
	p = dirname;
Packit 08bd4c
	len = strlen(p);
Packit 08bd4c
Packit 08bd4c
	/*
Packit 08bd4c
	 * Add "./" prefix.
Packit 08bd4c
	 * NOTE: If the pathname does not have a path separator, we have
Packit 08bd4c
	 * to add "./" to the head of the pathname because mtree reader
Packit 08bd4c
	 * will suppose that it is v1(a.k.a classic) mtree format and
Packit 08bd4c
	 * change the directory unexpectedly and so it will make a wrong
Packit 08bd4c
	 * path.
Packit 08bd4c
	 */
Packit 08bd4c
	if (strcmp(p, ".") != 0 && strncmp(p, "./", 2) != 0) {
Packit 08bd4c
		struct archive_string as;
Packit 08bd4c
		archive_string_init(&as);
Packit 08bd4c
		archive_strcpy(&as, "./");
Packit 08bd4c
		archive_strncat(&as, p, len);
Packit 08bd4c
		archive_string_empty(&file->parentdir);
Packit 08bd4c
		archive_string_concat(&file->parentdir, &as);
Packit 08bd4c
		archive_string_free(&as);
Packit 08bd4c
		p = file->parentdir.s;
Packit 08bd4c
		len = archive_strlen(&file->parentdir);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/*
Packit 08bd4c
	 * Find out the position which points the last position of
Packit 08bd4c
	 * path separator('/').
Packit 08bd4c
	 */
Packit 08bd4c
	slash = NULL;
Packit 08bd4c
	for (; *p != '\0'; p++) {
Packit 08bd4c
		if (*p == '/')
Packit 08bd4c
			slash = p;
Packit 08bd4c
	}
Packit 08bd4c
	if (slash == NULL) {
Packit 08bd4c
		/* The pathname doesn't have a parent directory. */
Packit 08bd4c
		file->parentdir.length = len;
Packit 08bd4c
		archive_string_copy(&(file->basename), &(file->parentdir));
Packit 08bd4c
		archive_string_empty(&(file->parentdir));
Packit 08bd4c
		*file->parentdir.s = '\0';
Packit 08bd4c
		return (ret);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* Make a basename from file->parentdir.s and slash */
Packit 08bd4c
	*slash  = '\0';
Packit 08bd4c
	file->parentdir.length = slash - file->parentdir.s;
Packit 08bd4c
	archive_strcpy(&(file->basename),  slash + 1);
Packit 08bd4c
	return (ret);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
mtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname,
Packit 08bd4c
    struct mtree_entry **m_entry)
Packit 08bd4c
{
Packit 08bd4c
	struct archive_entry *entry;
Packit 08bd4c
	struct mtree_entry *file;
Packit 08bd4c
	int r;
Packit 08bd4c
Packit 08bd4c
	entry = archive_entry_new();
Packit 08bd4c
	if (entry == NULL) {
Packit 08bd4c
		*m_entry = NULL;
Packit 08bd4c
		archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
		    "Can't allocate memory");
Packit 08bd4c
		return (ARCHIVE_FATAL);
Packit 08bd4c
	}
Packit 08bd4c
	archive_entry_copy_pathname(entry, pathname);
Packit 08bd4c
	archive_entry_set_mode(entry, AE_IFDIR | 0755);
Packit 08bd4c
	archive_entry_set_mtime(entry, time(NULL), 0);
Packit 08bd4c
Packit 08bd4c
	r = mtree_entry_new(a, entry, &file;;
Packit 08bd4c
	archive_entry_free(entry);
Packit 08bd4c
	if (r < ARCHIVE_WARN) {
Packit 08bd4c
		*m_entry = NULL;
Packit 08bd4c
		archive_set_error(&a->archive, ENOMEM,
Packit 08bd4c
		    "Can't allocate memory");
Packit 08bd4c
		return (ARCHIVE_FATAL);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	file->dir_info->virtual = 1;
Packit 08bd4c
Packit 08bd4c
	*m_entry = file;
Packit 08bd4c
	return (ARCHIVE_OK);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)
Packit 08bd4c
{
Packit 08bd4c
        file->next = NULL;
Packit 08bd4c
        *mtree->file_list.last = file;
Packit 08bd4c
        mtree->file_list.last = &(file->next);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
mtree_entry_register_init(struct mtree_writer *mtree)
Packit 08bd4c
{
Packit 08bd4c
	mtree->file_list.first = NULL;
Packit 08bd4c
	mtree->file_list.last = &(mtree->file_list.first);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static void
Packit 08bd4c
mtree_entry_register_free(struct mtree_writer *mtree)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_entry *file, *file_next;
Packit 08bd4c
Packit 08bd4c
	file = mtree->file_list.first;
Packit 08bd4c
	while (file != NULL) {
Packit 08bd4c
		file_next = file->next;
Packit 08bd4c
		mtree_entry_free(file);
Packit 08bd4c
		file = file_next;
Packit 08bd4c
	}
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
mtree_entry_add_child_tail(struct mtree_entry *parent,
Packit 08bd4c
    struct mtree_entry *child)
Packit 08bd4c
{
Packit 08bd4c
	child->dir_info->chnext = NULL;
Packit 08bd4c
	*parent->dir_info->children.last = child;
Packit 08bd4c
	parent->dir_info->children.last = &(child->dir_info->chnext);
Packit 08bd4c
	return (1);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Find a entry from a parent entry with the name.
Packit 08bd4c
 */
Packit 08bd4c
static struct mtree_entry *
Packit 08bd4c
mtree_entry_find_child(struct mtree_entry *parent, const char *child_name)
Packit 08bd4c
{
Packit 08bd4c
	struct mtree_entry *np;
Packit 08bd4c
Packit 08bd4c
	if (parent == NULL)
Packit 08bd4c
		return (NULL);
Packit 08bd4c
	np = (struct mtree_entry *)__archive_rb_tree_find_node(
Packit 08bd4c
	    &(parent->dir_info->rbtree), child_name);
Packit 08bd4c
	return (np);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
get_path_component(char *name, size_t n, const char *fn)
Packit 08bd4c
{
Packit 08bd4c
	char *p;
Packit 08bd4c
	size_t l;
Packit 08bd4c
Packit 08bd4c
	p = strchr(fn, '/');
Packit 08bd4c
	if (p == NULL) {
Packit 08bd4c
		if ((l = strlen(fn)) == 0)
Packit 08bd4c
			return (0);
Packit 08bd4c
	} else
Packit 08bd4c
		l = p - fn;
Packit 08bd4c
	if (l > n -1)
Packit 08bd4c
		return (-1);
Packit 08bd4c
	memcpy(name, fn, l);
Packit 08bd4c
	name[l] = '\0';
Packit 08bd4c
Packit 08bd4c
	return ((int)l);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
/*
Packit 08bd4c
 * Add a new entry into the tree.
Packit 08bd4c
 */
Packit 08bd4c
static int
Packit 08bd4c
mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
Packit 08bd4c
{
Packit 08bd4c
#if defined(_WIN32) && !defined(__CYGWIN__)
Packit 08bd4c
	char name[_MAX_FNAME];/* Included null terminator size. */
Packit 08bd4c
#elif defined(NAME_MAX) && NAME_MAX >= 255
Packit 08bd4c
	char name[NAME_MAX+1];
Packit 08bd4c
#else
Packit 08bd4c
	char name[256];
Packit 08bd4c
#endif
Packit 08bd4c
	struct mtree_writer *mtree = (struct mtree_writer *)a->format_data;
Packit 08bd4c
	struct mtree_entry *dent, *file, *np;
Packit 08bd4c
	const char *fn, *p;
Packit 08bd4c
	int l, r;
Packit 08bd4c
Packit 08bd4c
	file = *filep;
Packit 08bd4c
	if (file->parentdir.length == 0 && file->basename.length == 1 &&
Packit 08bd4c
	    file->basename.s[0] == '.') {
Packit 08bd4c
		file->parent = file;
Packit 08bd4c
		if (mtree->root != NULL) {
Packit 08bd4c
			np = mtree->root;
Packit 08bd4c
			goto same_entry;
Packit 08bd4c
		}
Packit 08bd4c
		mtree->root = file;
Packit 08bd4c
		mtree_entry_register_add(mtree, file);
Packit 08bd4c
		return (ARCHIVE_OK);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	if (file->parentdir.length == 0) {
Packit 08bd4c
		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit 08bd4c
		    "Internal programing error "
Packit 08bd4c
		    "in generating canonical name for %s",
Packit 08bd4c
		    file->pathname.s);
Packit 08bd4c
		return (ARCHIVE_FAILED);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	fn = p = file->parentdir.s;
Packit 08bd4c
Packit 08bd4c
	/*
Packit 08bd4c
	 * If the path of the parent directory of `file' entry is
Packit 08bd4c
	 * the same as the path of `cur_dirent', add `file' entry to
Packit 08bd4c
	 * `cur_dirent'.
Packit 08bd4c
	 */
Packit 08bd4c
	if (archive_strlen(&(mtree->cur_dirstr))
Packit 08bd4c
	      == archive_strlen(&(file->parentdir)) &&
Packit 08bd4c
	    strcmp(mtree->cur_dirstr.s, fn) == 0) {
Packit 08bd4c
		if (!__archive_rb_tree_insert_node(
Packit 08bd4c
		    &(mtree->cur_dirent->dir_info->rbtree),
Packit 08bd4c
		    (struct archive_rb_node *)file)) {
Packit 08bd4c
			/* There is the same name in the tree. */
Packit 08bd4c
			np = (struct mtree_entry *)__archive_rb_tree_find_node(
Packit 08bd4c
			    &(mtree->cur_dirent->dir_info->rbtree),
Packit 08bd4c
			    file->basename.s);
Packit 08bd4c
			goto same_entry;
Packit 08bd4c
		}
Packit 08bd4c
		file->parent = mtree->cur_dirent;
Packit 08bd4c
		mtree_entry_register_add(mtree, file);
Packit 08bd4c
		return (ARCHIVE_OK);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	dent = mtree->root;
Packit 08bd4c
	for (;;) {
Packit 08bd4c
		l = get_path_component(name, sizeof(name), fn);
Packit 08bd4c
		if (l == 0) {
Packit 08bd4c
			np = NULL;
Packit 08bd4c
			break;
Packit 08bd4c
		}
Packit 08bd4c
		if (l < 0) {
Packit 08bd4c
			archive_set_error(&a->archive,
Packit 08bd4c
			    ARCHIVE_ERRNO_MISC,
Packit 08bd4c
			    "A name buffer is too small");
Packit 08bd4c
			return (ARCHIVE_FATAL);
Packit 08bd4c
		}
Packit 08bd4c
		if (l == 1 && name[0] == '.' && dent != NULL &&
Packit 08bd4c
		    dent == mtree->root) {
Packit 08bd4c
			fn += l;
Packit 08bd4c
			if (fn[0] == '/')
Packit 08bd4c
				fn++;
Packit 08bd4c
			continue;
Packit 08bd4c
		}
Packit 08bd4c
Packit 08bd4c
		np = mtree_entry_find_child(dent, name);
Packit 08bd4c
		if (np == NULL || fn[0] == '\0')
Packit 08bd4c
			break;
Packit 08bd4c
Packit 08bd4c
		/* Find next sub directory. */
Packit 08bd4c
		if (!np->dir_info) {
Packit 08bd4c
			/* NOT Directory! */
Packit 08bd4c
			archive_set_error(&a->archive,
Packit 08bd4c
			    ARCHIVE_ERRNO_MISC,
Packit 08bd4c
			    "`%s' is not directory, we cannot insert `%s' ",
Packit 08bd4c
			    np->pathname.s, file->pathname.s);
Packit 08bd4c
			return (ARCHIVE_FAILED);
Packit 08bd4c
		}
Packit 08bd4c
		fn += l;
Packit 08bd4c
		if (fn[0] == '/')
Packit 08bd4c
			fn++;
Packit 08bd4c
		dent = np;
Packit 08bd4c
	}
Packit 08bd4c
	if (np == NULL) {
Packit 08bd4c
		/*
Packit 08bd4c
		 * Create virtual parent directories.
Packit 08bd4c
		 */
Packit 08bd4c
		while (fn[0] != '\0') {
Packit 08bd4c
			struct mtree_entry *vp;
Packit 08bd4c
			struct archive_string as;
Packit 08bd4c
Packit 08bd4c
			archive_string_init(&as);
Packit 08bd4c
			archive_strncat(&as, p, fn - p + l);
Packit 08bd4c
			if (as.s[as.length-1] == '/') {
Packit 08bd4c
				as.s[as.length-1] = '\0';
Packit 08bd4c
				as.length--;
Packit 08bd4c
			}
Packit 08bd4c
			r = mtree_entry_create_virtual_dir(a, as.s, &vp);
Packit 08bd4c
			archive_string_free(&as);
Packit 08bd4c
			if (r < ARCHIVE_WARN)
Packit 08bd4c
				return (r);
Packit 08bd4c
Packit 08bd4c
			if (strcmp(vp->pathname.s, ".") == 0) {
Packit 08bd4c
				vp->parent = vp;
Packit 08bd4c
				mtree->root = vp;
Packit 08bd4c
			} else {
Packit 08bd4c
				__archive_rb_tree_insert_node(
Packit 08bd4c
				    &(dent->dir_info->rbtree),
Packit 08bd4c
				    (struct archive_rb_node *)vp);
Packit 08bd4c
				vp->parent = dent;
Packit 08bd4c
			}
Packit 08bd4c
			mtree_entry_register_add(mtree, vp);
Packit 08bd4c
			np = vp;
Packit 08bd4c
Packit 08bd4c
			fn += l;
Packit 08bd4c
			if (fn[0] == '/')
Packit 08bd4c
				fn++;
Packit 08bd4c
			l = get_path_component(name, sizeof(name), fn);
Packit 08bd4c
			if (l < 0) {
Packit 08bd4c
				archive_string_free(&as);
Packit 08bd4c
				archive_set_error(&a->archive,
Packit 08bd4c
				    ARCHIVE_ERRNO_MISC,
Packit 08bd4c
				    "A name buffer is too small");
Packit 08bd4c
				return (ARCHIVE_FATAL);
Packit 08bd4c
			}
Packit 08bd4c
			dent = np;
Packit 08bd4c
		}
Packit 08bd4c
Packit 08bd4c
		/* Found out the parent directory where `file' can be
Packit 08bd4c
		 * inserted. */
Packit 08bd4c
		mtree->cur_dirent = dent;
Packit 08bd4c
		archive_string_empty(&(mtree->cur_dirstr));
Packit 08bd4c
		archive_string_ensure(&(mtree->cur_dirstr),
Packit 08bd4c
		    archive_strlen(&(dent->parentdir)) +
Packit 08bd4c
		    archive_strlen(&(dent->basename)) + 2);
Packit 08bd4c
		if (archive_strlen(&(dent->parentdir)) +
Packit 08bd4c
		    archive_strlen(&(dent->basename)) == 0)
Packit 08bd4c
			mtree->cur_dirstr.s[0] = 0;
Packit 08bd4c
		else {
Packit 08bd4c
			if (archive_strlen(&(dent->parentdir)) > 0) {
Packit 08bd4c
				archive_string_copy(&(mtree->cur_dirstr),
Packit 08bd4c
				    &(dent->parentdir));
Packit 08bd4c
				archive_strappend_char(
Packit 08bd4c
				    &(mtree->cur_dirstr), '/');
Packit 08bd4c
			}
Packit 08bd4c
			archive_string_concat(&(mtree->cur_dirstr),
Packit 08bd4c
			    &(dent->basename));
Packit 08bd4c
		}
Packit 08bd4c
Packit 08bd4c
		if (!__archive_rb_tree_insert_node(
Packit 08bd4c
		    &(dent->dir_info->rbtree),
Packit 08bd4c
		    (struct archive_rb_node *)file)) {
Packit 08bd4c
			np = (struct mtree_entry *)__archive_rb_tree_find_node(
Packit 08bd4c
			    &(dent->dir_info->rbtree), file->basename.s);
Packit 08bd4c
			goto same_entry;
Packit 08bd4c
		}
Packit 08bd4c
		file->parent = dent;
Packit 08bd4c
		mtree_entry_register_add(mtree, file);
Packit 08bd4c
		return (ARCHIVE_OK);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
same_entry:
Packit 08bd4c
	/*
Packit 08bd4c
	 * We have already has the entry the filename of which is
Packit 08bd4c
	 * the same.
Packit 08bd4c
	 */
Packit 08bd4c
	r = mtree_entry_exchange_same_entry(a, np, file);
Packit 08bd4c
	if (r < ARCHIVE_WARN)
Packit 08bd4c
		return (r);
Packit 08bd4c
	if (np->dir_info)
Packit 08bd4c
		np->dir_info->virtual = 0;
Packit 08bd4c
	*filep = np;
Packit 08bd4c
	mtree_entry_free(file);
Packit 08bd4c
	return (ARCHIVE_WARN);
Packit 08bd4c
}
Packit 08bd4c
Packit 08bd4c
static int
Packit 08bd4c
mtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np,
Packit 08bd4c
    struct mtree_entry *file)
Packit 08bd4c
{
Packit 08bd4c
Packit 08bd4c
	if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) {
Packit 08bd4c
		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
Packit 08bd4c
		    "Found duplicate entries `%s' and its file type is "
Packit 08bd4c
		    "different",
Packit 08bd4c
		    np->pathname.s);
Packit 08bd4c
		return (ARCHIVE_FAILED);
Packit 08bd4c
	}
Packit 08bd4c
Packit 08bd4c
	/* Update the existent mtree entry's attributes by the new one's. */
Packit 08bd4c
	archive_string_empty(&np->symlink);
Packit 08bd4c
	archive_string_concat(&np->symlink, &file->symlink);
Packit 08bd4c
	archive_string_empty(&np->uname);
Packit 08bd4c
	archive_string_concat(&np->uname, &file->uname);
Packit 08bd4c
	archive_string_empty(&np->gname);
Packit 08bd4c
	archive_string_concat(&np->gname, &file->gname);
Packit 08bd4c
	archive_string_empty(&np->fflags_text);
Packit 08bd4c
	archive_string_concat(&np->fflags_text, &file->fflags_text);
Packit 08bd4c
	np->nlink = file->nlink;
Packit 08bd4c
	np->filetype = file->filetype;
Packit 08bd4c
	np->mode = file->mode;
Packit 08bd4c
	np->size = file->size;
Packit 08bd4c
	np->uid = file->uid;
Packit 08bd4c
	np->gid = file->gid;
Packit 08bd4c
	np->fflags_set = file->fflags_set;
Packit 08bd4c
	np->fflags_clear = file->fflags_clear;
Packit 08bd4c
	np->mtime = file->mtime;
Packit 08bd4c
	np->mtime_nsec = file->mtime_nsec;
Packit 08bd4c
	np->rdevmajor = file->rdevmajor;
Packit 08bd4c
	np->rdevminor = file->rdevminor;
Packit 08bd4c
	np->devmajor = file->devmajor;
Packit 08bd4c
	np->devminor = file->devminor;
Packit 08bd4c
	np->ino = file->ino;
Packit 08bd4c
Packit 08bd4c
	return (ARCHIVE_WARN);
Packit 08bd4c
}