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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File size: 28.3 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 <fnmatch.h>
27#include <getopt.h>
28#include "libbb.h"
29#include "unarchive.h"
30
31#define block_buf bb_common_bufsiz1
32
33#if ENABLE_FEATURE_TAR_CREATE
34
35/* Tar file constants  */
36
37#define TAR_BLOCK_SIZE      512
38
39/* POSIX tar Header Block, from POSIX 1003.1-1990  */
40#define NAME_SIZE      100
41#define NAME_SIZE_STR "100"
42typedef struct TarHeader TarHeader;
43struct TarHeader {        /* byte offset */
44    char name[NAME_SIZE];     /*   0-99 */
45    char mode[8];             /* 100-107 */
46    char uid[8];              /* 108-115 */
47    char gid[8];              /* 116-123 */
48    char size[12];            /* 124-135 */
49    char mtime[12];           /* 136-147 */
50    char chksum[8];           /* 148-155 */
51    char typeflag;            /* 156-156 */
52    char linkname[NAME_SIZE]; /* 157-256 */
53    char magic[6];            /* 257-262 */
54    char version[2];          /* 263-264 */
55    char uname[32];           /* 265-296 */
56    char gname[32];           /* 297-328 */
57    char devmajor[8];         /* 329-336 */
58    char devminor[8];         /* 337-344 */
59    char prefix[155];         /* 345-499 */
60    char padding[12];         /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */
61};
62
63/*
64** writeTarFile(), writeFileToTarball(), and writeTarHeader() are
65** the only functions that deal with the HardLinkInfo structure.
66** Even these functions use the xxxHardLinkInfo() functions.
67*/
68typedef struct HardLinkInfo HardLinkInfo;
69struct HardLinkInfo {
70    HardLinkInfo *next; /* Next entry in list */
71    dev_t dev;          /* Device number */
72    ino_t ino;          /* Inode number */
73    short linkCount;    /* (Hard) Link Count */
74    char name[1];       /* Start of filename (must be last) */
75};
76
77/* Some info to be carried along when creating a new tarball */
78typedef struct TarBallInfo TarBallInfo;
79struct TarBallInfo {
80    int tarFd;              /* Open-for-write file descriptor
81                               for the tarball */
82    struct stat statBuf;    /* Stat info for the tarball, letting
83                               us know the inode and device that the
84                               tarball lives, so we can avoid trying
85                               to include the tarball into itself */
86    int verboseFlag;        /* Whether to print extra stuff or not */
87    const llist_t *excludeList; /* List of files to not include */
88    HardLinkInfo *hlInfoHead;   /* Hard Link Tracking Information */
89    HardLinkInfo *hlInfo;   /* Hard Link Info for the current file */
90};
91
92/* A nice enum with all the possible tar file content types */
93enum TarFileType {
94    REGTYPE = '0',      /* regular file */
95    REGTYPE0 = '\0',    /* regular file (ancient bug compat) */
96    LNKTYPE = '1',      /* hard link */
97    SYMTYPE = '2',      /* symbolic link */
98    CHRTYPE = '3',      /* character special */
99    BLKTYPE = '4',      /* block special */
100    DIRTYPE = '5',      /* directory */
101    FIFOTYPE = '6',     /* FIFO special */
102    CONTTYPE = '7',     /* reserved */
103    GNULONGLINK = 'K',  /* GNU long (>100 chars) link name */
104    GNULONGNAME = 'L',  /* GNU long (>100 chars) file name */
105};
106typedef enum TarFileType TarFileType;
107
108/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
109static void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr,
110                    struct stat *statbuf,
111                    const char *fileName)
112{
113    /* Note: hlInfoHeadPtr can never be NULL! */
114    HardLinkInfo *hlInfo;
115
116    hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName));
117    hlInfo->next = *hlInfoHeadPtr;
118    *hlInfoHeadPtr = hlInfo;
119    hlInfo->dev = statbuf->st_dev;
120    hlInfo->ino = statbuf->st_ino;
121    hlInfo->linkCount = statbuf->st_nlink;
122    strcpy(hlInfo->name, fileName);
123}
124
125static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr)
126{
127    HardLinkInfo *hlInfo;
128    HardLinkInfo *hlInfoNext;
129
130    if (hlInfoHeadPtr) {
131        hlInfo = *hlInfoHeadPtr;
132        while (hlInfo) {
133            hlInfoNext = hlInfo->next;
134            free(hlInfo);
135            hlInfo = hlInfoNext;
136        }
137        *hlInfoHeadPtr = NULL;
138    }
139}
140
141/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
142static HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf)
143{
144    while (hlInfo) {
145        if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev))
146            break;
147        hlInfo = hlInfo->next;
148    }
149    return hlInfo;
150}
151
152/* Put an octal string into the specified buffer.
153 * The number is zero padded and possibly null terminated.
154 * Stores low-order bits only if whole value does not fit. */
155static void putOctal(char *cp, int len, off_t value)
156{
157    char tempBuffer[sizeof(off_t)*3+1];
158    char *tempString = tempBuffer;
159    int width;
160
161    width = sprintf(tempBuffer, "%0*"OFF_FMT"o", len, value);
162    tempString += (width - len);
163
164    /* If string has leading zeroes, we can drop one */
165    /* and field will have trailing '\0' */
166    /* (increases chances of compat with other tars) */
167    if (tempString[0] == '0')
168        tempString++;
169
170    /* Copy the string to the field */
171    memcpy(cp, tempString, len);
172}
173#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b))
174
175static void chksum_and_xwrite(int fd, struct TarHeader* hp)
176{
177    /* POSIX says that checksum is done on unsigned bytes
178     * (Sun and HP-UX gets it wrong... more details in
179     * GNU tar source) */
180    const unsigned char *cp;
181    int chksum, size;
182
183    strcpy(hp->magic, "ustar  ");
184
185    /* Calculate and store the checksum (i.e., the sum of all of the bytes of
186     * the header).  The checksum field must be filled with blanks for the
187     * calculation.  The checksum field is formatted differently from the
188     * other fields: it has 6 digits, a null, then a space -- rather than
189     * digits, followed by a null like the other fields... */
190    memset(hp->chksum, ' ', sizeof(hp->chksum));
191    cp = (const unsigned char *) hp;
192    chksum = 0;
193    size = sizeof(*hp);
194    do { chksum += *cp++; } while (--size);
195    putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum);
196
197    /* Now write the header out to disk */
198    xwrite(fd, hp, sizeof(*hp));
199}
200
201#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
202static void writeLongname(int fd, int type, const char *name, int dir)
203{
204    static const struct {
205        char mode[8];             /* 100-107 */
206        char uid[8];              /* 108-115 */
207        char gid[8];              /* 116-123 */
208        char size[12];            /* 124-135 */
209        char mtime[12];           /* 136-147 */
210    } prefilled = {
211        "0000000",
212        "0000000",
213        "0000000",
214        "00000000000",
215        "00000000000",
216    };
217    struct TarHeader header;
218    int size;
219
220    dir = !!dir; /* normalize: 0/1 */
221    size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */
222    /* + dir: account for possible '/' */
223
224    memset(&header, 0, sizeof(header));
225    strcpy(header.name, "././@LongLink");
226    memcpy(header.mode, prefilled.mode, sizeof(prefilled));
227    PUT_OCTAL(header.size, size);
228    header.typeflag = type;
229    chksum_and_xwrite(fd, &header);
230
231    /* Write filename[/] and pad the block. */
232    /* dir=0: writes 'name<NUL>', pads */
233    /* dir=1: writes 'name', writes '/<NUL>', pads */
234    dir *= 2;
235    xwrite(fd, name, size - dir);
236    xwrite(fd, "/", dir);
237    size = (-size) & (TAR_BLOCK_SIZE-1);
238    memset(&header, 0, size);
239    xwrite(fd, &header, size);
240}
241#endif
242
243/* Write out a tar header for the specified file/directory/whatever */
244void BUG_tar_header_size(void);
245static int writeTarHeader(struct TarBallInfo *tbInfo,
246        const char *header_name, const char *fileName, struct stat *statbuf)
247{
248    struct TarHeader header;
249
250    if (sizeof(header) != 512)
251        BUG_tar_header_size();
252
253    memset(&header, 0, sizeof(struct TarHeader));
254
255    strncpy(header.name, header_name, sizeof(header.name));
256
257    /* POSIX says to mask mode with 07777. */
258    PUT_OCTAL(header.mode, statbuf->st_mode & 07777);
259    PUT_OCTAL(header.uid, statbuf->st_uid);
260    PUT_OCTAL(header.gid, statbuf->st_gid);
261    memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */
262    PUT_OCTAL(header.mtime, statbuf->st_mtime);
263
264    /* Enter the user and group names */
265    safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname));
266    safe_strncpy(header.gname, get_cached_groupname(statbuf->st_gid), sizeof(header.gname));
267
268    if (tbInfo->hlInfo) {
269        /* This is a hard link */
270        header.typeflag = LNKTYPE;
271        strncpy(header.linkname, tbInfo->hlInfo->name,
272                sizeof(header.linkname));
273#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
274        /* Write out long linkname if needed */
275        if (header.linkname[sizeof(header.linkname)-1])
276            writeLongname(tbInfo->tarFd, GNULONGLINK,
277                    tbInfo->hlInfo->name, 0);
278#endif
279    } else if (S_ISLNK(statbuf->st_mode)) {
280        char *lpath = xmalloc_readlink_or_warn(fileName);
281        if (!lpath)
282            return FALSE;
283        header.typeflag = SYMTYPE;
284        strncpy(header.linkname, lpath, sizeof(header.linkname));
285#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
286        /* Write out long linkname if needed */
287        if (header.linkname[sizeof(header.linkname)-1])
288            writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0);
289#else
290        /* If it is larger than 100 bytes, bail out */
291        if (header.linkname[sizeof(header.linkname)-1]) {
292            free(lpath);
293            bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
294            return FALSE;
295        }
296#endif
297        free(lpath);
298    } else if (S_ISDIR(statbuf->st_mode)) {
299        header.typeflag = DIRTYPE;
300        /* Append '/' only if there is a space for it */
301        if (!header.name[sizeof(header.name)-1])
302            header.name[strlen(header.name)] = '/';
303    } else if (S_ISCHR(statbuf->st_mode)) {
304        header.typeflag = CHRTYPE;
305        PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
306        PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
307    } else if (S_ISBLK(statbuf->st_mode)) {
308        header.typeflag = BLKTYPE;
309        PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
310        PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
311    } else if (S_ISFIFO(statbuf->st_mode)) {
312        header.typeflag = FIFOTYPE;
313    } else if (S_ISREG(statbuf->st_mode)) {
314        if (sizeof(statbuf->st_size) > 4
315         && statbuf->st_size > (off_t)0777777777777LL
316        ) {
317            bb_error_msg_and_die("cannot store file '%s' "
318                "of size %"OFF_FMT"d, aborting",
319                fileName, statbuf->st_size);
320        }
321        header.typeflag = REGTYPE;
322        PUT_OCTAL(header.size, statbuf->st_size);
323    } else {
324        bb_error_msg("%s: unknown file type", fileName);
325        return FALSE;
326    }
327
328#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
329    /* Write out long name if needed */
330    /* (we, like GNU tar, output long linkname *before* long name) */
331    if (header.name[sizeof(header.name)-1])
332        writeLongname(tbInfo->tarFd, GNULONGNAME,
333                header_name, S_ISDIR(statbuf->st_mode));
334#endif
335
336    /* Now write the header out to disk */
337    chksum_and_xwrite(tbInfo->tarFd, &header);
338
339    /* Now do the verbose thing (or not) */
340    if (tbInfo->verboseFlag) {
341        FILE *vbFd = stdout;
342
343        if (tbInfo->tarFd == STDOUT_FILENO) /* If the archive goes to stdout, verbose to stderr */
344            vbFd = stderr;
345        /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */
346        /* We don't have such excesses here: for us "v" == "vv" */
347        /* '/' is probably a GNUism */
348        fprintf(vbFd, "%s%s\n", header_name,
349                S_ISDIR(statbuf->st_mode) ? "/" : "");
350    }
351
352    return TRUE;
353}
354
355#if ENABLE_FEATURE_TAR_FROM
356static int exclude_file(const llist_t *excluded_files, const char *file)
357{
358    while (excluded_files) {
359        if (excluded_files->data[0] == '/') {
360            if (fnmatch(excluded_files->data, file,
361                        FNM_PATHNAME | FNM_LEADING_DIR) == 0)
362                return 1;
363        } else {
364            const char *p;
365
366            for (p = file; p[0] != '\0'; p++) {
367                if ((p == file || p[-1] == '/') && p[0] != '/' &&
368                    fnmatch(excluded_files->data, p,
369                            FNM_PATHNAME | FNM_LEADING_DIR) == 0)
370                    return 1;
371            }
372        }
373        excluded_files = excluded_files->link;
374    }
375
376    return 0;
377}
378#else
379#define exclude_file(excluded_files, file) 0
380#endif
381
382static int writeFileToTarball(const char *fileName, struct stat *statbuf,
383            void *userData, int depth ATTRIBUTE_UNUSED)
384{
385    struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
386    const char *header_name;
387    int inputFileFd = -1;
388
389    /*
390     * Check to see if we are dealing with a hard link.
391     * If so -
392     * Treat the first occurance of a given dev/inode as a file while
393     * treating any additional occurances as hard links.  This is done
394     * by adding the file information to the HardLinkInfo linked list.
395     */
396    tbInfo->hlInfo = NULL;
397    if (statbuf->st_nlink > 1) {
398        tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf);
399        if (tbInfo->hlInfo == NULL)
400            addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName);
401    }
402
403    /* It is against the rules to archive a socket */
404    if (S_ISSOCK(statbuf->st_mode)) {
405        bb_error_msg("%s: socket ignored", fileName);
406        return TRUE;
407    }
408
409    /* It is a bad idea to store the archive we are in the process of creating,
410     * so check the device and inode to be sure that this particular file isn't
411     * the new tarball */
412    if (tbInfo->statBuf.st_dev == statbuf->st_dev &&
413        tbInfo->statBuf.st_ino == statbuf->st_ino) {
414        bb_error_msg("%s: file is the archive; skipping", fileName);
415        return TRUE;
416    }
417
418    header_name = fileName;
419    while (header_name[0] == '/') {
420        static smallint warned;
421
422        if (!warned) {
423            bb_error_msg("removing leading '/' from member names");
424            warned = 1;
425        }
426        header_name++;
427    }
428
429#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
430    if (strlen(fileName) >= NAME_SIZE) {
431        bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
432        return TRUE;
433    }
434#endif
435
436    if (header_name[0] == '\0')
437        return TRUE;
438
439    if (exclude_file(tbInfo->excludeList, header_name))
440        return SKIP;
441
442    /* Is this a regular file? */
443    if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
444        /* open the file we want to archive, and make sure all is well */
445        inputFileFd = open_or_warn(fileName, O_RDONLY);
446        if (inputFileFd < 0) {
447            return FALSE;
448        }
449    }
450
451    /* Add an entry to the tarball */
452    if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
453        return FALSE;
454    }
455
456    /* If it was a regular file, write out the body */
457    if (inputFileFd >= 0) {
458        size_t readSize;
459        /* Write the file to the archive. */
460        /* We record size into header first, */
461        /* and then write out file. If file shrinks in between, */
462        /* tar will be corrupted. So we don't allow for that. */
463        /* NB: GNU tar 1.16 warns and pads with zeroes */
464        /* or even seeks back and updates header */
465        bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
466        ////off_t readSize;
467        ////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
468        ////if (readSize != statbuf->st_size && readSize >= 0) {
469        ////    bb_error_msg_and_die("short read from %s, aborting", fileName);
470        ////}
471
472        /* Check that file did not grow in between? */
473        /* if (safe_read(inputFileFd, 1) == 1) warn but continue? */
474
475        close(inputFileFd);
476
477        /* Pad the file up to the tar block size */
478        /* (a few tricks here in the name of code size) */
479        readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1);
480        memset(block_buf, 0, readSize);
481        xwrite(tbInfo->tarFd, block_buf, readSize);
482    }
483
484    return TRUE;
485}
486
487static int writeTarFile(const int tar_fd, const int verboseFlag,
488    const unsigned long dereferenceFlag, const llist_t *include,
489    const llist_t *exclude, const int gzip)
490{
491    pid_t gzipPid = 0;
492    int errorFlag = FALSE;
493    struct TarBallInfo tbInfo;
494
495    tbInfo.hlInfoHead = NULL;
496
497    fchmod(tar_fd, 0644);
498    tbInfo.tarFd = tar_fd;
499    tbInfo.verboseFlag = verboseFlag;
500
501    /* Store the stat info for the tarball's file, so
502     * can avoid including the tarball into itself....  */
503    if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
504        bb_perror_msg_and_die("cannot stat tar file");
505
506    if ((ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2) && gzip) {
507        int gzipDataPipe[2] = { -1, -1 };
508        int gzipStatusPipe[2] = { -1, -1 };
509        volatile int vfork_exec_errno = 0;
510        const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
511
512        xpipe(gzipDataPipe);
513        xpipe(gzipStatusPipe);
514
515        signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
516
517#if defined(__GNUC__) && __GNUC__
518        /* Avoid vfork clobbering */
519        (void) &include;
520        (void) &errorFlag;
521        (void) &zip_exec;
522#endif
523
524        gzipPid = vfork();
525
526        if (gzipPid == 0) {
527            dup2(gzipDataPipe[0], 0);
528            close(gzipDataPipe[1]);
529
530            dup2(tbInfo.tarFd, 1);
531
532            close(gzipStatusPipe[0]);
533            fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC);  /* close on exec shows success */
534
535            BB_EXECLP(zip_exec, zip_exec, "-f", NULL);
536            vfork_exec_errno = errno;
537
538            close(gzipStatusPipe[1]);
539            exit(-1);
540        } else if (gzipPid > 0) {
541            close(gzipDataPipe[0]);
542            close(gzipStatusPipe[1]);
543
544            while (1) {
545                char buf;
546
547                int n = full_read(gzipStatusPipe[0], &buf, 1);
548
549                if (n == 0 && vfork_exec_errno != 0) {
550                    errno = vfork_exec_errno;
551                    bb_perror_msg_and_die("cannot exec %s", zip_exec);
552                } else if ((n < 0) && (errno == EAGAIN || errno == EINTR))
553                    continue;   /* try it again */
554                break;
555            }
556            close(gzipStatusPipe[0]);
557
558            tbInfo.tarFd = gzipDataPipe[1];
559        } else bb_perror_msg_and_die("vfork gzip");
560    }
561
562    tbInfo.excludeList = exclude;
563
564    /* Read the directory/files and iterate over them one at a time */
565    while (include) {
566        if (!recursive_action(include->data, ACTION_RECURSE |
567                (dereferenceFlag ? ACTION_FOLLOWLINKS : 0),
568                writeFileToTarball, writeFileToTarball, &tbInfo, 0))
569        {
570            errorFlag = TRUE;
571        }
572        include = include->link;
573    }
574    /* Write two empty blocks to the end of the archive */
575    memset(block_buf, 0, 2*TAR_BLOCK_SIZE);
576    xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE);
577
578    /* To be pedantically correct, we would check if the tarball
579     * is smaller than 20 tar blocks, and pad it if it was smaller,
580     * but that isn't necessary for GNU tar interoperability, and
581     * so is considered a waste of space */
582
583    /* Close so the child process (if any) will exit */
584    close(tbInfo.tarFd);
585
586    /* Hang up the tools, close up shop, head home */
587    if (ENABLE_FEATURE_CLEAN_UP)
588        freeHardLinkInfo(&tbInfo.hlInfoHead);
589
590    if (errorFlag)
591        bb_error_msg("error exit delayed from previous errors");
592
593    if (gzipPid) {
594        int status;
595        if (waitpid(gzipPid, &status, 0) == -1)
596            bb_perror_msg("waitpid");
597        else if (!WIFEXITED(status) || WEXITSTATUS(status))
598            /* gzip was killed or has exited with nonzero! */
599            errorFlag = TRUE;
600    }
601    return errorFlag;
602}
603#else
604int writeTarFile(const int tar_fd, const int verboseFlag,
605    const unsigned long dereferenceFlag, const llist_t *include,
606    const llist_t *exclude, const int gzip);
607#endif /* FEATURE_TAR_CREATE */
608
609#if ENABLE_FEATURE_TAR_FROM
610static llist_t *append_file_list_to_list(llist_t *list)
611{
612    FILE *src_stream;
613    llist_t *cur = list;
614    llist_t *tmp;
615    char *line;
616    llist_t *newlist = NULL;
617
618    while (cur) {
619        src_stream = xfopen(cur->data, "r");
620        tmp = cur;
621        cur = cur->link;
622        free(tmp);
623        while ((line = xmalloc_getline(src_stream)) != NULL) {
624            /* kill trailing '/' unless the string is just "/" */
625            char *cp = last_char_is(line, '/');
626            if (cp > line)
627                *cp = '\0';
628            llist_add_to(&newlist, line);
629        }
630        fclose(src_stream);
631    }
632    return newlist;
633}
634#else
635#define append_file_list_to_list(x) 0
636#endif
637
638#if ENABLE_FEATURE_TAR_COMPRESS
639static char get_header_tar_Z(archive_handle_t *archive_handle)
640{
641    /* Can't lseek over pipes */
642    archive_handle->seek = seek_by_read;
643
644    /* do the decompression, and cleanup */
645    if (xread_char(archive_handle->src_fd) != 0x1f
646     || xread_char(archive_handle->src_fd) != 0x9d
647    ) {
648        bb_error_msg_and_die("invalid magic");
649    }
650
651    archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress, "uncompress", "uncompress", "-cf", "-", NULL);
652    archive_handle->offset = 0;
653    while (get_header_tar(archive_handle) == EXIT_SUCCESS)
654        /* nothing */;
655
656    /* Can only do one file at a time */
657    return EXIT_FAILURE;
658}
659#else
660#define get_header_tar_Z NULL
661#endif
662
663#ifdef CHECK_FOR_CHILD_EXITCODE
664/* Looks like it isn't needed - tar detects malformed (truncated)
665 * archive if e.g. bunzip2 fails */
666static int child_error;
667
668static void handle_SIGCHLD(int status)
669{
670    /* Actually, 'status' is a signo. We reuse it for other needs */
671
672    /* Wait for any child without blocking */
673    if (waitpid(-1, &status, WNOHANG) < 0)
674        /* wait failed?! I'm confused... */
675        return;
676
677    if (WIFEXITED(status) && WEXITSTATUS(status)==0)
678        /* child exited with 0 */
679        return;
680    /* Cannot happen?
681    if (!WIFSIGNALED(status) && !WIFEXITED(status)) return; */
682    child_error = 1;
683}
684#endif
685
686enum {
687    OPTBIT_KEEP_OLD = 7,
688    USE_FEATURE_TAR_CREATE(  OPTBIT_CREATE      ,)
689    USE_FEATURE_TAR_CREATE(  OPTBIT_DEREFERENCE ,)
690    USE_FEATURE_TAR_BZIP2(   OPTBIT_BZIP2       ,)
691    USE_FEATURE_TAR_LZMA(    OPTBIT_LZMA        ,)
692    USE_FEATURE_TAR_FROM(    OPTBIT_INCLUDE_FROM,)
693    USE_FEATURE_TAR_FROM(    OPTBIT_EXCLUDE_FROM,)
694    USE_FEATURE_TAR_GZIP(    OPTBIT_GZIP        ,)
695    USE_FEATURE_TAR_COMPRESS(OPTBIT_COMPRESS    ,)
696    OPTBIT_NOPRESERVE_OWN,
697    OPTBIT_NOPRESERVE_PERM,
698    OPT_TEST         = 1 << 0, // t
699    OPT_EXTRACT      = 1 << 1, // x
700    OPT_BASEDIR      = 1 << 2, // C
701    OPT_TARNAME      = 1 << 3, // f
702    OPT_2STDOUT      = 1 << 4, // O
703    OPT_P            = 1 << 5, // p
704    OPT_VERBOSE      = 1 << 6, // v
705    OPT_KEEP_OLD     = 1 << 7, // k
706    OPT_CREATE       = USE_FEATURE_TAR_CREATE(  (1<<OPTBIT_CREATE      )) + 0, // c
707    OPT_DEREFERENCE  = USE_FEATURE_TAR_CREATE(  (1<<OPTBIT_DEREFERENCE )) + 0, // h
708    OPT_BZIP2        = USE_FEATURE_TAR_BZIP2(   (1<<OPTBIT_BZIP2       )) + 0, // j
709    OPT_LZMA         = USE_FEATURE_TAR_LZMA(    (1<<OPTBIT_LZMA        )) + 0, // a
710    OPT_INCLUDE_FROM = USE_FEATURE_TAR_FROM(    (1<<OPTBIT_INCLUDE_FROM)) + 0, // T
711    OPT_EXCLUDE_FROM = USE_FEATURE_TAR_FROM(    (1<<OPTBIT_EXCLUDE_FROM)) + 0, // X
712    OPT_GZIP         = USE_FEATURE_TAR_GZIP(    (1<<OPTBIT_GZIP        )) + 0, // z
713    OPT_COMPRESS     = USE_FEATURE_TAR_COMPRESS((1<<OPTBIT_COMPRESS    )) + 0, // Z
714    OPT_NOPRESERVE_OWN  = 1 << OPTBIT_NOPRESERVE_OWN , // no-same-owner
715    OPT_NOPRESERVE_PERM = 1 << OPTBIT_NOPRESERVE_PERM, // no-same-permissions
716};
717#if ENABLE_FEATURE_TAR_LONG_OPTIONS
718static const char tar_longopts[] ALIGN1 =
719    "list\0"                No_argument       "t"
720    "extract\0"             No_argument       "x"
721    "directory\0"           Required_argument "C"
722    "file\0"                Required_argument "f"
723    "to-stdout\0"           No_argument       "O"
724    "same-permissions\0"    No_argument       "p"
725    "verbose\0"             No_argument       "v"
726    "keep-old\0"            No_argument       "k"
727# if ENABLE_FEATURE_TAR_CREATE
728    "create\0"              No_argument       "c"
729    "dereference\0"         No_argument       "h"
730# endif
731# if ENABLE_FEATURE_TAR_BZIP2
732    "bzip2\0"               No_argument       "j"
733# endif
734# if ENABLE_FEATURE_TAR_LZMA
735    "lzma\0"                No_argument       "a"
736# endif
737# if ENABLE_FEATURE_TAR_FROM
738    "files-from\0"          Required_argument "T"
739    "exclude-from\0"        Required_argument "X"
740# endif
741# if ENABLE_FEATURE_TAR_GZIP
742    "gzip\0"                No_argument       "z"
743# endif
744# if ENABLE_FEATURE_TAR_COMPRESS
745    "compress\0"            No_argument       "Z"
746# endif
747    "no-same-owner\0"       No_argument       "\xfd"
748    "no-same-permissions\0" No_argument       "\xfe"
749    /* --exclude takes next bit position in option mask, */
750    /* therefore we have to either put it _after_ --no-same-perm */
751    /* or add OPT[BIT]_EXCLUDE before OPT[BIT]_NOPRESERVE_OWN */
752# if ENABLE_FEATURE_TAR_FROM
753    "exclude\0"             Required_argument "\xff"
754# endif
755    ;
756#endif
757
758int tar_main(int argc, char **argv);
759int tar_main(int argc, char **argv)
760{
761    char (*get_header_ptr)(archive_handle_t *) = get_header_tar;
762    archive_handle_t *tar_handle;
763    char *base_dir = NULL;
764    const char *tar_filename = "-";
765    unsigned opt;
766    int verboseFlag = 0;
767#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
768    llist_t *excludes = NULL;
769#endif
770
771    /* Initialise default values */
772    tar_handle = init_handle();
773    tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS
774                      | ARCHIVE_PRESERVE_DATE
775                      | ARCHIVE_EXTRACT_UNCONDITIONAL;
776
777    /* Prepend '-' to the first argument if required */
778    opt_complementary = "--:" // first arg is options
779        "tt:vv:" // count -t,-v
780        "?:" // bail out with usage instead of error return
781        "X::T::" // cumulative lists
782#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
783        "\xff::" // cumulative lists for --exclude
784#endif
785        USE_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
786        USE_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
787        SKIP_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive
788#if ENABLE_FEATURE_TAR_LONG_OPTIONS
789    applet_long_options = tar_longopts;
790#endif
791    opt = getopt32(argv,
792        "txC:f:Opvk"
793        USE_FEATURE_TAR_CREATE(  "ch"  )
794        USE_FEATURE_TAR_BZIP2(   "j"   )
795        USE_FEATURE_TAR_LZMA(    "a"   )
796        USE_FEATURE_TAR_FROM(    "T:X:")
797        USE_FEATURE_TAR_GZIP(    "z"   )
798        USE_FEATURE_TAR_COMPRESS("Z"   )
799        , &base_dir // -C dir
800        , &tar_filename // -f filename
801        USE_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T
802        USE_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X
803#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
804        , &excludes // --exclude
805#endif
806        , &verboseFlag // combined count for -t and -v
807        , &verboseFlag // combined count for -t and -v
808        );
809
810    if (verboseFlag) tar_handle->action_header = header_verbose_list;
811    if (verboseFlag == 1) tar_handle->action_header = header_list;
812
813    if (opt & OPT_EXTRACT)
814        tar_handle->action_data = data_extract_all;
815
816    if (opt & OPT_2STDOUT)
817        tar_handle->action_data = data_extract_to_stdout;
818
819    if (opt & OPT_KEEP_OLD)
820        tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL;
821
822    if (opt & OPT_NOPRESERVE_OWN)
823        tar_handle->flags |= ARCHIVE_NOPRESERVE_OWN;
824
825    if (opt & OPT_NOPRESERVE_PERM)
826        tar_handle->flags |= ARCHIVE_NOPRESERVE_PERM;
827
828    if (opt & OPT_GZIP)
829        get_header_ptr = get_header_tar_gz;
830
831    if (opt & OPT_BZIP2)
832        get_header_ptr = get_header_tar_bz2;
833
834    if (opt & OPT_LZMA)
835        get_header_ptr = get_header_tar_lzma;
836
837    if (opt & OPT_COMPRESS)
838        get_header_ptr = get_header_tar_Z;
839
840#if ENABLE_FEATURE_TAR_FROM
841    tar_handle->reject = append_file_list_to_list(tar_handle->reject);
842#if ENABLE_FEATURE_TAR_LONG_OPTIONS
843    /* Append excludes to reject */
844    while (excludes) {
845        llist_t *next = excludes->link;
846        excludes->link = tar_handle->reject;
847        tar_handle->reject = excludes;
848        excludes = next;
849    }
850#endif
851    tar_handle->accept = append_file_list_to_list(tar_handle->accept);
852#endif
853
854    /* Check if we are reading from stdin */
855    if (argv[optind] && *argv[optind] == '-') {
856        /* Default is to read from stdin, so just skip to next arg */
857        optind++;
858    }
859
860    /* Setup an array of filenames to work with */
861    /* TODO: This is the same as in ar, separate function ? */
862    while (optind < argc) {
863        /* kill trailing '/' unless the string is just "/" */
864        char *cp = last_char_is(argv[optind], '/');
865        if (cp > argv[optind])
866            *cp = '\0';
867        llist_add_to_end(&tar_handle->accept, argv[optind]);
868        optind++;
869    }
870
871    if (tar_handle->accept || tar_handle->reject)
872        tar_handle->filter = filter_accept_reject_list;
873
874    /* Open the tar file */
875    {
876        FILE *tar_stream;
877        int flags;
878
879        if (opt & OPT_CREATE) {
880            /* Make sure there is at least one file to tar up.  */
881            if (tar_handle->accept == NULL)
882                bb_error_msg_and_die("empty archive");
883
884            tar_stream = stdout;
885            /* Mimicking GNU tar 1.15.1: */
886            flags = O_WRONLY|O_CREAT|O_TRUNC;
887        /* was doing unlink; open(O_WRONLY|O_CREAT|O_EXCL); why? */
888        } else {
889            tar_stream = stdin;
890            flags = O_RDONLY;
891        }
892
893        if (LONE_DASH(tar_filename)) {
894            tar_handle->src_fd = fileno(tar_stream);
895            tar_handle->seek = seek_by_read;
896        } else {
897            tar_handle->src_fd = xopen(tar_filename, flags);
898        }
899    }
900
901    if (base_dir)
902        xchdir(base_dir);
903
904#ifdef CHECK_FOR_CHILD_EXITCODE
905    /* We need to know whether child (gzip/bzip/etc) exits abnormally */
906    signal(SIGCHLD, handle_SIGCHLD);
907#endif
908
909    /* create an archive */
910    if (opt & OPT_CREATE) {
911        int zipMode = 0;
912        if (ENABLE_FEATURE_TAR_GZIP && get_header_ptr == get_header_tar_gz)
913            zipMode = 1;
914        if (ENABLE_FEATURE_TAR_BZIP2 && get_header_ptr == get_header_tar_bz2)
915            zipMode = 2;
916        /* NB: writeTarFile() closes tar_handle->src_fd */
917        return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE,
918                tar_handle->accept,
919                tar_handle->reject, zipMode);
920    }
921
922    while (get_header_ptr(tar_handle) == EXIT_SUCCESS)
923        /* nothing */;
924
925    /* Check that every file that should have been extracted was */
926    while (tar_handle->accept) {
927        if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
928         && !find_list_entry(tar_handle->passed, tar_handle->accept->data)
929        ) {
930            bb_error_msg_and_die("%s: not found in archive",
931                tar_handle->accept->data);
932        }
933        tar_handle->accept = tar_handle->accept->link;
934    }
935    if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */)
936        close(tar_handle->src_fd);
937
938    return EXIT_SUCCESS;
939}
Note: See TracBrowser for help on using the repository browser.