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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 16 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
RevLine 
[821]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
[1770]26#include <fnmatch.h>
[821]27#include <getopt.h>
[1770]28#include "libbb.h"
[821]29#include "unarchive.h"
30
[1770]31#define block_buf bb_common_bufsiz1
[821]32
[1770]33#if ENABLE_FEATURE_TAR_CREATE
34
[821]35/* Tar file constants */
36
37#define TAR_BLOCK_SIZE 512
38
39/* POSIX tar Header Block, from POSIX 1003.1-1990 */
[1770]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) */
[821]61};
62
63/*
[1770]64** writeTarFile(), writeFileToTarball(), and writeTarHeader() are
[821]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 */
[1770]78typedef struct TarBallInfo TarBallInfo;
[821]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;) */
[1770]109static void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr,
[821]110 struct stat *statbuf,
[1770]111 const char *fileName)
[821]112{
113 /* Note: hlInfoHeadPtr can never be NULL! */
114 HardLinkInfo *hlInfo;
115
[1770]116 hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName));
[821]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;
[1770]122 strcpy(hlInfo->name, fileName);
[821]123}
124
125static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr)
126{
[1770]127 HardLinkInfo *hlInfo;
128 HardLinkInfo *hlInfoNext;
[821]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;) */
[1770]142static HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf)
[821]143{
144 while (hlInfo) {
145 if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev))
146 break;
147 hlInfo = hlInfo->next;
148 }
[1770]149 return hlInfo;
[821]150}
151
152/* Put an octal string into the specified buffer.
[1770]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)
[821]156{
[1770]157 char tempBuffer[sizeof(off_t)*3+1];
[821]158 char *tempString = tempBuffer;
[1770]159 int width;
[821]160
[1770]161 width = sprintf(tempBuffer, "%0*"OFF_FMT"o", len, value);
162 tempString += (width - len);
[821]163
[1770]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')
[821]168 tempString++;
169
[1770]170 /* Copy the string to the field */
171 memcpy(cp, tempString, len);
172}
173#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b))
[821]174
[1770]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;
[821]182
[1770]183 strcpy(hp->magic, "ustar ");
[821]184
[1770]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));
[821]199}
200
[1770]201#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
202static void writeLongname(int fd, int type, const char *name, int dir)
[821]203{
[1770]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 };
[821]217 struct TarHeader header;
[1770]218 int size;
[821]219
[1770]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);
[821]238 memset(&header, 0, size);
[1770]239 xwrite(fd, &header, size);
240}
241#endif
[821]242
[1770]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;
[821]249
[1770]250 if (sizeof(header) != 512)
251 BUG_tar_header_size();
[821]252
[1770]253 memset(&header, 0, sizeof(struct TarHeader));
[821]254
[1770]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
[821]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));
[1770]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
[821]279 } else if (S_ISLNK(statbuf->st_mode)) {
[1770]280 char *lpath = xmalloc_readlink_or_warn(fileName);
281 if (!lpath)
282 return FALSE;
[821]283 header.typeflag = SYMTYPE;
284 strncpy(header.linkname, lpath, sizeof(header.linkname));
[1770]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
[821]297 free(lpath);
298 } else if (S_ISDIR(statbuf->st_mode)) {
299 header.typeflag = DIRTYPE;
[1770]300 /* Append '/' only if there is a space for it */
301 if (!header.name[sizeof(header.name)-1])
302 header.name[strlen(header.name)] = '/';
[821]303 } else if (S_ISCHR(statbuf->st_mode)) {
304 header.typeflag = CHRTYPE;
[1770]305 PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
306 PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
[821]307 } else if (S_ISBLK(statbuf->st_mode)) {
308 header.typeflag = BLKTYPE;
[1770]309 PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
310 PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
[821]311 } else if (S_ISFIFO(statbuf->st_mode)) {
312 header.typeflag = FIFOTYPE;
313 } else if (S_ISREG(statbuf->st_mode)) {
[1770]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 }
[821]321 header.typeflag = REGTYPE;
[1770]322 PUT_OCTAL(header.size, statbuf->st_size);
[821]323 } else {
[1770]324 bb_error_msg("%s: unknown file type", fileName);
325 return FALSE;
[821]326 }
327
[1770]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
[821]335
336 /* Now write the header out to disk */
[1770]337 chksum_and_xwrite(tbInfo->tarFd, &header);
338
[821]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;
[1770]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) ? "/" : "");
[821]350 }
351
[1770]352 return TRUE;
[821]353}
354
[1770]355#if ENABLE_FEATURE_TAR_FROM
356static int exclude_file(const llist_t *excluded_files, const char *file)
[821]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}
[1770]378#else
[821]379#define exclude_file(excluded_files, file) 0
[1770]380#endif
[821]381
382static int writeFileToTarball(const char *fileName, struct stat *statbuf,
[1770]383 void *userData, int depth ATTRIBUTE_UNUSED)
[821]384{
385 struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
386 const char *header_name;
387 int inputFileFd = -1;
388
389 /*
[1770]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.
[821]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);
[1770]406 return TRUE;
[821]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);
[1770]415 return TRUE;
[821]416 }
417
418 header_name = fileName;
419 while (header_name[0] == '/') {
[1770]420 static smallint warned;
[821]421
[1770]422 if (!warned) {
423 bb_error_msg("removing leading '/' from member names");
424 warned = 1;
[821]425 }
426 header_name++;
427 }
428
[1770]429#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
[821]430 if (strlen(fileName) >= NAME_SIZE) {
[1770]431 bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
432 return TRUE;
[821]433 }
[1770]434#endif
[821]435
436 if (header_name[0] == '\0')
437 return TRUE;
438
[1770]439 if (exclude_file(tbInfo->excludeList, header_name))
[821]440 return SKIP;
441
442 /* Is this a regular file? */
[1770]443 if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
[821]444 /* open the file we want to archive, and make sure all is well */
[1770]445 inputFileFd = open_or_warn(fileName, O_RDONLY);
446 if (inputFileFd < 0) {
447 return FALSE;
[821]448 }
449 }
450
451 /* Add an entry to the tarball */
452 if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
[1770]453 return FALSE;
[821]454 }
455
456 /* If it was a regular file, write out the body */
[1770]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 ////}
[821]471
[1770]472 /* Check that file did not grow in between? */
473 /* if (safe_read(inputFileFd, 1) == 1) warn but continue? */
474
[821]475 close(inputFileFd);
476
477 /* Pad the file up to the tar block size */
[1770]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);
[821]482 }
483
[1770]484 return TRUE;
[821]485}
486
[1770]487static int writeTarFile(const int tar_fd, const int verboseFlag,
[821]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)
[1770]504 bb_perror_msg_and_die("cannot stat tar file");
[821]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;
[1770]510 const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
[821]511
[1770]512 xpipe(gzipDataPipe);
513 xpipe(gzipStatusPipe);
[821]514
[1770]515 signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
[821]516
[1770]517#if defined(__GNUC__) && __GNUC__
518 /* Avoid vfork clobbering */
519 (void) &include;
520 (void) &errorFlag;
521 (void) &zip_exec;
522#endif
[821]523
524 gzipPid = vfork();
525
526 if (gzipPid == 0) {
527 dup2(gzipDataPipe[0], 0);
528 close(gzipDataPipe[1]);
529
[1770]530 dup2(tbInfo.tarFd, 1);
[821]531
532 close(gzipStatusPipe[0]);
533 fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC); /* close on exec shows success */
534
[1770]535 BB_EXECLP(zip_exec, zip_exec, "-f", NULL);
[821]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
[1770]547 int n = full_read(gzipStatusPipe[0], &buf, 1);
[821]548
549 if (n == 0 && vfork_exec_errno != 0) {
550 errno = vfork_exec_errno;
[1770]551 bb_perror_msg_and_die("cannot exec %s", zip_exec);
[821]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) {
[1770]566 if (!recursive_action(include->data, ACTION_RECURSE |
567 (dereferenceFlag ? ACTION_FOLLOWLINKS : 0),
568 writeFileToTarball, writeFileToTarball, &tbInfo, 0))
[821]569 {
570 errorFlag = TRUE;
571 }
572 include = include->link;
573 }
574 /* Write two empty blocks to the end of the archive */
[1770]575 memset(block_buf, 0, 2*TAR_BLOCK_SIZE);
576 xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE);
[821]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)
[1770]591 bb_error_msg("error exit delayed from previous errors");
[821]592
[1770]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;
[821]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);
[1770]607#endif /* FEATURE_TAR_CREATE */
[821]608
[1770]609#if ENABLE_FEATURE_TAR_FROM
[821]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) {
[1770]619 src_stream = xfopen(cur->data, "r");
[821]620 tmp = cur;
621 cur = cur->link;
622 free(tmp);
[1770]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 }
[821]630 fclose(src_stream);
631 }
632 return newlist;
633}
634#else
[1770]635#define append_file_list_to_list(x) 0
[821]636#endif
637
[1770]638#if ENABLE_FEATURE_TAR_COMPRESS
[821]639static char get_header_tar_Z(archive_handle_t *archive_handle)
640{
[1770]641 /* Can't lseek over pipes */
642 archive_handle->seek = seek_by_read;
[821]643
644 /* do the decompression, and cleanup */
[1770]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");
[821]649 }
650
[1770]651 archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress, "uncompress", "uncompress", "-cf", "-", NULL);
[821]652 archive_handle->offset = 0;
[1770]653 while (get_header_tar(archive_handle) == EXIT_SUCCESS)
654 /* nothing */;
[821]655
656 /* Can only do one file at a time */
[1770]657 return EXIT_FAILURE;
[821]658}
659#else
[1770]660#define get_header_tar_Z NULL
[821]661#endif
662
[1770]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;
[821]667
[1770]668static void handle_SIGCHLD(int status)
669{
670 /* Actually, 'status' is a signo. We reuse it for other needs */
[821]671
[1770]672 /* Wait for any child without blocking */
673 if (waitpid(-1, &status, WNOHANG) < 0)
674 /* wait failed?! I'm confused... */
675 return;
[821]676
[1770]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}
[821]684#endif
685
[1770]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"
[821]730# endif
[1770]731# if ENABLE_FEATURE_TAR_BZIP2
732 "bzip2\0" No_argument "j"
[821]733# endif
[1770]734# if ENABLE_FEATURE_TAR_LZMA
735 "lzma\0" No_argument "a"
[821]736# endif
[1770]737# if ENABLE_FEATURE_TAR_FROM
738 "files-from\0" Required_argument "T"
739 "exclude-from\0" Required_argument "X"
[821]740# endif
[1770]741# if ENABLE_FEATURE_TAR_GZIP
742 "gzip\0" No_argument "z"
[821]743# endif
[1770]744# if ENABLE_FEATURE_TAR_COMPRESS
745 "compress\0" No_argument "Z"
[821]746# endif
[1770]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 ;
[821]756#endif
757
[1770]758int tar_main(int argc, char **argv);
[821]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 = "-";
[1770]765 unsigned opt;
766 int verboseFlag = 0;
767#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
[821]768 llist_t *excludes = NULL;
[1770]769#endif
[821]770
771 /* Initialise default values */
772 tar_handle = init_handle();
[1770]773 tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS
774 | ARCHIVE_PRESERVE_DATE
775 | ARCHIVE_EXTRACT_UNCONDITIONAL;
[821]776
777 /* Prepend '-' to the first argument if required */
[1770]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
[821]784#endif
[1770]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 );
[821]809
[1770]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)
[821]814 tar_handle->action_data = data_extract_all;
815
[1770]816 if (opt & OPT_2STDOUT)
[821]817 tar_handle->action_data = data_extract_to_stdout;
818
[1770]819 if (opt & OPT_KEEP_OLD)
[821]820 tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL;
821
[1770]822 if (opt & OPT_NOPRESERVE_OWN)
[821]823 tar_handle->flags |= ARCHIVE_NOPRESERVE_OWN;
824
[1770]825 if (opt & OPT_NOPRESERVE_PERM)
[821]826 tar_handle->flags |= ARCHIVE_NOPRESERVE_PERM;
827
[1770]828 if (opt & OPT_GZIP)
[821]829 get_header_ptr = get_header_tar_gz;
830
[1770]831 if (opt & OPT_BZIP2)
[821]832 get_header_ptr = get_header_tar_bz2;
833
[1770]834 if (opt & OPT_LZMA)
[821]835 get_header_ptr = get_header_tar_lzma;
836
[1770]837 if (opt & OPT_COMPRESS)
[821]838 get_header_ptr = get_header_tar_Z;
839
[1770]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;
[821]849 }
[1770]850#endif
851 tar_handle->accept = append_file_list_to_list(tar_handle->accept);
852#endif
[821]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) {
[1770]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]);
[821]868 optind++;
869 }
870
[1770]871 if (tar_handle->accept || tar_handle->reject)
[821]872 tar_handle->filter = filter_accept_reject_list;
873
874 /* Open the tar file */
875 {
876 FILE *tar_stream;
877 int flags;
878
[1770]879 if (opt & OPT_CREATE) {
[821]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;
[1770]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? */
[821]888 } else {
889 tar_stream = stdin;
890 flags = O_RDONLY;
891 }
892
[1770]893 if (LONE_DASH(tar_filename)) {
[821]894 tar_handle->src_fd = fileno(tar_stream);
[1770]895 tar_handle->seek = seek_by_read;
[821]896 } else {
[1770]897 tar_handle->src_fd = xopen(tar_filename, flags);
[821]898 }
899 }
900
901 if (base_dir)
[1770]902 xchdir(base_dir);
[821]903
[1770]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
[821]909 /* create an archive */
[1770]910 if (opt & OPT_CREATE) {
[821]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;
[1770]916 /* NB: writeTarFile() closes tar_handle->src_fd */
917 return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE,
[821]918 tar_handle->accept,
[1770]919 tar_handle->reject, zipMode);
920 }
[821]921
[1770]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);
[821]932 }
[1770]933 tar_handle->accept = tar_handle->accept->link;
[821]934 }
[1770]935 if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */)
[821]936 close(tar_handle->src_fd);
937
[1770]938 return EXIT_SUCCESS;
[821]939}
Note: See TracBrowser for help on using the repository browser.