1/*
2 * Copyright (c) 2007 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 2003, Apple Computer, Inc.
35 * filetype_name() and associated structure.
36 * DRI: Kevin Van Vechten <kvv@apple.com>
37 */
38/*
39 * Portions Copyright 2006, Apple Computer, Inc.
40 * Christopher Ryan <ryanc@apple.com>
41*/
42
43#define _FILE_OFFSET_BITS 64
44
45#include "config.h"
46#ifndef HAVE_ASPRINTF
47#include "asprintf.h"
48#endif
49#include <stdlib.h>
50#include <stdio.h>
51#include <stdarg.h>
52#include <inttypes.h>
53#include <sys/types.h>
54#include <sys/stat.h>
55#include <sys/time.h>
56#include <fcntl.h>
57#include <time.h>
58#include <pwd.h>
59#include <grp.h>
60#include <string.h>
61#include <unistd.h>
62#include <limits.h>
63#include <errno.h>
64#include <libxml/hash.h>
65#include <libxml/xmlstring.h>
66#ifdef HAVE_SYS_ACL_H
67#include <sys/acl.h>
68#endif
69#include "xar.h"
70#include "arcmod.h"
71#include "archive.h"
72#include "util.h"
73
74#ifndef LLONG_MIN
75#define LLONG_MIN LONG_LONG_MIN
76#endif
77
78#ifndef LLONG_MAX
79#define LLONG_MAX LONG_LONG_MAX
80#endif
81
82static struct {
83	const char *name;
84	mode_t type;
85} filetypes [] = {
86	{ "file", S_IFREG },
87	{ "directory", S_IFDIR },
88	{ "symlink", S_IFLNK },
89	{ "fifo", S_IFIFO },
90	{ "character special", S_IFCHR },
91	{ "block special", S_IFBLK },
92	{ "socket", S_IFSOCK },
93#ifdef S_IFWHT
94	{ "whiteout", S_IFWHT },
95#endif
96	{ NULL, 0 }
97};
98
99static const char * filetype_name (mode_t mode) {
100	unsigned int i;
101	for (i = 0; filetypes[i].name; i++)
102		if (mode == filetypes[i].type)
103			return (filetypes[i].name);
104	return ("unknown");
105}
106
107static xar_file_t xar_link_lookup(xar_t x, dev_t dev, ino_t ino, xar_file_t f) {
108	char key[32];
109	xar_file_t ret;
110
111	memset(key, 0, sizeof(key));
112	snprintf(key, sizeof(key)-1, "%08" DEV_HEXSTRING "%08" INO_HEXSTRING, DEV_CAST dev, INO_CAST ino);
113	ret = xmlHashLookup(XAR(x)->ino_hash, BAD_CAST(key));
114	if( ret == NULL ) {
115		xmlHashAddEntry(XAR(x)->ino_hash, BAD_CAST(key), XAR_FILE(f));
116		return NULL;
117	}
118	return ret;
119}
120
121static int32_t aacls(xar_t x, xar_file_t f, const char *file) {
122#ifdef HAVE_SYS_ACL_H
123#if !defined(__APPLE__)
124	acl_t a;
125	const char *type;
126
127	xar_prop_get(f, "type", &type);
128	if( !type || (strcmp(type, "symlink") == 0) )
129		return 0;
130
131	if( !xar_check_prop(x, "acl") )
132		return 0;
133
134	a = acl_get_file(file, ACL_TYPE_DEFAULT);
135	if( a ) {
136		char *t;
137		acl_entry_t e;
138
139		/* If the acl is empty, or not valid, skip it */
140		if( acl_get_entry(a, ACL_FIRST_ENTRY, &e) != 1 )
141			goto NEXT;
142
143		t = acl_to_text(a, NULL);
144		if( t ) {
145			xar_prop_set(f, "acl/default", t);
146			acl_free(t);
147		}
148		acl_free(a);
149	}
150NEXT:
151
152	a = acl_get_file(file, ACL_TYPE_ACCESS);
153	if( a ) {
154		char *t;
155		acl_entry_t e;
156
157		/* If the acl is empty, or not valid, skip it */
158		if( acl_get_entry(a, ACL_FIRST_ENTRY, &e) != 1 )
159			goto DONE;
160
161		t = acl_to_text(a, NULL);
162		if( t ) {
163			xar_prop_set(f, "acl/access", t);
164			acl_free(t);
165		}
166		acl_free(a);
167	}
168DONE:
169#else /* !__APPLE__ */
170	acl_entry_t e = NULL;
171	acl_t a;
172	int i;
173
174	if( !xar_check_prop(x, "acl") )
175		return 0;
176
177	a = acl_get_file(file, ACL_TYPE_EXTENDED);
178	if( !a )
179		return 0;
180
181	for( i = 0; acl_get_entry(a, e == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &e) == 0; i++ ) {
182		char *t;
183		t = acl_to_text(a, NULL);
184		if( t ) {
185			xar_prop_set(f, "acl/appleextended", t);
186			acl_free(t);
187		}
188
189	}
190	acl_free(a);
191#endif /* !__APPLE__ */
192#endif
193	return 0;
194}
195
196static int32_t eacls(xar_t x, xar_file_t f, const char *file) {
197#ifdef HAVE_SYS_ACL_H
198#if !defined(__APPLE__)
199	const char *t;
200	acl_t a;
201	const char *type;
202
203	xar_prop_get(f, "type", &type);
204	if( !type || (strcmp(type, "symlink") == 0) )
205		return 0;
206
207
208	xar_prop_get(f, "acl/default", &t);
209	if( t ) {
210		a = acl_from_text(t);
211		if( !a ) {
212			xar_err_new(x);
213			xar_err_set_errno(x, errno);
214			xar_err_set_string(x, "Error extracting default acl from toc");
215			xar_err_set_file(x, f);
216			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
217		} else {
218			if( acl_set_file(file, ACL_TYPE_DEFAULT, a) != 0 ) {
219				xar_err_new(x);
220				xar_err_set_errno(x, errno);
221				xar_err_set_string(x, "Error setting default acl");
222				xar_err_set_file(x, f);
223				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
224			}
225		}
226	}
227
228	xar_prop_get(f, "acl/access", &t);
229	if( t ) {
230		a = acl_from_text(t);
231		if( !a ) {
232			xar_err_new(x);
233			xar_err_set_errno(x, errno);
234			xar_err_set_string(x, "Error extracting access acl from toc");
235			xar_err_set_file(x, f);
236			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
237		} else {
238			if( acl_set_file(file, ACL_TYPE_ACCESS, a) != 0 ) {
239				xar_err_new(x);
240				xar_err_set_errno(x, errno);
241				xar_err_set_string(x, "Error setting access acl");
242				xar_err_set_file(x, f);
243				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
244			}
245			acl_free(a);
246		}
247	}
248#else /* !__APPLE__ */
249	const char *t;
250	acl_t a;
251
252	xar_prop_get(f, "acl/appleextended", &t);
253	if( t ) {
254		a = acl_from_text(t);
255		if( !a ) {
256			xar_err_new(x);
257			xar_err_set_errno(x, errno);
258			xar_err_set_string(x, "Error extracting access acl from toc");
259			xar_err_set_file(x, f);
260			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
261		} else {
262			if( acl_set_file(file, ACL_TYPE_ACCESS, a) != 0 ) {
263				xar_err_new(x);
264				xar_err_set_errno(x, errno);
265				xar_err_set_string(x, "Error setting access acl");
266				xar_err_set_file(x, f);
267				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
268			}
269			acl_free(a);
270		}
271	}
272#endif /* !__APPLE__ */
273#endif
274	return 0;
275}
276
277#ifdef HAVE_STRUCT_STAT_ST_FLAGS
278#define XAR_FLAG_FORK "flags"
279static void x_addflag(xar_file_t f, const char *name) {
280	char opt[1024];
281	memset(opt, 0, sizeof(opt));
282	snprintf(opt, sizeof(opt)-1, "%s/%s", XAR_FLAG_FORK, name);
283	xar_prop_set(f, opt, NULL);
284	return;
285}
286#endif
287
288static int32_t flags_archive(xar_t x, xar_file_t f, const struct stat *sb) {
289#ifdef HAVE_STRUCT_STAT_ST_FLAGS
290	if( !sb->st_flags )
291		return 0;
292
293	if( !xar_check_prop(x, XAR_FLAG_FORK) )
294		return 0;
295#ifdef UF_NODUMP
296	if( sb->st_flags & UF_NODUMP )
297		x_addflag(f, "UserNoDump");
298#endif
299#ifdef UF_IMMUTABLE
300	if( sb->st_flags & UF_IMMUTABLE )
301		x_addflag(f, "UserImmutable");
302#endif
303#ifdef UF_APPEND
304	if( sb->st_flags & UF_APPEND )
305		x_addflag(f, "UserAppend");
306#endif
307#ifdef UF_OPAQUE
308	if( sb->st_flags & UF_OPAQUE )
309		x_addflag(f, "UserOpaque");
310#endif
311#ifdef SF_ARCHIVED
312	if( sb->st_flags & SF_ARCHIVED )
313		x_addflag(f, "SystemArchived");
314#endif
315#ifdef SF_IMMUTABLE
316	if( sb->st_flags & SF_IMMUTABLE )
317		x_addflag(f, "SystemImmutable");
318#endif
319#ifdef SF_APPEND
320	if( sb->st_flags & SF_APPEND )
321		x_addflag(f, "SystemAppend");
322#endif
323
324#endif /* HAVE_STRUCT_STAT_ST_FLAGS */
325	return 0;
326}
327
328#ifdef HAVE_CHFLAGS
329static int32_t x_getprop(xar_file_t f, const char *name, char **value) {
330	char v[1024];
331	memset(v, 0, sizeof(v));
332	snprintf(v, sizeof(v)-1, "%s/%s", XAR_FLAG_FORK, name);
333	return xar_prop_get(f, v, (const char **)value);
334}
335#endif
336
337int32_t xar_flags_extract(xar_t x, xar_file_t f, const char *file, char *buffer, size_t len) {
338#ifdef HAVE_CHFLAGS
339	char *tmp;
340	u_int flags = 0;
341
342	if( xar_prop_get(f, XAR_FLAG_FORK, NULL) )
343		return 0;
344
345#ifdef UF_NODUMP
346	if( x_getprop(f, "UserNoDump", (char **)&tmp) == 0 )
347		flags |= UF_NODUMP;
348#endif
349#ifdef UF_IMMUTABLE
350	if( x_getprop(f, "UserImmutable", (char **)&tmp) == 0 )
351		flags |= UF_IMMUTABLE;
352#endif
353#ifdef UF_APPEND
354	if( x_getprop(f, "UserAppend", (char **)&tmp) == 0 )
355		flags |= UF_APPEND;
356#endif
357#ifdef UF_OPAQUE
358	if( x_getprop(f, "UserOpaque", (char **)&tmp) == 0 )
359		flags |= UF_OPAQUE;
360#endif
361#ifdef SF_ARCHIVED
362	if( x_getprop(f, "SystemArchived", (char **)&tmp) == 0 )
363		flags |= SF_ARCHIVED;
364#endif
365#ifdef SF_IMMUTABLE
366	if( x_getprop(f, "SystemImmutable", (char **)&tmp) == 0 )
367		flags |= SF_IMMUTABLE;
368#endif
369#ifdef SF_APPEND
370	if( x_getprop(f, "SystemAppend", (char **)&tmp) == 0 )
371		flags |= SF_APPEND;
372#endif
373
374	if( !flags )
375		return 0;
376
377	if( chflags(file, flags) != 0 ) {
378		char e[1024];
379		memset(e, 0, sizeof(e));
380		snprintf(e, sizeof(e)-1, "chflags: %s", strerror(errno));
381		xar_err_new(x);
382		xar_err_set_file(x, f);
383		xar_err_set_string(x, e);
384		xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
385		return -1;
386	}
387#endif
388
389	return 0;
390}
391
392int32_t xar_stat_archive(xar_t x, xar_file_t f, const char *file, const char *buffer, size_t len) {
393	char *tmpstr;
394	struct passwd *pw;
395	struct group *gr;
396	char time[128];
397	struct tm t;
398	const char *type;
399
400	/* no stat attributes for data from a buffer, it is just a file */
401	if(len){
402		if( xar_check_prop(x, "type") )
403			xar_prop_set(f, "type", "file");
404		return 0;
405	}
406
407	if( S_ISREG(XAR(x)->sbcache.st_mode) && (XAR(x)->sbcache.st_nlink > 1) ) {
408		xar_file_t tmpf;
409		const char *id = xar_attr_get(f, NULL, "id");
410		if( !id ) {
411			xar_err_new(x);
412			xar_err_set_file(x, f);
413			xar_err_set_string(x, "stat: No file id for file");
414			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_CREATION);
415			return -1;
416		}
417		tmpf = xar_link_lookup(x, XAR(x)->sbcache.st_dev, XAR(x)->sbcache.st_ino, f);
418		if( xar_check_prop(x, "type") ) {
419			xar_prop_set(f, "type", "hardlink");
420			if( tmpf ) {
421				const char *id;
422				id = xar_attr_get(tmpf, NULL, "id");
423				xar_attr_set(f, "type", "link", id);
424			} else {
425				xar_attr_set(f, "type", "link", "original");
426			}
427		}
428	} else {
429		type = filetype_name(XAR(x)->sbcache.st_mode & S_IFMT);
430		if( xar_check_prop(x, "type") )
431			xar_prop_set(f, "type", type);
432	}
433
434	/* Record major/minor device node numbers */
435	if( xar_check_prop(x, "device") && (S_ISBLK(XAR(x)->sbcache.st_mode) || S_ISCHR(XAR(x)->sbcache.st_mode))) {
436		uint32_t major, minor;
437		char tmpstr[12];
438		xar_devmake(XAR(x)->sbcache.st_rdev, &major, &minor);
439		memset(tmpstr, 0, sizeof(tmpstr));
440		snprintf(tmpstr, sizeof(tmpstr)-1, "%u", major);
441		xar_prop_set(f, "device/major", tmpstr);
442		memset(tmpstr, 0, sizeof(tmpstr));
443		snprintf(tmpstr, sizeof(tmpstr)-1, "%u", minor);
444		xar_prop_set(f, "device/minor", tmpstr);
445	}
446
447	if( S_ISLNK(XAR(x)->sbcache.st_mode) ) {
448		char link[4096];
449		struct stat lsb;
450
451		memset(link, 0, sizeof(link));
452		readlink(file, link, sizeof(link)-1);
453		xar_prop_set(f, "link", link);
454		if( stat(file, &lsb) != 0 ) {
455			xar_attr_set(f, "link", "type", "broken");
456		} else {
457			type = filetype_name(lsb.st_mode & S_IFMT);
458			xar_attr_set(f, "link", "type", type);
459		}
460	}
461
462	if( xar_check_prop(x, "inode") ) {
463		asprintf(&tmpstr, "%"INO_STRING, XAR(x)->sbcache.st_ino);
464		xar_prop_set(f, "inode", tmpstr);
465		free(tmpstr);
466	}
467
468	if( xar_check_prop(x, "deviceno") ) {
469		asprintf(&tmpstr, "%"DEV_STRING, XAR(x)->sbcache.st_dev);
470		xar_prop_set(f, "deviceno", tmpstr);
471		free(tmpstr);
472	}
473
474	if( xar_check_prop(x, "mode") ) {
475		asprintf(&tmpstr, "%04o", XAR(x)->sbcache.st_mode & (~S_IFMT));
476		xar_prop_set(f, "mode", tmpstr);
477		free(tmpstr);
478	}
479
480	if( xar_check_prop(x, "uid") ) {
481		asprintf(&tmpstr, "%"PRIu64, (uint64_t)XAR(x)->sbcache.st_uid);
482		xar_prop_set(f, "uid", tmpstr);
483		free(tmpstr);
484	}
485
486	if( xar_check_prop(x, "user") ) {
487		pw = getpwuid(XAR(x)->sbcache.st_uid);
488		if( pw )
489			xar_prop_set(f, "user", pw->pw_name);
490	}
491
492	if( xar_check_prop(x, "gid") ) {
493		asprintf(&tmpstr, "%"PRIu64, (uint64_t)XAR(x)->sbcache.st_gid);
494		xar_prop_set(f, "gid", tmpstr);
495		free(tmpstr);
496	}
497
498	if( xar_check_prop(x, "group") ) {
499		gr = getgrgid(XAR(x)->sbcache.st_gid);
500		if( gr )
501			xar_prop_set(f, "group", gr->gr_name);
502	}
503
504	if( xar_check_prop(x, "atime") ) {
505		gmtime_r(&XAR(x)->sbcache.st_atime, &t);
506		memset(time, 0, sizeof(time));
507		strftime(time, sizeof(time), "%FT%T", &t);
508		strcat(time, "Z");
509		xar_prop_set(f, "atime", time);
510	}
511
512	if( xar_check_prop(x, "mtime") ) {
513		gmtime_r(&XAR(x)->sbcache.st_mtime, &t);
514		memset(time, 0, sizeof(time));
515		strftime(time, sizeof(time), "%FT%T", &t);
516		strcat(time, "Z");
517		xar_prop_set(f, "mtime", time);
518	}
519
520	if( xar_check_prop(x, "ctime") ) {
521		gmtime_r(&XAR(x)->sbcache.st_ctime, &t);
522		memset(time, 0, sizeof(time));
523		strftime(time, sizeof(time), "%FT%T", &t);
524		strcat(time, "Z");
525		xar_prop_set(f, "ctime", time);
526	}
527
528	flags_archive(x, f, &(XAR(x)->sbcache));
529
530	aacls(x, f, file);
531
532	return 0;
533}
534
535int32_t xar_set_perm(xar_t x, xar_file_t f, const char *file, char *buffer, size_t len) {
536	const char *opt;
537	int32_t m=0, mset=0;
538	uid_t u;
539	gid_t g;
540	const char *timestr;
541	struct tm t;
542	enum {ATIME=0, MTIME};
543	struct timeval tv[2];
544	char savesuid = 0;
545
546	/* when writing to a buffer, there are no permissions to set */
547	if ( len )
548		return 0;
549
550	/* in case we don't find anything useful in the archive */
551	u = geteuid();
552	g = getegid();
553
554	opt = xar_opt_get(x, XAR_OPT_OWNERSHIP);
555	if( opt && (strcmp(opt, XAR_OPT_VAL_SYMBOLIC) == 0) ) {
556		struct passwd *pw;
557		struct group *gr;
558
559		xar_prop_get(f, "user", &opt);
560		if( opt ) {
561			pw = getpwnam(opt);
562			if( pw ) {
563				u = pw->pw_uid;
564			}
565		}
566		xar_prop_get(f, "group", &opt);
567		if( opt ) {
568			gr = getgrnam(opt);
569			if( gr ) {
570				g = gr->gr_gid;
571			}
572		}
573		savesuid = 1;
574	}
575	if( opt && (strcmp(opt, XAR_OPT_VAL_NUMERIC) == 0) ) {
576		xar_prop_get(f, "uid", &opt);
577		if( opt ) {
578			long long tmp;
579			tmp = strtol(opt, NULL, 10);
580			if( ( (tmp == LLONG_MIN) || (tmp == LLONG_MAX) ) && (errno == ERANGE) ) {
581				return -1;
582			}
583			u = (uid_t)tmp;
584		}
585
586		xar_prop_get(f, "gid", &opt);
587		if( opt ) {
588			long long tmp;
589			tmp = strtol(opt, NULL, 10);
590			if( ( (tmp == LLONG_MIN) || (tmp == LLONG_MAX) ) && (errno == ERANGE) ) {
591				return -1;
592			}
593			g = (gid_t)tmp;
594		}
595		savesuid = 1;
596	}
597
598	opt = xar_opt_get(x, XAR_OPT_SAVESUID);
599	if( opt && (strcmp(opt, XAR_OPT_VAL_TRUE) == 0) ) {
600		savesuid = 1;
601	}
602
603	xar_prop_get(f, "mode", &opt);
604	if( opt ) {
605		long long tmp;
606		tmp = strtoll(opt, NULL, 8);
607		if( ( (tmp == LLONG_MIN) || (tmp == LLONG_MAX) ) && (errno == ERANGE) ) {
608			return -1;
609		}
610		m = (mode_t)tmp;
611		if( !savesuid ) {
612			m &= ~S_ISUID;
613			m &= ~S_ISGID;
614		}
615		mset = 1;
616	}
617
618	xar_prop_get(f, "type", &opt);
619	if( opt && !mset ) {
620		mode_t u = umask(0);
621		umask(u);
622		if( strcmp(opt, "directory") == 0 ) {
623			m = (mode_t)(0777 & ~u);
624		} else {
625			m = (mode_t)(0666 & ~u);
626		}
627		mset = 1;
628	}
629	if( opt && (strcmp(opt, "symlink") == 0) ) {
630#ifdef HAVE_LCHOWN
631		if( lchown(file, u, g) ) {
632			xar_err_new(x);
633			xar_err_set_file(x, f);
634			xar_err_set_string(x, "perm: could not lchown symlink");
635			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
636			m &= ~S_ISUID;
637			m &= ~S_ISGID;
638		}
639#ifdef HAVE_LCHMOD
640		if( mset )
641			if( lchmod(file, m) ) {
642				xar_err_new(x);
643				xar_err_set_file(x, f);
644				xar_err_set_string(x, "perm: could not lchmod symlink");
645				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
646			}
647#endif
648#endif
649	} else {
650		if( chown(file, u, g) ) {
651			xar_err_new(x);
652			xar_err_set_file(x, f);
653			xar_err_set_string(x, "perm: could not chown file");
654			xar_err_set_errno(x, errno);
655			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
656			m &= ~S_ISUID;
657			m &= ~S_ISGID;
658		}
659		if( mset )
660			if( chmod(file, m) ) {
661				xar_err_new(x);
662				xar_err_set_file(x, f);
663				xar_err_set_string(x, "perm: could not chmod file");
664				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
665			}
666	}
667
668	eacls(x, f, file);
669
670	memset(tv, 0, sizeof(struct timeval) * 2);
671	xar_prop_get(f, "atime", &timestr);
672	if( timestr ) {
673		memset(&t, 0, sizeof(t));
674		strptime(timestr, "%FT%T", &t);
675		tv[ATIME].tv_sec = timegm(&t);
676	} else {
677		tv[ATIME].tv_sec = time(NULL);
678	}
679
680	xar_prop_get(f, "mtime", &timestr);
681	if( timestr ) {
682		memset(&t, 0, sizeof(t));
683		strptime(timestr, "%FT%T", &t);
684		tv[MTIME].tv_sec = timegm(&t);
685	} else {
686		tv[MTIME].tv_sec = time(NULL);
687	}
688	utimes(file, tv);
689
690	return 0;
691}
692
693int32_t xar_stat_extract(xar_t x, xar_file_t f, const char *file, char *buffer, size_t len) {
694	const char *opt;
695	int ret, fd;
696	mode_t modet = 0;
697
698	xar_prop_get(f, "type", &opt);
699	if(opt && (strcmp(opt, "character special") == 0))
700		modet = S_IFCHR;
701	if(opt && (strcmp(opt, "block special") == 0))
702		modet = S_IFBLK;
703
704	if( modet ) {
705		uint32_t major, minor;
706		long long tmpll;
707		dev_t devt;
708
709		xar_prop_get(f, "device/major", &opt);
710		tmpll = strtoll(opt, NULL, 10);
711		if( ( (tmpll == LLONG_MIN) || (tmpll == LLONG_MAX) ) && (errno == ERANGE) )
712			return -1;
713		if( (tmpll < 0) || (tmpll > 255) )
714			return -1;
715		major = tmpll;
716
717		xar_prop_get(f, "device/minor", &opt);
718		tmpll = strtoll(opt, NULL, 10);
719		if( ( (tmpll == LLONG_MIN) || (tmpll == LLONG_MAX) ) && (errno == ERANGE) )
720			return -1;
721		if( (tmpll < 0) || (tmpll > 255) )
722			return -1;
723		minor = tmpll;
724
725		devt = xar_makedev(major, minor);
726		unlink(file);
727		if( mknod(file, modet, devt) ) {
728			xar_err_new(x);
729			xar_err_set_file(x, f);
730			xar_err_set_string(x, "mknod: Could not create character device");
731			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
732			return -1;
733		}
734		return 0;
735	}
736	if(opt && (strcmp(opt, "directory") == 0)) {
737		ret = mkdir(file, 0700);
738		if( (ret != 0) && (errno != EEXIST) ) {
739			xar_err_new(x);
740			xar_err_set_file(x, f);
741			xar_err_set_string(x, "stat: Could not create directory");
742			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
743			return ret;
744		}
745		return 0;
746	}
747	if(opt && (strcmp(opt, "symlink") == 0)) {
748		xar_prop_get(f, "link", &opt);
749		if( opt ) {
750			unlink(file);
751			ret = symlink(opt, file);
752			if( ret != 0 ) {
753				xar_err_new(x);
754				xar_err_set_file(x, f);
755				xar_err_set_string(x, "stat: Could not create symlink");
756				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
757			}
758			return ret;
759		}
760	}
761	if(opt && (strcmp(opt, "hardlink") == 0)) {
762		xar_file_t tmpf;
763		opt = xar_attr_get(f, "type", "link");
764		if( !opt )
765			return 0;
766		if( strcmp(opt, "original") == 0 )
767			goto CREATEFILE;
768
769		tmpf = xmlHashLookup(XAR(x)->link_hash, BAD_CAST(opt));
770		if( !tmpf ) {
771			xar_err_new(x);
772			xar_err_set_file(x, f);
773			xar_err_set_string(x, "stat: Encountered hardlink with no original");
774			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
775			return -1;
776		}
777
778		unlink(file);
779		if( link(XAR_FILE(tmpf)->fspath, file) != 0 ) {
780			if( errno == ENOENT ) {
781				xar_iter_t i;
782				const char *ptr;
783				i = xar_iter_new();
784				for(ptr = xar_prop_first(tmpf, i); ptr; ptr = xar_prop_next(i)) {
785					xar_iter_t a;
786					const char *val = NULL;
787					const char *akey, *aval;
788					if( strncmp("data", ptr, 4) != 0 )
789						continue;
790
791					if( xar_prop_get(tmpf, ptr, &val) )
792						continue;
793
794					xar_prop_set(f, ptr, val);
795					a = xar_iter_new();
796					for(akey = xar_attr_first(tmpf, ptr, a); akey; akey = xar_attr_next(a)) {
797						aval = xar_attr_get(tmpf, ptr, akey);
798						xar_attr_set(f, ptr, akey, aval);
799					}
800					xar_iter_free(a);
801				}
802				xar_iter_free(i);
803				xar_attr_set(f, "type", "link", "original");
804				return 0;
805			} else {
806				xar_err_new(x);
807				xar_err_set_file(x, f);
808				xar_err_set_string(x, "stat: Could not link hardlink to original");
809				xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
810				return -1;
811			}
812		}
813		return 0;
814	}
815
816	if(opt && (strcmp(opt, "fifo") == 0)) {
817		unlink(file);
818		if( mkfifo(file, 0) ) {
819			xar_err_new(x);
820			xar_err_set_file(x, f);
821			xar_err_set_string(x, "mkfifo: Could not create fifo");
822			xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
823			return -1;
824		}
825		return 0;
826	}
827
828	/* skip sockets */
829	if(opt && (strcmp(opt, "socket") == 0)) {
830		return 0;
831	}
832
833CREATEFILE:
834	if (!file)
835        return 0;
836
837	unlink(file);
838	fd = open(file, O_RDWR|O_CREAT|O_TRUNC, 0600);
839	if( fd > 0 )
840		close(fd);
841	return 0;
842}
843