1/*
2 * Copyright (c) 2005-2008 Rob Braun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Rob Braun nor the names of his contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * 03-Apr-2005
31 * DRI: Rob Braun <bbraun@synack.net>
32 */
33/*
34 * Portions Copyright 2006, Apple Computer, Inc.
35 * Christopher Ryan <ryanc@apple.com>
36 */
37
38#define _FILE_OFFSET_BITS 64
39
40#include "config.h"
41#include <stdlib.h>
42#include <stdio.h>
43#include <string.h>
44#include <unistd.h>
45#include <fcntl.h>
46#include <libgen.h>
47#include <errno.h>
48#include <limits.h>
49#include <sys/stat.h>
50#include <arpa/inet.h> /* for ntoh{l,s} */
51#include <inttypes.h>  /* for PRIu64 */
52#include <time.h>
53#include <libxml/xmlwriter.h>
54#include <libxml/xmlreader.h>
55#include <libxml/xmlstring.h>
56#ifndef HAVE_ASPRINTF
57#include "asprintf.h"
58#endif
59#include "xar.h"
60#include "filetree.h"
61#include "archive.h"
62#include "signature.h"
63#include "arcmod.h"
64#include "io.h"
65#include "util.h"
66#include "subdoc.h"
67#include "darwinattr.h"
68
69#ifndef O_EXLOCK
70#define O_EXLOCK 0
71#endif
72#ifndef O_SHLOCK
73#define O_SHLOCK 0
74#endif
75
76#ifndef LONG_MAX
77#define LONG_MAX INT32_MAX
78#endif
79#ifndef LONG_MIN
80#define LONG_MIN INT32_MIN
81#endif
82
83#if LIBXML_VERSION < 20618
84#define xmlDictCleanup()	/* function doesn't exist in older API */
85#endif
86
87static int32_t xar_unserialize(xar_t x);
88void xar_serialize(xar_t x, const char *file);
89
90/* xar_new
91 * Returns: newly allocated xar_t structure
92 * Summary: just does basicallocation and initialization of
93 * xar_t structure.
94 */
95static xar_t xar_new() {
96	xar_t ret;
97	ret = malloc(sizeof(struct __xar_t));
98	if(!ret) return NULL;
99	memset(XAR(ret), 0, sizeof(struct __xar_t));
100	XAR(ret)->readbuf_len = 4096;
101	XAR(ret)->readbuf = malloc(XAR(ret)->readbuf_len);
102	if(!XAR(ret)->readbuf) {
103		free((void *)ret);
104		return NULL;
105	}
106	XAR(ret)->offset = 0;
107
108	XAR(ret)->zs.zalloc = Z_NULL;
109	XAR(ret)->zs.zfree = Z_NULL;
110	XAR(ret)->zs.opaque = Z_NULL;
111	XAR(ret)->ino_hash = xmlHashCreate(0);
112	XAR(ret)->link_hash = xmlHashCreate(0);
113	XAR(ret)->csum_hash = xmlHashCreate(0);
114	XAR(ret)->subdocs = NULL;
115
116	XAR(ret)->attrcopy_to_heap = xar_attrcopy_to_heap;
117	XAR(ret)->attrcopy_from_heap = xar_attrcopy_from_heap;
118	XAR(ret)->heap_to_archive = xar_heap_to_archive;
119
120	return ret;
121}
122
123/* xar_parse_header
124 * x: archive to operate on.
125 * Returns: 0 on success, -1 on failure
126 * Summary: internal helper function to read in the xar header.
127 */
128static int32_t xar_parse_header(xar_t x) {
129	ssize_t r;
130	int off = 0;
131	int sz2read = 0;
132
133	/* read just the magic, verify it, read the header length,
134	 * then read in the size of the header according to the
135	 * recorded header length, or the length of the structure
136	 * we expect, whichever is smaller.  Then seek forward
137	 * if the recorded header length is greater than the
138	 * expected header length.
139	 */
140	r = xar_read_fd(XAR(x)->fd, (char *)&XAR(x)->header.magic+off, sizeof(XAR(x)->header.magic)-off);
141	if ( r == -1 )
142		return r;
143
144	/* Verify the header.  If the header doesn't match, exit without
145	 * attempting to read any more.
146	 */
147	XAR(x)->header.magic = ntohl(XAR(x)->header.magic);
148
149	if( XAR(x)->header.magic != XAR_HEADER_MAGIC ) {
150		return -1;
151	}
152
153	r = xar_read_fd(XAR(x)->fd, (char *)&XAR(x)->header.size+off, sizeof(XAR(x)->header.size)-off);
154	if ( r == -1 )
155		return r;
156
157	XAR(x)->header.size = ntohs(XAR(x)->header.size);
158
159	if( XAR(x)->header.size > sizeof(xar_header_t) )
160		sz2read = sizeof(xar_header_t);
161	else
162		sz2read = XAR(x)->header.size;
163
164	off = sizeof(XAR(x)->header.magic) + sizeof(XAR(x)->header.size);
165	r = xar_read_fd(XAR(x)->fd, ((char *)&XAR(x)->header)+off, sizeof(xar_header_t)-off);
166	if ( r == -1 )
167		return r;
168
169	XAR(x)->header.version = ntohs(XAR(x)->header.version);
170	XAR(x)->header.toc_length_compressed = xar_ntoh64(XAR(x)->header.toc_length_compressed);
171	XAR(x)->header.toc_length_uncompressed = xar_ntoh64(XAR(x)->header.toc_length_uncompressed);
172	XAR(x)->header.cksum_alg = ntohl(XAR(x)->header.cksum_alg);
173
174	off = XAR(x)->header.size - sz2read;
175	if( off > 0 )
176		r = lseek(XAR(x)->fd, (off_t)off, SEEK_CUR);
177
178	if ( (r == -1) && (errno != ESPIPE) )
179		/* Some fatal error here perhaps? */ ;
180
181	return 0;
182}
183
184/* xar_open
185 * file: filename to open
186 * flags: flags on how to open the file.  0 for readonly, !0 for read/write
187 * Returns: allocated and initialized xar structure with an open
188 * file descriptor to the target xar file.  If the xarchive is opened
189 * for writing, the file is created, and a heap file is opened.
190 */
191xar_t xar_open(const char *file, int32_t flags) {
192	xar_t ret;
193
194	ret = xar_new();
195	if( !ret ) return NULL;
196	if( !file )
197		file = "-";
198	XAR(ret)->filename = strdup(file);
199	if( flags ) { // writing
200		char *tmp1, *tmp2, *tmp3, *tmp4;
201		tmp1 = tmp2 = strdup(file);
202		tmp3 = dirname(tmp2);
203		XAR(ret)->dirname = strdup(tmp3);
204		/* Create the heap file in the directory which will contain
205		 * the target archive.  /tmp or elsewhere may fill up.
206		 */
207		asprintf(&tmp4, "%s/xar.heap.XXXXXX", tmp3);
208		free(tmp1);
209		if( strcmp(file, "-") == 0 )
210			XAR(ret)->fd = 1;
211		else{
212			XAR(ret)->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_EXLOCK, 0644);
213			if( (-1 == XAR(ret)->fd ) && (ENOTSUP == errno) ){
214				XAR(ret)->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC , 0644);
215			}
216		}
217		XAR(ret)->heap_fd = mkstemp(tmp4);
218		if( XAR(ret)->heap_fd < 0 ) {
219			close(XAR(ret)->fd);
220			free(XAR(ret));
221			return NULL;
222		}
223
224		unlink(tmp4);
225		free(tmp4);
226
227		deflateInit(&XAR(ret)->zs, Z_BEST_COMPRESSION);
228
229		if( XAR(ret)->fd < 0 ) {
230			xar_close(ret);
231			return NULL;
232		}
233
234		/* default to using sha1, if nothing else is
235		 * specified.
236		 */
237		XAR(ret)->heap_offset += 20;
238		XAR(ret)->heap_len += 20;
239
240		xar_opt_set(ret, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP);
241		xar_opt_set(ret, XAR_OPT_FILECKSUM, XAR_OPT_VAL_SHA1);
242		xar_opt_set(ret, XAR_OPT_TOCCKSUM, XAR_OPT_VAL_SHA1);
243	} else {
244		if( strcmp(file, "-") == 0 )
245			XAR(ret)->fd = 0;
246		else{
247			XAR(ret)->fd = open(file, O_RDONLY | O_SHLOCK);
248
249			if( (-1 == XAR(ret)->fd ) && (ENOTSUP == errno) ){
250				XAR(ret)->fd = open(file, O_RDONLY);
251			}
252
253		}
254		XAR(ret)->heap_fd = -1;
255		inflateInit(&XAR(ret)->zs);
256		if( XAR(ret)->fd < 0 ) {
257			xar_close(ret);
258			return NULL;
259		}
260
261		if( xar_parse_header(ret) != 0 ) {
262			xar_close(ret);
263			return NULL;
264		}
265
266		switch(XAR(ret)->header.cksum_alg) {
267		case XAR_CKSUM_NONE:
268			break;
269		case XAR_CKSUM_SHA1:
270			XAR(ret)->toc_hash_ctx = xar_hash_new("sha1", (void *)ret);
271			break;
272		case XAR_CKSUM_SHA256:
273			XAR(ret)->toc_hash_ctx = xar_hash_new("sha256", (void *)ret);
274			break;
275		case XAR_CKSUM_SHA512:
276			XAR(ret)->toc_hash_ctx = xar_hash_new("sha512", (void *)ret);
277			break;
278		case XAR_CKSUM_MD5:
279			XAR(ret)->toc_hash_ctx = xar_hash_new("md5", (void *)ret);
280			break;
281		default:
282			fprintf(stderr, "Unknown hashing algorithm, skipping\n");
283			break;
284		};
285
286		if( xar_unserialize(ret) != 0 ) {
287			xar_close(ret);
288			return NULL;
289		}
290
291		/* check for inconsistency between checksum style in header,
292		 * and the one described in the TOC; otherwise, you can flip
293		 * the header bit to XAR_CKSUM_NONE, and nothing will ever
294		 * verify that the TOC matches the checksum stored in the
295		 * heap, and the signature check will pass on a modified
296		 * file! <rdar://problem/6134714>
297		 */
298		int cksum_match = 0;
299		const char* cksum_style = xar_attr_get(XAR_FILE(ret), "checksum", "style");
300		switch(XAR(ret)->header.cksum_alg) {
301			case XAR_CKSUM_NONE:
302				cksum_match = (cksum_style == NULL || strcmp(cksum_style, XAR_OPT_VAL_NONE) == 0);
303				break;
304			case XAR_CKSUM_SHA1:
305				cksum_match = (cksum_style != NULL && strcmp(cksum_style, XAR_OPT_VAL_SHA1) == 0);
306				break;
307			case XAR_CKSUM_SHA256:
308				cksum_match = (cksum_style != NULL && strcmp(cksum_style, XAR_OPT_VAL_SHA256) == 0);
309				break;
310			case XAR_CKSUM_SHA512:
311				cksum_match = (cksum_style != NULL && strcmp(cksum_style, XAR_OPT_VAL_SHA512) == 0);
312				break;
313			case XAR_CKSUM_MD5:
314				cksum_match = (cksum_style != NULL && strcmp(cksum_style, XAR_OPT_VAL_MD5) == 0);
315				break;
316			default:
317				cksum_match = 0;
318				break;
319		}
320		if( !cksum_match ) {
321			fprintf(stderr, "Checksum style mismatch!\n");
322			xar_close(ret);
323			return NULL;
324		}
325
326		/* also check for consistency between the checksum style and
327		 * the existence (or not) of signatures: since the signature
328		 * is signing the checksum, we must have a checksum to verify
329		 * that the TOC has not been modified <rdar://problem/6134714>
330		 */
331		if( xar_signature_first(ret) != NULL && XAR(ret)->header.cksum_alg == XAR_CKSUM_NONE ) {
332			fprintf(stderr, "Checksum/signature mismatch!\n");
333			xar_close(ret);
334			return NULL;
335		}
336
337		/* If we aren't checksumming the TOC, we're done */
338		if( !XAR(ret)->toc_hash_ctx )
339			return ret;
340
341        /* if TOC specifies a location for the checksum, make sure that
342         * we read the checksum from there: this is required for an archive
343         * with a signature, because the signature will be checked against
344         * the checksum at the specified location <rdar://problem/7041949>
345         */
346		const char *value;
347		uint64_t offset = 0;
348		uint64_t length = 0;
349		if( xar_prop_get( XAR_FILE(ret) , "checksum/offset", &value) == 0 ) {
350			errno = 0;
351			offset = strtoull( value, (char **)NULL, 10);
352			if( errno != 0 ) {
353				xar_close(ret);
354				return NULL;
355			}
356		} else if( xar_signature_first(ret) != NULL ) {
357			// All archives that have a signature also specify the location
358			// of the checksum.  If the location isn't specified, error out.
359			xar_close(ret);
360			return NULL;
361		}
362
363		XAR(ret)->heap_offset = xar_get_heap_offset(ret) + offset;
364		if( lseek(XAR(ret)->fd, XAR(ret)->heap_offset, SEEK_SET) == -1 ) {
365			xar_close(ret);
366			return NULL;
367		}
368		if( xar_prop_get( XAR_FILE(ret) , "checksum/size", &value) == 0 ) {
369			errno = 0;
370			length = strtoull( value, (char **)NULL, 10);
371			if( errno != 0 ) {
372				xar_close(ret);
373				return NULL;
374			}
375		} else if( xar_signature_first(ret) != NULL ) {
376			xar_close(ret);
377			return NULL;
378		}
379
380		size_t tlen = 0;
381		void *toccksum = xar_hash_finish(XAR(ret)->toc_hash_ctx, &tlen);
382
383		if( length != tlen ) {
384			free(toccksum);
385			xar_close(ret);
386			return NULL;
387		}
388
389		void *cval = calloc(1, tlen);
390		if( ! cval ) {
391			free(toccksum);
392			xar_close(ret);
393			return NULL;
394		}
395
396		xar_read_fd(XAR(ret)->fd, cval, tlen);
397		XAR(ret)->heap_offset += tlen;
398		if( memcmp(cval, toccksum, tlen) != 0 ) {
399			fprintf(stderr, "Checksums do not match!\n");
400			free(toccksum);
401			free(cval);
402			xar_close(ret);
403			return NULL;
404		}
405
406		free(toccksum);
407		free(cval);
408	}
409
410	return ret;
411}
412
413/* xar_close
414 * x: the xar_t to close
415 * Summary: closes all open file descriptors, frees all
416 * file structures and options, deallocates the xar_t its self.
417 * Returns 0 for success, -1 for failure.
418 */
419int xar_close(xar_t x) {
420	xar_attr_t a;
421	xar_file_t f;
422	int ret, retval = 0;
423
424	/* If we're creating an archive */
425	if( XAR(x)->heap_fd != -1 ) {
426		char *tmpser;
427		void *rbuf, *wbuf = NULL;
428		int fd, r, off, wbytes, rbytes;
429		long rsize, wsize;
430		z_stream zs;
431		uint64_t ungztoc, gztoc;
432		int tocfd;
433		char timestr[128];
434		struct tm tmptm;
435		time_t t;
436
437		tmpser = (char *)xar_opt_get(x, XAR_OPT_TOCCKSUM);
438		/* If no checksum type is specified, default to sha1 */
439		if( !tmpser ) tmpser = XAR_OPT_VAL_SHA1;
440
441		if( (strcmp(tmpser, XAR_OPT_VAL_NONE) != 0) ) {
442			xar_prop_set(XAR_FILE(x), "checksum", NULL);
443			if( strcmp(tmpser, XAR_OPT_VAL_SHA1) == 0 ) {
444				XAR(x)->toc_hash_ctx = xar_hash_new("sha1", (void *)x);
445				XAR(x)->header.cksum_alg = htonl(XAR_CKSUM_SHA1);
446				xar_attr_set(XAR_FILE(x), "checksum", "style", XAR_OPT_VAL_SHA1);
447				xar_prop_set(XAR_FILE(x), "checksum/size", "20");
448			}
449			if( strcmp(tmpser, XAR_OPT_VAL_SHA256) == 0 ) {
450				XAR(x)->toc_hash_ctx = xar_hash_new("sha256", (void *)x);
451				XAR(x)->header.cksum_alg = htonl(XAR_CKSUM_SHA256);
452				xar_attr_set(XAR_FILE(x), "checksum", "style", XAR_OPT_VAL_SHA256);
453				xar_prop_set(XAR_FILE(x), "checksum/size", "32");
454			}
455			if( strcmp(tmpser, XAR_OPT_VAL_SHA512) == 0 ) {
456				XAR(x)->toc_hash_ctx = xar_hash_new("sha512", (void *)x);
457				XAR(x)->header.cksum_alg = htonl(XAR_CKSUM_SHA512);
458				xar_attr_set(XAR_FILE(x), "checksum", "style", XAR_OPT_VAL_SHA512);
459				xar_prop_set(XAR_FILE(x), "checksum/size", "64");
460			}
461			if( strcmp(tmpser, XAR_OPT_VAL_MD5) == 0 ) {
462				XAR(x)->toc_hash_ctx = xar_hash_new("md5", (void *)x);
463				XAR(x)->header.cksum_alg = htonl(XAR_CKSUM_MD5);
464				xar_attr_set(XAR_FILE(x), "checksum", "style", XAR_OPT_VAL_MD5);
465				xar_prop_set(XAR_FILE(x), "checksum/size", "16");
466			}
467
468			xar_prop_set(XAR_FILE(x), "checksum/offset", "0");
469		} else {
470			XAR(x)->header.cksum_alg = XAR_CKSUM_NONE;
471		}
472
473		t = time(NULL);
474		gmtime_r(&t, &tmptm);
475		memset(timestr, 0, sizeof(timestr));
476		strftime(timestr, sizeof(timestr), "%FT%T", &tmptm);
477		xar_prop_set(XAR_FILE(x), "creation-time", timestr);
478
479		/* serialize the toc to a tmp file */
480		asprintf(&tmpser, "%s/xar.toc.XXXXXX", XAR(x)->dirname);
481		fd = mkstemp(tmpser);
482		xar_serialize(x, tmpser);
483		unlink(tmpser);
484		free(tmpser);
485		asprintf(&tmpser, "%s/xar.toc.XXXXXX", XAR(x)->dirname);
486		tocfd = mkstemp(tmpser);
487		unlink(tmpser);
488		free(tmpser);
489
490
491		/* read the toc from the tmp file, compress it, and write it
492	 	* out to the archive.
493	 	*/
494		rsize = wsize = 4096;
495		const char * opt = xar_opt_get(x, XAR_OPT_RSIZE);
496		if ( opt ) {
497		  rsize = strtol(opt, NULL, 0);
498		  if ( ((rsize == LONG_MAX) || (rsize == LONG_MIN)) && (errno == ERANGE) ) {
499		    rsize = wsize;
500		  }
501		}
502
503		rbuf = malloc(rsize);
504		if( !rbuf ) {
505			retval = -1;
506			close(fd);
507			close(tocfd);
508			goto CLOSE_BAIL;
509		}
510		zs.zalloc = Z_NULL;
511		zs.zfree = Z_NULL;
512		zs.opaque = Z_NULL;
513		deflateInit(&zs, Z_BEST_COMPRESSION);
514
515		ungztoc = gztoc = 0;
516
517		while(1) {
518			r = read(fd, rbuf, rsize);
519			if( (r < 0) && (errno == EINTR) )
520				continue;
521			if( r == 0 )
522				break;
523
524			ungztoc += r;
525
526			zs.avail_in = r;
527			zs.next_in = (void *)rbuf;
528			zs.next_out = NULL;
529			zs.avail_out = 0;
530
531			wsize = rsize/2;
532
533			off = 0;
534			while( zs.avail_in != 0 ) {
535				wsize *= 2;
536				wbuf = realloc(wbuf, wsize);
537
538				zs.next_out = ((unsigned char *)wbuf) + off;
539				zs.avail_out = wsize - off;
540
541				ret = deflate(&zs, Z_SYNC_FLUSH);
542				off = wsize - zs.avail_out;
543			}
544
545			wbytes = off;
546			off = 0;
547			do {
548				r = write(tocfd, ((char *)wbuf)+off, wbytes-off);
549				if( (r < 0) && (errno == EINTR) )
550					continue;
551				if( r < 0 ) {
552					xar_err_new(x);
553					xar_err_set_string(x, "Error closing xar archive");
554					retval = -1;
555					goto CLOSEEND;
556				}
557				if( XAR(x)->toc_hash_ctx )
558					xar_hash_update(XAR(x)->toc_hash_ctx, ((char*)wbuf)+off, r);
559				off += r;
560				gztoc += r;
561			} while( off < wbytes );
562
563		}
564
565		zs.next_in = NULL;
566		zs.avail_in = 0;
567		zs.next_out = wbuf;
568		zs.avail_out = wsize;
569
570		deflate(&zs, Z_FINISH);
571		r = write(tocfd, wbuf, wsize - zs.avail_out);
572		gztoc += r;
573		if( XAR(x)->toc_hash_ctx )
574			xar_hash_update(XAR(x)->toc_hash_ctx, wbuf, r);
575
576		deflateEnd(&zs);
577
578		/* populate the header and write it out */
579		XAR(x)->header.magic = htonl(XAR_HEADER_MAGIC);
580		XAR(x)->header.size = ntohs(sizeof(xar_header_t));
581		XAR(x)->header.version = ntohs(1);
582		XAR(x)->header.toc_length_uncompressed = xar_ntoh64(ungztoc);
583		XAR(x)->header.toc_length_compressed = xar_ntoh64(gztoc);
584
585		write(XAR(x)->fd, &XAR(x)->header, sizeof(xar_header_t));
586
587		/* Copy the temp compressed toc file into the file */
588		lseek(tocfd, (off_t)0, SEEK_SET);
589		while(1) {
590			r = read(tocfd, rbuf, rsize);
591			if( (r < 0) && (errno == EINTR) )
592				continue;
593			if( r == 0 )
594				break;
595
596			wbytes = r;
597			off = 0;
598			do {
599				r = write(XAR(x)->fd, ((char *)rbuf)+off, wbytes-off);
600				if( (r < 0) && (errno == EINTR) )
601					continue;
602				if( r < 0 ) {
603					xar_err_new(x);
604					xar_err_set_string(x, "Error closing xar archive");
605					retval = -1;
606					goto CLOSEEND;
607				}
608
609				off += r;
610			} while( off < wbytes );
611		}
612
613		if( XAR(x)->toc_hash_ctx ) {
614			size_t chklen = 0;
615			void *chkstr = xar_hash_finish(XAR(x)->toc_hash_ctx, &chklen);
616			write(XAR(x)->fd, chkstr, chklen);
617
618			/* If there are any signatures, get the signed data a sign it */
619			if( XAR(x)->signatures ) {
620				xar_signature_t sig;
621				uint32_t signed_len = 0;
622				uint8_t *signed_data = NULL;
623
624				/* Loop through the signatures */
625				for(sig = XAR(x)->signatures; sig; sig = XAR_SIGNATURE(sig)->next ){
626					signed_len = XAR_SIGNATURE(sig)->len;
627
628					/* If callback returns something other then 0, bail */
629					if( 0 != sig->signer_callback( sig, sig->callback_context, chkstr, chklen, &signed_data, &signed_len ) ){
630						fprintf(stderr, "Error signing data.\n");
631						free(chkstr);
632						retval = -1;
633						close(fd);
634						close(tocfd);
635						goto CLOSE_BAIL;
636					}
637
638					if( signed_len != XAR_SIGNATURE(sig)->len ){
639						fprintf(stderr, "Signed data not the proper length.  %i should be %i.\n",signed_len,XAR_SIGNATURE(sig)->len);
640						free(chkstr);
641						retval = -1;
642						close(fd);
643						close(tocfd);
644						goto CLOSE_BAIL;
645					}
646
647					/* Write the signed data to the heap */
648					write(XAR(x)->fd, signed_data,XAR_SIGNATURE(sig)->len);
649
650					free(signed_data);
651				}
652				free(chkstr);
653			}
654
655			xar_signature_remove( XAR(x)->signatures );
656			XAR(x)->signatures = NULL;
657		}
658
659		/* copy the heap from the temporary heap into the archive */
660		if( 0 > XAR(x)->heap_to_archive(x) ) {
661			xar_err_new(x);
662			xar_err_set_string(x, "Error while copying heap contents into archive");
663			xar_err_set_errno(x, errno);
664			xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION);
665			retval = -1;
666			close(fd);
667			close(tocfd);
668			goto CLOSEEND;
669		}
670
671CLOSEEND:
672		close(fd);
673		close(tocfd);
674		free(rbuf);
675		free(wbuf);
676		deflateEnd(&XAR(x)->zs);
677	} else {
678
679		xar_signature_remove( XAR(x)->signatures );
680		XAR(x)->signatures = NULL;
681
682		inflateEnd(&XAR(x)->zs);
683	}
684
685CLOSE_BAIL:
686	/* continue deallocating the archive and return */
687	while(XAR(x)->subdocs) {
688		xar_subdoc_remove(XAR(x)->subdocs);
689	}
690
691	while(XAR(x)->attrs) {
692		a = XAR(x)->attrs;
693		XAR(x)->attrs = XAR_ATTR(a)->next;
694		xar_attr_free(a);
695	}
696
697	while(XAR(x)->props) {
698		xar_prop_t p;
699		p = XAR(x)->props;
700		XAR(x)->props = XAR_PROP(p)->next;
701		xar_prop_free(p);
702	}
703
704	while(XAR(x)->files) {
705		f = XAR(x)->files;
706		XAR(x)->files = XAR_FILE(f)->next;
707		xar_file_free(f);
708	}
709
710	xmlHashFree(XAR(x)->ino_hash, NULL);
711	xmlHashFree(XAR(x)->link_hash, NULL);
712	xmlHashFree(XAR(x)->csum_hash, NULL);
713	close(XAR(x)->fd);
714	if( XAR(x)->heap_fd >= 0 )
715		close(XAR(x)->heap_fd);
716	free((char *)XAR(x)->filename);
717	free((char *)XAR(x)->dirname);
718	free(XAR(x)->readbuf);
719	free((void *)x);
720
721	return retval;
722}
723
724/* xar_opt_get
725 * x: archive to get the option from
726 * option: name of the option
727 * Returns: a pointer to the value of the option
728 * In the case of more than one option with the same name, this will
729 * return the first match.
730 */
731const char *xar_opt_get(xar_t x, const char *option) {
732	xar_attr_t i;
733	for(i = XAR(x)->attrs; i && XAR_ATTR(i)->next; i = XAR_ATTR(i)->next) {
734		if(strcmp(XAR_ATTR(i)->key, option)==0)
735			return XAR_ATTR(i)->value;
736	}
737	if( i && (strcmp(XAR_ATTR(i)->key, option)==0) )
738		return XAR_ATTR(i)->value;
739	return NULL;
740}
741
742/* xar_opt_set
743 * x: the archive to set the option of
744 * option: the name of the option to set the value of
745 * value: the value to set the option to
746 * Returns: 0 for sucess, -1 for failure
747 */
748int32_t xar_opt_set(xar_t x, const char *option, const char *value) {
749	xar_attr_t currentAttr, a;
750
751	if( (strcmp(option, XAR_OPT_TOCCKSUM) == 0) ) {
752		XAR(x)->heap_offset = xar_io_get_toc_checksum_length_for_type(value);
753	}
754
755	/*	This was an edit from xar-1.4
756		Looks like we would only allow one definition for a particular key in this list
757		But xar_opt_unset is implemented to remove many pairs for the same key
758
759	// if the attribute is already defined, find it and free its value,
760	// replace and return
761	for(currentAttr = XAR(x)->attrs; currentAttr ; currentAttr = XAR_ATTR(currentAttr)->next) {
762		if(strcmp(XAR_ATTR(currentAttr)->key, option)==0) {
763			free((char*)XAR_ATTR(currentAttr)->value);
764			XAR_ATTR(currentAttr)->value = strdup(value);
765			return 0;
766		}
767	} */
768
769	// otherwise create a new attribute
770	a = xar_attr_new();
771	XAR_ATTR(a)->key = strdup(option);
772	XAR_ATTR(a)->value = strdup(value);
773	// and prepend it to the attrs list for the archive
774	XAR_ATTR(a)->next = XAR(x)->attrs;
775	XAR(x)->attrs = a;
776	return 0;
777}
778
779/* xar_opt_unset
780 * x: the archive to set the option of
781 * option: the name of the option to delete
782 * This will delete ALL instances of the option name
783 */
784int32_t xar_opt_unset(xar_t x, const char *option) {
785	xar_attr_t currentAttr, previousAttr = NULL;
786	for(currentAttr = XAR(x)->attrs;
787		currentAttr ;
788		previousAttr = currentAttr, currentAttr = XAR_ATTR(currentAttr)->next) {
789		if(strcmp(XAR_ATTR(currentAttr)->key, option)==0) {
790
791			// if this attribute match is the head of the attrs list
792			// promote the next list item to the head
793			if( previousAttr == NULL )
794				XAR(previousAttr)->attrs = XAR_ATTR(currentAttr)->next;
795
796			// otherwise splice the list around this attr
797			else
798				XAR_ATTR(previousAttr)->next = XAR_ATTR(currentAttr)->next;
799			xar_attr_free(currentAttr);
800
801			// keep going to find other instances
802			currentAttr = previousAttr;
803		}
804	}
805	return 0;
806}
807
808/* xar_add_node
809 * x: archive the file should belong to
810 * f: parent node, possibly NULL
811 * name: name of the node to add
812 * realpath: real path to item, this is used if the item being archived is to be located at a different location in the tree
813 * then it is on the real filesystem.
814 * Returns: newly allocated and populated node
815 * Summary: helper function which adds a child of f and populates
816 * its properties.  If f is NULL, the node will be added as a top
817 * level node of the archive, x.
818 */
819static xar_file_t xar_add_node(xar_t x, xar_file_t f, const char *name, const char *prefix, const char *realpath, int srcpath) {
820	xar_file_t ret;
821	const char *path;
822	char *tmp;
823	char idstr[32];
824
825	if( !f ) {
826		if( realpath )
827			asprintf(&tmp, "%s", realpath);
828		else
829			asprintf(&tmp, "%s%s%s", XAR(x)->path_prefix, prefix, name);
830
831		if( lstat(tmp, &XAR(x)->sbcache) != 0 ) {
832			free(tmp);
833			return NULL;
834		}
835
836		ret = xar_file_new(NULL);
837		if( !ret )
838			return NULL;
839		memset(idstr, 0, sizeof(idstr));
840		snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
841		xar_attr_set(ret, NULL, "id", idstr);
842		XAR_FILE(ret)->parent = NULL;
843		XAR_FILE(ret)->fspath = tmp;
844		if( XAR(x)->files == NULL )
845			XAR(x)->files = ret;
846		else {
847			XAR_FILE(ret)->next = XAR(x)->files;
848			XAR(x)->files = ret;
849		}
850	} else {
851		path = XAR_FILE(f)->fspath;
852		if( strcmp(prefix, "../") == 0 ) {
853			int len1, len2;
854			len1 = strlen(path);
855			len2 = strlen(name);
856			if( (len1>=len2) && (strcmp(path+(len1-len2), name) == 0) ) {
857				return f;
858			}
859
860		}
861
862		if( realpath ){
863			asprintf(&tmp, "%s", realpath);
864		}else
865			asprintf(&tmp, "%s/%s%s", path, prefix, name);
866
867		if( lstat(tmp, &XAR(x)->sbcache) != 0 ) {
868			free(tmp);
869			return NULL;
870		}
871
872		ret = xar_file_new(f);
873		if( !ret )
874			return NULL;
875		memset(idstr, 0, sizeof(idstr));
876		snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
877		xar_attr_set(ret, NULL, "id", idstr);
878		XAR_FILE(ret)->fspath = tmp;
879	}
880
881	xar_prop_set(ret, "name", name);
882
883	if( xar_arcmod_archive(x, ret, XAR_FILE(ret)->fspath, NULL, 0) < 0 ) {
884		xar_file_t i = NULL;
885		if( f ) {
886			if( ret == XAR_FILE(f)->children )
887				XAR_FILE(f)->children = XAR_FILE(ret)->next;
888			else
889				for( i = XAR_FILE(f)->children; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
890		} else {
891			if( ret == XAR(x)->files )
892				XAR(x)->files = XAR_FILE(ret)->next;
893			else
894				for( i = XAR(x)->files; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
895		}
896		if( i )
897			XAR_FILE(i)->next = XAR_FILE(ret)->next;
898		xar_file_free(ret);
899		return NULL;
900	}
901
902	return ret;
903}
904
905/* xar_add_pseudodir
906 * Summary: Adds a placeholder directory when archiving a file prior
907 * to archiving its path.
908 */
909static xar_file_t xar_add_pseudodir(xar_t x, xar_file_t f, const char *name, const char *prefix, const char *realpath)
910{
911	xar_file_t ret;
912	const char *path;
913	char *tmp;
914	char idstr[32];
915
916	if( !f ) {
917		if( realpath )
918			asprintf(&tmp, "%s", realpath);
919		else
920			asprintf(&tmp, "%s%s%s", XAR(x)->path_prefix, prefix, name);
921
922		if( lstat(tmp, &XAR(x)->sbcache) != 0 ) {
923			free(tmp);
924			return NULL;
925		}
926
927		ret = xar_file_new(NULL);
928		if( !ret )
929			return NULL;
930		memset(idstr, 0, sizeof(idstr));
931		snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
932		xar_attr_set(ret, NULL, "id", idstr);
933		XAR_FILE(ret)->parent = NULL;
934		XAR_FILE(ret)->fspath = tmp;
935		if( XAR(x)->files == NULL )
936			XAR(x)->files = ret;
937		else {
938			XAR_FILE(ret)->next = XAR(x)->files;
939			XAR(x)->files = ret;
940		}
941	} else {
942		path = XAR_FILE(f)->fspath;
943		if( strcmp(prefix, "../") == 0 ) {
944			int len1, len2;
945			len1 = strlen(path);
946			len2 = strlen(name);
947			if( (len1>=len2) && (strcmp(path+(len1-len2), name) == 0) ) {
948				return f;
949			}
950
951		}
952
953		if( realpath ){
954			asprintf(&tmp, "%s", realpath);
955		}else
956			asprintf(&tmp, "%s/%s%s", path, prefix, name);
957
958		if( lstat(tmp, &XAR(x)->sbcache) != 0 ) {
959			free(tmp);
960			return NULL;
961		}
962
963		ret = xar_file_new(f);
964		if( !ret )
965			return NULL;
966		memset(idstr, 0, sizeof(idstr));
967		snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
968		xar_attr_set(ret, NULL, "id", idstr);
969		XAR_FILE(ret)->fspath = tmp;
970	}
971	xar_prop_set(ret, "name", name);
972	xar_prop_set(ret, "type", "directory");
973
974	return ret;
975}
976
977/* xar_add_r
978 * Summary: a recursive helper function for adding a node to the
979 * tree.  This will search all children of node f, looking for
980 * the path component.  If found, will recurse into it.  If not,
981 * will add the path component to the tree, and recurse into it.
982 * If f is NULL, will start with x->files.
983 */
984static xar_file_t xar_add_r(xar_t x, xar_file_t f, const char *path, const char *prefix) {
985	xar_file_t i = NULL, ret, ret2, start = NULL;
986	char *tmp1, *tmp2, *tmp3;
987
988	if( path && (path[0] == '\0') ) {
989		return f;
990	}
991
992	tmp1 = tmp2 = strdup(path);
993	tmp3 = strsep(&tmp2, "/");
994
995	if( tmp3 && tmp2 && (tmp3[0] == '\0') ) {
996		ret2 = xar_add_r(x, f, tmp2, "");
997		free(tmp1);
998		return ret2;
999	}
1000
1001	if( strcmp(tmp3, "..") == 0 ) {
1002		char *prefixstr;
1003		if( !XAR(x)->skipwarn ) {
1004			xar_err_new(x);
1005			xar_err_set_string(x, "Skipping .. in path");
1006			xar_err_callback(x, XAR_SEVERITY_WARNING, XAR_ERR_ARCHIVE_CREATION);
1007			XAR(x)->skipwarn = 1;
1008		}
1009		asprintf(&prefixstr, "%s../", prefix);
1010		ret2 = xar_add_r(x, f, tmp2, prefixstr);
1011		free(prefixstr);
1012		free(tmp1);
1013		return ret2;
1014	}
1015
1016	if( strcmp(tmp3, ".") == 0 ) {
1017		if( tmp2 )
1018			ret2 = xar_add_r(x, f, tmp2, prefix);
1019		else
1020			ret2 = NULL;
1021		free(tmp1);
1022		return ret2;
1023	}
1024
1025	if( !f ) {
1026		start = XAR(x)->files;
1027	} else {
1028		start = XAR_FILE(f)->children;
1029	}
1030
1031	/* Search all the siblings */
1032	for( i = start; i; i = XAR_FILE(i)->next ) {
1033		const char *n;
1034		xar_prop_get(i, "name", &n);
1035		if( strcmp(n, tmp3) == 0 ) {
1036			if( !tmp2 ) {
1037				/* Node already exists, and it is i */
1038				free(tmp1);
1039				return i;
1040			}
1041			ret2 = xar_add_r(x, i, tmp2, "");
1042			free(tmp1);
1043			return ret2;
1044		}
1045	}
1046
1047	/* tmp3 was not found in children of start, so we add it */
1048	if( tmp2 ) {
1049		//ret = xar_add_node(x, f, tmp3, prefix, NULL,  1);
1050		ret = xar_add_pseudodir(x, f, tmp3, prefix, NULL);
1051	} else {
1052		ret = xar_add_node(x, f, tmp3, prefix, NULL,  0);
1053	}
1054
1055	if( !ret ) {
1056		free(tmp1);
1057		return NULL;
1058	}
1059
1060	if( !tmp2 ) {
1061		/* We've added the final piece, done, don't recurse */
1062		free(tmp1);
1063		return ret;
1064	}
1065
1066	/* still more to add, recurse */
1067	ret2 = xar_add_r(x, ret, tmp2, "");
1068	free(tmp1);
1069	return ret2;
1070}
1071
1072/* xar_add
1073 * x: archive to add the file to
1074 * path: path to file
1075 * Returns: allocated an populated xar_file_t representing the
1076 * specified file.
1077 * Summary: if a full path "foo/bar/blah" is specified, then any
1078 * directories not already existing in the archive will be added
1079 * automagically.  The returned xar_file_t represents the file
1080 * specified, not the parent of the directory tree.
1081 * For instance, if "foo/bar/blah" is specified, the xar_file_t
1082 * representing "blah" will be returned.
1083 */
1084xar_file_t xar_add(xar_t x, const char *path) {
1085#ifdef __APPLE__
1086	xar_file_t ret;
1087	if( (ret = xar_underbar_check(x, NULL, path)) )
1088		return ret;
1089#endif
1090
1091	if( path[0] == '/' ) {
1092		XAR(x)->path_prefix = "/";
1093		path++;
1094	} else
1095		XAR(x)->path_prefix = "";
1096	return xar_add_r(x, NULL, path, "");
1097}
1098
1099/* xar_add_frombuffer
1100* x: archive to add the file to
1101* parent: parent node, possibly NULL
1102* name: name of file
1103* buffer: buffer for file contents
1104* length: length of buffer
1105* Returns: allocated an populated xar_file_t representing the
1106* specified file.
1107* Summary: Use this to add chunks of named data to a xar without
1108* using the filesystem.
1109*/
1110
1111xar_file_t xar_add_frombuffer(xar_t x, xar_file_t parent, const char *name, char *buffer, size_t length) {
1112	xar_file_t ret;
1113	char idstr[32];
1114
1115	if( !parent ) {
1116		ret = xar_file_new(NULL);
1117		if( !ret )
1118			return NULL;
1119		memset(idstr, 0, sizeof(idstr));
1120		snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
1121		xar_attr_set(ret, NULL, "id", idstr);
1122		XAR_FILE(ret)->parent = NULL;
1123		if( XAR(x)->files == NULL )
1124			XAR(x)->files = ret;
1125		else {
1126			XAR_FILE(ret)->next = XAR(x)->files;
1127			XAR(x)->files = ret;
1128		}
1129	} else {
1130		ret = xar_file_new(parent);
1131		if( !ret )
1132			return NULL;
1133		memset(idstr, 0, sizeof(idstr));
1134		snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
1135		xar_attr_set(ret, NULL, "id", idstr);
1136		XAR_FILE(ret)->fspath = NULL;
1137	}
1138
1139	xar_prop_set(ret, "name", name);
1140
1141	//int32_t xar_arcmod_archive(xar_t x, xar_file_t f, const char *file, const char *buffer, size_t len)
1142	if( xar_arcmod_archive(x, ret, NULL , buffer , length) < 0 ) {
1143		xar_file_t i;
1144		if( parent ) {
1145			for( i = XAR_FILE(parent)->children; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
1146		} else {
1147			for( i = XAR(x)->files; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
1148		}
1149		if( i )
1150			XAR_FILE(i)->next = XAR_FILE(ret)->next;
1151		xar_file_free(ret);
1152		return NULL;
1153	}
1154
1155	return ret;
1156}
1157
1158xar_file_t xar_add_folder(xar_t x, xar_file_t f, const char *name, struct stat *info)
1159{
1160	xar_file_t ret;
1161	char idstr[32];
1162
1163	if( info )
1164		memcpy(&XAR(x)->sbcache,info,sizeof(struct stat));
1165
1166	ret = xar_file_new(f);
1167	if( !ret )
1168		return NULL;
1169
1170	memset(idstr, 0, sizeof(idstr));
1171	snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
1172	xar_attr_set(ret, NULL, "id", idstr);
1173	XAR_FILE(ret)->fspath = NULL;
1174
1175	if( !f ) {
1176		XAR_FILE(ret)->parent = NULL;
1177
1178		if( XAR(x)->files == NULL )
1179			XAR(x)->files = ret;
1180		else {
1181			XAR_FILE(ret)->next = XAR(x)->files;
1182			XAR(x)->files = ret;
1183		}
1184	}
1185
1186	xar_prop_set(ret, "name", name);
1187
1188	if( xar_arcmod_archive(x, ret, XAR_FILE(ret)->fspath, NULL, 0) < 0 ) {
1189		xar_file_t i;
1190		if( f ) {
1191			for( i = XAR_FILE(f)->children; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
1192		} else {
1193			for( i = XAR(x)->files; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
1194		}
1195		if( i )
1196			XAR_FILE(i)->next = XAR_FILE(ret)->next;
1197		xar_file_free(ret);
1198		return NULL;
1199	}
1200
1201	return ret;
1202}
1203
1204xar_file_t xar_add_frompath(xar_t x, xar_file_t parent, const char *name, const char *realpath)
1205{
1206	return xar_add_node(x, parent, name , "" , realpath,  1);
1207}
1208
1209xar_file_t xar_add_from_archive(xar_t x, xar_file_t parent, const char *name, xar_t sourcearchive, xar_file_t sourcefile)
1210{
1211	xar_file_t ret;
1212	char idstr[32];
1213
1214	ret = xar_file_replicate(sourcefile, parent);
1215
1216	if( !ret )
1217		return NULL;
1218
1219	memset(idstr, 0, sizeof(idstr));
1220	snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
1221	xar_attr_set(ret, NULL, "id", idstr);
1222	XAR_FILE(ret)->fspath = NULL;
1223
1224	if( !parent ) {
1225		XAR_FILE(ret)->parent = NULL;
1226
1227		if( XAR(x)->files == NULL )
1228			XAR(x)->files = ret;
1229		else {
1230			XAR_FILE(ret)->next = XAR(x)->files;
1231			XAR(x)->files = ret;
1232		}
1233	}
1234
1235	xar_prop_set(ret, "name", name);
1236
1237	/* iterate through all the properties, see if any of them have an offset */
1238	xar_prop_t p = xar_prop_pfirst(ret);
1239
1240	do{
1241		xar_prop_t tmpp;
1242
1243		tmpp = xar_prop_pget(p, "offset");
1244		if(tmpp) {
1245			if( 0 != xar_attrcopy_from_heap_to_heap(sourcearchive, sourcefile, p, x, ret)){
1246				xar_file_free(ret);
1247				ret = NULL;
1248				break;
1249			}
1250		}
1251
1252	}while( (p = xar_prop_pnext(p)) );
1253
1254	return ret;
1255}
1256
1257/* xar_extract_tofile
1258* x: archive to extract from
1259* f: file associated with x
1260* Returns 0 on success, -1 on failure
1261* Summary: This actually does the file extraction.
1262* No traversal is performed, it is assumed all directory paths
1263* leading up to f already exist.
1264*/
1265int32_t xar_extract_tofile(xar_t x, xar_file_t f, const char *path) {
1266	return xar_arcmod_extract(x, f, path,NULL, 0);
1267}
1268
1269
1270/* xar_extract_tobuffer
1271* x: archive to extract from
1272* buffer: buffer to extract to
1273* Returns 0 on success, -1 on failure.
1274* Summary: This is the entry point for extraction to a buffer.
1275* On success, a buffer is allocated with the contents of the file
1276* specified.  The caller is responsible for freeing the returend buffer.
1277* Example: xar_extract_tobuffer(x, "foo/bar/blah",&buffer)
1278*/
1279int32_t xar_extract_tobuffer(xar_t x, xar_file_t f, char **buffer) {
1280	size_t size;
1281
1282	return xar_extract_tobuffersz(x, f, buffer, &size);
1283}
1284
1285/* xar_extract_tobuffer
1286* x: archive to extract from
1287* buffer: buffer to extract to
1288* size: On return, this will contain the size of the memory pointed to by buffer
1289* Returns 0 on success, -1 on failure.
1290* Summary: This is the entry point for extraction to a buffer.
1291* On success, a buffer is allocated with the contents of the file
1292* specified.  The caller is responsible for freeing the returend buffer.
1293* Example: xar_extract_tobuffer(x, "foo/bar/blah",&buffer)
1294*/
1295int32_t xar_extract_tobuffersz(xar_t x, xar_file_t f, char **buffer, size_t *size) {
1296	const char *sizestring = NULL;
1297	int32_t ret;
1298
1299	if(0 != xar_prop_get(f,"data/size",&sizestring)){
1300		if(0 != xar_prop_get(f, "type", &sizestring))
1301			return -1;
1302		if(strcmp(sizestring, "file") == 0) {
1303			*size = 0;
1304			return 0;
1305		}
1306		return -1;
1307	}
1308
1309	*size = strtoull(sizestring, (char **)NULL, 10);
1310	*buffer = malloc(*size);
1311
1312	if(!(*buffer)){
1313		return -1;
1314	}
1315
1316	ret = xar_arcmod_extract(x,f,NULL,*buffer,*size);
1317	if( ret ) {
1318		*size = 0;
1319		free(*buffer);
1320		*buffer = NULL;
1321	}
1322
1323	return ret;
1324}
1325
1326int32_t xar_extract_tostream_init(xar_t x, xar_file_t f, xar_stream *stream) {
1327	xar_prop_t tmpp;
1328
1329	if( !xar_check_prop(x, "data") )
1330		return XAR_STREAM_OK;
1331
1332	tmpp = xar_prop_pfirst(f);
1333	if( tmpp )
1334		tmpp = xar_prop_find(tmpp, "data");
1335	if( !tmpp )
1336		return XAR_STREAM_OK;
1337
1338	return xar_attrcopy_from_heap_to_stream_init(x, f, tmpp, stream);
1339}
1340
1341int32_t xar_extract_tostream(xar_stream *stream) {
1342	return xar_attrcopy_from_heap_to_stream(stream);
1343}
1344
1345int32_t xar_extract_tostream_end(xar_stream *stream) {
1346	return xar_attrcopy_from_heap_to_stream_end(stream);
1347}
1348
1349/* xar_extract
1350 * x: archive to extract from
1351 * path: path to file to extract
1352 * Returns 0 on success, -1 on failure.
1353 * Summary: This is the entry point for extraction.  This will find
1354 * the file node described by path, extract any directories needed
1355 * to extract the node, and finally extract the file.
1356 * Example: xar_extract(x, "foo/bar/blah")
1357 * If foo does not exist, xar_extract will extract foo from the
1358 * archive, extract bar from the archive, and then extract blah.
1359 * Total extractions will be "foo", "foo/bar", and "foo/bar/blah".
1360 */
1361int32_t xar_extract(xar_t x, xar_file_t f) {
1362	struct stat sb;
1363	char *tmp1, *dname;
1364	xar_file_t tmpf;
1365
1366	if( (strstr(XAR_FILE(f)->fspath, "/") != NULL) && (stat(XAR_FILE(f)->fspath, &sb)) && (XAR_FILE(f)->parent_extracted == 0) ) {
1367		tmp1 = strdup(XAR_FILE(f)->fspath);
1368		dname = dirname(tmp1);
1369		tmpf = xar_file_find(XAR(x)->files, dname);
1370		if( !tmpf ) {
1371			xar_err_set_string(x, "Unable to find file");
1372			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
1373			return -1;
1374		}
1375		free(tmp1);
1376		XAR_FILE(f)->parent_extracted++;
1377		xar_extract(x, tmpf);
1378	}
1379
1380	return xar_extract_tofile(x, f, XAR_FILE(f)->fspath);
1381}
1382
1383/* xar_verify
1384* x: archive to extract from
1385* f: file to verify
1386* Returns 0 on success, -1 on failure.
1387* Summary: This function allows for verification of
1388* an entry without extraction.  If there is no checksum
1389* the verification will pass.
1390*/
1391int32_t xar_verify(xar_t x, xar_file_t f) {
1392	return xar_arcmod_verify(x,f);
1393}
1394
1395/* toc_read_callback
1396 * context: context passed through from the reader
1397 * buffer: buffer to read into
1398 * len: size of buffer
1399 * Returns: number of bytes read or -1 in case of error
1400 * Summary: internal callback for xmlReaderForIO.
1401 */
1402static int toc_read_callback(void *context, char *buffer, int len) {
1403	xar_t x = (xar_t)context;
1404	int ret, off = 0;
1405
1406	if ( ((!XAR(x)->offset) || (XAR(x)->offset == XAR(x)->readbuf_len)) && (XAR(x)->toc_count != XAR(x)->header.toc_length_compressed) ) {
1407		XAR(x)->offset = 0;
1408		if( (XAR(x)->readbuf_len - off) + XAR(x)->toc_count > XAR(x)->header.toc_length_compressed )
1409			ret = xar_read_fd(XAR(x)->fd, XAR(x)->readbuf, XAR(x)->header.toc_length_compressed - XAR(x)->toc_count);
1410		else
1411			ret = read(XAR(x)->fd, XAR(x)->readbuf, XAR(x)->readbuf_len);
1412		if ( ret == -1 )
1413			return ret;
1414
1415		if ( XAR(x)->toc_hash_ctx )
1416			xar_hash_update(XAR(x)->toc_hash_ctx, XAR(x)->readbuf, ret);
1417
1418		XAR(x)->toc_count += ret;
1419		off += ret;
1420	}
1421
1422	if( off && (off < XAR(x)->readbuf_len) )
1423		XAR(x)->readbuf_len = off;
1424	XAR(x)->zs.next_in = ((unsigned char *)XAR(x)->readbuf) + XAR(x)->offset;
1425	XAR(x)->zs.avail_in = XAR(x)->readbuf_len - XAR(x)->offset;
1426	XAR(x)->zs.next_out = (void *)buffer;
1427	XAR(x)->zs.avail_out = len;
1428
1429	ret = inflate(&XAR(x)->zs, Z_SYNC_FLUSH);
1430	if( ret < 0 )
1431		return -1;
1432
1433	XAR(x)->offset = XAR(x)->readbuf_len - XAR(x)->zs.avail_in;
1434
1435	return len - XAR(x)->zs.avail_out;
1436}
1437
1438/* close_callback
1439 * context: this will be a xar_t
1440 * Returns: 0 or -1 in case of error
1441 * Summary: this is the callback for xmlTextReaderForIO to close the IO
1442 */
1443static int close_callback(void *context) {
1444	return 0;
1445}
1446
1447/* xar_serialize
1448 * x: xar to serialize
1449 * file: file to serialize to
1450 * Summary: serializes the archive out to xml.
1451 */
1452void xar_serialize(xar_t x, const char *file) {
1453	xmlTextWriterPtr writer;
1454	xar_subdoc_t i;
1455
1456	writer = xmlNewTextWriterFilename(file, 0);
1457	xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
1458	xmlTextWriterSetIndent(writer, 4);
1459	xmlTextWriterStartElement(writer, BAD_CAST("xar"));
1460
1461	for( i = XAR(x)->subdocs; i; i = xar_subdoc_next(i) )
1462		xar_subdoc_serialize(i, writer, 1);
1463
1464	xmlTextWriterStartElement(writer, BAD_CAST("toc"));
1465
1466	if( XAR(x)->props )
1467		xar_prop_serialize(XAR(x)->props, writer);
1468
1469	if( XAR(x)->signatures )
1470		xar_signature_serialize(XAR(x)->signatures,writer);
1471
1472	if( XAR(x)->files )
1473		xar_file_serialize(XAR(x)->files, writer);
1474
1475	xmlTextWriterEndDocument(writer);
1476	xmlFreeTextWriter(writer);
1477	return;
1478}
1479
1480/* xar_unserialize
1481 * x: xar archive to unserialize to.  Must have been allocated with xar_open
1482 * file: the xml filename to unserialize from
1483 * Summary: Takes the TOC representation from file and creates the
1484 * corresponding in-memory representation.
1485 */
1486static int32_t xar_unserialize(xar_t x) {
1487	xmlTextReaderPtr reader;
1488	xar_file_t f = NULL;
1489	const xmlChar *name, *prefix, *uri;
1490	int type, noattr, ret;
1491
1492	reader = xmlReaderForIO(toc_read_callback, close_callback, XAR(x), NULL, NULL, 0);
1493	if( !reader ) return -1;
1494
1495	while( (ret = xmlTextReaderRead(reader)) == 1 ) {
1496		type = xmlTextReaderNodeType(reader);
1497		noattr = xmlTextReaderAttributeCount(reader);
1498		name = xmlTextReaderConstLocalName(reader);
1499		if( type != XML_READER_TYPE_ELEMENT )
1500			continue;
1501		if(strcmp((const char*)name, "xar") != 0)
1502			continue;
1503		while( (ret = xmlTextReaderRead(reader)) == 1 ) {
1504			type = xmlTextReaderNodeType(reader);
1505			noattr = xmlTextReaderAttributeCount(reader);
1506			name = xmlTextReaderConstLocalName(reader);
1507			if( type == XML_READER_TYPE_ELEMENT ) {
1508				if(strcmp((const char*)name, "toc") == 0) {
1509					while( (ret = xmlTextReaderRead(reader)) == 1 ) {
1510						type = xmlTextReaderNodeType(reader);
1511						noattr = xmlTextReaderAttributeCount(reader);
1512						name = xmlTextReaderConstLocalName(reader);
1513						if( type == XML_READER_TYPE_ELEMENT ) {
1514							if(strcmp((const char*)name, "file") == 0) {
1515								f = xar_file_unserialize(x, NULL, reader);
1516								XAR_FILE(f)->next = XAR(x)->files;
1517								XAR(x)->files = f;
1518							} else if( strcmp((const char*)name, "signature") == 0
1519#ifdef __APPLE__
1520                                      || strcmp((const char*)name, "x-signature") == 0
1521#endif
1522                                      ){
1523								xar_signature_t sig = NULL;
1524								sig = xar_signature_unserialize(x, reader );
1525
1526								if( !sig ) {
1527									xmlFreeTextReader(reader);
1528									return -1;
1529								}
1530
1531								if( XAR(x)->signatures )
1532									XAR_SIGNATURE(XAR(x)->signatures)->next = XAR_SIGNATURE(sig);
1533								else
1534									XAR(x)->signatures = sig;
1535
1536							} else {
1537								xar_prop_unserialize(XAR_FILE(x), NULL, reader);
1538							}
1539						}
1540					}
1541					if( ret == -1 ) {
1542						xmlFreeTextReader(reader);
1543						return -1;
1544					}
1545				} else {
1546					xar_subdoc_t s;
1547					int i;
1548
1549					prefix = xmlTextReaderPrefix(reader);
1550					uri = xmlTextReaderNamespaceUri(reader);
1551
1552					i = xmlTextReaderAttributeCount(reader);
1553					if( i > 0 ) {
1554						for(i = xmlTextReaderMoveToFirstAttribute(reader); i == 1; i = xmlTextReaderMoveToNextAttribute(reader)) {
1555							xar_attr_t a;
1556							const char *aname = (const char *)xmlTextReaderConstLocalName(reader);
1557							const char *avalue = (const char *)xmlTextReaderConstValue(reader);
1558
1559							if( aname && (strcmp("subdoc_name", aname) == 0) ) {
1560								name = (const unsigned char *)avalue;
1561							} else {
1562								a = xar_attr_new();
1563								XAR_ATTR(a)->key = strdup(aname);
1564								XAR_ATTR(a)->value = strdup(avalue);
1565								XAR_ATTR(a)->next = XAR_SUBDOC(s)->attrs;
1566								XAR_SUBDOC(s)->attrs = XAR_ATTR(a);
1567							}
1568						}
1569					}
1570
1571					s = xar_subdoc_new(x, (const char *)name);
1572                    if(s){
1573                        xar_subdoc_unserialize(s, reader);
1574                    }else{
1575                        xmlFreeTextReader(reader);
1576                        return -1;
1577                    }
1578				}
1579			}
1580			if( (type == XML_READER_TYPE_END_ELEMENT) && (strcmp((const char *)name, "toc")==0) ) {
1581				break;
1582			}
1583		}
1584		if( ret == -1 ) {
1585			xmlFreeTextReader(reader);
1586			return -1;
1587		}
1588	}
1589
1590	if( ret == -1 ) {
1591		xmlFreeTextReader(reader);
1592		return -1;
1593	}
1594
1595	xmlFreeTextReader(reader);
1596	return 0;
1597}
1598