1/*
2 * Copyright (c) 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 its 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 * 24-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#include "config.h"
39#include <stdio.h>
40#include <unistd.h>
41#include <string.h>
42#include <libgen.h>
43#include <fcntl.h>
44#include <sys/stat.h>
45#include <arpa/inet.h>
46#include "xar.h"
47#include "arcmod.h"
48#include "b64.h"
49#include <errno.h>
50#include <string.h>
51#include "util.h"
52#include "linuxattr.h"
53#include "io.h"
54#include "appledouble.h"
55#include "stat.h"
56#include "archive.h"
57
58#if defined(HAVE_SYS_XATTR_H)
59#include <sys/xattr.h>
60#endif
61
62struct _darwinattr_context{
63	int fd;
64	char *finfo;
65	char *buf;
66	int   len;
67	int   off;
68};
69
70#define DARWINATTR_CONTEXT(x) ((struct _darwinattr_context *)(x))
71#if defined(__APPLE__)
72#ifdef HAVE_GETATTRLIST
73#include <sys/attr.h>
74#include <sys/vnode.h>
75struct fi {
76    uint32_t     length;
77    fsobj_type_t objtype;
78    char finderinfo[32];
79};
80
81/* finfo_read
82 * This is for archiving the finderinfo via the getattrlist method.
83 * This function is used from the nonea_archive() function.
84 */
85static int32_t finfo_read(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
86	if( len < 32 )
87		return -1;
88
89	if( DARWINATTR_CONTEXT(context)->finfo == NULL )
90		return 0;
91
92	memcpy(buf, DARWINATTR_CONTEXT(context)->finfo, 32);
93	DARWINATTR_CONTEXT(context)->finfo = NULL;
94	return 32;
95}
96
97/* finfo_write
98 * This is for extracting the finderinfo via the setattrlist method.
99 * This function is used from the nonea_extract() function.
100 */
101static int32_t finfo_write(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
102	struct attrlist attrs;
103	struct fi finfo;
104
105	if( len < 32 )
106		return -1;
107	if( DARWINATTR_CONTEXT(context)->finfo == NULL )
108		return 0;
109
110	memset(&attrs, 0, sizeof(attrs));
111	attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
112	attrs.commonattr = ATTR_CMN_OBJTYPE | ATTR_CMN_FNDRINFO;
113
114	getattrlist(DARWINATTR_CONTEXT(context)->finfo, &attrs, &finfo, sizeof(finfo), 0);
115
116	attrs.commonattr = ATTR_CMN_FNDRINFO;
117	if( setattrlist(DARWINATTR_CONTEXT(context)->finfo, &attrs, buf, 32, 0) != 0 )
118		return -1;
119
120	DARWINATTR_CONTEXT(context)->finfo = NULL;
121	return 32;
122}
123#endif /* HAVE_GETATTRLIST */
124
125/* xar_rsrc_read
126 * This is the read callback function for archiving the resource fork via
127 * the ..namedfork method.  This callback is used from nonea_archive()
128 */
129static int32_t xar_rsrc_read(xar_t x, xar_file_t f, void *inbuf, size_t bsize, void *context) {
130	int32_t r;
131
132	while(1) {
133		r = read(DARWINATTR_CONTEXT(context)->fd, inbuf, bsize);
134		if( (r < 0) && (errno == EINTR) )
135			continue;
136		return r;
137	}
138}
139#endif /* __APPLE__ */
140
141/* xar_rsrc_write
142 * This is the write callback function for writing the resource fork
143 * back to the file via ..namedfork method.  This is the callback used
144 * in nonea_extract() and underbar_extract().
145 */
146static int32_t xar_rsrc_write(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
147	int32_t r;
148	size_t off = 0;
149	do {
150		r = write(DARWINATTR_CONTEXT(context)->fd, ((char *)buf)+off, len-off);
151		if( (r < 0) && (errno != EINTR) )
152			return r;
153		off += r;
154	} while( off < len );
155	return off;
156}
157
158#ifdef __APPLE__
159#if defined(HAVE_GETXATTR)
160
161static int32_t xar_ea_read(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
162	if( DARWINATTR_CONTEXT(context)->buf == NULL )
163		return 0;
164
165	if( ((DARWINATTR_CONTEXT(context)->len)-(DARWINATTR_CONTEXT(context)->off)) <= len ) {
166		int siz = (DARWINATTR_CONTEXT(context)->len)-(DARWINATTR_CONTEXT(context)->off);
167		memcpy(buf, DARWINATTR_CONTEXT(context)->buf+DARWINATTR_CONTEXT(context)->off, siz);
168		free(DARWINATTR_CONTEXT(context)->buf);
169		DARWINATTR_CONTEXT(context)->buf = NULL;
170		DARWINATTR_CONTEXT(context)->off = 0;
171		DARWINATTR_CONTEXT(context)->len = 0;
172		return siz;
173	}
174
175	memcpy(buf, DARWINATTR_CONTEXT(context)->buf+DARWINATTR_CONTEXT(context)->off, len);
176	DARWINATTR_CONTEXT(context)->off += len;
177
178	if( DARWINATTR_CONTEXT(context)->off == DARWINATTR_CONTEXT(context)->len ) {
179		free(DARWINATTR_CONTEXT(context)->buf);
180		DARWINATTR_CONTEXT(context)->buf = NULL;
181		DARWINATTR_CONTEXT(context)->off = 0;
182		DARWINATTR_CONTEXT(context)->len = 0;
183	}
184
185	return len;
186}
187
188static int32_t xar_ea_write(xar_t x, xar_file_t f, void *buf, size_t len, void *context) {
189	if( DARWINATTR_CONTEXT(context)->buf == NULL )
190		return 0;
191
192	if( DARWINATTR_CONTEXT(context)->off == DARWINATTR_CONTEXT(context)->len )
193		return 0;
194
195	if( ((DARWINATTR_CONTEXT(context)->len)-(DARWINATTR_CONTEXT(context)->off)) <= len ) {
196		int siz = (DARWINATTR_CONTEXT(context)->len)-(DARWINATTR_CONTEXT(context)->off);
197		memcpy((DARWINATTR_CONTEXT(context)->buf)+(DARWINATTR_CONTEXT(context)->off), buf, siz);
198		return siz;
199	}
200
201	memcpy((DARWINATTR_CONTEXT(context)->buf)+(DARWINATTR_CONTEXT(context)->off), buf, len);
202	DARWINATTR_CONTEXT(context)->off += len;
203
204	return len;
205}
206
207static int32_t ea_archive(xar_t x, xar_file_t f, const char* file, void *context) {
208	char *buf, *i;
209	int ret, bufsz, attrsz;
210	int32_t retval = 0;
211
212	if( file == NULL )
213		return 0;
214
215	ret = listxattr(file, NULL, 0, XATTR_NOFOLLOW);
216	if( ret < 0 )
217		return -1;
218	if( ret == 0 )
219		return 0;
220	bufsz = ret;
221
222TRYAGAIN:
223	buf = malloc(bufsz);
224	if( !buf )
225		goto TRYAGAIN;
226	ret = listxattr(file, buf, bufsz, XATTR_NOFOLLOW);
227	if( ret < 0 ) {
228		switch(errno) {
229		case ERANGE: bufsz = bufsz*2; free(buf); goto TRYAGAIN;
230		case ENOTSUP: retval = 0; goto BAIL;
231		default: retval = -1; goto BAIL;
232		};
233	}
234	if( ret == 0 ) {
235		retval = 0;
236		goto BAIL;
237	}
238
239	attrsz = ret;
240	for( i = buf; (i-buf) < attrsz; i += strlen(i)+1 ) {
241		xar_ea_t e;
242
243		ret = getxattr(file, i, NULL, 0, 0, XATTR_NOFOLLOW);
244		if( ret < 0 )
245			continue;
246		DARWINATTR_CONTEXT(context)->len = ret;
247		DARWINATTR_CONTEXT(context)->buf = malloc(DARWINATTR_CONTEXT(context)->len);
248		if( !DARWINATTR_CONTEXT(context)->buf )
249			goto BAIL;
250
251		ret = getxattr(file, i, DARWINATTR_CONTEXT(context)->buf, DARWINATTR_CONTEXT(context)->len, 0, XATTR_NOFOLLOW);
252		if( ret < 0 ) {
253			free(DARWINATTR_CONTEXT(context)->buf);
254			DARWINATTR_CONTEXT(context)->buf = NULL;
255			DARWINATTR_CONTEXT(context)->len = 0;
256			continue;
257		}
258
259		e = xar_ea_new(f, i);
260		XAR(x)->attrcopy_to_heap(x, f, xar_ea_root(e), xar_ea_read, context);
261	}
262BAIL:
263	free(buf);
264	return retval;
265}
266
267static int32_t ea_extract(xar_t x, xar_file_t f, const char* file, void *context) {
268	xar_prop_t p;
269
270	for(p = xar_prop_pfirst(f); p; p = xar_prop_pnext(p)) {
271		const char *opt;
272		const char *name = NULL;
273		int len;
274		xar_prop_t tmpp;
275
276		name = xar_prop_getkey(p);
277		if( strncmp(name, XAR_EA_FORK, strlen(XAR_EA_FORK)) )
278			continue;
279		if( strlen(name) != strlen(XAR_EA_FORK) )
280			continue;
281
282		opt = NULL;
283		tmpp = xar_prop_pget(p, "size");
284		if( tmpp )
285			opt = xar_prop_getvalue(tmpp);
286		if( !opt )
287			continue;
288
289		len = strtol(opt, NULL, 10);
290		DARWINATTR_CONTEXT(context)->buf = malloc(len);
291		if( !DARWINATTR_CONTEXT(context)->buf )
292			return -1;
293		DARWINATTR_CONTEXT(context)->len = len;
294
295		XAR(x)->attrcopy_from_heap(x, f, p, xar_ea_write, context);
296
297		name = NULL;
298		tmpp = xar_prop_pget(p, "name");
299		if( tmpp )
300			name = xar_prop_getvalue(tmpp);
301		if( !name )
302			continue;
303
304		setxattr(file, name, DARWINATTR_CONTEXT(context)->buf, DARWINATTR_CONTEXT(context)->len, 0, XATTR_NOFOLLOW);
305		free(DARWINATTR_CONTEXT(context)->buf);
306		DARWINATTR_CONTEXT(context)->buf = NULL;
307		DARWINATTR_CONTEXT(context)->len = 0;
308		DARWINATTR_CONTEXT(context)->off = 0;
309	}
310
311	return 0;
312}
313#endif /* HAVE_GETXATTR */
314
315/* nonea_archive
316 * Archive the finderinfo and resource fork through getattrlist and
317 * ..namedfork methods rather than via EAs.  This is mainly for 10.3
318 * and earlier support
319 */
320static int32_t nonea_archive(xar_t x, xar_file_t f, const char* file, void *context) {
321	char rsrcname[4096];
322	struct stat sb;
323	xar_ea_t e;
324#ifdef HAVE_GETATTRLIST
325	struct attrlist attrs;
326	struct fi finfo;
327	int ret;
328	char z[32];
329
330	memset(&attrs, 0, sizeof(attrs));
331	attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
332	attrs.commonattr = ATTR_CMN_OBJTYPE | ATTR_CMN_FNDRINFO;
333
334	ret = getattrlist(file, &attrs, &finfo, sizeof(finfo), 0);
335	if( ret != 0 )
336		return -1;
337
338	memset(z, 0, sizeof(z));
339	if( memcmp(finfo.finderinfo, z, sizeof(finfo.finderinfo)) != 0 ) {
340		e = xar_ea_new(f, "com.apple.FinderInfo");
341		DARWINATTR_CONTEXT(context)->finfo = finfo.finderinfo;
342		XAR(x)->attrcopy_to_heap(x, f, xar_ea_root(e), finfo_read, context);
343	}
344
345
346#endif /* HAVE_GETATTRLIST */
347
348
349	memset(rsrcname, 0, sizeof(rsrcname));
350	snprintf(rsrcname, sizeof(rsrcname)-1, "%s/..namedfork/rsrc", file);
351	if( lstat(rsrcname, &sb) != 0 )
352		return 0;
353
354	if( sb.st_size == 0 )
355		return 0;
356
357	DARWINATTR_CONTEXT(context)->fd = open(rsrcname, O_RDONLY, 0);
358	if( DARWINATTR_CONTEXT(context)->fd < 0 )
359		return -1;
360
361	e = xar_ea_new(f, "com.apple.ResourceFork");
362	XAR(x)->attrcopy_to_heap(x, f, xar_ea_root(e), xar_rsrc_read, context);
363	close(DARWINATTR_CONTEXT(context)->fd);
364	return 0;
365}
366
367/* nonea_extract
368 * Extract the finderinfo and resource fork through setattrlist and
369 * ..namedfork methods rather than via EAs.  This is mainly for 10.3
370 * and earlier support
371 */
372static int32_t nonea_extract(xar_t x, xar_file_t f, const char* file, void *context) {
373	char rsrcname[4096];
374	xar_prop_t p;
375#ifdef HAVE_SETATTRLIST
376	struct attrlist attrs;
377	struct fi finfo;
378	int ret;
379
380	memset(&attrs, 0, sizeof(attrs));
381	attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
382	attrs.commonattr = ATTR_CMN_OBJTYPE | ATTR_CMN_FNDRINFO;
383
384	ret = getattrlist(file, &attrs, &finfo, sizeof(finfo), 0);
385	if( ret != 0 )
386		return -1;
387
388	DARWINATTR_CONTEXT(context)->finfo = (char *)file;
389
390	p = xar_ea_find(f, "com.apple.ResourceFork");
391	if( p )
392		XAR(x)->attrcopy_from_heap(x, f, p, finfo_write, context);
393#endif /* HAVE_SETATTRLIST */
394
395	memset(rsrcname, 0, sizeof(rsrcname));
396	snprintf(rsrcname, sizeof(rsrcname)-1, "%s/..namedfork/rsrc", file);
397	DARWINATTR_CONTEXT(context)->fd = open(rsrcname, O_RDWR|O_TRUNC);
398	if( DARWINATTR_CONTEXT(context)->fd < 0 )
399		return 0;
400
401	p = xar_ea_find(f, "com.apple.ResourceFork");
402	if( p )
403		XAR(x)->attrcopy_from_heap(x, f, p, xar_rsrc_write, context);
404	close(DARWINATTR_CONTEXT(context)->fd);
405	return 0;
406}
407#endif /* __APPLE__ */
408
409/* xar_underbar_check
410 * Check to see if the file we're archiving is a ._ file.  If so,
411 * stop the archival process.
412 */
413xar_file_t xar_underbar_check(xar_t x, xar_file_t f, const char* file, void *context) {
414	char *bname, *tmp;
415
416	tmp = strdup(file);
417	bname = basename(tmp);
418
419	if(bname && (bname[0] == '.') && (bname[1] == '_')) {
420		char *nonunderbar, *nupath, *tmp2, *dname;
421		struct stat sb;
422
423		nonunderbar = bname+2;
424		tmp2 = strdup(file);
425		dname = dirname(tmp2);
426		asprintf(&nupath, "%s/%s", dname, nonunderbar);
427		free(tmp2);
428
429		/* if there is no file that the ._ corresponds to, archive
430		 * it like a normal file.
431		 */
432		if( stat(nupath, &sb) ) {
433			free(tmp);
434			free(nupath);
435			return NULL;
436		}
437
438		asprintf(&tmp2, "%s/..namedfork/rsrc", nupath);
439
440		/* If there is a file that the ._ file corresponds to, and
441		 * there is no resource fork, assume the ._ file contains
442		 * the file's resource fork, and skip it (to be picked up
443		 * when the file is archived.
444		 */
445		if( stat(tmp2, &sb) ) {
446			xar_file_t tmpf;
447			tmpf = xar_file_find(XAR(x)->files, nupath);
448			if( !tmpf ) {
449				tmpf = xar_add(x, nupath);
450			}
451			free(nupath);
452			free(tmp2);
453			free(tmp);
454			return tmpf;
455		}
456
457		/* otherwise, we have a corresponding file and it supports
458		 * resource forks, so we assume this is a detached ._ file
459		 * and archive it as a real file.
460		 */
461		free(nupath);
462		free(tmp2);
463		free(tmp);
464		return NULL;
465	}
466
467	free(tmp);
468	return NULL;
469}
470
471#ifdef __APPLE__
472/* This only really makes sense on OSX */
473static int32_t underbar_archive(xar_t x, xar_file_t f, const char* file, void *context) {
474	struct stat sb;
475	char underbarname[4096], z[32];
476	char *dname, *bname, *tmp, *tmp2;
477	struct AppleSingleHeader ash;
478	struct AppleSingleEntry  ase;
479	int num_entries = 0, i, r;
480	off_t off;
481
482	if( !file )
483		return 0;
484
485	tmp = strdup(file);
486	tmp2 = strdup(file);
487	dname = dirname(tmp2);
488	bname = basename(tmp);
489
490	memset(underbarname, 0, sizeof(underbarname));
491	snprintf(underbarname, sizeof(underbarname)-1, "%s/._%s", dname, bname);
492	free(tmp);
493	free(tmp2);
494
495	if( stat(underbarname, &sb) != 0 )
496		return 0;
497
498	DARWINATTR_CONTEXT(context)->fd = open(underbarname, O_RDONLY);
499	if( DARWINATTR_CONTEXT(context)->fd < 0 )
500		return -1;
501
502	memset(&ash, 0, sizeof(ash));
503	memset(&ase, 0, sizeof(ase));
504	r = read(DARWINATTR_CONTEXT(context)->fd, &ash, XAR_ASH_SIZE);
505	if( r < XAR_ASH_SIZE ) {
506		close(DARWINATTR_CONTEXT(context)->fd);
507		return -1;
508	}
509
510	if( ntohl(ash.magic) != APPLEDOUBLE_MAGIC ) {
511		close(DARWINATTR_CONTEXT(context)->fd);
512		return -1;
513	}
514	if( ntohl(ash.version) != APPLEDOUBLE_VERSION ) {
515		close(DARWINATTR_CONTEXT(context)->fd);
516		return -1;
517	}
518
519	off = XAR_ASH_SIZE;
520	num_entries = ntohs(ash.entries);
521
522	for(i = 0; i < num_entries; i++) {
523		off_t entoff;
524		r = read(DARWINATTR_CONTEXT(context)->fd, &ase, sizeof(ase));
525		if( r < sizeof(ase) ) {
526			close(DARWINATTR_CONTEXT(context)->fd);
527			return -1;
528		}
529		off+=r;
530
531		if( ntohl(ase.entry_id) == AS_ID_FINDER ) {
532			xar_ea_t e;
533			entoff = (off_t)ntohl(ase.offset);
534			if( lseek(DARWINATTR_CONTEXT(context)->fd, entoff, SEEK_SET) == -1 ) {
535				close(DARWINATTR_CONTEXT(context)->fd);
536				return -1;
537			}
538			r = read(DARWINATTR_CONTEXT(context)->fd, z, sizeof(z));
539			if( r < sizeof(z) ) {
540				close(DARWINATTR_CONTEXT(context)->fd);
541				return -1;
542			}
543
544			DARWINATTR_CONTEXT(context)->finfo = z;
545			e = xar_ea_new(f, "com.apple.FinderInfo");
546			XAR(x)->attrcopy_to_heap(x, f, xar_ea_root(e), finfo_read, context);
547			if( lseek(DARWINATTR_CONTEXT(context)->fd, (off_t)off, SEEK_SET) == -1 ) {
548				close(DARWINATTR_CONTEXT(context)->fd);
549				return -1;
550			}
551		}
552		if( ntohl(ase.entry_id) == AS_ID_RESOURCE ) {
553			xar_ea_t e;
554			entoff = (off_t)ntohl(ase.offset);
555			if( lseek(DARWINATTR_CONTEXT(context)->fd, entoff, SEEK_SET) == -1 ) {
556				close(DARWINATTR_CONTEXT(context)->fd);
557				return -1;
558			}
559
560			e = xar_ea_new(f, "com.apple.ResourceFork");
561			XAR(x)->attrcopy_to_heap(x, f, xar_ea_root(e), xar_rsrc_read, context);
562
563			if( lseek(DARWINATTR_CONTEXT(context)->fd, (off_t)off, SEEK_SET) == -1 ) {
564				close(DARWINATTR_CONTEXT(context)->fd);
565				return -1;
566			}
567		}
568	}
569
570	close(DARWINATTR_CONTEXT(context)->fd);
571	DARWINATTR_CONTEXT(context)->fd = 0;
572	return 0;
573}
574#endif
575
576/* underbar_extract
577 * Extract finderinfo and resource fork information to an appledouble
578 * ._ file.
579 */
580static int32_t underbar_extract(xar_t x, xar_file_t f, const char* file, void *context) {
581	char underbarname[4096];
582	char *dname, *bname, *tmp, *tmp2;
583	const char *rsrclenstr;
584	struct AppleSingleHeader ash;
585	struct AppleSingleEntry  ase;
586	int num_entries = 0, rsrclen = 0, have_rsrc = 0, have_fi = 0;
587	xar_prop_t p;
588	xar_prop_t rfprop = NULL, fiprop = NULL;
589
590	fiprop = xar_ea_find(f, "com.apple.FinderInfo");
591	if( fiprop ) {
592		have_fi = 1;
593		num_entries++;
594	}
595
596	rfprop = xar_ea_find(f, "com.apple.ResourceFork");
597	if( rfprop ) {
598		have_rsrc = 1;
599		num_entries++;
600	}
601
602	if( num_entries == 0 )
603		return 0;
604
605	tmp = strdup(file);
606	tmp2 = strdup(file);
607	dname = dirname(tmp2);
608	bname = basename(tmp);
609
610	memset(underbarname, 0, sizeof(underbarname));
611	snprintf(underbarname, sizeof(underbarname)-1, "%s/._%s", dname, bname);
612	free(tmp);
613	free(tmp2);
614
615	DARWINATTR_CONTEXT(context)->fd = open(underbarname, O_RDWR | O_CREAT | O_TRUNC, 0);
616	if( DARWINATTR_CONTEXT(context)->fd < 0 )
617		return -1;
618
619	rsrclenstr = NULL;
620	if( rfprop ) {
621		p = xar_prop_pget(rfprop, "size");
622		if( p )
623			rsrclenstr = xar_prop_getvalue(p);
624		if( rsrclenstr )
625			rsrclen = strtol(rsrclenstr, NULL, 10);
626	}
627
628	memset(&ash, 0, sizeof(ash));
629	memset(&ase, 0, sizeof(ase));
630	ash.magic = htonl(APPLEDOUBLE_MAGIC);
631	ash.version = htonl(APPLEDOUBLE_VERSION);
632	ash.entries = htons(num_entries);
633
634	write(DARWINATTR_CONTEXT(context)->fd, &ash, XAR_ASH_SIZE);
635
636	ase.offset = htonl(XAR_ASH_SIZE + ntohs(ash.entries)*12);
637	if( have_fi ) {
638		ase.entry_id = htonl(AS_ID_FINDER);
639		ase.length = htonl(32);
640		write(DARWINATTR_CONTEXT(context)->fd, &ase, 12);
641	}
642
643	if( have_rsrc ) {
644		ase.entry_id = htonl(AS_ID_RESOURCE);
645		ase.offset = htonl(ntohl(ase.offset) + ntohl(ase.length));
646		ase.length = htonl(rsrclen);
647		write(DARWINATTR_CONTEXT(context)->fd, &ase, 12);
648	}
649
650	if( have_fi )
651		XAR(x)->attrcopy_from_heap(x, f, fiprop, xar_rsrc_write, context);
652	if( have_rsrc )
653		XAR(x)->attrcopy_from_heap(x, f, rfprop, xar_rsrc_write, context);
654	close(DARWINATTR_CONTEXT(context)->fd);
655
656	DARWINATTR_CONTEXT(context)->fd = 0;
657
658	xar_set_perm(x, f, underbarname, NULL, 0 );
659
660	return 0;
661}
662
663#if defined(__APPLE__)
664static int32_t stragglers_archive(xar_t x, xar_file_t f, const char* file, void *context) {
665#ifdef HAVE_GETATTRLIST
666	struct fits {
667    		uint32_t     length;
668		struct timespec ts;
669	};
670	struct fits fts;
671	struct attrlist attrs;
672	int ret;
673
674	if( !xar_check_prop(x, "FinderCreateTime") )
675		return 0;
676
677	memset(&attrs, 0, sizeof(attrs));
678	attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
679	attrs.commonattr = ATTR_CMN_CRTIME;
680	ret = getattrlist(file, &attrs, &fts, sizeof(fts), 0);
681	if( ret == 0 ) {
682		xar_prop_t tmpp;
683
684		tmpp = xar_prop_new(f, NULL);
685		if( tmpp ) {
686			char tmpc[128];
687			struct tm tm;
688			xar_prop_setkey(tmpp, "FinderCreateTime");
689			xar_prop_setvalue(tmpp, NULL);
690			memset(tmpc, 0, sizeof(tmpc));
691			gmtime_r(&fts.ts.tv_sec, &tm);
692			strftime(tmpc, sizeof(tmpc), "%FT%T", &tm);
693			xar_prop_pset(f, tmpp, "time", tmpc);
694			memset(tmpc, 0, sizeof(tmpc));
695			sprintf(tmpc, "%ld", fts.ts.tv_nsec);
696			xar_prop_pset(f, tmpp, "nanoseconds", tmpc);
697		}
698	}
699#endif
700	return 0;
701}
702
703static int32_t stragglers_extract(xar_t x, xar_file_t f, const char* file, void *context) {
704#ifdef HAVE_GETATTRLIST
705	const char *tmpc = NULL;
706	struct tm tm;
707	struct timespec ts;
708	struct attrlist attrs;
709
710	xar_prop_get(f, "FinderCreateTime/time", &tmpc);
711	if( tmpc ) {
712		strptime(tmpc, "%FT%T", &tm);
713		ts.tv_sec = timegm(&tm);
714		xar_prop_get(f, "FinderCreateTime/nanoseconds", &tmpc);
715		ts.tv_nsec = strtol(tmpc, NULL, 10);
716		memset(&attrs, 0, sizeof(attrs));
717		attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
718		attrs.commonattr = ATTR_CMN_CRTIME;
719		setattrlist(file, &attrs, &ts, sizeof(ts), 0);
720	}
721
722#endif
723	return 0;
724}
725#endif /* __APPLE__ */
726
727int32_t xar_darwinattr_archive(xar_t x, xar_file_t f, const char* file, const char *buffer, size_t len)
728{
729	struct _darwinattr_context context;
730
731	memset(&context,0,sizeof(struct _darwinattr_context));
732
733#if defined(__APPLE__)
734	if( len )
735		return 0;
736	stragglers_archive(x, f, file, (void *)&context);
737
738	/* From here on out, props are only EA's */
739	if( !xar_check_prop(x, "ea") )
740		return 0;
741
742#if defined(HAVE_GETXATTR)
743	if( ea_archive(x, f, file, (void *)&context) == 0 )
744		return 0;
745#endif
746	if( nonea_archive(x, f, file, (void *)&context) == 0 )
747		return 0;
748	return underbar_archive(x, f, file, (void *)&context);
749#endif /* __APPLE__ */
750	return 0;
751}
752
753int32_t xar_darwinattr_extract(xar_t x, xar_file_t f, const char* file, char *buffer, size_t len)
754{
755	struct _darwinattr_context context;
756
757	memset(&context,0,sizeof(struct _darwinattr_context));
758
759#if defined(__APPLE__)
760	if( len )
761		return 0;
762	stragglers_extract(x, f, file, (void *)&context);
763
764#if defined(HAVE_GETXATTR)
765	if( ea_extract(x, f, file, (void *)&context) == 0 )
766		return 0;
767#endif
768	if( nonea_extract(x, f, file, (void *)&context) == 0 )
769		return 0;
770#endif /* __APPLE__ */
771	return underbar_extract(x, f, file, (void *)&context);
772}
773