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 | |
| 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.
| -rw-r--r-- | archival/tar.c | 1024 | ||||
| -rw-r--r-- | tar.c | 1024 |
2 files changed, 626 insertions, 1422 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 | } | ||
| @@ -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 | } | ||
