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 <fcntl.h>
44#include <errno.h>
45#include <string.h>
46#include <limits.h>
47#include <unistd.h>
48#include <inttypes.h>
49#include <sys/types.h>
50#include <assert.h>
51
52#ifndef HAVE_ASPRINTF
53#include "asprintf.h"
54#endif
55#include "xar.h"
56#include "filetree.h"
57#include "archive.h"
58#include "io.h"
59#include "zxar.h"
60#include "bzxar.h"
61#include "lzmaxar.h"
62#include "hash.h"
63#include "script.h"
64#include "macho.h"
65#include "util.h"
66
67#if !defined(LLONG_MAX) && defined(LONG_LONG_MAX)
68#define LLONG_MAX LONG_LONG_MAX
69#endif
70
71#if !defined(LLONG_MIN) && defined(LONG_LONG_MIN)
72#define LLONG_MIN LONG_LONG_MIN
73#endif
74
75// IMPORTANT: Keep datamod count up to date in io.h!
76struct datamod xar_datamods[] = {
77	{ xar_hash_fromheap_in,
78		xar_hash_fromheap_out,
79		xar_hash_fromheap_done,
80		xar_hash_toheap_in,
81		xar_hash_toheap_out,
82		xar_hash_toheap_done
83	},
84	{ (fromheap_in)NULL,
85		(fromheap_out)NULL,
86		(fromheap_done)NULL,
87		xar_script_in,
88		(toheap_out)NULL,
89		xar_script_done
90	},
91	{ (fromheap_in)NULL,
92		(fromheap_out)NULL,
93		(fromheap_done)NULL,
94		xar_macho_in,
95		(toheap_out)NULL,
96		xar_macho_done
97	},
98	{ xar_gzip_fromheap_in,
99		(fromheap_out)NULL,
100		xar_gzip_fromheap_done,
101		xar_gzip_toheap_in,
102		(toheap_out)NULL,
103		xar_gzip_toheap_done
104	},
105	{ xar_bzip_fromheap_in,
106		(fromheap_out)NULL,
107		xar_bzip_fromheap_done,
108		xar_bzip_toheap_in,
109		(toheap_out)NULL,
110		xar_bzip_toheap_done
111	},
112	{ xar_lzma_fromheap_in,
113		(fromheap_out)NULL,
114		xar_lzma_fromheap_done,
115		xar_lzma_toheap_in,
116		(toheap_out)NULL,
117		xar_lzma_toheap_done
118	}
119};
120
121size_t xar_io_get_rsize(xar_t x) {
122	size_t bsize;
123	const char *opt = NULL;
124
125	opt = xar_opt_get(x, "rsize");
126	if( !opt ) {
127		bsize = 4096;
128	} else {
129		bsize = strtol(opt, NULL, 0);
130		if( ((bsize == LONG_MAX) || (bsize == LONG_MIN)) && (errno == ERANGE) ) {
131			bsize = 4096;
132		}
133	}
134
135	return bsize;
136}
137
138off_t xar_io_get_heap_base_offset(xar_t x) {
139	return XAR(x)->toc_count + sizeof(xar_header_t);
140}
141
142size_t xar_io_get_toc_checksum_length_for_type(const char *type) {
143	if( !type ) {
144		return 0;
145	} else if( strcmp(type, XAR_OPT_VAL_NONE) == 0 ) {
146		return 0;
147	} else if( strcmp(type, XAR_OPT_VAL_SHA1) == 0 ) {
148		return 20;
149	} else if( strcmp(type, XAR_OPT_VAL_SHA256) == 0 ) {
150		return 32;
151	} else if( strcmp(type, XAR_OPT_VAL_SHA512) == 0 ) {
152		return 64;
153	} else if( strcmp(type, XAR_OPT_VAL_MD5) == 0 ) {
154		return 16;
155	} else {
156		return 0;
157	}
158}
159
160size_t xar_io_get_toc_checksum_length(xar_t x) {
161	switch(XAR(x)->header.cksum_alg) {
162		case XAR_CKSUM_NONE:
163			return 0;
164		case XAR_CKSUM_SHA1:
165			return 20;
166		case XAR_CKSUM_SHA256:
167			return 32;
168		case XAR_CKSUM_SHA512:
169			return 64;
170		case XAR_CKSUM_MD5:
171			return 16;
172		default:
173			fprintf(stderr, "Unknown hashing algorithm, skipping\n");
174			return 0;
175	};
176}
177
178off_t xar_io_get_file_offset(xar_t x, xar_file_t f, xar_prop_t p) {
179	xar_prop_t tmpp;
180	const char *opt = NULL;
181	tmpp = xar_prop_pget(p, "offset");
182	if( tmpp ) {
183		opt = xar_prop_getvalue(tmpp);
184		return strtoll(opt, NULL, 0);
185	} else {
186		return -1;
187	}
188}
189
190int64_t xar_io_get_length(xar_prop_t p) {
191	const char *opt = NULL;
192	int64_t fsize = 0;
193	xar_prop_t tmpp;
194
195	tmpp = xar_prop_pget(p, "length");
196	if( tmpp )
197		opt = xar_prop_getvalue(tmpp);
198	if( !opt ) {
199		return 0;
200	} else {
201		fsize = strtoll(opt, NULL, 10);
202		if( ((fsize == LLONG_MAX) || (fsize == LLONG_MIN)) && (errno == ERANGE) ) {
203			return -1;
204		}
205	}
206
207	return fsize;
208}
209
210static void xar_io_seek(xar_t x, xar_file_t f, off_t seekoff) {
211	int r;
212
213	if( XAR(x)->fd >= 0 ) {
214		r = lseek(XAR(x)->fd, seekoff, SEEK_SET);
215		if( r == -1 ) {
216			if( errno == ESPIPE ) {
217				ssize_t rr;
218				char *buf;
219				unsigned int len;
220
221				len = seekoff - XAR(x)->toc_count;
222				len -= sizeof(xar_header_t);
223				if( XAR(x)->heap_offset > len ) {
224					xar_err_new(x);
225					xar_err_set_file(x, f);
226					xar_err_set_string(x, "Unable to seek");
227					xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
228				} else {
229					len -= XAR(x)->heap_offset;
230					buf = malloc(len);
231					assert(buf);
232					rr = xar_read_fd(XAR(x)->fd, buf, len);
233					if( rr < len ) {
234						xar_err_new(x);
235						xar_err_set_file(x, f);
236						xar_err_set_string(x, "Unable to seek");
237						xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
238					}
239					XAR(x)->heap_offset += rr;
240					free(buf);
241				}
242			} else {
243				xar_err_new(x);
244				xar_err_set_file(x, f);
245				xar_err_set_string(x, "Unable to seek");
246				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
247			}
248		}
249	}
250}
251
252int32_t xar_attrcopy_to_heap(xar_t x, xar_file_t f, xar_prop_t p, read_callback rcb, void *context) {
253	int modulecount = (sizeof(xar_datamods)/sizeof(struct datamod));
254	void *modulecontext[modulecount];
255	int r, i;
256	size_t bsize, rsize;
257	int64_t readsize=0, writesize=0, inc = 0, this_write=0;
258	void *inbuf;
259	char *tmpstr = NULL;
260	const char *opt = NULL, *csum = NULL;
261	off_t orig_heap_offset = XAR(x)->heap_offset;
262	xar_file_t tmpf = NULL;
263	xar_prop_t tmpp = NULL;
264
265	memset(modulecontext, 0, sizeof(void*)*modulecount);
266
267	bsize = xar_io_get_rsize(x);
268
269	r = 1;
270
271	// (Apple) allocate once
272	inbuf = malloc(bsize);
273	if( !inbuf )
274		return -1;
275
276	while(r != 0) {
277
278		r = rcb(x, f, inbuf, bsize, context);
279		if( r < 0 ) {
280			free(inbuf);
281			return -1;
282		}
283
284		readsize+=r;
285		inc += r;
286		rsize = r;
287
288		/* filter the data through the in modules */
289		for( i = 0; i < modulecount; i++) {
290			if( xar_datamods[i].th_in ) {
291				xar_datamods[i].th_in(x, f, p, &inbuf, &rsize, &(modulecontext[i]));
292			}
293		}
294
295		/* filter the data through the out modules */
296		for( i = 0; i < modulecount; i++) {
297			if( xar_datamods[i].th_out )
298				xar_datamods[i].th_out(x, f, p, inbuf, rsize, &(modulecontext[i]));
299		}
300
301		size_t written = 0;
302		if( rsize != 0 ) {
303			while(written < rsize) {
304				this_write = xar_write_fd(XAR(x)->heap_fd, inbuf, rsize);
305				if( this_write < 0 ) {
306					xar_err_new(x);
307					xar_err_set_string(x, "write(2) error when writing to heap");
308					xar_err_set_errno(x, errno);
309					xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION);
310					free(inbuf);
311					return -1;
312				}
313				written += this_write;
314			}
315		}
316		XAR(x)->heap_offset += written;
317		writesize += written;
318	}
319
320	free(inbuf);
321
322
323	/* If size is 0, don't bother having anything in the heap */
324	if( readsize == 0 ) {
325		XAR(x)->heap_offset = orig_heap_offset;
326		lseek(XAR(x)->heap_fd, -writesize, SEEK_CUR);
327		for( i = 0; i < modulecount; i++) {
328			if( xar_datamods[i].th_done )
329				xar_datamods[i].th_done(x, f, p, &(modulecontext[i]));
330		}
331		return 0;
332	}
333	/* finish up anything that still needs doing */
334	for( i = 0; i < modulecount; i++) {
335		if( xar_datamods[i].th_done )
336			xar_datamods[i].th_done(x, f, p, &(modulecontext[i]));
337	}
338
339	XAR(x)->heap_len += writesize;
340	tmpp = xar_prop_pget(p, "archived-checksum");
341	if( tmpp )
342		csum = xar_prop_getvalue(tmpp);
343	if( csum )
344		tmpf = xmlHashLookup(XAR(x)->csum_hash, BAD_CAST(csum));
345	if( tmpf ) {
346		const char *attr = xar_prop_getkey(p);
347		opt = xar_opt_get(x, XAR_OPT_LINKSAME);
348		if( opt && (strcmp(attr, "data") == 0) ) {
349			const char *id = xar_attr_pget(tmpf, NULL, "id");
350			xar_prop_pset(f, NULL, "type", "hardlink");
351			tmpp = xar_prop_pfirst(f);
352			if( tmpp )
353				tmpp = xar_prop_find(tmpp, "type");
354			if( tmpp )
355				xar_attr_pset(f, tmpp, "link", id);
356
357			xar_prop_pset(tmpf, NULL, "type", "hardlink");
358			tmpp = xar_prop_pfirst(tmpf);
359			if( tmpp )
360				tmpp = xar_prop_find(tmpp, "type");
361			if( tmpp )
362				xar_attr_pset(tmpf, tmpp, "link", "original");
363
364			tmpp = xar_prop_pfirst(f);
365			if( tmpp )
366				tmpp = xar_prop_find(tmpp, "data");
367			xar_prop_punset(f, tmpp);
368
369			XAR(x)->heap_offset = orig_heap_offset;
370			lseek(XAR(x)->heap_fd, -writesize, SEEK_CUR);
371			XAR(x)->heap_len -= writesize;
372			return 0;
373		}
374		opt = xar_opt_get(x, XAR_OPT_COALESCE);
375		if( opt ) {
376			long long tmpoff;
377			const char *offstr = NULL;
378			tmpp = xar_prop_pfirst(tmpf);
379			if( tmpp ) {
380				const char *key;
381				key = xar_prop_getkey(p);
382				tmpp = xar_prop_find(tmpp, key);
383			}
384			if( tmpp )
385				tmpp = xar_prop_pget(tmpp, "offset");
386			if( tmpp )
387				offstr = xar_prop_getvalue(tmpp);
388			if( offstr ) {
389				tmpoff = strtoll(offstr, NULL, 10);
390				XAR(x)->heap_offset = orig_heap_offset;
391				lseek(XAR(x)->heap_fd, -writesize, SEEK_CUR);
392				orig_heap_offset = tmpoff;
393				XAR(x)->heap_len -= writesize;
394			}
395
396		}
397	} else if( csum ) {
398		xmlHashAddEntry(XAR(x)->csum_hash, BAD_CAST(csum), XAR_FILE(f));
399	} else {
400		xar_err_new(x);
401		xar_err_set_file(x, f);
402		xar_err_set_string(x, "No archived-checksum");
403		xar_err_callback(x, XAR_SEVERITY_WARNING, XAR_ERR_ARCHIVE_CREATION);
404	}
405
406	asprintf(&tmpstr, "%"PRIu64, readsize);
407	xar_prop_pset(f, p, "size", tmpstr);
408	free(tmpstr);
409
410	asprintf(&tmpstr, "%"PRIu64, (uint64_t)orig_heap_offset);
411	xar_prop_pset(f, p, "offset", tmpstr);
412	free(tmpstr);
413
414	tmpstr = (char *)xar_opt_get(x, XAR_OPT_COMPRESSION);
415	if( tmpstr && (strcmp(tmpstr, XAR_OPT_VAL_NONE) == 0) ) {
416		xar_prop_pset(f, p, "encoding", NULL);
417		tmpp = xar_prop_pget(p, "encoding");
418		if( tmpp )
419			xar_attr_pset(f, tmpp, "style", "application/octet-stream");
420	}
421
422	asprintf(&tmpstr, "%"PRIu64, writesize);
423	xar_prop_pset(f, p, "length", tmpstr);
424	free(tmpstr);
425
426	return 0;
427}
428
429/* xar_copy_from_heap
430 * This is the arcmod extraction entry point for extracting the file's
431 * data from the heap file.
432 * It is assumed the heap_fd is already positioned appropriately.
433 */
434int32_t xar_attrcopy_from_heap(xar_t x, xar_file_t f, xar_prop_t p, write_callback wcb, void *context) {
435	int modulecount = (sizeof(xar_datamods)/sizeof(struct datamod));
436	void	*modulecontext[modulecount];
437	int r, i;
438	size_t bsize, def_bsize;
439	int64_t fsize, inc = 0, seekoff;
440	void *inbuf;
441	const char *opt;
442	xar_prop_t tmpp;
443
444	memset(modulecontext, 0, sizeof(void*)*modulecount);
445
446	def_bsize = xar_io_get_rsize(x);
447
448	seekoff = xar_io_get_file_offset(x, f, p);
449	if( seekoff == -1 ) {
450		wcb(x, f, NULL, 0, context);
451		return 0;
452	} else if( ((seekoff == LLONG_MAX) || (seekoff == LLONG_MIN)) && (errno == ERANGE) ) {
453		return -1;
454	}
455	seekoff += xar_io_get_heap_base_offset(x);
456	xar_io_seek(x, f, seekoff);
457
458	fsize = xar_io_get_length(p);
459	if( fsize == 0 )
460		return 0;
461	if( fsize < 0 )
462		return -1;
463
464	bsize = def_bsize;
465	inbuf = malloc(bsize);
466	if( !inbuf ) {
467		return -1;
468	}
469
470	while(1) {
471		/* Size has been reached */
472		if( fsize == inc )
473			break;
474		if( (fsize - inc) < bsize )
475			bsize = fsize - inc;
476		r = read(XAR(x)->fd, inbuf, bsize);
477		if( r == 0 )
478			break;
479		if( (r < 0) && (errno == EINTR) )
480			continue;
481		if( r < 0 ) {
482			free(inbuf);
483			return -1;
484		}
485
486		XAR(x)->heap_offset += r;
487		inc += r;
488		bsize = r;
489
490		/* filter the data through the in modules */
491		for( i = 0; i < modulecount; i++) {
492			if( xar_datamods[i].fh_in ) {
493				int32_t ret;
494				ret = xar_datamods[i].fh_in(x, f, p, &inbuf, &bsize, &(modulecontext[i]));
495				if( ret < 0 ) {
496					free(inbuf); // (Apple) don't leak inbuf
497					return -1;
498				}
499			}
500		}
501
502		/* Only due the write phase, if there is a write function to call */
503		if(wcb){
504
505			/* filter the data through the out modules */
506			for( i = 0; i < modulecount; i++) {
507				if( xar_datamods[i].fh_out ) {
508					int32_t ret;
509					ret = xar_datamods[i].fh_out(x, f, p, inbuf, bsize, &(modulecontext[i]));
510					if( ret < 0 ) {
511						free(inbuf); // (Apple) don't leak inbuf
512						return -1;
513					}
514				}
515			}
516
517			wcb(x, f, inbuf, bsize, context);
518		}
519
520		bsize = def_bsize;
521	}
522
523	free(inbuf);
524	/* finish up anything that still needs doing */
525	for( i = 0; i < modulecount; i++) {
526		if( xar_datamods[i].fh_done ) {
527			int32_t ret;
528			ret = xar_datamods[i].fh_done(x, f, p, &(modulecontext[i]));
529			if( ret < 0 )
530				return ret;
531		}
532	}
533	return 0;
534}
535
536/* xar_attrcopy_from_heap_to_heap
537* This does a simple copy of the heap data from one head (read-only) to another heap (write only).
538* This does not set any properties or attributes of the file, so this should not be used alone.
539*/
540int32_t xar_attrcopy_from_heap_to_heap(xar_t xsource, xar_file_t fsource, xar_prop_t p, xar_t xdest, xar_file_t fdest){
541	int r, off;
542	size_t bsize;
543	int64_t fsize, inc = 0, seekoff, writesize=0;
544	off_t orig_heap_offset = XAR(xdest)->heap_offset;
545	void *inbuf;
546	const char *opt;
547	char *tmpstr = NULL;
548	xar_prop_t tmpp;
549
550	bsize = xar_io_get_rsize(xsource);
551
552	seekoff = xar_io_get_file_offset(xsource, fsource, p);
553	if( seekoff < 0 )
554		return -1;
555
556	seekoff += XAR(xsource)->toc_count + sizeof(xar_header_t);
557	xar_io_seek(xsource, fsource, seekoff);
558
559	fsize = xar_io_get_length(p);
560	if( fsize == 0 )
561		return 0;
562	if( fsize < 0 )
563		return -1;
564
565	inbuf = malloc(bsize);
566	if( !inbuf ) {
567		return -1;
568	}
569
570
571	while(1) {
572		/* Size has been reached */
573		if( fsize == inc )
574			break;
575		if( (fsize - inc) < bsize )
576			bsize = fsize - inc;
577		r = read(XAR(xsource)->fd, inbuf, bsize);
578		if( r == 0 )
579			break;
580		if( (r < 0) && (errno == EINTR) )
581			continue;
582		if( r < 0 ) {
583			free(inbuf);
584			return -1;
585		}
586
587		XAR(xsource)->heap_offset += r;
588		inc += r;
589		bsize = r;
590
591		off = 0;
592
593		do {
594			r = write(XAR(xdest)->heap_fd, ((char *)inbuf)+off, r-off );
595			off += r;
596			writesize += r;
597		} while( off < r );
598		XAR(xdest)->heap_offset += off;
599		XAR(xdest)->heap_len += off;
600	}
601
602	asprintf(&tmpstr, "%"PRIu64, (uint64_t)orig_heap_offset);
603	opt = xar_prop_getkey(p);
604	tmpp = xar_prop_pfirst(fdest);
605	if( tmpp )
606		tmpp = xar_prop_find(tmpp, opt);
607	if( tmpp )
608		xar_prop_pset(fdest, tmpp, "offset", tmpstr);
609	free(tmpstr);
610
611
612	free(inbuf);
613
614	/* It is the caller's responsibility to copy the attributes of the file, etc, this only copies the data in the heap */
615
616	return 0;
617}
618
619static int32_t flush_stream(xar_stream *stream) {
620	xar_stream_state_t *state = (xar_stream_state_t *)(stream->state);
621
622	if( state->pending_buf && stream->avail_out ) {
623		size_t len = state->pending_buf_size;
624
625		if( stream->avail_out < len ) {
626			len = stream->avail_out;
627		}
628
629		memcpy(stream->next_out, state->pending_buf, len);
630		stream->next_out += len;
631		stream->avail_out -= len;
632		stream->total_out += len;
633
634		if( state->pending_buf_size == len )  {
635			state->pending_buf_size = 0;
636
637			free(state->pending_buf);
638			state->pending_buf = NULL;
639		} else if( state->pending_buf_size > len ) {
640			state->pending_buf_size -= len;
641			memcpy(state->pending_buf, state->pending_buf + len, state->pending_buf_size);
642		}
643	}
644
645	return XAR_STREAM_OK;
646}
647
648static int32_t write_to_stream(void *inbuf, size_t inlen, xar_stream *stream) {
649	xar_stream_state_t *state = (xar_stream_state_t *)stream->state;
650	size_t len = inlen;
651
652	if( stream->avail_out < len ) {
653		len = stream->avail_out;
654	}
655
656	memcpy(stream->next_out, inbuf, len);
657	stream->next_out += len;
658	stream->avail_out -= len;
659	stream->total_out += len;
660
661	if( inlen > len ) {
662		state->pending_buf_size = inlen - len;
663		state->pending_buf = malloc(state->pending_buf_size);
664
665		memcpy(state->pending_buf, ((char *)inbuf) + len, state->pending_buf_size);
666	}
667
668	return XAR_STREAM_OK;
669}
670
671int32_t xar_attrcopy_from_heap_to_stream_init(xar_t x, xar_file_t f, xar_prop_t p, xar_stream *stream) {
672	xar_stream_state_t *state;
673	off_t seekoff;
674
675	seekoff = xar_io_get_file_offset(x, f, p);
676	if( seekoff < 0 )
677		return XAR_STREAM_ERR;
678
679	state = calloc(1, sizeof(xar_stream_state_t));
680	if( !state ) {
681		return XAR_STREAM_ERR;
682	}
683	stream->state = (void*)state;
684	state->bsize = xar_io_get_rsize(x);
685
686	state->modulecount = (sizeof(xar_datamods)/sizeof(struct datamod));
687	state->modulecontext = calloc(1, sizeof(void*)*state->modulecount);
688	if( !state->modulecontext ) {
689		free(state);
690		return XAR_STREAM_ERR;
691	}
692
693	seekoff += XAR(x)->toc_count + sizeof(xar_header_t);
694	xar_io_seek(x, f, seekoff);
695
696	stream->total_in = 0;
697	stream->total_out = 0;
698
699	state->fsize = xar_io_get_length(p);
700
701	if(state->fsize == 0) {
702		return XAR_STREAM_OK;
703	} else if(state->fsize == -1) {
704		free(state->modulecontext);
705		free(state);
706		return XAR_STREAM_ERR;
707	}
708
709	state->pending_buf = NULL;
710	state->pending_buf_size = 0;
711	state->x = x;
712	state->f = f;
713	state->p = p;
714
715	return XAR_STREAM_OK;
716}
717
718int32_t xar_attrcopy_from_heap_to_stream(xar_stream *stream) {
719	xar_stream_state_t *state = stream->state;
720
721	int r, i;
722	size_t bsize;
723	void *inbuf;
724
725	if( state->pending_buf_size ) {
726		return flush_stream(stream);
727	}
728
729	bsize = state->bsize;
730	inbuf = malloc(bsize);
731	if( !inbuf ) {
732		return XAR_STREAM_ERR;
733	}
734
735	/* Size has been reached */
736	if( state->fsize == stream->total_in ) {
737		free(inbuf);
738		return XAR_STREAM_END;
739	}
740	if( (state->fsize - stream->total_in) < bsize )
741		bsize = state->fsize - stream->total_in;
742	r = read(XAR(state->x)->fd, inbuf, bsize);
743	if( r == 0 ) {
744		free(inbuf);
745		return XAR_STREAM_END;
746	}
747	if( (r < 0) && (errno == EINTR) ) {
748		free(inbuf);
749		return XAR_STREAM_OK;
750	}
751	if( r < 0 ) {
752		free(inbuf);
753		return XAR_STREAM_ERR;
754	}
755
756	XAR(state->x)->heap_offset += r;
757	stream->total_in += r;
758	bsize = r;
759
760	/* filter the data through the in modules */
761	for( i = 0; i < state->modulecount; i++) {
762	if( xar_datamods[i].fh_in ) {
763		int32_t ret;
764		ret = xar_datamods[i].fh_in(state->x, state->f, state->p, &inbuf, &bsize, &(state->modulecontext[i]));
765		if( ret < 0 )
766			return XAR_STREAM_ERR;
767		}
768	}
769
770	/* filter the data through the out modules */
771	for( i = 0; i < state->modulecount; i++) {
772		if( xar_datamods[i].fh_out ) {
773			int32_t ret;
774			ret = xar_datamods[i].fh_out(state->x, state->f, state->p, inbuf, bsize, &(state->modulecontext[i]));
775			if( ret < 0 )
776				return XAR_STREAM_ERR;
777		}
778	}
779
780	write_to_stream(inbuf, bsize, stream);
781
782	free(inbuf);
783
784	return XAR_STREAM_OK;
785}
786
787int32_t xar_attrcopy_from_heap_to_stream_end(xar_stream *stream) {
788	xar_stream_state_t *state = (xar_stream_state_t *)stream->state;
789	int i;
790
791	/* finish up anything that still needs doing */
792	for( i = 0; i < state->modulecount; i++) {
793		if( xar_datamods[i].fh_done ) {
794			int32_t ret;
795			ret = xar_datamods[i].fh_done(state->x, state->f, state->p, &(state->modulecontext[i]));
796			if( ret < 0 )
797				return ret;
798		}
799	}
800
801	if( state->pending_buf ) {
802		free(state->pending_buf);
803	}
804
805	free(state->modulecontext);
806	free(state);
807
808	return XAR_STREAM_OK;
809}
810
811/* xar_heap_to_archive
812 * x: archive to operate on
813 * Returns 0 on success, -1 on error
814 * Summary: copies the heap into the archive.
815 */
816int32_t xar_heap_to_archive(xar_t x) {
817	long bsize;
818	ssize_t r;
819	int off;
820	const char *opt;
821	char *b;
822
823	opt = xar_opt_get(x, "rsize");
824	if( !opt ) {
825		bsize = 4096;
826	} else {
827		bsize = strtol(opt, NULL, 0);
828		if( ((bsize == LONG_MAX) || (bsize == LONG_MIN)) && (errno == ERANGE) ) {
829			bsize = 4096;
830		}
831	}
832
833	b = malloc(bsize);
834	if( !b ) return -1;
835
836	lseek(XAR(x)->heap_fd, 0, SEEK_SET);
837	while(1) {
838		r = read(XAR(x)->heap_fd, b, bsize);
839		if( r == 0 ) break;
840		if( (r < 0) && (errno == EINTR) ) continue;
841		if( r < 0 ) {
842			free(b);
843			return -1;
844		}
845
846		off = 0;
847		do {
848			r = write(XAR(x)->fd, b+off, bsize-off);
849			if( (r < 0) && (errno != EINTR) ) {
850				free(b);
851				return -1;
852			}
853			off += r;
854		} while( off < bsize );
855	}
856	free(b);
857	return 0;
858}
859