source: MondoRescue/branches/stable/mindi-busybox/archival/tar.c @ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 25.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 *  Glenn McGrath <bug1@iinet.net.au>
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 the 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 tarball for details.
24 */
25
26#include <fcntl.h>
27#include <getopt.h>
28#include <search.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <fnmatch.h>
33#include <string.h>
34#include <errno.h>
35#include <signal.h>
36#include <sys/wait.h>
37#include <sys/socket.h>
38#include <sys/sysmacros.h>     /* major() and minor() */
39#include "unarchive.h"
40#include "busybox.h"
41
42#ifdef CONFIG_FEATURE_TAR_CREATE
43
44/* Tar file constants  */
45
46#define TAR_BLOCK_SIZE      512
47
48/* POSIX tar Header Block, from POSIX 1003.1-1990  */
49#define NAME_SIZE           100
50struct TarHeader {      /* byte offset */
51    char name[NAME_SIZE];   /*   0-99 */
52    char mode[8];       /* 100-107 */
53    char uid[8];        /* 108-115 */
54    char gid[8];        /* 116-123 */
55    char size[12];      /* 124-135 */
56    char mtime[12];     /* 136-147 */
57    char chksum[8];     /* 148-155 */
58    char typeflag;      /* 156-156 */
59    char linkname[NAME_SIZE];   /* 157-256 */
60    char magic[6];      /* 257-262 */
61    char version[2];    /* 263-264 */
62    char uname[32];     /* 265-296 */
63    char gname[32];     /* 297-328 */
64    char devmajor[8];   /* 329-336 */
65    char devminor[8];   /* 337-344 */
66    char prefix[155];   /* 345-499 */
67    char padding[12];   /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */
68};
69typedef struct TarHeader TarHeader;
70
71/*
72** writeTarFile(),  writeFileToTarball(), and writeTarHeader() are
73** the only functions that deal with the HardLinkInfo structure.
74** Even these functions use the xxxHardLinkInfo() functions.
75*/
76typedef struct HardLinkInfo HardLinkInfo;
77struct HardLinkInfo {
78    HardLinkInfo *next; /* Next entry in list */
79    dev_t dev;          /* Device number */
80    ino_t ino;          /* Inode number */
81    short linkCount;    /* (Hard) Link Count */
82    char name[1];       /* Start of filename (must be last) */
83};
84
85/* Some info to be carried along when creating a new tarball */
86struct TarBallInfo {
87    char *fileName;         /* File name of the tarball */
88    int tarFd;              /* Open-for-write file descriptor
89                               for the tarball */
90    struct stat statBuf;    /* Stat info for the tarball, letting
91                               us know the inode and device that the
92                               tarball lives, so we can avoid trying
93                               to include the tarball into itself */
94    int verboseFlag;        /* Whether to print extra stuff or not */
95    const llist_t *excludeList; /* List of files to not include */
96    HardLinkInfo *hlInfoHead;   /* Hard Link Tracking Information */
97    HardLinkInfo *hlInfo;   /* Hard Link Info for the current file */
98};
99typedef struct TarBallInfo TarBallInfo;
100
101/* A nice enum with all the possible tar file content types */
102enum TarFileType {
103    REGTYPE = '0',      /* regular file */
104    REGTYPE0 = '\0',    /* regular file (ancient bug compat) */
105    LNKTYPE = '1',      /* hard link */
106    SYMTYPE = '2',      /* symbolic link */
107    CHRTYPE = '3',      /* character special */
108    BLKTYPE = '4',      /* block special */
109    DIRTYPE = '5',      /* directory */
110    FIFOTYPE = '6',     /* FIFO special */
111    CONTTYPE = '7',     /* reserved */
112    GNULONGLINK = 'K',  /* GNU long (>100 chars) link name */
113    GNULONGNAME = 'L',  /* GNU long (>100 chars) file name */
114};
115typedef enum TarFileType TarFileType;
116
117/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
118static inline void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr,
119                    struct stat *statbuf,
120                    const char *name)
121{
122    /* Note: hlInfoHeadPtr can never be NULL! */
123    HardLinkInfo *hlInfo;
124
125    hlInfo = (HardLinkInfo *) xmalloc(sizeof(HardLinkInfo) + strlen(name));
126    hlInfo->next = *hlInfoHeadPtr;
127    *hlInfoHeadPtr = hlInfo;
128    hlInfo->dev = statbuf->st_dev;
129    hlInfo->ino = statbuf->st_ino;
130    hlInfo->linkCount = statbuf->st_nlink;
131    strcpy(hlInfo->name, name);
132}
133
134static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr)
135{
136    HardLinkInfo *hlInfo = NULL;
137    HardLinkInfo *hlInfoNext = NULL;
138
139    if (hlInfoHeadPtr) {
140        hlInfo = *hlInfoHeadPtr;
141        while (hlInfo) {
142            hlInfoNext = hlInfo->next;
143            free(hlInfo);
144            hlInfo = hlInfoNext;
145        }
146        *hlInfoHeadPtr = NULL;
147    }
148    return;
149}
150
151/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
152static inline HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf)
153{
154    while (hlInfo) {
155        if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev))
156            break;
157        hlInfo = hlInfo->next;
158    }
159    return (hlInfo);
160}
161
162/* Put an octal string into the specified buffer.
163 * The number is zero and space padded and possibly null padded.
164 * Returns TRUE if successful.  */
165static int putOctal(char *cp, int len, long value)
166{
167    int tempLength;
168    char tempBuffer[32];
169    char *tempString = tempBuffer;
170
171    /* Create a string of the specified length with an initial space,
172     * leading zeroes and the octal number, and a trailing null.  */
173    sprintf(tempString, "%0*lo", len - 1, value);
174
175    /* If the string is too large, suppress the leading space.  */
176    tempLength = strlen(tempString) + 1;
177    if (tempLength > len) {
178        tempLength--;
179        tempString++;
180    }
181
182    /* If the string is still too large, suppress the trailing null.  */
183    if (tempLength > len)
184        tempLength--;
185
186    /* If the string is still too large, fail.  */
187    if (tempLength > len)
188        return FALSE;
189
190    /* Copy the string to the field.  */
191    memcpy(cp, tempString, len);
192
193    return TRUE;
194}
195
196/* Write out a tar header for the specified file/directory/whatever */
197static inline int writeTarHeader(struct TarBallInfo *tbInfo,
198        const char *header_name, const char *real_name, struct stat *statbuf)
199{
200    long chksum = 0;
201    struct TarHeader header;
202    const unsigned char *cp = (const unsigned char *) &header;
203    ssize_t size = sizeof(struct TarHeader);
204
205    memset(&header, 0, size);
206
207    safe_strncpy(header.name, header_name, sizeof(header.name));
208
209    putOctal(header.mode, sizeof(header.mode), statbuf->st_mode);
210    putOctal(header.uid, sizeof(header.uid), statbuf->st_uid);
211    putOctal(header.gid, sizeof(header.gid), statbuf->st_gid);
212    putOctal(header.size, sizeof(header.size), 0);  /* Regular file size is handled later */
213    putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime);
214    strcpy(header.magic, "ustar  ");
215
216    /* Enter the user and group names (default to root if it fails) */
217    if (bb_getpwuid(header.uname, statbuf->st_uid, sizeof(header.uname)) == NULL)
218        strcpy(header.uname, "root");
219    if (bb_getgrgid(header.gname, statbuf->st_gid, sizeof(header.gname)) == NULL)
220        strcpy(header.gname, "root");
221
222    if (tbInfo->hlInfo) {
223        /* This is a hard link */
224        header.typeflag = LNKTYPE;
225        strncpy(header.linkname, tbInfo->hlInfo->name,
226                sizeof(header.linkname));
227    } else if (S_ISLNK(statbuf->st_mode)) {
228        char *lpath = xreadlink(real_name);
229
230        if (!lpath)     /* Already printed err msg inside xreadlink() */
231            return (FALSE);
232        header.typeflag = SYMTYPE;
233        strncpy(header.linkname, lpath, sizeof(header.linkname));
234        free(lpath);
235    } else if (S_ISDIR(statbuf->st_mode)) {
236        header.typeflag = DIRTYPE;
237        strncat(header.name, "/", sizeof(header.name));
238    } else if (S_ISCHR(statbuf->st_mode)) {
239        header.typeflag = CHRTYPE;
240        putOctal(header.devmajor, sizeof(header.devmajor),
241                 major(statbuf->st_rdev));
242        putOctal(header.devminor, sizeof(header.devminor),
243                 minor(statbuf->st_rdev));
244    } else if (S_ISBLK(statbuf->st_mode)) {
245        header.typeflag = BLKTYPE;
246        putOctal(header.devmajor, sizeof(header.devmajor),
247                 major(statbuf->st_rdev));
248        putOctal(header.devminor, sizeof(header.devminor),
249                 minor(statbuf->st_rdev));
250    } else if (S_ISFIFO(statbuf->st_mode)) {
251        header.typeflag = FIFOTYPE;
252    } else if (S_ISREG(statbuf->st_mode)) {
253        header.typeflag = REGTYPE;
254        putOctal(header.size, sizeof(header.size), statbuf->st_size);
255    } else {
256        bb_error_msg("%s: Unknown file type", real_name);
257        return (FALSE);
258    }
259
260    /* Calculate and store the checksum (i.e., the sum of all of the bytes of
261     * the header).  The checksum field must be filled with blanks for the
262     * calculation.  The checksum field is formatted differently from the
263     * other fields: it has [6] digits, a null, then a space -- rather than
264     * digits, followed by a null like the other fields... */
265    memset(header.chksum, ' ', sizeof(header.chksum));
266    cp = (const unsigned char *) &header;
267    while (size-- > 0)
268        chksum += *cp++;
269    putOctal(header.chksum, 7, chksum);
270
271    /* Now write the header out to disk */
272    if ((size =
273         bb_full_write(tbInfo->tarFd, (char *) &header,
274                    sizeof(struct TarHeader))) < 0) {
275        bb_error_msg(bb_msg_io_error, real_name);
276        return (FALSE);
277    }
278    /* Pad the header up to the tar block size */
279    for (; size < TAR_BLOCK_SIZE; size++) {
280        write(tbInfo->tarFd, "\0", 1);
281    }
282    /* Now do the verbose thing (or not) */
283
284    if (tbInfo->verboseFlag) {
285        FILE *vbFd = stdout;
286
287        if (tbInfo->tarFd == STDOUT_FILENO) /* If the archive goes to stdout, verbose to stderr */
288            vbFd = stderr;
289
290        fprintf(vbFd, "%s\n", header.name);
291    }
292
293    return (TRUE);
294}
295
296# ifdef CONFIG_FEATURE_TAR_FROM
297static inline int exclude_file(const llist_t *excluded_files, const char *file)
298{
299    while (excluded_files) {
300        if (excluded_files->data[0] == '/') {
301            if (fnmatch(excluded_files->data, file,
302                        FNM_PATHNAME | FNM_LEADING_DIR) == 0)
303                return 1;
304        } else {
305            const char *p;
306
307            for (p = file; p[0] != '\0'; p++) {
308                if ((p == file || p[-1] == '/') && p[0] != '/' &&
309                    fnmatch(excluded_files->data, p,
310                            FNM_PATHNAME | FNM_LEADING_DIR) == 0)
311                    return 1;
312            }
313        }
314        excluded_files = excluded_files->link;
315    }
316
317    return 0;
318}
319# else
320#define exclude_file(excluded_files, file) 0
321# endif
322
323static int writeFileToTarball(const char *fileName, struct stat *statbuf,
324                              void *userData)
325{
326    struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
327    const char *header_name;
328    int inputFileFd = -1;
329
330    /*
331       ** Check to see if we are dealing with a hard link.
332       ** If so -
333       ** Treat the first occurance of a given dev/inode as a file while
334       ** treating any additional occurances as hard links.  This is done
335       ** by adding the file information to the HardLinkInfo linked list.
336     */
337    tbInfo->hlInfo = NULL;
338    if (statbuf->st_nlink > 1) {
339        tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf);
340        if (tbInfo->hlInfo == NULL)
341            addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName);
342    }
343
344    /* It is against the rules to archive a socket */
345    if (S_ISSOCK(statbuf->st_mode)) {
346        bb_error_msg("%s: socket ignored", fileName);
347        return (TRUE);
348    }
349
350    /* It is a bad idea to store the archive we are in the process of creating,
351     * so check the device and inode to be sure that this particular file isn't
352     * the new tarball */
353    if (tbInfo->statBuf.st_dev == statbuf->st_dev &&
354        tbInfo->statBuf.st_ino == statbuf->st_ino) {
355        bb_error_msg("%s: file is the archive; skipping", fileName);
356        return (TRUE);
357    }
358
359    header_name = fileName;
360    while (header_name[0] == '/') {
361        static int alreadyWarned = FALSE;
362
363        if (alreadyWarned == FALSE) {
364            bb_error_msg("Removing leading '/' from member names");
365            alreadyWarned = TRUE;
366        }
367        header_name++;
368    }
369
370    if (strlen(fileName) >= NAME_SIZE) {
371        bb_error_msg(bb_msg_name_longer_than_foo, NAME_SIZE);
372        return (TRUE);
373    }
374
375    if (header_name[0] == '\0')
376        return TRUE;
377
378    if (ENABLE_FEATURE_TAR_FROM &&
379            exclude_file(tbInfo->excludeList, header_name)) {
380        return SKIP;
381    }
382
383    /* Is this a regular file? */
384    if ((tbInfo->hlInfo == NULL) && (S_ISREG(statbuf->st_mode))) {
385
386        /* open the file we want to archive, and make sure all is well */
387        if ((inputFileFd = open(fileName, O_RDONLY)) < 0) {
388            bb_perror_msg("%s: Cannot open", fileName);
389            return (FALSE);
390        }
391    }
392
393    /* Add an entry to the tarball */
394    if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
395        return (FALSE);
396    }
397
398    /* If it was a regular file, write out the body */
399    if (inputFileFd >= 0 ) {
400        ssize_t readSize = 0;
401
402        /* write the file to the archive */
403        readSize = bb_copyfd_eof(inputFileFd, tbInfo->tarFd);
404        close(inputFileFd);
405
406        /* Pad the file up to the tar block size */
407        for (; (readSize % TAR_BLOCK_SIZE) != 0; readSize++)
408            write(tbInfo->tarFd, "\0", 1);
409    }
410
411    return (TRUE);
412}
413
414static inline int writeTarFile(const int tar_fd, const int verboseFlag,
415    const unsigned long dereferenceFlag, const llist_t *include,
416    const llist_t *exclude, const int gzip)
417{
418    pid_t gzipPid = 0;
419
420    int errorFlag = FALSE;
421    ssize_t size;
422    struct TarBallInfo tbInfo;
423
424    tbInfo.hlInfoHead = NULL;
425
426    fchmod(tar_fd, 0644);
427    tbInfo.tarFd = tar_fd;
428    tbInfo.verboseFlag = verboseFlag;
429
430    /* Store the stat info for the tarball's file, so
431     * can avoid including the tarball into itself....  */
432    if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
433        bb_perror_msg_and_die("Couldnt stat tar file");
434
435    if ((ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2) && gzip) {
436        int gzipDataPipe[2] = { -1, -1 };
437        int gzipStatusPipe[2] = { -1, -1 };
438        volatile int vfork_exec_errno = 0;
439        char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
440
441
442        if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0)
443            bb_perror_msg_and_die("create pipe");
444
445        signal(SIGPIPE, SIG_IGN);   /* we only want EPIPE on errors */
446
447# if __GNUC__
448            /* Avoid vfork clobbering */
449            (void) &include;
450            (void) &errorFlag;
451            (void) &zip_exec;
452# endif
453
454        gzipPid = vfork();
455
456        if (gzipPid == 0) {
457            dup2(gzipDataPipe[0], 0);
458            close(gzipDataPipe[1]);
459
460            if (tbInfo.tarFd != 1)
461                dup2(tbInfo.tarFd, 1);
462
463            close(gzipStatusPipe[0]);
464            fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC);  /* close on exec shows success */
465
466            execlp(zip_exec, zip_exec, "-f", NULL);
467            vfork_exec_errno = errno;
468
469            close(gzipStatusPipe[1]);
470            exit(-1);
471        } else if (gzipPid > 0) {
472            close(gzipDataPipe[0]);
473            close(gzipStatusPipe[1]);
474
475            while (1) {
476                char buf;
477
478                int n = bb_full_read(gzipStatusPipe[0], &buf, 1);
479
480                if (n == 0 && vfork_exec_errno != 0) {
481                    errno = vfork_exec_errno;
482                    bb_perror_msg_and_die("Could not exec %s", zip_exec);
483                } else if ((n < 0) && (errno == EAGAIN || errno == EINTR))
484                    continue;   /* try it again */
485                break;
486            }
487            close(gzipStatusPipe[0]);
488
489            tbInfo.tarFd = gzipDataPipe[1];
490        } else bb_perror_msg_and_die("vfork gzip");
491    }
492
493    tbInfo.excludeList = exclude;
494
495    /* Read the directory/files and iterate over them one at a time */
496    while (include) {
497        if (!recursive_action(include->data, TRUE, dereferenceFlag,
498                FALSE, writeFileToTarball, writeFileToTarball, &tbInfo))
499        {
500            errorFlag = TRUE;
501        }
502        include = include->link;
503    }
504    /* Write two empty blocks to the end of the archive */
505    for (size = 0; size < (2 * TAR_BLOCK_SIZE); size++)
506        write(tbInfo.tarFd, "\0", 1);
507
508    /* To be pedantically correct, we would check if the tarball
509     * is smaller than 20 tar blocks, and pad it if it was smaller,
510     * but that isn't necessary for GNU tar interoperability, and
511     * so is considered a waste of space */
512
513    /* Close so the child process (if any) will exit */
514    close(tbInfo.tarFd);
515
516    /* Hang up the tools, close up shop, head home */
517    if (ENABLE_FEATURE_CLEAN_UP)
518        freeHardLinkInfo(&tbInfo.hlInfoHead);
519
520    if (errorFlag)
521        bb_error_msg("Error exit delayed from previous errors");
522
523    if (gzipPid && waitpid(gzipPid, NULL, 0)==-1)
524        bb_error_msg("Couldnt wait");
525
526    return !errorFlag;
527}
528#else
529int writeTarFile(const int tar_fd, const int verboseFlag,
530    const unsigned long dereferenceFlag, const llist_t *include,
531    const llist_t *exclude, const int gzip);
532#endif  /* tar_create */
533
534#ifdef CONFIG_FEATURE_TAR_FROM
535static llist_t *append_file_list_to_list(llist_t *list)
536{
537    FILE *src_stream;
538    llist_t *cur = list;
539    llist_t *tmp;
540    char *line;
541    llist_t *newlist = NULL;
542
543    while (cur) {
544        src_stream = bb_xfopen(cur->data, "r");
545        tmp = cur;
546        cur = cur->link;
547        free(tmp);
548        while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL)
549                llist_add_to(&newlist, line);
550        fclose(src_stream);
551    }
552    return newlist;
553}
554#else
555#define append_file_list_to_list(x) 0
556#endif
557
558#ifdef CONFIG_FEATURE_TAR_COMPRESS
559static char get_header_tar_Z(archive_handle_t *archive_handle)
560{
561    /* Cant lseek over pipe's */
562    archive_handle->seek = seek_by_char;
563
564    /* do the decompression, and cleanup */
565    if (bb_xread_char(archive_handle->src_fd) != 0x1f ||
566        bb_xread_char(archive_handle->src_fd) != 0x9d)
567    {
568        bb_error_msg_and_die("Invalid magic");
569    }
570
571    archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress);
572    archive_handle->offset = 0;
573    while (get_header_tar(archive_handle) == EXIT_SUCCESS);
574
575    /* Can only do one file at a time */
576    return(EXIT_FAILURE);
577}
578#else
579#define get_header_tar_Z    0
580#endif
581
582#define CTX_TEST                          (1 << 0)
583#define CTX_EXTRACT                       (1 << 1)
584#define TAR_OPT_BASEDIR                   (1 << 2)
585#define TAR_OPT_TARNAME                   (1 << 3)
586#define TAR_OPT_2STDOUT                   (1 << 4)
587#define TAR_OPT_P                         (1 << 5)
588#define TAR_OPT_VERBOSE                   (1 << 6)
589#define TAR_OPT_KEEP_OLD                  (1 << 7)
590
591#define TAR_OPT_AFTER_START               8
592
593#define CTX_CREATE                        (1 << (TAR_OPT_AFTER_START))
594#define TAR_OPT_DEREFERENCE               (1 << (TAR_OPT_AFTER_START + 1))
595#ifdef CONFIG_FEATURE_TAR_CREATE
596# define TAR_OPT_STR_CREATE               "ch"
597# define TAR_OPT_AFTER_CREATE             TAR_OPT_AFTER_START + 2
598#else
599# define TAR_OPT_STR_CREATE               ""
600# define TAR_OPT_AFTER_CREATE             TAR_OPT_AFTER_START
601#endif
602
603#define TAR_OPT_BZIP2                     (1 << (TAR_OPT_AFTER_CREATE))
604#ifdef CONFIG_FEATURE_TAR_BZIP2
605# define TAR_OPT_STR_BZIP2                "j"
606# define TAR_OPT_AFTER_BZIP2              TAR_OPT_AFTER_CREATE + 1
607#else
608# define TAR_OPT_STR_BZIP2                ""
609# define TAR_OPT_AFTER_BZIP2              TAR_OPT_AFTER_CREATE
610#endif
611
612#define TAR_OPT_LZMA                      (1 << (TAR_OPT_AFTER_BZIP2))
613#ifdef CONFIG_FEATURE_TAR_LZMA
614# define TAR_OPT_STR_LZMA                 "a"
615# define TAR_OPT_AFTER_LZMA               TAR_OPT_AFTER_BZIP2 + 1
616#else
617# define TAR_OPT_STR_LZMA                 ""
618# define TAR_OPT_AFTER_LZMA               TAR_OPT_AFTER_BZIP2
619#endif
620
621#define TAR_OPT_INCLUDE_FROM              (1 << (TAR_OPT_AFTER_LZMA))
622#define TAR_OPT_EXCLUDE_FROM              (1 << (TAR_OPT_AFTER_LZMA + 1))
623#ifdef CONFIG_FEATURE_TAR_FROM
624# define TAR_OPT_STR_FROM                 "T:X:"
625# define TAR_OPT_AFTER_FROM               TAR_OPT_AFTER_LZMA + 2
626#else
627# define TAR_OPT_STR_FROM                 ""
628# define TAR_OPT_AFTER_FROM               TAR_OPT_AFTER_LZMA
629#endif
630
631#define TAR_OPT_GZIP                      (1 << (TAR_OPT_AFTER_FROM))
632#ifdef CONFIG_FEATURE_TAR_GZIP
633# define TAR_OPT_STR_GZIP                 "z"
634# define TAR_OPT_AFTER_GZIP               TAR_OPT_AFTER_FROM + 1
635#else
636# define TAR_OPT_STR_GZIP                 ""
637# define TAR_OPT_AFTER_GZIP               TAR_OPT_AFTER_FROM
638#endif
639
640#define TAR_OPT_UNCOMPRESS                (1 << (TAR_OPT_AFTER_GZIP))
641#ifdef CONFIG_FEATURE_TAR_COMPRESS
642# define TAR_OPT_STR_COMPRESS             "Z"
643# define TAR_OPT_AFTER_COMPRESS           TAR_OPT_AFTER_GZIP + 1
644#else
645# define TAR_OPT_STR_COMPRESS             ""
646# define TAR_OPT_AFTER_COMPRESS           TAR_OPT_AFTER_GZIP
647#endif
648
649#define TAR_OPT_NOPRESERVE_OWN            (1 << (TAR_OPT_AFTER_COMPRESS))
650#define TAR_OPT_NOPRESERVE_PERM           (1 << (TAR_OPT_AFTER_COMPRESS + 1))
651#define TAR_OPT_STR_NOPRESERVE            "\203\213"
652#define TAR_OPT_AFTER_NOPRESERVE          TAR_OPT_AFTER_COMPRESS + 2
653
654static const char tar_options[]="txC:f:Opvk" \
655    TAR_OPT_STR_CREATE \
656    TAR_OPT_STR_BZIP2 \
657    TAR_OPT_STR_LZMA \
658    TAR_OPT_STR_FROM \
659    TAR_OPT_STR_GZIP \
660    TAR_OPT_STR_COMPRESS \
661    TAR_OPT_STR_NOPRESERVE;
662
663#ifdef CONFIG_FEATURE_TAR_LONG_OPTIONS
664static const struct option tar_long_options[] = {
665    { "list",               0,  NULL,   't' },
666    { "extract",            0,  NULL,   'x' },
667    { "directory",          1,  NULL,   'C' },
668    { "file",               1,  NULL,   'f' },
669    { "to-stdout",          0,  NULL,   'O' },
670    { "same-permissions",   0,  NULL,   'p' },
671    { "verbose",            0,  NULL,   'v' },
672    { "keep-old",           0,  NULL,   'k' },
673    { "no-same-owner",      0,  NULL,   '\203' },
674    { "no-same-permissions",0,  NULL,   '\213' },
675# ifdef CONFIG_FEATURE_TAR_CREATE
676    { "create",             0,  NULL,   'c' },
677    { "dereference",        0,  NULL,   'h' },
678# endif
679# ifdef CONFIG_FEATURE_TAR_BZIP2
680    { "bzip2",              0,  NULL,   'j' },
681# endif
682# ifdef CONFIG_FEATURE_TAR_LZMA
683    { "lzma",               0,  NULL,   'a' },
684# endif
685# ifdef CONFIG_FEATURE_TAR_FROM
686    { "files-from",         1,  NULL,   'T' },
687    { "exclude-from",       1,  NULL,   'X' },
688    { "exclude",            1,  NULL,   '\n' },
689# endif
690# ifdef CONFIG_FEATURE_TAR_GZIP
691    { "gzip",               0,  NULL,   'z' },
692# endif
693# ifdef CONFIG_FEATURE_TAR_COMPRESS
694    { "compress",           0,  NULL,   'Z' },
695# endif
696    { 0,                    0, 0, 0 }
697};
698#else
699#define tar_long_options    0
700#endif
701
702int tar_main(int argc, char **argv)
703{
704    char (*get_header_ptr)(archive_handle_t *) = get_header_tar;
705    archive_handle_t *tar_handle;
706    char *base_dir = NULL;
707    const char *tar_filename = "-";
708    unsigned long opt;
709    llist_t *excludes = NULL;
710
711    /* Initialise default values */
712    tar_handle = init_handle();
713    tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_PRESERVE_DATE | ARCHIVE_EXTRACT_UNCONDITIONAL;
714
715    /* Prepend '-' to the first argument if required */
716    bb_opt_complementally = ENABLE_FEATURE_TAR_CREATE ?
717        "--:X::T::\n::c:t:x:?:c--tx:t--cx:x--ct" :
718        "--:X::T::\n::t:x:?:t--x:x--t";
719    if (ENABLE_FEATURE_TAR_LONG_OPTIONS)
720        bb_applet_long_options = tar_long_options;
721    opt = bb_getopt_ulflags(argc, argv, tar_options,
722                &base_dir,      /* Change to dir <optarg> */
723                &tar_filename /* archive filename */
724#ifdef CONFIG_FEATURE_TAR_FROM
725                , &(tar_handle->accept),
726                &(tar_handle->reject),
727                &excludes
728#endif
729                );
730
731    if (opt & CTX_TEST) {
732        if ((tar_handle->action_header == header_list) ||
733            (tar_handle->action_header == header_verbose_list))
734        {
735                tar_handle->action_header = header_verbose_list;
736        } else tar_handle->action_header = header_list;
737    }
738    if((opt & CTX_EXTRACT) && tar_handle->action_data != data_extract_to_stdout)
739        tar_handle->action_data = data_extract_all;
740
741    if (opt & TAR_OPT_2STDOUT)
742        tar_handle->action_data = data_extract_to_stdout;
743
744    if (opt & TAR_OPT_VERBOSE) {
745        if ((tar_handle->action_header == header_list) ||
746            (tar_handle->action_header == header_verbose_list))
747        {
748            tar_handle->action_header = header_verbose_list;
749        } else
750            tar_handle->action_header = header_list;
751    }
752    if (opt & TAR_OPT_KEEP_OLD)
753        tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL;
754
755    if (opt & TAR_OPT_NOPRESERVE_OWN)
756        tar_handle->flags |= ARCHIVE_NOPRESERVE_OWN;
757
758    if (opt & TAR_OPT_NOPRESERVE_PERM)
759        tar_handle->flags |= ARCHIVE_NOPRESERVE_PERM;
760
761    if (ENABLE_FEATURE_TAR_GZIP && (opt & TAR_OPT_GZIP))
762        get_header_ptr = get_header_tar_gz;
763
764    if (ENABLE_FEATURE_TAR_BZIP2 && (opt & TAR_OPT_BZIP2))
765        get_header_ptr = get_header_tar_bz2;
766
767    if (ENABLE_FEATURE_TAR_LZMA && (opt & TAR_OPT_LZMA))
768        get_header_ptr = get_header_tar_lzma;
769
770    if (ENABLE_FEATURE_TAR_COMPRESS && (opt & TAR_OPT_UNCOMPRESS))
771        get_header_ptr = get_header_tar_Z;
772
773    if (ENABLE_FEATURE_TAR_FROM) {
774        tar_handle->reject = append_file_list_to_list(tar_handle->reject);
775        /* Append excludes to reject */
776        while (excludes) {
777            llist_t *temp = excludes->link;
778            excludes->link = tar_handle->reject;
779            tar_handle->reject = excludes;
780            excludes = temp;
781        }
782        tar_handle->accept = append_file_list_to_list(tar_handle->accept);
783    }
784
785    /* Check if we are reading from stdin */
786    if (argv[optind] && *argv[optind] == '-') {
787        /* Default is to read from stdin, so just skip to next arg */
788        optind++;
789    }
790
791    /* Setup an array of filenames to work with */
792    /* TODO: This is the same as in ar, separate function ? */
793    while (optind < argc) {
794        char *filename_ptr = last_char_is(argv[optind], '/');
795        if (filename_ptr > argv[optind])
796            *filename_ptr = '\0';
797
798        llist_add_to(&(tar_handle->accept), argv[optind]);
799        optind++;
800    }
801
802    if ((tar_handle->accept) || (tar_handle->reject))
803        tar_handle->filter = filter_accept_reject_list;
804
805    /* Open the tar file */
806    {
807        FILE *tar_stream;
808        int flags;
809
810        if (ENABLE_FEATURE_TAR_CREATE && (opt & CTX_CREATE)) {
811            /* Make sure there is at least one file to tar up.  */
812            if (tar_handle->accept == NULL)
813                bb_error_msg_and_die("empty archive");
814
815            tar_stream = stdout;
816            flags = O_WRONLY | O_CREAT | O_EXCL;
817            unlink(tar_filename);
818        } else {
819            tar_stream = stdin;
820            flags = O_RDONLY;
821        }
822
823        if ((tar_filename[0] == '-') && (tar_filename[1] == '\0')) {
824            tar_handle->src_fd = fileno(tar_stream);
825            tar_handle->seek = seek_by_char;
826        } else {
827            tar_handle->src_fd = bb_xopen(tar_filename, flags);
828        }
829    }
830
831    if (base_dir)
832        bb_xchdir(base_dir);
833
834    /* create an archive */
835    if (ENABLE_FEATURE_TAR_CREATE && (opt & CTX_CREATE)) {
836        int verboseFlag = FALSE;
837        int zipMode = 0;
838
839        if (ENABLE_FEATURE_TAR_GZIP && get_header_ptr == get_header_tar_gz)
840            zipMode = 1;
841        if (ENABLE_FEATURE_TAR_BZIP2 && get_header_ptr == get_header_tar_bz2)
842            zipMode = 2;
843
844        if ((tar_handle->action_header == header_list) ||
845                (tar_handle->action_header == header_verbose_list))
846        {
847            verboseFlag = TRUE;
848        }
849        writeTarFile(tar_handle->src_fd, verboseFlag, opt & TAR_OPT_DEREFERENCE,
850                tar_handle->accept,
851            tar_handle->reject, zipMode);
852    } else {
853        while (get_header_ptr(tar_handle) == EXIT_SUCCESS);
854
855        /* Check that every file that should have been extracted was */
856        while (tar_handle->accept) {
857            if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
858                && !find_list_entry(tar_handle->passed, tar_handle->accept->data))
859            {
860                bb_error_msg_and_die("%s: Not found in archive", tar_handle->accept->data);
861            }
862            tar_handle->accept = tar_handle->accept->link;
863        }
864    }
865
866    if (ENABLE_FEATURE_CLEAN_UP && tar_handle->src_fd != STDIN_FILENO)
867        close(tar_handle->src_fd);
868
869    return(EXIT_SUCCESS);
870}
Note: See TracBrowser for help on using the repository browser.