1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
4 */
5
6#include "libbb.h"
7#include "unarchive.h"
8
9void data_extract_all(archive_handle_t *archive_handle)
10{
11	file_header_t *file_header = archive_handle->file_header;
12	int dst_fd;
13	int res;
14
15	if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) {
16		char *name = xstrdup(file_header->name);
17		bb_make_directory(dirname(name), -1, FILEUTILS_RECUR);
18		free(name);
19	}
20
21	/* Check if the file already exists */
22	if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) {
23		/* Remove the existing entry if it exists */
24		if (((file_header->mode & S_IFMT) != S_IFDIR)
25		 && (unlink(file_header->name) == -1)
26		 && (errno != ENOENT)
27		) {
28			bb_perror_msg_and_die("cannot remove old file %s",
29					file_header->name);
30		}
31	}
32	else if (archive_handle->flags & ARCHIVE_EXTRACT_NEWER) {
33		/* Remove the existing entry if its older than the extracted entry */
34		struct stat statbuf;
35		if (lstat(file_header->name, &statbuf) == -1) {
36			if (errno != ENOENT) {
37				bb_perror_msg_and_die("cannot stat old file");
38			}
39		}
40		else if (statbuf.st_mtime <= file_header->mtime) {
41			if (!(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
42				bb_error_msg("%s not created: newer or "
43					"same age file exists", file_header->name);
44			}
45			data_skip(archive_handle);
46			return;
47		}
48		else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) {
49			bb_perror_msg_and_die("cannot remove old file %s",
50					file_header->name);
51		}
52	}
53
54	/* Handle hard links separately
55	 * We identified hard links as regular files of size 0 with a symlink */
56	if (S_ISREG(file_header->mode) && (file_header->link_target)
57	 && (file_header->size == 0)
58	) {
59		/* hard link */
60		res = link(file_header->link_target, file_header->name);
61		if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
62			bb_perror_msg("cannot create %slink "
63					"from %s to %s", "hard",
64					file_header->name,
65					file_header->link_target);
66		}
67	} else {
68		/* Create the filesystem entry */
69		switch (file_header->mode & S_IFMT) {
70		case S_IFREG: {
71			/* Regular file */
72			dst_fd = xopen3(file_header->name, O_WRONLY | O_CREAT | O_EXCL,
73							file_header->mode);
74			bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
75			close(dst_fd);
76			break;
77		}
78		case S_IFDIR:
79			res = mkdir(file_header->name, file_header->mode);
80			if ((res == -1) && (errno != EISDIR)
81			 && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)
82			) {
83				bb_perror_msg("cannot make dir %s", file_header->name);
84			}
85			break;
86		case S_IFLNK:
87			/* Symlink */
88			res = symlink(file_header->link_target, file_header->name);
89			if ((res == -1)
90			 && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)
91			) {
92				bb_perror_msg("cannot create %slink "
93					"from %s to %s", "sym",
94					file_header->name,
95					file_header->link_target);
96			}
97			break;
98		case S_IFSOCK:
99		case S_IFBLK:
100		case S_IFCHR:
101		case S_IFIFO:
102			res = mknod(file_header->name, file_header->mode, file_header->device);
103			if ((res == -1)
104			 && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)
105			) {
106				bb_perror_msg("cannot create node %s", file_header->name);
107			}
108			break;
109		default:
110			bb_error_msg_and_die("unrecognized file type");
111		}
112	}
113
114	if (!(archive_handle->flags & ARCHIVE_NOPRESERVE_OWN)) {
115		lchown(file_header->name, file_header->uid, file_header->gid);
116	}
117	if ((file_header->mode & S_IFMT) != S_IFLNK) {
118		/* uclibc has no lchmod, glibc is even stranger -
119		 * it has lchmod which seems to do nothing!
120		 * so we use chmod... */
121		if (!(archive_handle->flags & ARCHIVE_NOPRESERVE_PERM)) {
122			chmod(file_header->name, file_header->mode);
123		}
124		/* same for utime */
125		if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) {
126			struct utimbuf t;
127			t.actime = t.modtime = file_header->mtime;
128			utime(file_header->name, &t);
129		}
130	}
131}
132