source: branches/2.2.9/mindi-busybox/archival/tar.c @ 2725

Last change on this file since 2725 was 2725, checked in by Bruno Cornec, 9 years ago
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File size: 33.6 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini tar implementation for busybox
4 *
5 * Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg
6 *  by Glenn McGrath
7 *
8 * Note, that as of BusyBox-0.43, tar has been completely rewritten from the
9 * ground up.  It still has remnants 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-2004 by Erik Andersen <andersen@codepoet.org>
13 *
14 * Based in part in the tar implementation in sash
15 *  Copyright (c) 1999 by David I. Bell
16 *  Permission is granted to use, distribute, or modify this source,
17 *  provided that this copyright notice remains intact.
18 *  Permission to distribute sash derived code under GPL has been granted.
19 *
20 * Based in part on the tar implementation from busybox-0.28
21 *  Copyright (C) 1995 Bruce Perens
22 *
23 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
24 */
25
26#include <fnmatch.h>
27#include "libbb.h"
28#include "archive.h"
29/* FIXME: Stop using this non-standard feature */
30#ifndef FNM_LEADING_DIR
31# define FNM_LEADING_DIR 0
32#endif
33
34
35//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__)
36#define DBG(...) ((void)0)
37
38
39#define block_buf bb_common_bufsiz1
40
41
42#if !ENABLE_FEATURE_SEAMLESS_GZ && !ENABLE_FEATURE_SEAMLESS_BZ2
43/* Do not pass gzip flag to writeTarFile() */
44#define writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude, gzip) \
45    writeTarFile(tar_fd, verboseFlag, dereferenceFlag, include, exclude)
46#endif
47
48
49#if ENABLE_FEATURE_TAR_CREATE
50
51/*
52** writeTarFile(), writeFileToTarball(), and writeTarHeader() are
53** the only functions that deal with the HardLinkInfo structure.
54** Even these functions use the xxxHardLinkInfo() functions.
55*/
56typedef struct HardLinkInfo {
57    struct HardLinkInfo *next; /* Next entry in list */
58    dev_t dev;                 /* Device number */
59    ino_t ino;                 /* Inode number */
60//  short linkCount;           /* (Hard) Link Count */
61    char name[1];              /* Start of filename (must be last) */
62} HardLinkInfo;
63
64/* Some info to be carried along when creating a new tarball */
65typedef struct TarBallInfo {
66    int tarFd;                      /* Open-for-write file descriptor
67                                     * for the tarball */
68    int verboseFlag;                /* Whether to print extra stuff or not */
69    const llist_t *excludeList;     /* List of files to not include */
70    HardLinkInfo *hlInfoHead;       /* Hard Link Tracking Information */
71    HardLinkInfo *hlInfo;           /* Hard Link Info for the current file */
72//TODO: save only st_dev + st_ino
73    struct stat tarFileStatBuf;     /* Stat info for the tarball, letting
74                                     * us know the inode and device that the
75                                     * tarball lives, so we can avoid trying
76                                     * to include the tarball into itself */
77} TarBallInfo;
78
79/* A nice enum with all the possible tar file content types */
80enum {
81    REGTYPE = '0',      /* regular file */
82    REGTYPE0 = '\0',    /* regular file (ancient bug compat) */
83    LNKTYPE = '1',      /* hard link */
84    SYMTYPE = '2',      /* symbolic link */
85    CHRTYPE = '3',      /* character special */
86    BLKTYPE = '4',      /* block special */
87    DIRTYPE = '5',      /* directory */
88    FIFOTYPE = '6',     /* FIFO special */
89    CONTTYPE = '7',     /* reserved */
90    GNULONGLINK = 'K',  /* GNU long (>100 chars) link name */
91    GNULONGNAME = 'L',  /* GNU long (>100 chars) file name */
92};
93
94/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
95static void addHardLinkInfo(HardLinkInfo **hlInfoHeadPtr,
96                    struct stat *statbuf,
97                    const char *fileName)
98{
99    /* Note: hlInfoHeadPtr can never be NULL! */
100    HardLinkInfo *hlInfo;
101
102    hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName));
103    hlInfo->next = *hlInfoHeadPtr;
104    *hlInfoHeadPtr = hlInfo;
105    hlInfo->dev = statbuf->st_dev;
106    hlInfo->ino = statbuf->st_ino;
107//  hlInfo->linkCount = statbuf->st_nlink;
108    strcpy(hlInfo->name, fileName);
109}
110
111static void freeHardLinkInfo(HardLinkInfo **hlInfoHeadPtr)
112{
113    HardLinkInfo *hlInfo;
114    HardLinkInfo *hlInfoNext;
115
116    if (hlInfoHeadPtr) {
117        hlInfo = *hlInfoHeadPtr;
118        while (hlInfo) {
119            hlInfoNext = hlInfo->next;
120            free(hlInfo);
121            hlInfo = hlInfoNext;
122        }
123        *hlInfoHeadPtr = NULL;
124    }
125}
126
127/* Might be faster (and bigger) if the dev/ino were stored in numeric order ;) */
128static HardLinkInfo *findHardLinkInfo(HardLinkInfo *hlInfo, struct stat *statbuf)
129{
130    while (hlInfo) {
131        if (statbuf->st_ino == hlInfo->ino
132         && statbuf->st_dev == hlInfo->dev
133        ) {
134            DBG("found hardlink:'%s'", hlInfo->name);
135            break;
136        }
137        hlInfo = hlInfo->next;
138    }
139    return hlInfo;
140}
141
142/* Put an octal string into the specified buffer.
143 * The number is zero padded and possibly null terminated.
144 * Stores low-order bits only if whole value does not fit. */
145static void putOctal(char *cp, int len, off_t value)
146{
147    char tempBuffer[sizeof(off_t)*3 + 1];
148    char *tempString = tempBuffer;
149    int width;
150
151    width = sprintf(tempBuffer, "%0*"OFF_FMT"o", len, value);
152    tempString += (width - len);
153
154    /* If string has leading zeroes, we can drop one */
155    /* and field will have trailing '\0' */
156    /* (increases chances of compat with other tars) */
157    if (tempString[0] == '0')
158        tempString++;
159
160    /* Copy the string to the field */
161    memcpy(cp, tempString, len);
162}
163#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b))
164
165static void chksum_and_xwrite(int fd, struct tar_header_t* hp)
166{
167    /* POSIX says that checksum is done on unsigned bytes
168     * (Sun and HP-UX gets it wrong... more details in
169     * GNU tar source) */
170    const unsigned char *cp;
171    int chksum, size;
172
173    strcpy(hp->magic, "ustar  ");
174
175    /* Calculate and store the checksum (i.e., the sum of all of the bytes of
176     * the header).  The checksum field must be filled with blanks for the
177     * calculation.  The checksum field is formatted differently from the
178     * other fields: it has 6 digits, a null, then a space -- rather than
179     * digits, followed by a null like the other fields... */
180    memset(hp->chksum, ' ', sizeof(hp->chksum));
181    cp = (const unsigned char *) hp;
182    chksum = 0;
183    size = sizeof(*hp);
184    do { chksum += *cp++; } while (--size);
185    putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum);
186
187    /* Now write the header out to disk */
188    xwrite(fd, hp, sizeof(*hp));
189}
190
191#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
192static void writeLongname(int fd, int type, const char *name, int dir)
193{
194    static const struct {
195        char mode[8];             /* 100-107 */
196        char uid[8];              /* 108-115 */
197        char gid[8];              /* 116-123 */
198        char size[12];            /* 124-135 */
199        char mtime[12];           /* 136-147 */
200    } prefilled = {
201        "0000000",
202        "0000000",
203        "0000000",
204        "00000000000",
205        "00000000000",
206    };
207    struct tar_header_t header;
208    int size;
209
210    dir = !!dir; /* normalize: 0/1 */
211    size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */
212    /* + dir: account for possible '/' */
213
214    memset(&header, 0, sizeof(header));
215    strcpy(header.name, "././@LongLink");
216    memcpy(header.mode, prefilled.mode, sizeof(prefilled));
217    PUT_OCTAL(header.size, size);
218    header.typeflag = type;
219    chksum_and_xwrite(fd, &header);
220
221    /* Write filename[/] and pad the block. */
222    /* dir=0: writes 'name<NUL>', pads */
223    /* dir=1: writes 'name', writes '/<NUL>', pads */
224    dir *= 2;
225    xwrite(fd, name, size - dir);
226    xwrite(fd, "/", dir);
227    size = (-size) & (TAR_BLOCK_SIZE-1);
228    memset(&header, 0, size);
229    xwrite(fd, &header, size);
230}
231#endif
232
233/* Write out a tar header for the specified file/directory/whatever */
234static int writeTarHeader(struct TarBallInfo *tbInfo,
235        const char *header_name, const char *fileName, struct stat *statbuf)
236{
237    struct tar_header_t header;
238
239    memset(&header, 0, sizeof(header));
240
241    strncpy(header.name, header_name, sizeof(header.name));
242
243    /* POSIX says to mask mode with 07777. */
244    PUT_OCTAL(header.mode, statbuf->st_mode & 07777);
245    PUT_OCTAL(header.uid, statbuf->st_uid);
246    PUT_OCTAL(header.gid, statbuf->st_gid);
247    memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */
248    PUT_OCTAL(header.mtime, statbuf->st_mtime);
249
250    /* Enter the user and group names */
251    safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname));
252    safe_strncpy(header.gname, get_cached_groupname(statbuf->st_gid), sizeof(header.gname));
253
254    if (tbInfo->hlInfo) {
255        /* This is a hard link */
256        header.typeflag = LNKTYPE;
257        strncpy(header.linkname, tbInfo->hlInfo->name,
258                sizeof(header.linkname));
259#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
260        /* Write out long linkname if needed */
261        if (header.linkname[sizeof(header.linkname)-1])
262            writeLongname(tbInfo->tarFd, GNULONGLINK,
263                    tbInfo->hlInfo->name, 0);
264#endif
265    } else if (S_ISLNK(statbuf->st_mode)) {
266        char *lpath = xmalloc_readlink_or_warn(fileName);
267        if (!lpath)
268            return FALSE;
269        header.typeflag = SYMTYPE;
270        strncpy(header.linkname, lpath, sizeof(header.linkname));
271#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
272        /* Write out long linkname if needed */
273        if (header.linkname[sizeof(header.linkname)-1])
274            writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0);
275#else
276        /* If it is larger than 100 bytes, bail out */
277        if (header.linkname[sizeof(header.linkname)-1]) {
278            free(lpath);
279            bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
280            return FALSE;
281        }
282#endif
283        free(lpath);
284    } else if (S_ISDIR(statbuf->st_mode)) {
285        header.typeflag = DIRTYPE;
286        /* Append '/' only if there is a space for it */
287        if (!header.name[sizeof(header.name)-1])
288            header.name[strlen(header.name)] = '/';
289    } else if (S_ISCHR(statbuf->st_mode)) {
290        header.typeflag = CHRTYPE;
291        PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
292        PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
293    } else if (S_ISBLK(statbuf->st_mode)) {
294        header.typeflag = BLKTYPE;
295        PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
296        PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
297    } else if (S_ISFIFO(statbuf->st_mode)) {
298        header.typeflag = FIFOTYPE;
299    } else if (S_ISREG(statbuf->st_mode)) {
300        if (sizeof(statbuf->st_size) > 4
301         && statbuf->st_size > (off_t)0777777777777LL
302        ) {
303            bb_error_msg_and_die("can't store file '%s' "
304                "of size %"OFF_FMT"u, aborting",
305                fileName, statbuf->st_size);
306        }
307        header.typeflag = REGTYPE;
308        PUT_OCTAL(header.size, statbuf->st_size);
309    } else {
310        bb_error_msg("%s: unknown file type", fileName);
311        return FALSE;
312    }
313
314#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
315    /* Write out long name if needed */
316    /* (we, like GNU tar, output long linkname *before* long name) */
317    if (header.name[sizeof(header.name)-1])
318        writeLongname(tbInfo->tarFd, GNULONGNAME,
319                header_name, S_ISDIR(statbuf->st_mode));
320#endif
321
322    /* Now write the header out to disk */
323    chksum_and_xwrite(tbInfo->tarFd, &header);
324
325    /* Now do the verbose thing (or not) */
326    if (tbInfo->verboseFlag) {
327        FILE *vbFd = stdout;
328
329        /* If archive goes to stdout, verbose goes to stderr */
330        if (tbInfo->tarFd == STDOUT_FILENO)
331            vbFd = stderr;
332        /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */
333        /* We don't have such excesses here: for us "v" == "vv" */
334        /* '/' is probably a GNUism */
335        fprintf(vbFd, "%s%s\n", header_name,
336                S_ISDIR(statbuf->st_mode) ? "/" : "");
337    }
338
339    return TRUE;
340}
341
342#if ENABLE_FEATURE_TAR_FROM
343static int exclude_file(const llist_t *excluded_files, const char *file)
344{
345    while (excluded_files) {
346        if (excluded_files->data[0] == '/') {
347            if (fnmatch(excluded_files->data, file,
348                    FNM_PATHNAME | FNM_LEADING_DIR) == 0)
349                return 1;
350        } else {
351            const char *p;
352
353            for (p = file; p[0] != '\0'; p++) {
354                if ((p == file || p[-1] == '/')
355                 && p[0] != '/'
356                 && fnmatch(excluded_files->data, p,
357                        FNM_PATHNAME | FNM_LEADING_DIR) == 0
358                ) {
359                    return 1;
360                }
361            }
362        }
363        excluded_files = excluded_files->link;
364    }
365
366    return 0;
367}
368#else
369# define exclude_file(excluded_files, file) 0
370#endif
371
372static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf,
373            void *userData, int depth UNUSED_PARAM)
374{
375    struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
376    const char *header_name;
377    int inputFileFd = -1;
378
379    DBG("writeFileToTarball('%s')", fileName);
380
381    /* Strip leading '/' (must be before memorizing hardlink's name) */
382    header_name = fileName;
383    while (header_name[0] == '/') {
384        static smallint warned;
385
386        if (!warned) {
387            bb_error_msg("removing leading '/' from member names");
388            warned = 1;
389        }
390        header_name++;
391    }
392
393    if (header_name[0] == '\0')
394        return TRUE;
395
396    /* It is against the rules to archive a socket */
397    if (S_ISSOCK(statbuf->st_mode)) {
398        bb_error_msg("%s: socket ignored", fileName);
399        return TRUE;
400    }
401
402    /*
403     * Check to see if we are dealing with a hard link.
404     * If so -
405     * Treat the first occurance of a given dev/inode as a file while
406     * treating any additional occurances as hard links.  This is done
407     * by adding the file information to the HardLinkInfo linked list.
408     */
409    tbInfo->hlInfo = NULL;
410    if (!S_ISDIR(statbuf->st_mode) && statbuf->st_nlink > 1) {
411        DBG("'%s': st_nlink > 1", header_name);
412        tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf);
413        if (tbInfo->hlInfo == NULL) {
414            DBG("'%s': addHardLinkInfo", header_name);
415            addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, header_name);
416        }
417    }
418
419    /* It is a bad idea to store the archive we are in the process of creating,
420     * so check the device and inode to be sure that this particular file isn't
421     * the new tarball */
422    if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev
423     && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino
424    ) {
425        bb_error_msg("%s: file is the archive; skipping", fileName);
426        return TRUE;
427    }
428
429    if (exclude_file(tbInfo->excludeList, header_name))
430        return SKIP;
431
432#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
433    if (strlen(header_name) >= NAME_SIZE) {
434        bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
435        return TRUE;
436    }
437#endif
438
439    /* Is this a regular file? */
440    if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
441        /* open the file we want to archive, and make sure all is well */
442        inputFileFd = open_or_warn(fileName, O_RDONLY);
443        if (inputFileFd < 0) {
444            return FALSE;
445        }
446    }
447
448    /* Add an entry to the tarball */
449    if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
450        return FALSE;
451    }
452
453    /* If it was a regular file, write out the body */
454    if (inputFileFd >= 0) {
455        size_t readSize;
456        /* Write the file to the archive. */
457        /* We record size into header first, */
458        /* and then write out file. If file shrinks in between, */
459        /* tar will be corrupted. So we don't allow for that. */
460        /* NB: GNU tar 1.16 warns and pads with zeroes */
461        /* or even seeks back and updates header */
462        bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
463        ////off_t readSize;
464        ////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
465        ////if (readSize != statbuf->st_size && readSize >= 0) {
466        ////    bb_error_msg_and_die("short read from %s, aborting", fileName);
467        ////}
468
469        /* Check that file did not grow in between? */
470        /* if (safe_read(inputFileFd, 1) == 1) warn but continue? */
471
472        close(inputFileFd);
473
474        /* Pad the file up to the tar block size */
475        /* (a few tricks here in the name of code size) */
476        readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1);
477        memset(block_buf, 0, readSize);
478        xwrite(tbInfo->tarFd, block_buf, readSize);
479    }
480
481    return TRUE;
482}
483
484#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
485# if !(ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2)
486#  define vfork_compressor(tar_fd, gzip) vfork_compressor(tar_fd)
487# endif
488/* Don't inline: vfork scares gcc and pessimizes code */
489static void NOINLINE vfork_compressor(int tar_fd, int gzip)
490{
491    pid_t gzipPid;
492# if ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2
493    const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
494# elif ENABLE_FEATURE_SEAMLESS_GZ
495    const char *zip_exec = "gzip";
496# else /* only ENABLE_FEATURE_SEAMLESS_BZ2 */
497    const char *zip_exec = "bzip2";
498# endif
499    // On Linux, vfork never unpauses parent early, although standard
500    // allows for that. Do we want to waste bytes checking for it?
501# define WAIT_FOR_CHILD 0
502    volatile int vfork_exec_errno = 0;
503    struct fd_pair gzipDataPipe;
504# if WAIT_FOR_CHILD
505    struct fd_pair gzipStatusPipe;
506    xpiped_pair(gzipStatusPipe);
507# endif
508    xpiped_pair(gzipDataPipe);
509
510    signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
511
512# if defined(__GNUC__) && __GNUC__
513    /* Avoid vfork clobbering */
514    (void) &zip_exec;
515# endif
516
517    gzipPid = xvfork();
518
519    if (gzipPid == 0) {
520        /* child */
521        /* NB: close _first_, then move fds! */
522        close(gzipDataPipe.wr);
523# if WAIT_FOR_CHILD
524        close(gzipStatusPipe.rd);
525        /* gzipStatusPipe.wr will close only on exec -
526         * parent waits for this close to happen */
527        fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC);
528# endif
529        xmove_fd(gzipDataPipe.rd, 0);
530        xmove_fd(tar_fd, 1);
531        /* exec gzip/bzip2 program/applet */
532        BB_EXECLP(zip_exec, zip_exec, "-f", NULL);
533        vfork_exec_errno = errno;
534        _exit(EXIT_FAILURE);
535    }
536
537    /* parent */
538    xmove_fd(gzipDataPipe.wr, tar_fd);
539    close(gzipDataPipe.rd);
540# if WAIT_FOR_CHILD
541    close(gzipStatusPipe.wr);
542    while (1) {
543        char buf;
544        int n;
545
546        /* Wait until child execs (or fails to) */
547        n = full_read(gzipStatusPipe.rd, &buf, 1);
548        if (n < 0 /* && errno == EAGAIN */)
549            continue;   /* try it again */
550    }
551    close(gzipStatusPipe.rd);
552# endif
553    if (vfork_exec_errno) {
554        errno = vfork_exec_errno;
555        bb_perror_msg_and_die("can't execute '%s'", zip_exec);
556    }
557}
558#endif /* ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 */
559
560
561/* gcc 4.2.1 inlines it, making code bigger */
562static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
563    int dereferenceFlag, const llist_t *include,
564    const llist_t *exclude, int gzip)
565{
566    int errorFlag = FALSE;
567    struct TarBallInfo tbInfo;
568
569    tbInfo.hlInfoHead = NULL;
570    tbInfo.tarFd = tar_fd;
571    tbInfo.verboseFlag = verboseFlag;
572
573    /* Store the stat info for the tarball's file, so
574     * can avoid including the tarball into itself....  */
575    xfstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf, "can't stat tar file");
576
577#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
578    if (gzip)
579        vfork_compressor(tbInfo.tarFd, gzip);
580#endif
581
582    tbInfo.excludeList = exclude;
583
584    /* Read the directory/files and iterate over them one at a time */
585    while (include) {
586        if (!recursive_action(include->data, ACTION_RECURSE |
587                (dereferenceFlag ? ACTION_FOLLOWLINKS : 0),
588                writeFileToTarball, writeFileToTarball, &tbInfo, 0)
589        ) {
590            errorFlag = TRUE;
591        }
592        include = include->link;
593    }
594    /* Write two empty blocks to the end of the archive */
595    memset(block_buf, 0, 2*TAR_BLOCK_SIZE);
596    xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE);
597
598    /* To be pedantically correct, we would check if the tarball
599     * is smaller than 20 tar blocks, and pad it if it was smaller,
600     * but that isn't necessary for GNU tar interoperability, and
601     * so is considered a waste of space */
602
603    /* Close so the child process (if any) will exit */
604    close(tbInfo.tarFd);
605
606    /* Hang up the tools, close up shop, head home */
607    if (ENABLE_FEATURE_CLEAN_UP)
608        freeHardLinkInfo(&tbInfo.hlInfoHead);
609
610    if (errorFlag)
611        bb_error_msg("error exit delayed from previous errors");
612
613#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
614    if (gzip) {
615        int status;
616        if (safe_waitpid(-1, &status, 0) == -1)
617            bb_perror_msg("waitpid");
618        else if (!WIFEXITED(status) || WEXITSTATUS(status))
619            /* gzip was killed or has exited with nonzero! */
620            errorFlag = TRUE;
621    }
622#endif
623    return errorFlag;
624}
625#else
626int writeTarFile(int tar_fd, int verboseFlag,
627    int dereferenceFlag, const llist_t *include,
628    const llist_t *exclude, int gzip);
629#endif /* FEATURE_TAR_CREATE */
630
631#if ENABLE_FEATURE_TAR_FROM
632static llist_t *append_file_list_to_list(llist_t *list)
633{
634    FILE *src_stream;
635    char *line;
636    llist_t *newlist = NULL;
637
638    while (list) {
639        src_stream = xfopen_for_read(llist_pop(&list));
640        while ((line = xmalloc_fgetline(src_stream)) != NULL) {
641            /* kill trailing '/' unless the string is just "/" */
642            char *cp = last_char_is(line, '/');
643            if (cp > line)
644                *cp = '\0';
645            llist_add_to(&newlist, line);
646        }
647        fclose(src_stream);
648    }
649    return newlist;
650}
651#else
652# define append_file_list_to_list(x) 0
653#endif
654
655#if ENABLE_FEATURE_SEAMLESS_Z
656static char FAST_FUNC get_header_tar_Z(archive_handle_t *archive_handle)
657{
658    /* Can't lseek over pipes */
659    archive_handle->seek = seek_by_read;
660
661    /* do the decompression, and cleanup */
662    if (xread_char(archive_handle->src_fd) != 0x1f
663     || xread_char(archive_handle->src_fd) != 0x9d
664    ) {
665        bb_error_msg_and_die("invalid magic");
666    }
667
668    open_transformer(archive_handle->src_fd, unpack_Z_stream, "uncompress");
669    archive_handle->offset = 0;
670    while (get_header_tar(archive_handle) == EXIT_SUCCESS)
671        continue;
672
673    /* Can only do one file at a time */
674    return EXIT_FAILURE;
675}
676#else
677# define get_header_tar_Z NULL
678#endif
679
680#ifdef CHECK_FOR_CHILD_EXITCODE
681/* Looks like it isn't needed - tar detects malformed (truncated)
682 * archive if e.g. bunzip2 fails */
683static int child_error;
684
685static void handle_SIGCHLD(int status)
686{
687    /* Actually, 'status' is a signo. We reuse it for other needs */
688
689    /* Wait for any child without blocking */
690    if (wait_any_nohang(&status) < 0)
691        /* wait failed?! I'm confused... */
692        return;
693
694    if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
695        /* child exited with 0 */
696        return;
697    /* Cannot happen?
698    if (!WIFSIGNALED(status) && !WIFEXITED(status)) return; */
699    child_error = 1;
700}
701#endif
702
703//usage:#define tar_trivial_usage
704//usage:       "-[" IF_FEATURE_TAR_CREATE("c") "xt" IF_FEATURE_SEAMLESS_GZ("z")
705//usage:    IF_FEATURE_SEAMLESS_BZ2("j") IF_FEATURE_SEAMLESS_LZMA("a")
706//usage:    IF_FEATURE_SEAMLESS_Z("Z") IF_FEATURE_TAR_NOPRESERVE_TIME("m") "vO] "
707//usage:    IF_FEATURE_TAR_FROM("[-X FILE] ")
708//usage:       "[-f TARFILE] [-C DIR] [FILE]..."
709//usage:#define tar_full_usage "\n\n"
710//usage:    IF_FEATURE_TAR_CREATE("Create, extract, ")
711//usage:    IF_NOT_FEATURE_TAR_CREATE("Extract ")
712//usage:    "or list files from a tar file\n"
713//usage:     "\nOperation:"
714//usage:    IF_FEATURE_TAR_CREATE(
715//usage:     "\n    c   Create"
716//usage:    )
717//usage:     "\n    x   Extract"
718//usage:     "\n    t   List"
719//usage:     "\nOptions:"
720//usage:     "\n    f   Name of TARFILE ('-' for stdin/out)"
721//usage:     "\n    C   Change to DIR before operation"
722//usage:     "\n    v   Verbose"
723//usage:    IF_FEATURE_SEAMLESS_GZ(
724//usage:     "\n    z   (De)compress using gzip"
725//usage:    )
726//usage:    IF_FEATURE_SEAMLESS_BZ2(
727//usage:     "\n    j   (De)compress using bzip2"
728//usage:    )
729//usage:    IF_FEATURE_SEAMLESS_LZMA(
730//usage:     "\n    a   (De)compress using lzma"
731//usage:    )
732//usage:    IF_FEATURE_SEAMLESS_Z(
733//usage:     "\n    Z   (De)compress using compress"
734//usage:    )
735//usage:     "\n    O   Extract to stdout"
736//usage:    IF_FEATURE_TAR_CREATE(
737//usage:     "\n    h   Follow symlinks"
738//usage:    )
739//usage:    IF_FEATURE_TAR_NOPRESERVE_TIME(
740//usage:     "\n    m   Don't restore mtime"
741//usage:    )
742//usage:    IF_FEATURE_TAR_FROM(
743//usage:    IF_FEATURE_TAR_LONG_OPTIONS(
744//usage:     "\n    exclude File to exclude"
745//usage:    )
746//usage:     "\n    X   File with names to exclude"
747//usage:     "\n    T   File with names to include"
748//usage:    )
749//usage:
750//usage:#define tar_example_usage
751//usage:       "$ zcat /tmp/tarball.tar.gz | tar -xf -\n"
752//usage:       "$ tar -cf /tmp/tarball.tar /usr/local\n"
753
754// Supported but aren't in --help:
755//  o   no-same-owner
756//  p   same-permissions
757//  k   keep-old
758//  numeric-owner
759//  no-same-permissions
760//  overwrite
761//IF_FEATURE_TAR_TO_COMMAND(
762//  to-command
763//)
764
765enum {
766    OPTBIT_KEEP_OLD = 8,
767    IF_FEATURE_TAR_CREATE(   OPTBIT_CREATE      ,)
768    IF_FEATURE_TAR_CREATE(   OPTBIT_DEREFERENCE ,)
769    IF_FEATURE_SEAMLESS_BZ2( OPTBIT_BZIP2       ,)
770    IF_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA        ,)
771    IF_FEATURE_TAR_FROM(     OPTBIT_INCLUDE_FROM,)
772    IF_FEATURE_TAR_FROM(     OPTBIT_EXCLUDE_FROM,)
773    IF_FEATURE_SEAMLESS_GZ(  OPTBIT_GZIP        ,)
774    IF_FEATURE_SEAMLESS_Z(   OPTBIT_COMPRESS    ,) // 16th bit
775    IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,)
776#if ENABLE_FEATURE_TAR_LONG_OPTIONS
777    IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND   ,)
778    OPTBIT_NUMERIC_OWNER,
779    OPTBIT_NOPRESERVE_PERM,
780    OPTBIT_OVERWRITE,
781#endif
782    OPT_TEST         = 1 << 0, // t
783    OPT_EXTRACT      = 1 << 1, // x
784    OPT_BASEDIR      = 1 << 2, // C
785    OPT_TARNAME      = 1 << 3, // f
786    OPT_2STDOUT      = 1 << 4, // O
787    OPT_NOPRESERVE_OWNER = 1 << 5, // o == no-same-owner
788    OPT_P            = 1 << 6, // p
789    OPT_VERBOSE      = 1 << 7, // v
790    OPT_KEEP_OLD     = 1 << 8, // k
791    OPT_CREATE       = IF_FEATURE_TAR_CREATE(   (1 << OPTBIT_CREATE      )) + 0, // c
792    OPT_DEREFERENCE  = IF_FEATURE_TAR_CREATE(   (1 << OPTBIT_DEREFERENCE )) + 0, // h
793    OPT_BZIP2        = IF_FEATURE_SEAMLESS_BZ2( (1 << OPTBIT_BZIP2       )) + 0, // j
794    OPT_LZMA         = IF_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA        )) + 0, // a
795    OPT_INCLUDE_FROM = IF_FEATURE_TAR_FROM(     (1 << OPTBIT_INCLUDE_FROM)) + 0, // T
796    OPT_EXCLUDE_FROM = IF_FEATURE_TAR_FROM(     (1 << OPTBIT_EXCLUDE_FROM)) + 0, // X
797    OPT_GZIP         = IF_FEATURE_SEAMLESS_GZ(  (1 << OPTBIT_GZIP        )) + 0, // z
798    OPT_COMPRESS     = IF_FEATURE_SEAMLESS_Z(   (1 << OPTBIT_COMPRESS    )) + 0, // Z
799    OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m
800    OPT_2COMMAND        = IF_FEATURE_TAR_TO_COMMAND(  (1 << OPTBIT_2COMMAND       )) + 0, // to-command
801    OPT_NUMERIC_OWNER   = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER  )) + 0, // numeric-owner
802    OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions
803    OPT_OVERWRITE       = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE      )) + 0, // overwrite
804};
805#if ENABLE_FEATURE_TAR_LONG_OPTIONS
806static const char tar_longopts[] ALIGN1 =
807    "list\0"                No_argument       "t"
808    "extract\0"             No_argument       "x"
809    "directory\0"           Required_argument "C"
810    "file\0"                Required_argument "f"
811    "to-stdout\0"           No_argument       "O"
812    /* do not restore owner */
813    /* Note: GNU tar handles 'o' as no-same-owner only on extract,
814     * on create, 'o' is --old-archive. We do not support --old-archive. */
815    "no-same-owner\0"       No_argument       "o"
816    "same-permissions\0"    No_argument       "p"
817    "verbose\0"             No_argument       "v"
818    "keep-old\0"            No_argument       "k"
819# if ENABLE_FEATURE_TAR_CREATE
820    "create\0"              No_argument       "c"
821    "dereference\0"         No_argument       "h"
822# endif
823# if ENABLE_FEATURE_SEAMLESS_BZ2
824    "bzip2\0"               No_argument       "j"
825# endif
826# if ENABLE_FEATURE_SEAMLESS_LZMA
827    "lzma\0"                No_argument       "a"
828# endif
829# if ENABLE_FEATURE_TAR_FROM
830    "files-from\0"          Required_argument "T"
831    "exclude-from\0"        Required_argument "X"
832# endif
833# if ENABLE_FEATURE_SEAMLESS_GZ
834    "gzip\0"                No_argument       "z"
835# endif
836# if ENABLE_FEATURE_SEAMLESS_Z
837    "compress\0"            No_argument       "Z"
838# endif
839# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME
840    "touch\0"               No_argument       "m"
841# endif
842# if ENABLE_FEATURE_TAR_TO_COMMAND
843    "to-command\0"      Required_argument "\xfb"
844# endif
845    /* use numeric uid/gid from tar header, not textual */
846    "numeric-owner\0"       No_argument       "\xfc"
847    /* do not restore mode */
848    "no-same-permissions\0" No_argument       "\xfd"
849    /* on unpack, open with O_TRUNC and !O_EXCL */
850    "overwrite\0"           No_argument       "\xfe"
851    /* --exclude takes next bit position in option mask, */
852    /* therefore we have to put it _after_ --no-same-permissions */
853# if ENABLE_FEATURE_TAR_FROM
854    "exclude\0"             Required_argument "\xff"
855# endif
856    ;
857#endif
858
859int tar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
860int tar_main(int argc UNUSED_PARAM, char **argv)
861{
862    char FAST_FUNC (*get_header_ptr)(archive_handle_t *) = get_header_tar;
863    archive_handle_t *tar_handle;
864    char *base_dir = NULL;
865    const char *tar_filename = "-";
866    unsigned opt;
867    int verboseFlag = 0;
868#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
869    llist_t *excludes = NULL;
870#endif
871
872    /* Initialise default values */
873    tar_handle = init_handle();
874    tar_handle->ah_flags = ARCHIVE_CREATE_LEADING_DIRS
875                         | ARCHIVE_RESTORE_DATE
876                         | ARCHIVE_UNLINK_OLD;
877
878    /* Apparently only root's tar preserves perms (see bug 3844) */
879    if (getuid() != 0)
880        tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM;
881
882    /* Prepend '-' to the first argument if required */
883    opt_complementary = "--:" // first arg is options
884        "tt:vv:" // count -t,-v
885        "?:" // bail out with usage instead of error return
886        "X::T::" // cumulative lists
887#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
888        "\xff::" // cumulative lists for --exclude
889#endif
890        IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
891        IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
892        IF_NOT_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive
893#if ENABLE_FEATURE_TAR_LONG_OPTIONS
894    applet_long_options = tar_longopts;
895#endif
896#if ENABLE_DESKTOP
897    if (argv[1] && argv[1][0] != '-') {
898        /* Compat:
899         * 1st argument without dash handles options with parameters
900         * differently from dashed one: it takes *next argv[i]*
901         * as paramenter even if there are more chars in 1st argument:
902         *  "tar fx TARFILE" - "x" is not taken as f's param
903         *  but is interpreted as -x option
904         *  "tar -xf TARFILE" - dashed equivalent of the above
905         *  "tar -fx ..." - "x" is taken as f's param
906         * getopt32 wouldn't handle 1st command correctly.
907         * Unfortunately, people do use such commands.
908         * We massage argv[1] to work around it by moving 'f'
909         * to the end of the string.
910         * More contrived "tar fCx TARFILE DIR" still fails,
911         * but such commands are much less likely to be used.
912         */
913        char *f = strchr(argv[1], 'f');
914        if (f) {
915            while (f[1] != '\0') {
916                *f = f[1];
917                f++;
918            }
919            *f = 'f';
920        }
921    }
922#endif
923    opt = getopt32(argv,
924        "txC:f:Oopvk"
925        IF_FEATURE_TAR_CREATE(   "ch"  )
926        IF_FEATURE_SEAMLESS_BZ2( "j"   )
927        IF_FEATURE_SEAMLESS_LZMA("a"   )
928        IF_FEATURE_TAR_FROM(     "T:X:")
929        IF_FEATURE_SEAMLESS_GZ(  "z"   )
930        IF_FEATURE_SEAMLESS_Z(   "Z"   )
931        IF_FEATURE_TAR_NOPRESERVE_TIME("m")
932        , &base_dir // -C dir
933        , &tar_filename // -f filename
934        IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T
935        IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X
936        IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command
937#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
938        , &excludes // --exclude
939#endif
940        , &verboseFlag // combined count for -t and -v
941        , &verboseFlag // combined count for -t and -v
942        );
943    //bb_error_msg("opt:%08x", opt);
944    argv += optind;
945
946    if (verboseFlag) tar_handle->action_header = header_verbose_list;
947    if (verboseFlag == 1) tar_handle->action_header = header_list;
948
949    if (opt & OPT_EXTRACT)
950        tar_handle->action_data = data_extract_all;
951
952    if (opt & OPT_2STDOUT)
953        tar_handle->action_data = data_extract_to_stdout;
954
955    if (opt & OPT_2COMMAND) {
956        putenv((char*)"TAR_FILETYPE=f");
957        signal(SIGPIPE, SIG_IGN);
958        tar_handle->action_data = data_extract_to_command;
959    }
960
961    if (opt & OPT_KEEP_OLD)
962        tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD;
963
964    if (opt & OPT_NUMERIC_OWNER)
965        tar_handle->ah_flags |= ARCHIVE_NUMERIC_OWNER;
966
967    if (opt & OPT_NOPRESERVE_OWNER)
968        tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_OWNER;
969
970    if (opt & OPT_NOPRESERVE_PERM)
971        tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM;
972
973    if (opt & OPT_OVERWRITE) {
974        tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD;
975        tar_handle->ah_flags |= ARCHIVE_O_TRUNC;
976    }
977
978    if (opt & OPT_GZIP)
979        get_header_ptr = get_header_tar_gz;
980
981    if (opt & OPT_BZIP2)
982        get_header_ptr = get_header_tar_bz2;
983
984    if (opt & OPT_LZMA)
985        get_header_ptr = get_header_tar_lzma;
986
987    if (opt & OPT_COMPRESS)
988        get_header_ptr = get_header_tar_Z;
989
990    if (opt & OPT_NOPRESERVE_TIME)
991        tar_handle->ah_flags &= ~ARCHIVE_RESTORE_DATE;
992
993#if ENABLE_FEATURE_TAR_FROM
994    tar_handle->reject = append_file_list_to_list(tar_handle->reject);
995# if ENABLE_FEATURE_TAR_LONG_OPTIONS
996    /* Append excludes to reject */
997    while (excludes) {
998        llist_t *next = excludes->link;
999        excludes->link = tar_handle->reject;
1000        tar_handle->reject = excludes;
1001        excludes = next;
1002    }
1003# endif
1004    tar_handle->accept = append_file_list_to_list(tar_handle->accept);
1005#endif
1006
1007    /* Setup an array of filenames to work with */
1008    /* TODO: This is the same as in ar, make a separate function? */
1009    while (*argv) {
1010        /* kill trailing '/' unless the string is just "/" */
1011        char *cp = last_char_is(*argv, '/');
1012        if (cp > *argv)
1013            *cp = '\0';
1014        llist_add_to_end(&tar_handle->accept, *argv);
1015        argv++;
1016    }
1017
1018    if (tar_handle->accept || tar_handle->reject)
1019        tar_handle->filter = filter_accept_reject_list;
1020
1021    /* Open the tar file */
1022    {
1023        int tar_fd = STDIN_FILENO;
1024        int flags = O_RDONLY;
1025
1026        if (opt & OPT_CREATE) {
1027            /* Make sure there is at least one file to tar up */
1028            if (tar_handle->accept == NULL)
1029                bb_error_msg_and_die("empty archive");
1030
1031            tar_fd = STDOUT_FILENO;
1032            /* Mimicking GNU tar 1.15.1: */
1033            flags = O_WRONLY | O_CREAT | O_TRUNC;
1034        }
1035
1036        if (LONE_DASH(tar_filename)) {
1037            tar_handle->src_fd = tar_fd;
1038            tar_handle->seek = seek_by_read;
1039        } else {
1040            if (ENABLE_FEATURE_TAR_AUTODETECT
1041             && flags == O_RDONLY
1042             && get_header_ptr == get_header_tar
1043            ) {
1044                tar_handle->src_fd = open_zipped(tar_filename);
1045                if (tar_handle->src_fd < 0)
1046                    bb_perror_msg_and_die("can't open '%s'", tar_filename);
1047            } else {
1048                tar_handle->src_fd = xopen(tar_filename, flags);
1049            }
1050        }
1051    }
1052
1053    if (base_dir)
1054        xchdir(base_dir);
1055
1056#ifdef CHECK_FOR_CHILD_EXITCODE
1057    /* We need to know whether child (gzip/bzip/etc) exits abnormally */
1058    signal(SIGCHLD, handle_SIGCHLD);
1059#endif
1060
1061    /* Create an archive */
1062    if (opt & OPT_CREATE) {
1063#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
1064        int zipMode = 0;
1065        if (ENABLE_FEATURE_SEAMLESS_GZ && (opt & OPT_GZIP))
1066            zipMode = 1;
1067        if (ENABLE_FEATURE_SEAMLESS_BZ2 && (opt & OPT_BZIP2))
1068            zipMode = 2;
1069#endif
1070        /* NB: writeTarFile() closes tar_handle->src_fd */
1071        return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE,
1072                tar_handle->accept,
1073                tar_handle->reject, zipMode);
1074    }
1075
1076    while (get_header_ptr(tar_handle) == EXIT_SUCCESS)
1077        continue;
1078
1079    /* Check that every file that should have been extracted was */
1080    while (tar_handle->accept) {
1081        if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
1082         && !find_list_entry(tar_handle->passed, tar_handle->accept->data)
1083        ) {
1084            bb_error_msg_and_die("%s: not found in archive",
1085                tar_handle->accept->data);
1086        }
1087        tar_handle->accept = tar_handle->accept->link;
1088    }
1089    if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */)
1090        close(tar_handle->src_fd);
1091
1092    return EXIT_SUCCESS;
1093}
Note: See TracBrowser for help on using the repository browser.