source: MondoRescue/branches/3.3/mindi-busybox/archival/libarchive/data_extract_all.c@ 3626

Last change on this file since 3626 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 6.7 KB
RevLine 
[2725]1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 */
5
6#include "libbb.h"
[3232]7#include "bb_archive.h"
[2725]8
9void FAST_FUNC 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;
[3621]14 char *hard_link;
15#if ENABLE_FEATURE_TAR_LONG_OPTIONS
16 char *dst_name;
17#else
18# define dst_name (file_header->name)
19#endif
[2725]20
21#if ENABLE_FEATURE_TAR_SELINUX
[3232]22 char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
[2725]23 if (!sctx)
[3232]24 sctx = archive_handle->tar__sctx[PAX_GLOBAL];
[2725]25 if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
26 setfscreatecon(sctx);
[3232]27 free(archive_handle->tar__sctx[PAX_NEXT_FILE]);
28 archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL;
[2725]29 }
30#endif
31
[3621]32 /* Hard links are encoded as regular files of size 0
33 * with a nonempty link field */
34 hard_link = NULL;
35 if (S_ISREG(file_header->mode) && file_header->size == 0)
36 hard_link = file_header->link_target;
37
38#if ENABLE_FEATURE_TAR_LONG_OPTIONS
39 dst_name = file_header->name;
40 if (archive_handle->tar__strip_components) {
41 unsigned n = archive_handle->tar__strip_components;
42 do {
43 dst_name = strchr(dst_name, '/');
44 if (!dst_name || dst_name[1] == '\0') {
45 data_skip(archive_handle);
46 goto ret;
47 }
48 dst_name++;
49 /*
50 * Link target is shortened only for hardlinks:
51 * softlinks restored unchanged.
52 */
53 if (hard_link) {
54// GNU tar 1.26 does not check that we reached end of link name:
55// if "dir/hardlink" is hardlinked to "file",
56// tar xvf a.tar --strip-components=1 says:
57// tar: hardlink: Cannot hard link to '': No such file or directory
58// and continues processing. We silently skip such entries.
59 hard_link = strchr(hard_link, '/');
60 if (!hard_link || hard_link[1] == '\0') {
61 data_skip(archive_handle);
62 goto ret;
63 }
64 hard_link++;
65 }
66 } while (--n != 0);
67 }
68#endif
69
[2725]70 if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
[3621]71 char *slash = strrchr(dst_name, '/');
[2725]72 if (slash) {
73 *slash = '\0';
[3621]74 bb_make_directory(dst_name, -1, FILEUTILS_RECUR);
[2725]75 *slash = '/';
76 }
77 }
78
79 if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) {
80 /* Remove the entry if it exists */
81 if (!S_ISDIR(file_header->mode)) {
[3621]82 if (hard_link) {
[2725]83 /* Ugly special case:
84 * tar cf t.tar hardlink1 hardlink2 hardlink1
85 * results in this tarball structure:
86 * hardlink1
87 * hardlink2 -> hardlink1
88 * hardlink1 -> hardlink1 <== !!!
89 */
[3621]90 if (strcmp(hard_link, dst_name) == 0)
[2725]91 goto ret;
92 }
93 /* Proceed with deleting */
[3621]94 if (unlink(dst_name) == -1
[2725]95 && errno != ENOENT
96 ) {
97 bb_perror_msg_and_die("can't remove old file %s",
[3621]98 dst_name);
[2725]99 }
100 }
101 }
102 else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) {
103 /* Remove the existing entry if its older than the extracted entry */
104 struct stat existing_sb;
[3621]105 if (lstat(dst_name, &existing_sb) == -1) {
[2725]106 if (errno != ENOENT) {
107 bb_perror_msg_and_die("can't stat old file");
108 }
109 }
110 else if (existing_sb.st_mtime >= file_header->mtime) {
111 if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
112 && !S_ISDIR(file_header->mode)
113 ) {
114 bb_error_msg("%s not created: newer or "
[3621]115 "same age file exists", dst_name);
[2725]116 }
117 data_skip(archive_handle);
118 goto ret;
119 }
[3621]120 else if ((unlink(dst_name) == -1) && (errno != EISDIR)) {
[2725]121 bb_perror_msg_and_die("can't remove old file %s",
[3621]122 dst_name);
[2725]123 }
124 }
125
[3621]126 /* Handle hard links separately */
127 if (hard_link) {
128 res = link(hard_link, dst_name);
129 if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
[2725]130 bb_perror_msg("can't create %slink "
131 "from %s to %s", "hard",
[3621]132 dst_name,
133 hard_link);
[2725]134 }
135 /* Hardlinks have no separate mode/ownership, skip chown/chmod */
136 goto ret;
137 }
138
139 /* Create the filesystem entry */
140 switch (file_header->mode & S_IFMT) {
141 case S_IFREG: {
142 /* Regular file */
[3621]143 char *dst_nameN;
[2725]144 int flags = O_WRONLY | O_CREAT | O_EXCL;
145 if (archive_handle->ah_flags & ARCHIVE_O_TRUNC)
146 flags = O_WRONLY | O_CREAT | O_TRUNC;
[3621]147 dst_nameN = dst_name;
148#ifdef ARCHIVE_REPLACE_VIA_RENAME
149 if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME)
150 /* rpm-style temp file name */
151 dst_nameN = xasprintf("%s;%x", dst_name, (int)getpid());
152#endif
153 dst_fd = xopen3(dst_nameN,
[2725]154 flags,
155 file_header->mode
156 );
157 bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
158 close(dst_fd);
[3621]159#ifdef ARCHIVE_REPLACE_VIA_RENAME
160 if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) {
161 xrename(dst_nameN, dst_name);
162 free(dst_nameN);
163 }
164#endif
[2725]165 break;
166 }
167 case S_IFDIR:
[3621]168 res = mkdir(dst_name, file_header->mode);
[2725]169 if ((res == -1)
170 && (errno != EISDIR) /* btw, Linux doesn't return this */
171 && (errno != EEXIST)
172 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
173 ) {
[3621]174 bb_perror_msg("can't make dir %s", dst_name);
[2725]175 }
176 break;
177 case S_IFLNK:
178 /* Symlink */
179//TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
[3621]180 res = symlink(file_header->link_target, dst_name);
181 if (res != 0
[2725]182 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
183 ) {
184 bb_perror_msg("can't create %slink "
185 "from %s to %s", "sym",
[3621]186 dst_name,
[2725]187 file_header->link_target);
188 }
189 break;
190 case S_IFSOCK:
191 case S_IFBLK:
192 case S_IFCHR:
193 case S_IFIFO:
[3621]194 res = mknod(dst_name, file_header->mode, file_header->device);
[2725]195 if ((res == -1)
196 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
197 ) {
[3621]198 bb_perror_msg("can't create node %s", dst_name);
[2725]199 }
200 break;
201 default:
202 bb_error_msg_and_die("unrecognized file type");
203 }
204
205 if (!S_ISLNK(file_header->mode)) {
206 if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) {
207 uid_t uid = file_header->uid;
208 gid_t gid = file_header->gid;
209#if ENABLE_FEATURE_TAR_UNAME_GNAME
210 if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) {
211 if (file_header->tar__uname) {
212//TODO: cache last name/id pair?
213 struct passwd *pwd = getpwnam(file_header->tar__uname);
214 if (pwd) uid = pwd->pw_uid;
215 }
216 if (file_header->tar__gname) {
217 struct group *grp = getgrnam(file_header->tar__gname);
218 if (grp) gid = grp->gr_gid;
219 }
220 }
221#endif
222 /* GNU tar 1.15.1 uses chown, not lchown */
[3621]223 chown(dst_name, uid, gid);
[2725]224 }
225 /* uclibc has no lchmod, glibc is even stranger -
226 * it has lchmod which seems to do nothing!
227 * so we use chmod... */
228 if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) {
[3621]229 chmod(dst_name, file_header->mode);
[2725]230 }
231 if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) {
232 struct timeval t[2];
233
234 t[1].tv_sec = t[0].tv_sec = file_header->mtime;
235 t[1].tv_usec = t[0].tv_usec = 0;
[3621]236 utimes(dst_name, t);
[2725]237 }
238 }
239
240 ret: ;
241#if ENABLE_FEATURE_TAR_SELINUX
242 if (sctx) {
243 /* reset the context after creating an entry */
244 setfscreatecon(NULL);
245 }
246#endif
247}
Note: See TracBrowser for help on using the repository browser.