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