diff options
Diffstat (limited to 'tar.c')
-rw-r--r-- | tar.c | 745 |
1 files changed, 0 insertions, 745 deletions
diff --git a/tar.c b/tar.c deleted file mode 100644 index f7a3da66f..000000000 --- a/tar.c +++ /dev/null | |||
@@ -1,745 +0,0 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini tar implementation for busybox | ||
4 | * | ||
5 | * Modifed to use common extraction code used by ar, cpio, dpkg-deb, dpkg | ||
6 | * Glenn McGrath <bug1@optushome.com.au> | ||
7 | * | ||
8 | * Note, that as of BusyBox-0.43, tar has been completely rewritten from the | ||
9 | * ground up. It still has remnents of the old code lying about, but it is | ||
10 | * very different now (i.e., cleaner, less global variables, etc.) | ||
11 | * | ||
12 | * Copyright (C) 1999,2000,2001 by Lineo, inc. | ||
13 | * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org> | ||
14 | * | ||
15 | * Based in part in the tar implementation in sash | ||
16 | * Copyright (c) 1999 by David I. Bell | ||
17 | * Permission is granted to use, distribute, or modify this source, | ||
18 | * provided that this copyright notice remains intact. | ||
19 | * Permission to distribute sash derived code under the GPL has been granted. | ||
20 | * | ||
21 | * Based in part on the tar implementation from busybox-0.28 | ||
22 | * Copyright (C) 1995 Bruce Perens | ||
23 | * This is free software under the GNU General Public License. | ||
24 | * | ||
25 | * This program is free software; you can redistribute it and/or modify | ||
26 | * it under the terms of the GNU General Public License as published by | ||
27 | * the Free Software Foundation; either version 2 of the License, or | ||
28 | * (at your option) any later version. | ||
29 | * | ||
30 | * This program is distributed in the hope that it will be useful, | ||
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
33 | * General Public License for more details. | ||
34 | * | ||
35 | * You should have received a copy of the GNU General Public License | ||
36 | * along with this program; if not, write to the Free Software | ||
37 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
38 | * | ||
39 | */ | ||
40 | |||
41 | #include <fcntl.h> | ||
42 | #include <getopt.h> | ||
43 | #include <search.h> | ||
44 | #include <stdio.h> | ||
45 | #include <stdlib.h> | ||
46 | #include <unistd.h> | ||
47 | #include <fnmatch.h> | ||
48 | #include <string.h> | ||
49 | #include <errno.h> | ||
50 | #include "busybox.h" | ||
51 | |||
52 | #ifdef BB_FEATURE_TAR_CREATE | ||
53 | |||
54 | /* Tar file constants */ | ||
55 | # define TAR_MAGIC "ustar" /* ustar and a null */ | ||
56 | # define TAR_VERSION " " /* Be compatable with GNU tar format */ | ||
57 | |||
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; | ||
66 | |||
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' */ | ||
69 | struct TarHeader | ||
70 | { /* byte offset */ | ||
71 | char name[NAME_SIZE]; /* 0-99 */ | ||
72 | char mode[8]; /* 100-107 */ | ||
73 | char uid[8]; /* 108-115 */ | ||
74 | char gid[8]; /* 116-123 */ | ||
75 | char size[12]; /* 124-135 */ | ||
76 | char mtime[12]; /* 136-147 */ | ||
77 | char chksum[8]; /* 148-155 */ | ||
78 | char typeflag; /* 156-156 */ | ||
79 | char linkname[NAME_SIZE]; /* 157-256 */ | ||
80 | char magic[6]; /* 257-262 */ | ||
81 | char version[2]; /* 263-264 */ | ||
82 | char uname[32]; /* 265-296 */ | ||
83 | char gname[32]; /* 297-328 */ | ||
84 | char devmajor[8]; /* 329-336 */ | ||
85 | char devminor[8]; /* 337-344 */ | ||
86 | char prefix[155]; /* 345-499 */ | ||
87 | char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */ | ||
88 | }; | ||
89 | typedef struct TarHeader TarHeader; | ||
90 | |||
91 | /* | ||
92 | ** writeTarFile(), writeFileToTarball(), and writeTarHeader() are | ||
93 | ** the only functions that deal with the HardLinkInfo structure. | ||
94 | ** Even these functions use the xxxHardLinkInfo() functions. | ||
95 | */ | ||
96 | typedef struct HardLinkInfo HardLinkInfo; | ||
97 | struct HardLinkInfo | ||
98 | { | ||
99 | HardLinkInfo *next; /* Next entry in list */ | ||
100 | dev_t dev; /* Device number */ | ||
101 | ino_t ino; /* Inode number */ | ||
102 | short linkCount; /* (Hard) Link Count */ | ||
103 | char name[1]; /* Start of filename (must be last) */ | ||
104 | }; | ||
105 | |||
106 | /* Some info to be carried along when creating a new tarball */ | ||
107 | struct TarBallInfo | ||
108 | { | ||
109 | char* fileName; /* File name of the tarball */ | ||
110 | int tarFd; /* Open-for-write file descriptor | ||
111 | for the tarball */ | ||
112 | struct stat statBuf; /* Stat info for the tarball, letting | ||
113 | us know the inode and device that the | ||
114 | tarball lives, so we can avoid trying | ||
115 | to include the tarball into itself */ | ||
116 | int verboseFlag; /* Whether to print extra stuff or not */ | ||
117 | char** excludeList; /* List of files to not include */ | ||
118 | HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ | ||
119 | HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ | ||
120 | }; | ||
121 | typedef struct TarBallInfo TarBallInfo; | ||
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; | ||
139 | |||
140 | /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ | ||
141 | static void | ||
142 | addHardLinkInfo (HardLinkInfo **hlInfoHeadPtr, dev_t dev, ino_t ino, | ||
143 | short linkCount, const char *name) | ||
144 | { | ||
145 | /* Note: hlInfoHeadPtr can never be NULL! */ | ||
146 | HardLinkInfo *hlInfo; | ||
147 | |||
148 | hlInfo = (HardLinkInfo *)xmalloc(sizeof(HardLinkInfo)+strlen(name)+1); | ||
149 | if (hlInfo) { | ||
150 | hlInfo->next = *hlInfoHeadPtr; | ||
151 | *hlInfoHeadPtr = hlInfo; | ||
152 | hlInfo->dev = dev; | ||
153 | hlInfo->ino = ino; | ||
154 | hlInfo->linkCount = linkCount; | ||
155 | strcpy(hlInfo->name, name); | ||
156 | } | ||
157 | return; | ||
158 | } | ||
159 | |||
160 | static void | ||
161 | freeHardLinkInfo (HardLinkInfo **hlInfoHeadPtr) | ||
162 | { | ||
163 | HardLinkInfo *hlInfo = NULL; | ||
164 | HardLinkInfo *hlInfoNext = NULL; | ||
165 | |||
166 | if (hlInfoHeadPtr) { | ||
167 | hlInfo = *hlInfoHeadPtr; | ||
168 | while (hlInfo) { | ||
169 | hlInfoNext = hlInfo->next; | ||
170 | free(hlInfo); | ||
171 | hlInfo = hlInfoNext; | ||
172 | } | ||
173 | *hlInfoHeadPtr = NULL; | ||
174 | } | ||
175 | return; | ||
176 | } | ||
177 | |||
178 | /* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */ | ||
179 | static HardLinkInfo * | ||
180 | findHardLinkInfo (HardLinkInfo *hlInfo, dev_t dev, ino_t ino) | ||
181 | { | ||
182 | while(hlInfo) { | ||
183 | if ((ino == hlInfo->ino) && (dev == hlInfo->dev)) | ||
184 | break; | ||
185 | hlInfo = hlInfo->next; | ||
186 | } | ||
187 | return(hlInfo); | ||
188 | } | ||
189 | |||
190 | /* Put an octal string into the specified buffer. | ||
191 | * The number is zero and space padded and possibly null padded. | ||
192 | * Returns TRUE if successful. */ | ||
193 | static int putOctal (char *cp, int len, long value) | ||
194 | { | ||
195 | int tempLength; | ||
196 | char tempBuffer[32]; | ||
197 | char *tempString = tempBuffer; | ||
198 | |||
199 | /* Create a string of the specified length with an initial space, | ||
200 | * leading zeroes and the octal number, and a trailing null. */ | ||
201 | sprintf (tempString, "%0*lo", len - 1, value); | ||
202 | |||
203 | /* If the string is too large, suppress the leading space. */ | ||
204 | tempLength = strlen (tempString) + 1; | ||
205 | if (tempLength > len) { | ||
206 | tempLength--; | ||
207 | tempString++; | ||
208 | } | ||
209 | |||
210 | /* If the string is still too large, suppress the trailing null. */ | ||
211 | if (tempLength > len) | ||
212 | tempLength--; | ||
213 | |||
214 | /* If the string is still too large, fail. */ | ||
215 | if (tempLength > len) | ||
216 | return FALSE; | ||
217 | |||
218 | /* Copy the string to the field. */ | ||
219 | memcpy (cp, tempString, len); | ||
220 | |||
221 | return TRUE; | ||
222 | } | ||
223 | |||
224 | /* Write out a tar header for the specified file/directory/whatever */ | ||
225 | static int | ||
226 | writeTarHeader(struct TarBallInfo *tbInfo, const char *header_name, | ||
227 | const char *real_name, struct stat *statbuf) | ||
228 | { | ||
229 | long chksum=0; | ||
230 | struct TarHeader header; | ||
231 | const unsigned char *cp = (const unsigned char *) &header; | ||
232 | ssize_t size = sizeof(struct TarHeader); | ||
233 | |||
234 | memset( &header, 0, size); | ||
235 | |||
236 | strncpy(header.name, header_name, sizeof(header.name)); | ||
237 | |||
238 | putOctal(header.mode, sizeof(header.mode), statbuf->st_mode); | ||
239 | putOctal(header.uid, sizeof(header.uid), statbuf->st_uid); | ||
240 | putOctal(header.gid, sizeof(header.gid), statbuf->st_gid); | ||
241 | putOctal(header.size, sizeof(header.size), 0); /* Regular file size is handled later */ | ||
242 | putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime); | ||
243 | strncpy(header.magic, TAR_MAGIC TAR_VERSION, | ||
244 | TAR_MAGIC_LEN + TAR_VERSION_LEN ); | ||
245 | |||
246 | /* Enter the user and group names (default to root if it fails) */ | ||
247 | my_getpwuid(header.uname, statbuf->st_uid); | ||
248 | if (! *header.uname) | ||
249 | strcpy(header.uname, "root"); | ||
250 | my_getgrgid(header.gname, statbuf->st_gid); | ||
251 | if (! *header.uname) | ||
252 | strcpy(header.uname, "root"); | ||
253 | |||
254 | if (tbInfo->hlInfo) { | ||
255 | /* This is a hard link */ | ||
256 | header.typeflag = LNKTYPE; | ||
257 | strncpy(header.linkname, tbInfo->hlInfo->name, sizeof(header.linkname)); | ||
258 | } else if (S_ISLNK(statbuf->st_mode)) { | ||
259 | char *lpath = xreadlink(real_name); | ||
260 | if (!lpath) /* Already printed err msg inside xreadlink() */ | ||
261 | return ( FALSE); | ||
262 | header.typeflag = SYMTYPE; | ||
263 | strncpy(header.linkname, lpath, sizeof(header.linkname)); | ||
264 | free(lpath); | ||
265 | } else if (S_ISDIR(statbuf->st_mode)) { | ||
266 | header.typeflag = DIRTYPE; | ||
267 | strncat(header.name, "/", sizeof(header.name)); | ||
268 | } else if (S_ISCHR(statbuf->st_mode)) { | ||
269 | header.typeflag = CHRTYPE; | ||
270 | putOctal(header.devmajor, sizeof(header.devmajor), MAJOR(statbuf->st_rdev)); | ||
271 | putOctal(header.devminor, sizeof(header.devminor), MINOR(statbuf->st_rdev)); | ||
272 | } else if (S_ISBLK(statbuf->st_mode)) { | ||
273 | header.typeflag = BLKTYPE; | ||
274 | putOctal(header.devmajor, sizeof(header.devmajor), MAJOR(statbuf->st_rdev)); | ||
275 | putOctal(header.devminor, sizeof(header.devminor), MINOR(statbuf->st_rdev)); | ||
276 | } else if (S_ISFIFO(statbuf->st_mode)) { | ||
277 | header.typeflag = FIFOTYPE; | ||
278 | } else if (S_ISREG(statbuf->st_mode)) { | ||
279 | header.typeflag = REGTYPE; | ||
280 | putOctal(header.size, sizeof(header.size), statbuf->st_size); | ||
281 | } else { | ||
282 | error_msg("%s: Unknown file type", real_name); | ||
283 | return ( FALSE); | ||
284 | } | ||
285 | |||
286 | /* Calculate and store the checksum (i.e., the sum of all of the bytes of | ||
287 | * the header). The checksum field must be filled with blanks for the | ||
288 | * calculation. The checksum field is formatted differently from the | ||
289 | * other fields: it has [6] digits, a null, then a space -- rather than | ||
290 | * digits, followed by a null like the other fields... */ | ||
291 | memset(header.chksum, ' ', sizeof(header.chksum)); | ||
292 | cp = (const unsigned char *) &header; | ||
293 | while (size-- > 0) | ||
294 | chksum += *cp++; | ||
295 | putOctal(header.chksum, 7, chksum); | ||
296 | |||
297 | /* Now write the header out to disk */ | ||
298 | if ((size=full_write(tbInfo->tarFd, (char*)&header, sizeof(struct TarHeader))) < 0) { | ||
299 | error_msg(io_error, real_name, strerror(errno)); | ||
300 | return ( FALSE); | ||
301 | } | ||
302 | /* Pad the header up to the tar block size */ | ||
303 | for (; size<TAR_BLOCK_SIZE; size++) { | ||
304 | write(tbInfo->tarFd, "\0", 1); | ||
305 | } | ||
306 | /* Now do the verbose thing (or not) */ | ||
307 | if (tbInfo->verboseFlag==TRUE) { | ||
308 | FILE *vbFd = stdout; | ||
309 | if (tbInfo->tarFd == fileno(stdout)) // If the archive goes to stdout, verbose to stderr | ||
310 | vbFd = stderr; | ||
311 | fprintf(vbFd, "%s\n", header.name); | ||
312 | } | ||
313 | |||
314 | return ( TRUE); | ||
315 | } | ||
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 | } | ||
343 | |||
344 | static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData) | ||
345 | { | ||
346 | struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData; | ||
347 | const char *header_name; | ||
348 | |||
349 | /* | ||
350 | ** Check to see if we are dealing with a hard link. | ||
351 | ** If so - | ||
352 | ** Treat the first occurance of a given dev/inode as a file while | ||
353 | ** treating any additional occurances as hard links. This is done | ||
354 | ** by adding the file information to the HardLinkInfo linked list. | ||
355 | */ | ||
356 | tbInfo->hlInfo = NULL; | ||
357 | if (statbuf->st_nlink > 1) { | ||
358 | tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf->st_dev, | ||
359 | statbuf->st_ino); | ||
360 | if (tbInfo->hlInfo == NULL) | ||
361 | addHardLinkInfo (&tbInfo->hlInfoHead, statbuf->st_dev, | ||
362 | statbuf->st_ino, statbuf->st_nlink, fileName); | ||
363 | } | ||
364 | |||
365 | /* It is against the rules to archive a socket */ | ||
366 | if (S_ISSOCK(statbuf->st_mode)) { | ||
367 | error_msg("%s: socket ignored", fileName); | ||
368 | return( TRUE); | ||
369 | } | ||
370 | |||
371 | /* It is a bad idea to store the archive we are in the process of creating, | ||
372 | * so check the device and inode to be sure that this particular file isn't | ||
373 | * the new tarball */ | ||
374 | if (tbInfo->statBuf.st_dev == statbuf->st_dev && | ||
375 | tbInfo->statBuf.st_ino == statbuf->st_ino) { | ||
376 | error_msg("%s: file is the archive; skipping", fileName); | ||
377 | return( TRUE); | ||
378 | } | ||
379 | |||
380 | header_name = fileName; | ||
381 | while (header_name[0] == '/') { | ||
382 | static int alreadyWarned=FALSE; | ||
383 | if (alreadyWarned==FALSE) { | ||
384 | error_msg("Removing leading '/' from member names"); | ||
385 | alreadyWarned=TRUE; | ||
386 | } | ||
387 | header_name++; | ||
388 | } | ||
389 | |||
390 | if (strlen(fileName) >= NAME_SIZE) { | ||
391 | error_msg(name_longer_than_foo, NAME_SIZE); | ||
392 | return ( TRUE); | ||
393 | } | ||
394 | |||
395 | if (header_name[0] == '\0') | ||
396 | return TRUE; | ||
397 | |||
398 | # if defined BB_FEATURE_TAR_EXCLUDE | ||
399 | if (exclude_file(tbInfo->excludeList, header_name)) { | ||
400 | return SKIP; | ||
401 | } | ||
402 | # endif //BB_FEATURE_TAR_EXCLUDE | ||
403 | |||
404 | if (writeTarHeader(tbInfo, header_name, fileName, statbuf)==FALSE) { | ||
405 | return( FALSE); | ||
406 | } | ||
407 | |||
408 | /* Now, if the file is a regular file, copy it out to the tarball */ | ||
409 | if ((tbInfo->hlInfo == NULL) | ||
410 | && (S_ISREG(statbuf->st_mode))) { | ||
411 | int inputFileFd; | ||
412 | char buffer[BUFSIZ]; | ||
413 | ssize_t size=0, readSize=0; | ||
414 | |||
415 | /* open the file we want to archive, and make sure all is well */ | ||
416 | if ((inputFileFd = open(fileName, O_RDONLY)) < 0) { | ||
417 | error_msg("%s: Cannot open: %s", fileName, strerror(errno)); | ||
418 | return( FALSE); | ||
419 | } | ||
420 | |||
421 | /* write the file to the archive */ | ||
422 | while ( (size = full_read(inputFileFd, buffer, sizeof(buffer))) > 0 ) { | ||
423 | if (full_write(tbInfo->tarFd, buffer, size) != size ) { | ||
424 | /* Output file seems to have a problem */ | ||
425 | error_msg(io_error, fileName, strerror(errno)); | ||
426 | return( FALSE); | ||
427 | } | ||
428 | readSize+=size; | ||
429 | } | ||
430 | if (size == -1) { | ||
431 | error_msg(io_error, fileName, strerror(errno)); | ||
432 | return( FALSE); | ||
433 | } | ||
434 | /* Pad the file up to the tar block size */ | ||
435 | for (; (readSize%TAR_BLOCK_SIZE) != 0; readSize++) { | ||
436 | write(tbInfo->tarFd, "\0", 1); | ||
437 | } | ||
438 | close( inputFileFd); | ||
439 | } | ||
440 | |||
441 | return( TRUE); | ||
442 | } | ||
443 | |||
444 | static int writeTarFile(const char* tarName, int verboseFlag, char **argv, | ||
445 | char** excludeList) | ||
446 | { | ||
447 | int tarFd=-1; | ||
448 | int errorFlag=FALSE; | ||
449 | ssize_t size; | ||
450 | struct TarBallInfo tbInfo; | ||
451 | tbInfo.verboseFlag = verboseFlag; | ||
452 | tbInfo.hlInfoHead = NULL; | ||
453 | |||
454 | /* Make sure there is at least one file to tar up. */ | ||
455 | if (*argv == NULL) | ||
456 | error_msg_and_die("Cowardly refusing to create an empty archive"); | ||
457 | |||
458 | /* Open the tar file for writing. */ | ||
459 | if (!strcmp(tarName, "-")) | ||
460 | tbInfo.tarFd = fileno(stdout); | ||
461 | else | ||
462 | tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644); | ||
463 | if (tbInfo.tarFd < 0) { | ||
464 | perror_msg( "Error opening '%s'", tarName); | ||
465 | freeHardLinkInfo(&tbInfo.hlInfoHead); | ||
466 | return ( FALSE); | ||
467 | } | ||
468 | tbInfo.excludeList=excludeList; | ||
469 | /* Store the stat info for the tarball's file, so | ||
470 | * can avoid including the tarball into itself.... */ | ||
471 | if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) | ||
472 | error_msg_and_die(io_error, tarName, strerror(errno)); | ||
473 | |||
474 | /* Read the directory/files and iterate over them one at a time */ | ||
475 | while (*argv != NULL) { | ||
476 | if (recursive_action(*argv++, TRUE, FALSE, FALSE, | ||
477 | writeFileToTarball, writeFileToTarball, | ||
478 | (void*) &tbInfo) == FALSE) { | ||
479 | errorFlag = TRUE; | ||
480 | } | ||
481 | } | ||
482 | /* Write two empty blocks to the end of the archive */ | ||
483 | for (size=0; size<(2*TAR_BLOCK_SIZE); size++) { | ||
484 | write(tbInfo.tarFd, "\0", 1); | ||
485 | } | ||
486 | |||
487 | /* To be pedantically correct, we would check if the tarball | ||
488 | * is smaller than 20 tar blocks, and pad it if it was smaller, | ||
489 | * but that isn't necessary for GNU tar interoperability, and | ||
490 | * so is considered a waste of space */ | ||
491 | |||
492 | /* Hang up the tools, close up shop, head home */ | ||
493 | close(tarFd); | ||
494 | if (errorFlag == TRUE) { | ||
495 | error_msg("Error exit delayed from previous errors"); | ||
496 | freeHardLinkInfo(&tbInfo.hlInfoHead); | ||
497 | return(FALSE); | ||
498 | } | ||
499 | freeHardLinkInfo(&tbInfo.hlInfoHead); | ||
500 | return( TRUE); | ||
501 | } | ||
502 | #endif //tar_create | ||
503 | |||
504 | void append_file_to_list(const char *new_name, char ***list, int *list_count) | ||
505 | { | ||
506 | *list = realloc(*list, sizeof(char *) * (*list_count + 2)); | ||
507 | (*list)[*list_count] = xstrdup(new_name); | ||
508 | (*list_count)++; | ||
509 | (*list)[*list_count] = NULL; | ||
510 | } | ||
511 | |||
512 | void append_file_list_to_list(char *filename, char ***name_list, int *num_of_entries) | ||
513 | { | ||
514 | FILE *src_stream; | ||
515 | char *line; | ||
516 | char *line_ptr; | ||
517 | |||
518 | src_stream = xfopen(filename, "r"); | ||
519 | while ((line = get_line_from_file(src_stream)) != NULL) { | ||
520 | line_ptr = last_char_is(line, '\n'); | ||
521 | if (line_ptr) { | ||
522 | *line_ptr = '\0'; | ||
523 | } | ||
524 | append_file_to_list(line, name_list, num_of_entries); | ||
525 | free(line); | ||
526 | } | ||
527 | fclose(src_stream); | ||
528 | } | ||
529 | |||
530 | #ifdef BB_FEATURE_TAR_EXCLUDE | ||
531 | /* | ||
532 | * Create a list of names that are in the include list AND NOT in the exclude lists | ||
533 | */ | ||
534 | char **list_and_not_list(char **include_list, char **exclude_list) | ||
535 | { | ||
536 | char **new_include_list = NULL; | ||
537 | int new_include_count = 0; | ||
538 | int include_count = 0; | ||
539 | int exclude_count; | ||
540 | |||
541 | if (include_list == NULL) { | ||
542 | return(NULL); | ||
543 | } | ||
544 | |||
545 | while (include_list[include_count] != NULL) { | ||
546 | int found = FALSE; | ||
547 | exclude_count = 0; | ||
548 | while (exclude_list[exclude_count] != NULL) { | ||
549 | if (strcmp(include_list[include_count], exclude_list[exclude_count]) == 0) { | ||
550 | found = TRUE; | ||
551 | break; | ||
552 | } | ||
553 | exclude_count++; | ||
554 | } | ||
555 | |||
556 | if (found == FALSE) { | ||
557 | new_include_list = realloc(new_include_list, sizeof(char *) * (include_count + 2)); | ||
558 | new_include_list[new_include_count] = include_list[include_count]; | ||
559 | new_include_count++; | ||
560 | } else { | ||
561 | free(include_list[include_count]); | ||
562 | } | ||
563 | include_count++; | ||
564 | } | ||
565 | new_include_list[new_include_count] = NULL; | ||
566 | return(new_include_list); | ||
567 | } | ||
568 | #endif | ||
569 | |||
570 | int tar_main(int argc, char **argv) | ||
571 | { | ||
572 | enum untar_funct_e { | ||
573 | /* These are optional */ | ||
574 | untar_from_file = 1, | ||
575 | untar_from_stdin = 2, | ||
576 | untar_unzip = 4, | ||
577 | /* Require one and only one of these */ | ||
578 | untar_list = 8, | ||
579 | untar_create = 16, | ||
580 | untar_extract = 32 | ||
581 | }; | ||
582 | |||
583 | FILE *src_stream = NULL; | ||
584 | FILE *uncompressed_stream = NULL; | ||
585 | char **include_list = NULL; | ||
586 | char **exclude_list = NULL; | ||
587 | char *src_filename = NULL; | ||
588 | char *dst_prefix = NULL; | ||
589 | char *file_list_name = NULL; | ||
590 | int opt; | ||
591 | unsigned short untar_funct = 0; | ||
592 | unsigned short untar_funct_required = 0; | ||
593 | unsigned short extract_function = 0; | ||
594 | int include_list_count = 0; | ||
595 | int exclude_list_count = 0; | ||
596 | int gunzip_pid; | ||
597 | int gz_fd = 0; | ||
598 | |||
599 | if (argc < 2) { | ||
600 | show_usage(); | ||
601 | } | ||
602 | |||
603 | /* Prepend '-' to the first argument if required */ | ||
604 | if (argv[1][0] != '-') { | ||
605 | char *tmp = xmalloc(strlen(argv[1]) + 2); | ||
606 | tmp[0] = '-'; | ||
607 | strcpy(tmp + 1, argv[1]); | ||
608 | argv[1] = tmp; | ||
609 | } | ||
610 | |||
611 | while ((opt = getopt(argc, argv, "ctxT:X:C:f:Opvz")) != -1) { | ||
612 | switch (opt) { | ||
613 | |||
614 | /* One and only one of these is required */ | ||
615 | case 'c': | ||
616 | untar_funct_required |= untar_create; | ||
617 | break; | ||
618 | case 't': | ||
619 | untar_funct_required |= untar_list; | ||
620 | extract_function |= extract_list |extract_unconditional; | ||
621 | break; | ||
622 | case 'x': | ||
623 | untar_funct_required |= untar_extract; | ||
624 | extract_function |= (extract_all_to_fs | extract_unconditional | extract_create_leading_dirs); | ||
625 | break; | ||
626 | |||
627 | /* These are optional */ | ||
628 | /* Exclude or Include files listed in <filename>*/ | ||
629 | #ifdef BB_FEATURE_TAR_EXCLUDE | ||
630 | case 'X': | ||
631 | append_file_list_to_list(optarg, &exclude_list, &exclude_list_count); | ||
632 | break; | ||
633 | #endif | ||
634 | case 'T': | ||
635 | // by default a list is an include list | ||
636 | append_file_list_to_list(optarg, &include_list, &include_list_count); | ||
637 | break; | ||
638 | |||
639 | case 'C': // Change to dir <optarg> | ||
640 | /* Make sure dst_prefix ends in a '/' */ | ||
641 | dst_prefix = concat_path_file(optarg, "/"); | ||
642 | break; | ||
643 | case 'f': // archive filename | ||
644 | if (strcmp(optarg, "-") == 0) { | ||
645 | // Untar from stdin to stdout | ||
646 | untar_funct |= untar_from_stdin; | ||
647 | } else { | ||
648 | untar_funct |= untar_from_file; | ||
649 | src_filename = xstrdup(optarg); | ||
650 | } | ||
651 | break; | ||
652 | case 'O': | ||
653 | extract_function |= extract_to_stdout; | ||
654 | break; | ||
655 | case 'p': | ||
656 | break; | ||
657 | case 'v': | ||
658 | if (extract_function & extract_list) { | ||
659 | extract_function |= extract_verbose_list; | ||
660 | } | ||
661 | extract_function |= extract_list; | ||
662 | break; | ||
663 | #ifdef BB_FEATURE_TAR_GZIP | ||
664 | case 'z': | ||
665 | untar_funct |= untar_unzip; | ||
666 | break; | ||
667 | #endif | ||
668 | default: | ||
669 | show_usage(); | ||
670 | } | ||
671 | } | ||
672 | |||
673 | /* Make sure the valid arguments were passed */ | ||
674 | if (untar_funct_required == 0) { | ||
675 | error_msg_and_die("You must specify one of the `-ctx' options"); | ||
676 | } | ||
677 | if ((untar_funct_required != untar_create) && | ||
678 | (untar_funct_required != untar_extract) && | ||
679 | (untar_funct_required != untar_list)) { | ||
680 | error_msg_and_die("You may not specify more than one `ctx' option."); | ||
681 | } | ||
682 | untar_funct |= untar_funct_required; | ||
683 | |||
684 | /* Setup an array of filenames to work with */ | ||
685 | while (optind < argc) { | ||
686 | append_file_to_list(argv[optind], &include_list, &include_list_count); | ||
687 | optind++; | ||
688 | } | ||
689 | |||
690 | if (extract_function & (extract_list | extract_all_to_fs)) { | ||
691 | if (dst_prefix == NULL) { | ||
692 | dst_prefix = xstrdup("./"); | ||
693 | } | ||
694 | |||
695 | /* Setup the source of the tar data */ | ||
696 | if (untar_funct & untar_from_file) { | ||
697 | src_stream = xfopen(src_filename, "r"); | ||
698 | } else { | ||
699 | src_stream = stdin; | ||
700 | } | ||
701 | #ifdef BB_FEATURE_TAR_GZIP | ||
702 | /* Get a binary tree of all the tar file headers */ | ||
703 | if (untar_funct & untar_unzip) { | ||
704 | uncompressed_stream = gz_open(src_stream, &gunzip_pid); | ||
705 | } else | ||
706 | #endif // BB_FEATURE_TAR_GZIP | ||
707 | uncompressed_stream = src_stream; | ||
708 | |||
709 | /* extract or list archive */ | ||
710 | unarchive(uncompressed_stream, stdout, &get_header_tar, extract_function, dst_prefix, include_list, exclude_list); | ||
711 | fclose(uncompressed_stream); | ||
712 | } | ||
713 | #ifdef BB_FEATURE_TAR_CREATE | ||
714 | /* create an archive */ | ||
715 | else if (untar_funct & untar_create) { | ||
716 | int verboseFlag = FALSE; | ||
717 | |||
718 | #ifdef BB_FEATURE_TAR_GZIP | ||
719 | if (untar_funct && untar_unzip) { | ||
720 | error_msg_and_die("Creation of compressed tarfile not internally support by tar, pipe to busybox gunzip"); | ||
721 | } | ||
722 | #endif // BB_FEATURE_TAR_GZIP | ||
723 | if (extract_function & extract_verbose_list) { | ||
724 | verboseFlag = TRUE; | ||
725 | } | ||
726 | writeTarFile(src_filename, verboseFlag, &argv[argc - 1], include_list); | ||
727 | } | ||
728 | #endif // BB_FEATURE_TAR_CREATE | ||
729 | |||
730 | /* Cleanups */ | ||
731 | #ifdef BB_FEATURE_TAR_GZIP | ||
732 | if (untar_funct & untar_unzip) { | ||
733 | fclose(src_stream); | ||
734 | close(gz_fd); | ||
735 | gz_close(gunzip_pid); | ||
736 | } | ||
737 | #endif // BB_FEATURE_TAR_GZIP | ||
738 | if (src_filename) { | ||
739 | free(src_filename); | ||
740 | } | ||
741 | if (file_list_name) { | ||
742 | free(file_list_name); | ||
743 | } | ||
744 | return(EXIT_SUCCESS); | ||
745 | } | ||