diff options
author | Glenn L McGrath <bug1@ihug.co.nz> | 2001-10-05 02:58:48 +0000 |
---|---|---|
committer | Glenn L McGrath <bug1@ihug.co.nz> | 2001-10-05 02:58:48 +0000 |
commit | 2e772edacf70e9ff45a00a34af4c94e04d490fc2 (patch) | |
tree | c1cb95bdc501d6f090a02ff248638e8f09dd073c /archival/tar.c | |
parent | 4c557bf576e772bfd78429d5f9b2a436ccadf268 (diff) | |
download | busybox-w32-2e772edacf70e9ff45a00a34af4c94e04d490fc2.tar.gz busybox-w32-2e772edacf70e9ff45a00a34af4c94e04d490fc2.tar.bz2 busybox-w32-2e772edacf70e9ff45a00a34af4c94e04d490fc2.zip |
Change extraction/list code to use common unarchive code.
Diffstat (limited to 'archival/tar.c')
-rw-r--r-- | archival/tar.c | 1024 |
1 files changed, 313 insertions, 711 deletions
diff --git a/archival/tar.c b/archival/tar.c index 389d7f02e..cd563393b 100644 --- a/archival/tar.c +++ b/archival/tar.c | |||
@@ -2,6 +2,9 @@ | |||
2 | /* | 2 | /* |
3 | * Mini tar implementation for busybox | 3 | * Mini tar implementation for busybox |
4 | * | 4 | * |
5 | * Modifed to use common extraction code used by ar, cpio, dpkg-deb, dpkg | ||
6 | * Glenn McGrath <bug1@optushome.com.au> | ||
7 | * | ||
5 | * Note, that as of BusyBox-0.43, tar has been completely rewritten from the | 8 | * Note, that as of BusyBox-0.43, tar has been completely rewritten from the |
6 | * ground up. It still has remnents of the old code lying about, but it is | 9 | * ground up. It still has remnents of the old code lying about, but it is |
7 | * very different now (i.e., cleaner, less global variables, etc.) | 10 | * very different now (i.e., cleaner, less global variables, etc.) |
@@ -35,35 +38,36 @@ | |||
35 | * | 38 | * |
36 | */ | 39 | */ |
37 | 40 | ||
38 | |||
39 | #include <stdio.h> | ||
40 | #include <dirent.h> | ||
41 | #include <errno.h> | ||
42 | #include <fcntl.h> | 41 | #include <fcntl.h> |
43 | #include <signal.h> | ||
44 | #include <time.h> | ||
45 | #include <utime.h> | ||
46 | #include <sys/types.h> | ||
47 | #include <sys/sysmacros.h> | ||
48 | #include <getopt.h> | 42 | #include <getopt.h> |
49 | #include <fnmatch.h> | 43 | #include <search.h> |
50 | #include <string.h> | 44 | #include <stdio.h> |
51 | #include <stdlib.h> | 45 | #include <stdlib.h> |
52 | #include <unistd.h> | 46 | #include <unistd.h> |
47 | #include <fnmatch.h> | ||
48 | #include <string.h> | ||
49 | #include <errno.h> | ||
53 | #include "busybox.h" | 50 | #include "busybox.h" |
54 | 51 | ||
52 | #ifdef BB_FEATURE_TAR_CREATE | ||
53 | |||
55 | /* Tar file constants */ | 54 | /* Tar file constants */ |
56 | #ifndef MAJOR | 55 | # define TAR_MAGIC "ustar" /* ustar and a null */ |
57 | #define MAJOR(dev) (((dev)>>8)&0xff) | 56 | # define TAR_VERSION " " /* Be compatable with GNU tar format */ |
58 | #define MINOR(dev) ((dev)&0xff) | ||
59 | #endif | ||
60 | 57 | ||
61 | enum { NAME_SIZE = 100 }; /* because gcc won't let me use 'static const int' */ | 58 | # ifndef MAJOR |
59 | # define MAJOR(dev) (((dev)>>8)&0xff) | ||
60 | # define MINOR(dev) ((dev)&0xff) | ||
61 | # endif | ||
62 | |||
63 | static const int TAR_BLOCK_SIZE = 512; | ||
64 | static const int TAR_MAGIC_LEN = 6; | ||
65 | static const int TAR_VERSION_LEN = 2; | ||
62 | 66 | ||
63 | /* POSIX tar Header Block, from POSIX 1003.1-1990 */ | 67 | /* POSIX tar Header Block, from POSIX 1003.1-1990 */ |
68 | enum { NAME_SIZE = 100 }; /* because gcc won't let me use 'static const int' */ | ||
64 | struct TarHeader | 69 | struct TarHeader |
65 | { | 70 | { /* byte offset */ |
66 | /* byte offset */ | ||
67 | char name[NAME_SIZE]; /* 0-99 */ | 71 | char name[NAME_SIZE]; /* 0-99 */ |
68 | char mode[8]; /* 100-107 */ | 72 | char mode[8]; /* 100-107 */ |
69 | char uid[8]; /* 108-115 */ | 73 | char uid[8]; /* 108-115 */ |
@@ -84,697 +88,6 @@ struct TarHeader | |||
84 | }; | 88 | }; |
85 | typedef struct TarHeader TarHeader; | 89 | typedef struct TarHeader TarHeader; |
86 | 90 | ||
87 | |||
88 | /* A few useful constants */ | ||
89 | #define TAR_MAGIC "ustar" /* ustar and a null */ | ||
90 | #define TAR_VERSION " " /* Be compatable with GNU tar format */ | ||
91 | static const int TAR_MAGIC_LEN = 6; | ||
92 | static const int TAR_VERSION_LEN = 2; | ||
93 | static const int TAR_BLOCK_SIZE = 512; | ||
94 | |||
95 | /* A nice enum with all the possible tar file content types */ | ||
96 | enum TarFileType | ||
97 | { | ||
98 | REGTYPE = '0', /* regular file */ | ||
99 | REGTYPE0 = '\0', /* regular file (ancient bug compat)*/ | ||
100 | LNKTYPE = '1', /* hard link */ | ||
101 | SYMTYPE = '2', /* symbolic link */ | ||
102 | CHRTYPE = '3', /* character special */ | ||
103 | BLKTYPE = '4', /* block special */ | ||
104 | DIRTYPE = '5', /* directory */ | ||
105 | FIFOTYPE = '6', /* FIFO special */ | ||
106 | CONTTYPE = '7', /* reserved */ | ||
107 | GNULONGLINK = 'K', /* GNU long (>100 chars) link name */ | ||
108 | GNULONGNAME = 'L', /* GNU long (>100 chars) file name */ | ||
109 | }; | ||
110 | typedef enum TarFileType TarFileType; | ||
111 | |||
112 | /* This struct ignores magic, non-numeric user name, | ||
113 | * non-numeric group name, and the checksum, since | ||
114 | * these are all ignored by BusyBox tar. */ | ||
115 | struct TarInfo | ||
116 | { | ||
117 | int tarFd; /* An open file descriptor for reading from the tarball */ | ||
118 | char * name; /* File name */ | ||
119 | mode_t mode; /* Unix mode, including device bits. */ | ||
120 | uid_t uid; /* Numeric UID */ | ||
121 | gid_t gid; /* Numeric GID */ | ||
122 | size_t size; /* Size of file */ | ||
123 | time_t mtime; /* Last-modified time */ | ||
124 | enum TarFileType type; /* Regular, directory, link, etc. */ | ||
125 | char * linkname; /* Name for symbolic and hard links */ | ||
126 | long devmajor; /* Major number for special device */ | ||
127 | long devminor; /* Minor number for special device */ | ||
128 | }; | ||
129 | typedef struct TarInfo TarInfo; | ||
130 | |||
131 | /* Local procedures to restore files from a tar file. */ | ||
132 | static int readTarFile(int tarFd, int extractFlag, int listFlag, | ||
133 | int tostdoutFlag, int verboseFlag, char** extractList, | ||
134 | char** excludeList); | ||
135 | |||
136 | #ifdef BB_FEATURE_TAR_CREATE | ||
137 | /* Local procedures to save files into a tar file. */ | ||
138 | static int writeTarFile(const char* tarName, int verboseFlag, char **argv, | ||
139 | char** excludeList); | ||
140 | #endif | ||
141 | |||
142 | #if defined BB_FEATURE_TAR_EXCLUDE | ||
143 | static struct option longopts[] = { | ||
144 | { "exclude", 1, NULL, 'e' }, | ||
145 | { NULL, 0, NULL, 0 } | ||
146 | }; | ||
147 | #endif | ||
148 | |||
149 | extern int tar_main(int argc, char **argv) | ||
150 | { | ||
151 | char** excludeList=NULL; | ||
152 | char** extractList=NULL; | ||
153 | const char *tarName="-"; | ||
154 | const char *cwd=NULL; | ||
155 | #if defined BB_FEATURE_TAR_EXCLUDE | ||
156 | int excludeListSize=0; | ||
157 | FILE *fileList; | ||
158 | char file[256]; | ||
159 | #endif | ||
160 | #if defined BB_FEATURE_TAR_GZIP | ||
161 | FILE *comp_file = NULL; | ||
162 | int unzipFlag = FALSE; | ||
163 | #endif | ||
164 | int listFlag = FALSE; | ||
165 | int extractFlag = FALSE; | ||
166 | int createFlag = FALSE; | ||
167 | int verboseFlag = FALSE; | ||
168 | int tostdoutFlag = FALSE; | ||
169 | int status = FALSE; | ||
170 | int opt; | ||
171 | pid_t pid; | ||
172 | |||
173 | if (argc <= 1) | ||
174 | show_usage(); | ||
175 | |||
176 | if (argv[1][0] != '-') { | ||
177 | char *tmp = xmalloc(strlen(argv[1]) + 2); | ||
178 | tmp[0] = '-'; | ||
179 | strcpy(tmp + 1, argv[1]); | ||
180 | argv[1] = tmp; | ||
181 | } | ||
182 | |||
183 | while ( | ||
184 | #ifndef BB_FEATURE_TAR_EXCLUDE | ||
185 | (opt = getopt(argc, argv, "cxtzvOf:pC:")) | ||
186 | #else | ||
187 | (opt = getopt_long(argc, argv, "cxtzvOf:X:pC:", longopts, NULL)) | ||
188 | #endif | ||
189 | > 0) { | ||
190 | switch (opt) { | ||
191 | case 'c': | ||
192 | if (extractFlag == TRUE || listFlag == TRUE) | ||
193 | goto flagError; | ||
194 | createFlag = TRUE; | ||
195 | break; | ||
196 | case 'x': | ||
197 | if (listFlag == TRUE || createFlag == TRUE) | ||
198 | goto flagError; | ||
199 | extractFlag = TRUE; | ||
200 | break; | ||
201 | case 't': | ||
202 | if (extractFlag == TRUE || createFlag == TRUE) | ||
203 | goto flagError; | ||
204 | listFlag = TRUE; | ||
205 | break; | ||
206 | #ifdef BB_FEATURE_TAR_GZIP | ||
207 | case 'z': | ||
208 | unzipFlag = TRUE; | ||
209 | break; | ||
210 | #endif | ||
211 | case 'v': | ||
212 | verboseFlag = TRUE; | ||
213 | break; | ||
214 | case 'O': | ||
215 | tostdoutFlag = TRUE; | ||
216 | break; | ||
217 | case 'f': | ||
218 | if (*tarName != '-') | ||
219 | error_msg_and_die( "Only one 'f' option allowed"); | ||
220 | tarName = optarg; | ||
221 | break; | ||
222 | #if defined BB_FEATURE_TAR_EXCLUDE | ||
223 | case 'e': | ||
224 | excludeList=xrealloc( excludeList, | ||
225 | sizeof(char *) * (excludeListSize+2)); | ||
226 | excludeList[excludeListSize] = optarg; | ||
227 | /* Tack a NULL onto the end of the list */ | ||
228 | excludeList[++excludeListSize] = NULL; | ||
229 | case 'X': | ||
230 | fileList = xfopen(optarg, "r"); | ||
231 | while (fgets(file, sizeof(file), fileList) != NULL) { | ||
232 | excludeList = xrealloc(excludeList, | ||
233 | sizeof(char *) * (excludeListSize+2)); | ||
234 | chomp(file); | ||
235 | excludeList[excludeListSize] = xstrdup(file); | ||
236 | /* Tack a NULL onto the end of the list */ | ||
237 | excludeList[++excludeListSize] = NULL; | ||
238 | } | ||
239 | fclose(fileList); | ||
240 | break; | ||
241 | #endif | ||
242 | case 'p': | ||
243 | break; | ||
244 | case 'C': | ||
245 | cwd = xgetcwd((char *)cwd); | ||
246 | if (chdir(optarg)) { | ||
247 | printf("cd: %s: %s\n", optarg, strerror(errno)); | ||
248 | return EXIT_FAILURE; | ||
249 | } | ||
250 | break; | ||
251 | default: | ||
252 | show_usage(); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Do the correct type of action supplying the rest of the | ||
258 | * command line arguments as the list of files to process. | ||
259 | */ | ||
260 | if (createFlag == TRUE) { | ||
261 | #ifndef BB_FEATURE_TAR_CREATE | ||
262 | error_msg_and_die( "This version of tar was not compiled with tar creation support."); | ||
263 | #else | ||
264 | #ifdef BB_FEATURE_TAR_GZIP | ||
265 | if (unzipFlag==TRUE) | ||
266 | error_msg_and_die("Creation of compressed not internally support by tar, pipe to busybox gunzip"); | ||
267 | #endif | ||
268 | status = writeTarFile(tarName, verboseFlag, argv + optind, excludeList); | ||
269 | #endif | ||
270 | } | ||
271 | if (listFlag == TRUE || extractFlag == TRUE) { | ||
272 | int tarFd; | ||
273 | if (argv[optind]) | ||
274 | extractList = argv + optind; | ||
275 | /* Open the tar file for reading. */ | ||
276 | if (!strcmp(tarName, "-")) | ||
277 | tarFd = fileno(stdin); | ||
278 | else | ||
279 | tarFd = open(tarName, O_RDONLY); | ||
280 | if (tarFd < 0) | ||
281 | perror_msg_and_die("Error opening '%s'", tarName); | ||
282 | |||
283 | #ifdef BB_FEATURE_TAR_GZIP | ||
284 | /* unzip tarFd in a seperate process */ | ||
285 | if (unzipFlag == TRUE) { | ||
286 | comp_file = fdopen(tarFd, "r"); | ||
287 | |||
288 | /* set the buffer size */ | ||
289 | setvbuf(comp_file, NULL, _IOFBF, 0x8000); | ||
290 | |||
291 | if ((tarFd = fileno(gz_open(comp_file, &pid))) == EXIT_FAILURE) { | ||
292 | error_msg_and_die("Couldnt unzip file"); | ||
293 | } | ||
294 | } | ||
295 | #endif | ||
296 | status = readTarFile(tarFd, extractFlag, listFlag, tostdoutFlag, | ||
297 | verboseFlag, extractList, excludeList); | ||
298 | close(tarFd); | ||
299 | #ifdef BB_FEATURE_TAR_GZIP | ||
300 | if (unzipFlag == TRUE) { | ||
301 | gz_close(pid); | ||
302 | fclose(comp_file); | ||
303 | } | ||
304 | #endif | ||
305 | } | ||
306 | |||
307 | if (cwd) | ||
308 | chdir(cwd); | ||
309 | if (status == TRUE) | ||
310 | return EXIT_SUCCESS; | ||
311 | else | ||
312 | return EXIT_FAILURE; | ||
313 | |||
314 | flagError: | ||
315 | error_msg_and_die( "Exactly one of 'c', 'x' or 't' must be specified"); | ||
316 | } | ||
317 | |||
318 | static void | ||
319 | fixUpPermissions(TarInfo *header) | ||
320 | { | ||
321 | struct utimbuf t; | ||
322 | /* Now set permissions etc. for the new file */ | ||
323 | chown(header->name, header->uid, header->gid); | ||
324 | chmod(header->name, header->mode); | ||
325 | /* Reset the time */ | ||
326 | t.actime = time(0); | ||
327 | t.modtime = header->mtime; | ||
328 | utime(header->name, &t); | ||
329 | } | ||
330 | |||
331 | static int | ||
332 | tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag) | ||
333 | { | ||
334 | size_t writeSize; | ||
335 | size_t readSize; | ||
336 | size_t actualWriteSz; | ||
337 | char buffer[20 * TAR_BLOCK_SIZE]; | ||
338 | size_t size = header->size; | ||
339 | int outFd=fileno(stdout); | ||
340 | |||
341 | /* Open the file to be written, if a file is supposed to be written */ | ||
342 | if (extractFlag==TRUE && tostdoutFlag==FALSE) { | ||
343 | /* Create the path to the file, just in case it isn't there... | ||
344 | * This should not screw up path permissions or anything. */ | ||
345 | char *buf, *dir; | ||
346 | buf = xstrdup (header->name); | ||
347 | dir = dirname (buf); | ||
348 | make_directory (dir, -1, FILEUTILS_RECUR); | ||
349 | free (buf); | ||
350 | if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, | ||
351 | header->mode & ~S_IFMT)) < 0) { | ||
352 | error_msg(io_error, header->name, strerror(errno)); | ||
353 | return( FALSE); | ||
354 | } | ||
355 | } | ||
356 | |||
357 | /* Write out the file, if we are supposed to be doing that */ | ||
358 | while ( size > 0 ) { | ||
359 | actualWriteSz=0; | ||
360 | if ( size > sizeof(buffer) ) | ||
361 | writeSize = readSize = sizeof(buffer); | ||
362 | else { | ||
363 | int mod = size % TAR_BLOCK_SIZE; | ||
364 | if ( mod != 0 ) | ||
365 | readSize = size + (TAR_BLOCK_SIZE - mod); | ||
366 | else | ||
367 | readSize = size; | ||
368 | writeSize = size; | ||
369 | } | ||
370 | if ( (readSize = full_read(header->tarFd, buffer, readSize)) <= 0 ) { | ||
371 | /* Tarball seems to have a problem */ | ||
372 | error_msg("Unexpected EOF in archive"); | ||
373 | return( FALSE); | ||
374 | } | ||
375 | if ( readSize < writeSize ) | ||
376 | writeSize = readSize; | ||
377 | |||
378 | /* Write out the file, if we are supposed to be doing that */ | ||
379 | if (extractFlag==TRUE) { | ||
380 | |||
381 | if ((actualWriteSz=full_write(outFd, buffer, writeSize)) != writeSize ) { | ||
382 | /* Output file seems to have a problem */ | ||
383 | error_msg(io_error, header->name, strerror(errno)); | ||
384 | return( FALSE); | ||
385 | } | ||
386 | } else { | ||
387 | actualWriteSz=writeSize; | ||
388 | } | ||
389 | |||
390 | size -= actualWriteSz; | ||
391 | } | ||
392 | |||
393 | /* Now we are done writing the file out, so try | ||
394 | * and fix up the permissions and whatnot */ | ||
395 | if (extractFlag==TRUE && tostdoutFlag==FALSE) { | ||
396 | close(outFd); | ||
397 | fixUpPermissions(header); | ||
398 | } | ||
399 | return( TRUE); | ||
400 | } | ||
401 | |||
402 | static int | ||
403 | tarExtractDirectory(TarInfo *header, int extractFlag, int tostdoutFlag) | ||
404 | { | ||
405 | if (extractFlag==FALSE || tostdoutFlag==TRUE) | ||
406 | return( TRUE); | ||
407 | |||
408 | if (make_directory(header->name, header->mode, FILEUTILS_RECUR) < 0) | ||
409 | return( FALSE); | ||
410 | |||
411 | fixUpPermissions(header); | ||
412 | return( TRUE); | ||
413 | } | ||
414 | |||
415 | static int | ||
416 | tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag) | ||
417 | { | ||
418 | if (extractFlag==FALSE || tostdoutFlag==TRUE) | ||
419 | return( TRUE); | ||
420 | |||
421 | if (link(header->linkname, header->name) < 0) { | ||
422 | perror_msg("%s: Cannot create hard link to '%s'", header->name, | ||
423 | header->linkname); | ||
424 | return( FALSE); | ||
425 | } | ||
426 | |||
427 | /* Now set permissions etc. for the new directory */ | ||
428 | fixUpPermissions(header); | ||
429 | return( TRUE); | ||
430 | } | ||
431 | |||
432 | static int | ||
433 | tarExtractSymLink(TarInfo *header, int extractFlag, int tostdoutFlag) | ||
434 | { | ||
435 | if (extractFlag==FALSE || tostdoutFlag==TRUE) | ||
436 | return( TRUE); | ||
437 | |||
438 | #ifdef S_ISLNK | ||
439 | if (symlink(header->linkname, header->name) < 0) { | ||
440 | perror_msg("%s: Cannot create symlink to '%s'", header->name, | ||
441 | header->linkname); | ||
442 | return( FALSE); | ||
443 | } | ||
444 | /* Try to change ownership of the symlink. | ||
445 | * If libs doesn't support that, don't bother. | ||
446 | * Changing the pointed-to-file is the Wrong Thing(tm). | ||
447 | */ | ||
448 | #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) | ||
449 | lchown(header->name, header->uid, header->gid); | ||
450 | #endif | ||
451 | |||
452 | /* Do not change permissions or date on symlink, | ||
453 | * since it changes the pointed to file instead. duh. */ | ||
454 | #else | ||
455 | error_msg("%s: Cannot create symlink to '%s': %s", | ||
456 | header->name, header->linkname, | ||
457 | "symlinks not supported"); | ||
458 | #endif | ||
459 | return( TRUE); | ||
460 | } | ||
461 | |||
462 | static int | ||
463 | tarExtractSpecial(TarInfo *header, int extractFlag, int tostdoutFlag) | ||
464 | { | ||
465 | if (extractFlag==FALSE || tostdoutFlag==TRUE) | ||
466 | return( TRUE); | ||
467 | |||
468 | if (S_ISCHR(header->mode) || S_ISBLK(header->mode) || S_ISSOCK(header->mode)) { | ||
469 | if (mknod(header->name, header->mode, makedev(header->devmajor, header->devminor)) < 0) { | ||
470 | perror_msg("%s: Cannot mknod", header->name); | ||
471 | return( FALSE); | ||
472 | } | ||
473 | } else if (S_ISFIFO(header->mode)) { | ||
474 | if (mkfifo(header->name, header->mode) < 0) { | ||
475 | perror_msg("%s: Cannot mkfifo", header->name); | ||
476 | return( FALSE); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | /* Now set permissions etc. for the new directory */ | ||
481 | fixUpPermissions(header); | ||
482 | return( TRUE); | ||
483 | } | ||
484 | |||
485 | /* Parse the tar header and fill in the nice struct with the details */ | ||
486 | static int | ||
487 | readTarHeader(struct TarHeader *rawHeader, struct TarInfo *header) | ||
488 | { | ||
489 | int i; | ||
490 | long chksum, sum=0; | ||
491 | unsigned char *s = (unsigned char *)rawHeader; | ||
492 | |||
493 | header->name = rawHeader->name; | ||
494 | /* Check for and relativify any absolute paths */ | ||
495 | if ( *(header->name) == '/' ) { | ||
496 | static int alreadyWarned=FALSE; | ||
497 | |||
498 | while (*(header->name) == '/') | ||
499 | header->name++; | ||
500 | |||
501 | if (alreadyWarned == FALSE) { | ||
502 | error_msg("Removing leading '/' from member names"); | ||
503 | alreadyWarned = TRUE; | ||
504 | } | ||
505 | } | ||
506 | |||
507 | header->mode = strtol(rawHeader->mode, NULL, 8); | ||
508 | header->uid = strtol(rawHeader->uid, NULL, 8); | ||
509 | header->gid = strtol(rawHeader->gid, NULL, 8); | ||
510 | header->size = strtol(rawHeader->size, NULL, 8); | ||
511 | header->mtime = strtol(rawHeader->mtime, NULL, 8); | ||
512 | chksum = strtol(rawHeader->chksum, NULL, 8); | ||
513 | header->type = rawHeader->typeflag; | ||
514 | header->linkname = rawHeader->linkname; | ||
515 | header->devmajor = strtol(rawHeader->devmajor, NULL, 8); | ||
516 | header->devminor = strtol(rawHeader->devminor, NULL, 8); | ||
517 | |||
518 | /* Check the checksum */ | ||
519 | for (i = sizeof(*rawHeader); i-- != 0;) { | ||
520 | sum += *s++; | ||
521 | } | ||
522 | /* Remove the effects of the checksum field (replace | ||
523 | * with blanks for the purposes of the checksum) */ | ||
524 | s = rawHeader->chksum; | ||
525 | for (i = sizeof(rawHeader->chksum) ; i-- != 0;) { | ||
526 | sum -= *s++; | ||
527 | } | ||
528 | sum += ' ' * sizeof(rawHeader->chksum); | ||
529 | if (sum == chksum ) | ||
530 | return ( TRUE); | ||
531 | return( FALSE); | ||
532 | } | ||
533 | |||
534 | static int exclude_file(char **excluded_files, const char *file) | ||
535 | { | ||
536 | int i; | ||
537 | |||
538 | if (excluded_files == NULL) | ||
539 | return 0; | ||
540 | |||
541 | for (i = 0; excluded_files[i] != NULL; i++) { | ||
542 | if (excluded_files[i][0] == '/') { | ||
543 | if (fnmatch(excluded_files[i], file, | ||
544 | FNM_PATHNAME | FNM_LEADING_DIR) == 0) | ||
545 | return 1; | ||
546 | } else { | ||
547 | const char *p; | ||
548 | |||
549 | for (p = file; p[0] != '\0'; p++) { | ||
550 | if ((p == file || p[-1] == '/') && p[0] != '/' && | ||
551 | fnmatch(excluded_files[i], p, | ||
552 | FNM_PATHNAME | FNM_LEADING_DIR) == 0) | ||
553 | return 1; | ||
554 | } | ||
555 | } | ||
556 | } | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | static int extract_file(char **extract_files, const char *file) | ||
562 | { | ||
563 | int i; | ||
564 | |||
565 | if (extract_files == NULL) | ||
566 | return 1; | ||
567 | |||
568 | for (i = 0; extract_files[i] != NULL; i++) { | ||
569 | if (fnmatch(extract_files[i], file, FNM_LEADING_DIR) == 0) | ||
570 | return 1; | ||
571 | } | ||
572 | |||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | /* | ||
577 | * Read a tar file and extract or list the specified files within it. | ||
578 | * If the list is empty than all files are extracted or listed. | ||
579 | */ | ||
580 | static int readTarFile(int tarFd, int extractFlag, int listFlag, | ||
581 | int tostdoutFlag, int verboseFlag, char** extractList, | ||
582 | char** excludeList) | ||
583 | { | ||
584 | int status; | ||
585 | int errorFlag=FALSE; | ||
586 | int skipNextHeaderFlag=FALSE; | ||
587 | TarHeader rawHeader; | ||
588 | TarInfo header; | ||
589 | |||
590 | /* Read the tar file, and iterate over it one file at a time */ | ||
591 | while ( (status = full_read(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) { | ||
592 | |||
593 | /* Try to read the header */ | ||
594 | if ( readTarHeader(&rawHeader, &header) == FALSE ) { | ||
595 | if ( *(header.name) == '\0' ) { | ||
596 | goto endgame; | ||
597 | } else { | ||
598 | errorFlag=TRUE; | ||
599 | error_msg("Bad tar header, skipping"); | ||
600 | continue; | ||
601 | } | ||
602 | } | ||
603 | if ( *(header.name) == '\0' ) | ||
604 | continue; | ||
605 | header.tarFd = tarFd; | ||
606 | |||
607 | /* Skip funky extra GNU headers that precede long files */ | ||
608 | if ( (header.type == GNULONGNAME) || (header.type == GNULONGLINK) ) { | ||
609 | skipNextHeaderFlag=TRUE; | ||
610 | if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) | ||
611 | errorFlag = TRUE; | ||
612 | continue; | ||
613 | } | ||
614 | if ( skipNextHeaderFlag == TRUE ) { | ||
615 | skipNextHeaderFlag=FALSE; | ||
616 | error_msg(name_longer_than_foo, NAME_SIZE); | ||
617 | if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) | ||
618 | errorFlag = TRUE; | ||
619 | continue; | ||
620 | } | ||
621 | |||
622 | #if defined BB_FEATURE_TAR_EXCLUDE | ||
623 | if (exclude_file(excludeList, header.name)) { | ||
624 | /* There are not the droids you're looking for, move along */ | ||
625 | /* If it is a regular file, pretend to extract it with | ||
626 | * the extractFlag set to FALSE, so the junk in the tarball | ||
627 | * is properly skipped over */ | ||
628 | if ( header.type==REGTYPE || header.type==REGTYPE0 ) { | ||
629 | if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) | ||
630 | errorFlag = TRUE; | ||
631 | } | ||
632 | continue; | ||
633 | } | ||
634 | #endif | ||
635 | |||
636 | if (!extract_file(extractList, header.name)) { | ||
637 | /* There are not the droids you're looking for, move along */ | ||
638 | /* If it is a regular file, pretend to extract it with | ||
639 | * the extractFlag set to FALSE, so the junk in the tarball | ||
640 | * is properly skipped over */ | ||
641 | if ( header.type==REGTYPE || header.type==REGTYPE0 ) { | ||
642 | if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) | ||
643 | errorFlag = TRUE; | ||
644 | } | ||
645 | continue; | ||
646 | } | ||
647 | |||
648 | if (listFlag == TRUE) { | ||
649 | /* Special treatment if the list (-t) flag is on */ | ||
650 | if (verboseFlag == TRUE) { | ||
651 | int len, len1; | ||
652 | char buf[35]; | ||
653 | struct tm *tm = localtime (&(header.mtime)); | ||
654 | |||
655 | len=printf("%s ", mode_string(header.mode)); | ||
656 | my_getpwuid(buf, header.uid); | ||
657 | if (! *buf) | ||
658 | len+=printf("%d", header.uid); | ||
659 | else | ||
660 | len+=printf("%s", buf); | ||
661 | my_getgrgid(buf, header.gid); | ||
662 | if (! *buf) | ||
663 | len+=printf("/%-d ", header.gid); | ||
664 | else | ||
665 | len+=printf("/%-s ", buf); | ||
666 | |||
667 | if (header.type==CHRTYPE || header.type==BLKTYPE) { | ||
668 | len1=snprintf(buf, sizeof(buf), "%ld,%-ld ", | ||
669 | header.devmajor, header.devminor); | ||
670 | } else { | ||
671 | len1=snprintf(buf, sizeof(buf), "%lu ", (long)header.size); | ||
672 | } | ||
673 | /* Jump through some hoops to make the columns match up */ | ||
674 | for(;(len+len1)<31;len++) | ||
675 | printf(" "); | ||
676 | printf(buf); | ||
677 | |||
678 | /* Use ISO 8610 time format */ | ||
679 | if (tm) { | ||
680 | printf ("%04d-%02d-%02d %02d:%02d:%02d ", | ||
681 | tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, | ||
682 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
683 | } | ||
684 | } | ||
685 | printf("%s", header.name); | ||
686 | if (verboseFlag == TRUE) { | ||
687 | if (header.type==LNKTYPE) /* If this is a link, say so */ | ||
688 | printf(" link to %s", header.linkname); | ||
689 | else if (header.type==SYMTYPE) | ||
690 | printf(" -> %s", header.linkname); | ||
691 | } | ||
692 | printf("\n"); | ||
693 | } | ||
694 | |||
695 | /* List contents if we are supposed to do that */ | ||
696 | if (verboseFlag == TRUE && extractFlag == TRUE) { | ||
697 | /* Now the normal listing */ | ||
698 | FILE *vbFd = stdout; | ||
699 | if (tostdoutFlag == TRUE) // If the archive goes to stdout, verbose to stderr | ||
700 | vbFd = stderr; | ||
701 | fprintf(vbFd, "%s\n", header.name); | ||
702 | } | ||
703 | |||
704 | /* Remove files if we would overwrite them */ | ||
705 | if (extractFlag == TRUE && tostdoutFlag == FALSE) | ||
706 | unlink(header.name); | ||
707 | |||
708 | /* If we got here, we can be certain we have a legitimate | ||
709 | * header to work with. So work with it. */ | ||
710 | switch ( header.type ) { | ||
711 | case REGTYPE: | ||
712 | case REGTYPE0: | ||
713 | /* If the name ends in a '/' then assume it is | ||
714 | * supposed to be a directory, and fall through */ | ||
715 | if (!last_char_is(header.name,'/')) { | ||
716 | if (tarExtractRegularFile(&header, extractFlag, tostdoutFlag)==FALSE) | ||
717 | errorFlag=TRUE; | ||
718 | break; | ||
719 | } | ||
720 | case DIRTYPE: | ||
721 | if (tarExtractDirectory( &header, extractFlag, tostdoutFlag)==FALSE) | ||
722 | errorFlag=TRUE; | ||
723 | break; | ||
724 | case LNKTYPE: | ||
725 | if (tarExtractHardLink( &header, extractFlag, tostdoutFlag)==FALSE) | ||
726 | errorFlag=TRUE; | ||
727 | break; | ||
728 | case SYMTYPE: | ||
729 | if (tarExtractSymLink( &header, extractFlag, tostdoutFlag)==FALSE) | ||
730 | errorFlag=TRUE; | ||
731 | break; | ||
732 | case CHRTYPE: | ||
733 | case BLKTYPE: | ||
734 | case FIFOTYPE: | ||
735 | if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE) | ||
736 | errorFlag=TRUE; | ||
737 | break; | ||
738 | #if 0 | ||
739 | /* Handled earlier */ | ||
740 | case GNULONGNAME: | ||
741 | case GNULONGLINK: | ||
742 | skipNextHeaderFlag=TRUE; | ||
743 | break; | ||
744 | #endif | ||
745 | default: | ||
746 | error_msg("Unknown file type '%c' in tar file", header.type); | ||
747 | close( tarFd); | ||
748 | return( FALSE); | ||
749 | } | ||
750 | } | ||
751 | close(tarFd); | ||
752 | if (status > 0) { | ||
753 | /* Bummer - we read a partial header */ | ||
754 | perror_msg("Error reading tar file"); | ||
755 | return ( FALSE); | ||
756 | } | ||
757 | else if (errorFlag==TRUE) { | ||
758 | error_msg( "Error exit delayed from previous errors"); | ||
759 | return( FALSE); | ||
760 | } else | ||
761 | return( status); | ||
762 | |||
763 | /* Stuff to do when we are done */ | ||
764 | endgame: | ||
765 | close( tarFd); | ||
766 | if ( *(header.name) == '\0' ) { | ||
767 | if (errorFlag==TRUE) | ||
768 | error_msg( "Error exit delayed from previous errors"); | ||
769 | else | ||
770 | return( TRUE); | ||
771 | } | ||
772 | return( FALSE); | ||
773 | } | ||
774 | |||
775 | |||
776 | #ifdef BB_FEATURE_TAR_CREATE | ||
777 | |||
778 | /* | 91 | /* |
779 | ** writeTarFile(), writeFileToTarball(), and writeTarHeader() are | 92 | ** writeTarFile(), writeFileToTarball(), and writeTarHeader() are |
780 | ** the only functions that deal with the HardLinkInfo structure. | 93 | ** the only functions that deal with the HardLinkInfo structure. |
@@ -807,6 +120,22 @@ struct TarBallInfo | |||
807 | }; | 120 | }; |
808 | typedef struct TarBallInfo TarBallInfo; | 121 | typedef struct TarBallInfo TarBallInfo; |
809 | 122 | ||
123 | /* A nice enum with all the possible tar file content types */ | ||
124 | enum TarFileType | ||
125 | { | ||
126 | REGTYPE = '0', /* regular file */ | ||
127 | REGTYPE0 = '\0', /* regular file (ancient bug compat)*/ | ||
128 | LNKTYPE = '1', /* hard link */ | ||
129 | SYMTYPE = '2', /* symbolic link */ | ||
130 | CHRTYPE = '3', /* character special */ | ||
131 | BLKTYPE = '4', /* block special */ | ||
132 | DIRTYPE = '5', /* directory */ | ||
133 | FIFOTYPE = '6', /* FIFO special */ | ||
134 | CONTTYPE = '7', /* reserved */ | ||
135 | GNULONGLINK = 'K', /* GNU long (>100 chars) link name */ | ||
136 | GNULONGNAME = 'L', /* GNU long (>100 chars) file name */ | ||
137 | }; | ||
138 | typedef enum TarFileType TarFileType; | ||
810 | 139 | ||
811 | /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ | 140 | /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ |
812 | static void | 141 | static void |
@@ -985,6 +314,32 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *header_name, | |||
985 | return ( TRUE); | 314 | return ( TRUE); |
986 | } | 315 | } |
987 | 316 | ||
317 | static int exclude_file(char **excluded_files, const char *file) | ||
318 | { | ||
319 | int i; | ||
320 | |||
321 | if (excluded_files == NULL) | ||
322 | return 0; | ||
323 | |||
324 | for (i = 0; excluded_files[i] != NULL; i++) { | ||
325 | if (excluded_files[i][0] == '/') { | ||
326 | if (fnmatch(excluded_files[i], file, | ||
327 | FNM_PATHNAME | FNM_LEADING_DIR) == 0) | ||
328 | return 1; | ||
329 | } else { | ||
330 | const char *p; | ||
331 | |||
332 | for (p = file; p[0] != '\0'; p++) { | ||
333 | if ((p == file || p[-1] == '/') && p[0] != '/' && | ||
334 | fnmatch(excluded_files[i], p, | ||
335 | FNM_PATHNAME | FNM_LEADING_DIR) == 0) | ||
336 | return 1; | ||
337 | } | ||
338 | } | ||
339 | } | ||
340 | |||
341 | return 0; | ||
342 | } | ||
988 | 343 | ||
989 | static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData) | 344 | static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData) |
990 | { | 345 | { |
@@ -1040,11 +395,11 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* | |||
1040 | if (header_name[0] == '\0') | 395 | if (header_name[0] == '\0') |
1041 | return TRUE; | 396 | return TRUE; |
1042 | 397 | ||
1043 | #if defined BB_FEATURE_TAR_EXCLUDE | 398 | # if defined BB_FEATURE_TAR_EXCLUDE |
1044 | if (exclude_file(tbInfo->excludeList, header_name)) { | 399 | if (exclude_file(tbInfo->excludeList, header_name)) { |
1045 | return SKIP; | 400 | return SKIP; |
1046 | } | 401 | } |
1047 | #endif | 402 | # endif //BB_FEATURE_TAR_EXCLUDE |
1048 | 403 | ||
1049 | if (writeTarHeader(tbInfo, header_name, fileName, statbuf)==FALSE) { | 404 | if (writeTarHeader(tbInfo, header_name, fileName, statbuf)==FALSE) { |
1050 | return( FALSE); | 405 | return( FALSE); |
@@ -1144,7 +499,254 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv, | |||
1144 | freeHardLinkInfo(&tbInfo.hlInfoHead); | 499 | freeHardLinkInfo(&tbInfo.hlInfoHead); |
1145 | return( TRUE); | 500 | return( TRUE); |
1146 | } | 501 | } |
502 | #endif //tar_create | ||
1147 | 503 | ||
504 | void append_file_to_list(char *filename, char ***name_list, int *num_of_entries) | ||
505 | { | ||
506 | FILE *src_stream; | ||
507 | char *line; | ||
508 | char *line_ptr; | ||
509 | |||
510 | src_stream = xfopen(filename, "r"); | ||
511 | while ((line = get_line_from_file(src_stream)) != NULL) { | ||
512 | line_ptr = last_char_is(line, '\n'); | ||
513 | if (line_ptr) { | ||
514 | *line_ptr = '\0'; | ||
515 | } | ||
516 | *name_list = realloc(*name_list, sizeof(char *) * (*num_of_entries + 1)); | ||
517 | (*name_list)[*num_of_entries] = xstrdup(line); | ||
518 | (*num_of_entries)++; | ||
519 | free(line); | ||
520 | } | ||
521 | fclose(src_stream); | ||
522 | } | ||
1148 | 523 | ||
524 | #ifdef BB_FEATURE_TAR_EXCLUDE | ||
525 | char **merge_list(char **include_list, char **exclude_list) | ||
526 | { | ||
527 | char **new_include_list = NULL; | ||
528 | int new_include_count = 0; | ||
529 | int include_count = 0; | ||
530 | int exclude_count; | ||
531 | |||
532 | while (include_list[include_count] != NULL) { | ||
533 | int found = FALSE; | ||
534 | exclude_count = 0; | ||
535 | while (exclude_list[exclude_count] != NULL) { | ||
536 | if (strcmp(include_list[include_count], exclude_list[exclude_count]) == 0) { | ||
537 | found = TRUE; | ||
538 | break; | ||
539 | } | ||
540 | exclude_count++; | ||
541 | } | ||
542 | |||
543 | if (found == FALSE) { | ||
544 | new_include_list = realloc(new_include_list, sizeof(char *) * (include_count + 2)); | ||
545 | new_include_list[new_include_count] = include_list[include_count]; | ||
546 | new_include_count++; | ||
547 | new_include_list[new_include_count] = NULL; | ||
548 | } else { | ||
549 | free(include_list[include_count]); | ||
550 | } | ||
551 | include_count++; | ||
552 | } | ||
553 | return(new_include_list); | ||
554 | } | ||
1149 | #endif | 555 | #endif |
1150 | 556 | ||
557 | int tar_main(int argc, char **argv) | ||
558 | { | ||
559 | enum untar_funct_e { | ||
560 | /* These are optional */ | ||
561 | untar_from_file = 1, | ||
562 | untar_from_stdin = 2, | ||
563 | untar_unzip = 4, | ||
564 | /* Require one and only one of these */ | ||
565 | untar_list = 8, | ||
566 | untar_create = 16, | ||
567 | untar_extract = 32 | ||
568 | }; | ||
569 | |||
570 | #ifdef BB_FEATURE_TAR_EXCLUDE | ||
571 | char **exclude_list = NULL; | ||
572 | int exclude_count = 0; | ||
573 | #endif | ||
574 | |||
575 | FILE *src_stream = NULL; | ||
576 | FILE *uncompressed_stream = NULL; | ||
577 | char **include_list = NULL; | ||
578 | char *src_filename = NULL; | ||
579 | char *dst_prefix = NULL; | ||
580 | char *file_list_name = NULL; | ||
581 | int opt; | ||
582 | unsigned short untar_funct = 0; | ||
583 | unsigned short untar_funct_required = 0; | ||
584 | unsigned short extract_function = 0; | ||
585 | int include_count = 0; | ||
586 | int gunzip_pid; | ||
587 | int gz_fd = 0; | ||
588 | |||
589 | if (argc < 2) { | ||
590 | show_usage(); | ||
591 | } | ||
592 | |||
593 | /* Prepend '-' to the first argument if required */ | ||
594 | if (argv[1][0] != '-') { | ||
595 | char *tmp = xmalloc(strlen(argv[1]) + 2); | ||
596 | tmp[0] = '-'; | ||
597 | strcpy(tmp + 1, argv[1]); | ||
598 | argv[1] = tmp; | ||
599 | } | ||
600 | |||
601 | while ((opt = getopt(argc, argv, "ctxT:X:C:f:Opvz")) != -1) { | ||
602 | switch (opt) { | ||
603 | |||
604 | /* One and only one of these is required */ | ||
605 | case 'c': | ||
606 | untar_funct_required |= untar_create; | ||
607 | break; | ||
608 | case 't': | ||
609 | untar_funct_required |= untar_list; | ||
610 | extract_function |= extract_list |extract_unconditional; | ||
611 | break; | ||
612 | case 'x': | ||
613 | untar_funct_required |= untar_extract; | ||
614 | extract_function |= (extract_all_to_fs | extract_unconditional | extract_create_leading_dirs); | ||
615 | break; | ||
616 | |||
617 | /* These are optional */ | ||
618 | /* Exclude or Include files listed in <filename>*/ | ||
619 | #ifdef BB_FEATURE_TAR_EXCLUDE | ||
620 | case 'X': | ||
621 | append_file_to_list(optarg, &exclude_list, &exclude_count); | ||
622 | exclude_list[exclude_count] = NULL; | ||
623 | break; | ||
624 | #endif | ||
625 | case 'T': | ||
626 | // by default a list is an include list | ||
627 | append_file_to_list(optarg, &include_list, &include_count); | ||
628 | break; | ||
629 | |||
630 | case 'C': // Change to dir <optarg> | ||
631 | /* Make sure dst_prefix ends in a '/' */ | ||
632 | dst_prefix = concat_path_file(optarg, "/"); | ||
633 | break; | ||
634 | case 'f': // archive filename | ||
635 | if (strcmp(optarg, "-") == 0) { | ||
636 | // Untar from stdin to stdout | ||
637 | untar_funct |= untar_from_stdin; | ||
638 | } else { | ||
639 | untar_funct |= untar_from_file; | ||
640 | src_filename = xstrdup(optarg); | ||
641 | } | ||
642 | break; | ||
643 | case 'O': | ||
644 | extract_function |= extract_to_stdout; | ||
645 | break; | ||
646 | case 'p': | ||
647 | break; | ||
648 | case 'v': | ||
649 | if (extract_function & extract_list) { | ||
650 | extract_function |= extract_verbose_list; | ||
651 | } | ||
652 | extract_function |= extract_list; | ||
653 | break; | ||
654 | #ifdef BB_FEATURE_TAR_GZIP | ||
655 | case 'z': | ||
656 | untar_funct |= untar_unzip; | ||
657 | break; | ||
658 | #endif | ||
659 | default: | ||
660 | show_usage(); | ||
661 | } | ||
662 | } | ||
663 | |||
664 | /* Make sure the valid arguments were passed */ | ||
665 | if (untar_funct_required == 0) { | ||
666 | error_msg_and_die("You must specify one of the `-ctx' options"); | ||
667 | } | ||
668 | if ((untar_funct_required != untar_create) && | ||
669 | (untar_funct_required != untar_extract) && | ||
670 | (untar_funct_required != untar_list)) { | ||
671 | error_msg_and_die("You may not specify more than one `ctx' option."); | ||
672 | } | ||
673 | untar_funct |= untar_funct_required; | ||
674 | |||
675 | /* Setup an array of filenames to work with */ | ||
676 | while (optind < argc) { | ||
677 | include_list = realloc(include_list, sizeof(char *) * (include_count + 2)); | ||
678 | include_list[include_count] = xstrdup(argv[optind]); | ||
679 | include_count++; | ||
680 | optind++; | ||
681 | include_list[include_count] = NULL; | ||
682 | } | ||
683 | |||
684 | #ifdef BB_FEATURE_TAR_EXCLUDE | ||
685 | /* Remove excluded files from the include list */ | ||
686 | if (exclude_list != NULL) { | ||
687 | /* If both an exclude and include file list was present then | ||
688 | * its an exclude from include list only, if not its really an | ||
689 | * exclude list (and a poor choice of variable names) */ | ||
690 | if (include_list == NULL) { | ||
691 | extract_function |= extract_exclude_list; | ||
692 | } | ||
693 | include_list = merge_list(include_list, exclude_list); | ||
694 | } | ||
695 | #endif | ||
696 | |||
697 | if (extract_function & (extract_list | extract_all_to_fs)) { | ||
698 | if (dst_prefix == NULL) { | ||
699 | dst_prefix = xstrdup("./"); | ||
700 | } | ||
701 | |||
702 | /* Setup the source of the tar data */ | ||
703 | if (untar_funct & untar_from_file) { | ||
704 | src_stream = xfopen(src_filename, "r"); | ||
705 | } else { | ||
706 | src_stream = stdin; | ||
707 | } | ||
708 | #ifdef BB_FEATURE_TAR_GZIP | ||
709 | /* Get a binary tree of all the tar file headers */ | ||
710 | if (untar_funct & untar_unzip) { | ||
711 | uncompressed_stream = gz_open(src_stream, &gunzip_pid); | ||
712 | } else | ||
713 | #endif // BB_FEATURE_TAR_GZIP | ||
714 | uncompressed_stream = src_stream; | ||
715 | |||
716 | /* extract or list archive */ | ||
717 | unarchive(uncompressed_stream, stdout, &get_header_tar, extract_function, dst_prefix, include_list); | ||
718 | fclose(uncompressed_stream); | ||
719 | } | ||
720 | #ifdef BB_FEATURE_TAR_CREATE | ||
721 | /* create an archive */ | ||
722 | else if (untar_funct & untar_create) { | ||
723 | int verboseFlag = FALSE; | ||
724 | |||
725 | #ifdef BB_FEATURE_TAR_GZIP | ||
726 | if (untar_funct && untar_unzip) { | ||
727 | error_msg_and_die("Creation of compressed tarfile not internally support by tar, pipe to busybox gunzip"); | ||
728 | } | ||
729 | #endif // BB_FEATURE_TAR_GZIP | ||
730 | if (extract_function & extract_verbose_list) { | ||
731 | verboseFlag = TRUE; | ||
732 | } | ||
733 | writeTarFile(src_filename, verboseFlag, &argv[argc - 1], include_list); | ||
734 | } | ||
735 | #endif // BB_FEATURE_TAR_CREATE | ||
736 | |||
737 | /* Cleanups */ | ||
738 | #ifdef BB_FEATURE_TAR_GZIP | ||
739 | if (untar_funct & untar_unzip) { | ||
740 | fclose(src_stream); | ||
741 | close(gz_fd); | ||
742 | gz_close(gunzip_pid); | ||
743 | } | ||
744 | #endif // BB_FEATURE_TAR_GZIP | ||
745 | if (src_filename) { | ||
746 | free(src_filename); | ||
747 | } | ||
748 | if (file_list_name) { | ||
749 | free(file_list_name); | ||
750 | } | ||
751 | return(EXIT_SUCCESS); | ||
752 | } | ||