blob: f7b789f7c2d33c8e479fd8397b8819d5995ecebd [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersencc8ed391999-10-05 16:24:54 +00002/*
Erik Andersen1ad302a2000-03-24 00:54:46 +00003 * Mini tar implementation for busybox
Eric Andersenc4996011999-10-20 22:08:37 +00004 *
Erik Andersen1ad302a2000-03-24 00:54:46 +00005 * Copyright (C) 1999 by Lineo, inc.
6 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
Eric Andersen96bcfd31999-11-12 01:30:18 +00007 *
Eric Andersenc4996011999-10-20 22:08:37 +00008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
Eric Andersencc8ed391999-10-05 16:24:54 +000022 */
23
24
25#include "internal.h"
Erik Andersen1ad302a2000-03-24 00:54:46 +000026#define BB_DECLARE_EXTERN
27#define bb_need_io_error
28#include "messages.c"
Eric Andersencc8ed391999-10-05 16:24:54 +000029#include <stdio.h>
30#include <dirent.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <signal.h>
34#include <time.h>
Erik Andersen7dc16072000-01-04 01:10:25 +000035#include <utime.h>
Eric Andersen96bcfd31999-11-12 01:30:18 +000036#include <sys/types.h>
Eric Andersen08b10341999-11-19 02:38:58 +000037#include <sys/sysmacros.h>
Erik Andersene49d5ec2000-02-08 19:58:47 +000038#include <sys/param.h> /* for PATH_MAX */
Eric Andersencc8ed391999-10-05 16:24:54 +000039
Eric Andersene77ae3a1999-10-19 20:03:34 +000040
Erik Andersen05100cd2000-01-16 01:30:52 +000041#ifdef BB_FEATURE_TAR_CREATE
42
Eric Andersene77ae3a1999-10-19 20:03:34 +000043static const char tar_usage[] =
Erik Andersene49d5ec2000-02-08 19:58:47 +000044 "tar -[cxtvOf] [tarFileName] [FILE] ...\n\n"
45 "Create, extract, or list files from a tar file.\n\n"
46 "Options:\n"
47
48 "\tc=create, x=extract, t=list contents, v=verbose,\n"
49 "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n";
Eric Andersene77ae3a1999-10-19 20:03:34 +000050
Erik Andersen05100cd2000-01-16 01:30:52 +000051#else
52
53static const char tar_usage[] =
Erik Andersene49d5ec2000-02-08 19:58:47 +000054 "tar -[xtvOf] [tarFileName] [FILE] ...\n\n"
55 "Extract, or list files stored in a tar file. This\n"
56 "version of tar does not support creation of tar files.\n\n"
57 "Options:\n"
58
59 "\tx=extract, t=list contents, v=verbose,\n"
60 "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n";
Erik Andersen05100cd2000-01-16 01:30:52 +000061
62#endif
Eric Andersene77ae3a1999-10-19 20:03:34 +000063
64
Erik Andersen298854f2000-03-23 01:09:18 +000065/* Tar file constants */
Erik Andersen1ad302a2000-03-24 00:54:46 +000066#ifndef MAJOR
67#define MAJOR(dev) (((dev)>>8)&0xff)
68#define MINOR(dev) ((dev)&0xff)
69#endif
Eric Andersencc8ed391999-10-05 16:24:54 +000070
71
Erik Andersen298854f2000-03-23 01:09:18 +000072/* POSIX tar Header Block, from POSIX 1003.1-1990 */
73struct TarHeader
74{
75 /* byte offset */
76 char name[100]; /* 0 */
77 char mode[8]; /* 100 */
78 char uid[8]; /* 108 */
79 char gid[8]; /* 116 */
80 char size[12]; /* 124 */
81 char mtime[12]; /* 136 */
82 char chksum[8]; /* 148 */
83 char typeflag; /* 156 */
84 char linkname[100]; /* 157 */
85 char magic[6]; /* 257 */
86 char version[2]; /* 263 */
87 char uname[32]; /* 265 */
88 char gname[32]; /* 297 */
89 char devmajor[8]; /* 329 */
90 char devminor[8]; /* 337 */
91 char prefix[155]; /* 345 */
92 /* padding 500 */
93};
94typedef struct TarHeader TarHeader;
Eric Andersencc8ed391999-10-05 16:24:54 +000095
Eric Andersencc8ed391999-10-05 16:24:54 +000096
Erik Andersen298854f2000-03-23 01:09:18 +000097/* A few useful constants */
98#define TAR_MAGIC "ustar" /* ustar and a null */
99#define TAR_VERSION "00" /* 00 and no null */
100#define TAR_MAGIC_LEN 6
101#define TAR_VERSION_LEN 2
102#define TAR_NAME_LEN 100
103#define TAR_BLOCK_SIZE 512
104
105/* A nice enum with all the possible tar file content types */
106enum TarFileType
107{
108 REGTYPE = '0', /* regular file */
109 REGTYPE0 = '\0', /* regular file (ancient bug compat)*/
110 LNKTYPE = '1', /* hard link */
111 SYMTYPE = '2', /* symbolic link */
112 CHRTYPE = '3', /* character special */
113 BLKTYPE = '4', /* block special */
114 DIRTYPE = '5', /* directory */
115 FIFOTYPE = '6', /* FIFO special */
116 CONTTYPE = '7', /* reserved */
117};
118typedef enum TarFileType TarFileType;
119
120/* This struct ignores magic, non-numeric user name,
121 * non-numeric group name, and the checksum, since
122 * these are all ignored by BusyBox tar. */
123struct TarInfo
124{
125 int tarFd; /* An open file descriptor for reading from the tarball */
126 char * name; /* File name */
127 mode_t mode; /* Unix mode, including device bits. */
128 uid_t uid; /* Numeric UID */
129 gid_t gid; /* Numeric GID */
130 size_t size; /* Size of file */
131 time_t mtime; /* Last-modified time */
132 enum TarFileType type; /* Regular, directory, link, etc */
133 char * linkname; /* Name for symbolic and hard links */
Erik Andersen1ad302a2000-03-24 00:54:46 +0000134 long devmajor; /* Major number for special device */
135 long devminor; /* Minor number for special device */
Erik Andersen298854f2000-03-23 01:09:18 +0000136};
137typedef struct TarInfo TarInfo;
138
139/* Static data */
140static const unsigned long TarChecksumOffset = (const unsigned long)&(((TarHeader *)0)->chksum);
Eric Andersencc8ed391999-10-05 16:24:54 +0000141
142
Erik Andersene454fb62000-03-23 04:27:58 +0000143/* Local procedures to restore files from a tar file. */
Erik Andersen298854f2000-03-23 01:09:18 +0000144static int readTarFile(const char* tarName, int extractFlag, int listFlag,
145 int tostdoutFlag, int verboseFlag);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000146
147
Eric Andersencc8ed391999-10-05 16:24:54 +0000148
Erik Andersen05100cd2000-01-16 01:30:52 +0000149#ifdef BB_FEATURE_TAR_CREATE
Eric Andersencc8ed391999-10-05 16:24:54 +0000150/*
151 * Local procedures to save files into a tar file.
152 */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000153static void saveFile(const char *fileName, int seeLinks);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000154static void saveRegularFile(const char *fileName,
Erik Andersene49d5ec2000-02-08 19:58:47 +0000155 const struct stat *statbuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000156static void saveDirectory(const char *fileName,
Erik Andersene49d5ec2000-02-08 19:58:47 +0000157 const struct stat *statbuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000158static void writeHeader(const char *fileName, const struct stat *statbuf);
Erik Andersen298854f2000-03-23 01:09:18 +0000159static void writeTarFile(int argc, char **argv);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000160static void writeTarBlock(const char *buf, int len);
161static int putOctal(char *cp, int len, long value);
Eric Andersencc8ed391999-10-05 16:24:54 +0000162
Erik Andersen05100cd2000-01-16 01:30:52 +0000163#endif
164
Eric Andersencc8ed391999-10-05 16:24:54 +0000165
Erik Andersene49d5ec2000-02-08 19:58:47 +0000166extern int tar_main(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000167{
Erik Andersen298854f2000-03-23 01:09:18 +0000168 const char *tarName=NULL;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000169 const char *options;
Erik Andersen298854f2000-03-23 01:09:18 +0000170 int listFlag = FALSE;
171 int extractFlag = FALSE;
172 int createFlag = FALSE;
173 int verboseFlag = FALSE;
174 int tostdoutFlag = FALSE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000175
Erik Andersene49d5ec2000-02-08 19:58:47 +0000176 argc--;
177 argv++;
Eric Andersencc8ed391999-10-05 16:24:54 +0000178
Erik Andersene49d5ec2000-02-08 19:58:47 +0000179 if (argc < 1)
180 usage(tar_usage);
Eric Andersencc8ed391999-10-05 16:24:54 +0000181
Erik Andersen298854f2000-03-23 01:09:18 +0000182 /* Parse options */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000183 if (**argv == '-')
184 options = (*argv++) + 1;
185 else
186 options = (*argv++);
187 argc--;
Eric Andersencc8ed391999-10-05 16:24:54 +0000188
Erik Andersene49d5ec2000-02-08 19:58:47 +0000189 for (; *options; options++) {
190 switch (*options) {
191 case 'f':
Erik Andersen298854f2000-03-23 01:09:18 +0000192 if (tarName != NULL)
193 fatalError( "Only one 'f' option allowed\n");
Erik Andersende552872000-01-23 01:34:05 +0000194
Erik Andersene49d5ec2000-02-08 19:58:47 +0000195 tarName = *argv++;
Erik Andersen298854f2000-03-23 01:09:18 +0000196 if (tarName == NULL)
197 fatalError( "Option requires an argument: No file specified\n");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000198 argc--;
Erik Andersende552872000-01-23 01:34:05 +0000199
Erik Andersene49d5ec2000-02-08 19:58:47 +0000200 break;
Erik Andersende552872000-01-23 01:34:05 +0000201
Erik Andersene49d5ec2000-02-08 19:58:47 +0000202 case 't':
203 if (extractFlag == TRUE || createFlag == TRUE)
204 goto flagError;
205 listFlag = TRUE;
206 break;
Erik Andersende552872000-01-23 01:34:05 +0000207
Erik Andersene49d5ec2000-02-08 19:58:47 +0000208 case 'x':
209 if (listFlag == TRUE || createFlag == TRUE)
210 goto flagError;
211 extractFlag = TRUE;
212 break;
213 case 'c':
214 if (extractFlag == TRUE || listFlag == TRUE)
215 goto flagError;
216 createFlag = TRUE;
217 break;
Erik Andersende552872000-01-23 01:34:05 +0000218
Erik Andersene49d5ec2000-02-08 19:58:47 +0000219 case 'v':
220 verboseFlag = TRUE;
221 break;
Erik Andersende552872000-01-23 01:34:05 +0000222
Erik Andersene49d5ec2000-02-08 19:58:47 +0000223 case 'O':
224 tostdoutFlag = TRUE;
225 break;
Erik Andersende552872000-01-23 01:34:05 +0000226
Erik Andersene49d5ec2000-02-08 19:58:47 +0000227 case '-':
228 usage(tar_usage);
229 break;
Erik Andersende552872000-01-23 01:34:05 +0000230
Erik Andersene49d5ec2000-02-08 19:58:47 +0000231 default:
Erik Andersen298854f2000-03-23 01:09:18 +0000232 fatalError( "Unknown tar flag '%c'\n"
Erik Andersene49d5ec2000-02-08 19:58:47 +0000233 "Try `tar --help' for more information\n", *options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000234 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000235 }
236
Erik Andersene49d5ec2000-02-08 19:58:47 +0000237 /*
238 * Do the correct type of action supplying the rest of the
239 * command line arguments as the list of files to process.
240 */
241 if (createFlag == TRUE) {
Erik Andersende552872000-01-23 01:34:05 +0000242#ifndef BB_FEATURE_TAR_CREATE
Erik Andersen298854f2000-03-23 01:09:18 +0000243 fatalError( "This version of tar was not compiled with tar creation support.\n");
Erik Andersende552872000-01-23 01:34:05 +0000244#else
Erik Andersen298854f2000-03-23 01:09:18 +0000245 exit(writeTarFile(argc, argv));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000246#endif
247 } else {
Erik Andersen298854f2000-03-23 01:09:18 +0000248 exit(readTarFile(tarName, extractFlag, listFlag, tostdoutFlag, verboseFlag));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000249 }
Erik Andersende552872000-01-23 01:34:05 +0000250
Erik Andersene49d5ec2000-02-08 19:58:47 +0000251 flagError:
Erik Andersen298854f2000-03-23 01:09:18 +0000252 fatalError( "Exactly one of 'c', 'x' or 't' must be specified\n");
253}
254
255static void
Erik Andersen1ad302a2000-03-24 00:54:46 +0000256tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersen298854f2000-03-23 01:09:18 +0000257{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000258 size_t writeSize;
259 size_t readSize;
260 size_t actualWriteSz;
261 char buffer[BUFSIZ];
262 size_t size = header->size;
263 int outFd=fileno(stdout);
264
265 /* Open the file to be written, if a file is supposed to be written */
266 if (extractFlag==TRUE && tostdoutFlag==FALSE) {
267 if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, header->mode & ~S_IFMT)) < 0)
268 errorMsg(io_error, header->name, strerror(errno));
269 /* Create the path to the file, just in case it isn't there...
270 * This should not screw up path permissions or anything. */
271 createPath(header->name, 0777);
272 }
273
274 /* Write out the file, if we are supposed to be doing that */
275 while ( size > 0 ) {
276 actualWriteSz=0;
277 if ( size > sizeof(buffer) )
278 writeSize = readSize = sizeof(buffer);
279 else {
280 int mod = size % 512;
281 if ( mod != 0 )
282 readSize = size + (512 - mod);
283 else
284 readSize = size;
285 writeSize = size;
286 }
287 if ( (readSize = fullRead(header->tarFd, buffer, readSize)) <= 0 ) {
288 /* Tarball seems to have a problem */
289 errorMsg("Error reading tarfile: %s", strerror(errno));
290 return;
291 }
292 if ( readSize < writeSize )
293 writeSize = readSize;
294
295 /* Write out the file, if we are supposed to be doing that */
296 if (extractFlag==TRUE) {
297
298 if ((actualWriteSz=fullWrite(outFd, buffer, writeSize)) != writeSize ) {
299 /* Output file seems to have a problem */
300 errorMsg(io_error, header->name, strerror(errno));
301 return;
302 }
303 }
304
305 size -= actualWriteSz;
306 }
307
308 /* Now we are done writing the file out, so try
309 * and fix up the permissions and whatnot */
310 if (extractFlag==TRUE && tostdoutFlag==FALSE) {
311 struct utimbuf t;
312 /* Now set permissions etc for the new file */
313 fchown(outFd, header->uid, header->gid);
314 fchmod(outFd, header->mode & ~S_IFMT);
315 close(outFd);
316 /* File must be closed before trying to change the date */
317 t.actime = time(0);
318 t.modtime = header->mtime;
319 utime(header->name, &t);
320 }
321}
322
323static void
324fixUpPermissions(TarInfo *header)
325{
326 struct utimbuf t;
327 /* Now set permissions etc for the new file */
328 chown(header->name, header->uid, header->gid);
329 chmod(header->name, header->mode);
330 /* Reset the time */
331 t.actime = time(0);
332 t.modtime = header->mtime;
333 utime(header->name, &t);
Eric Andersencc8ed391999-10-05 16:24:54 +0000334}
Erik Andersene454fb62000-03-23 04:27:58 +0000335
336static void
Erik Andersen1ad302a2000-03-24 00:54:46 +0000337tarExtractDirectory(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersene454fb62000-03-23 04:27:58 +0000338{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000339
340 if (extractFlag==FALSE || tostdoutFlag==TRUE)
341 return;
342
343 if (createPath(header->name, header->mode) != TRUE) {
344 errorMsg("Error creating directory '%s': %s", header->name, strerror(errno));
345 return;
346 }
347 /* make the final component, just in case it was
348 * omitted by createPath() (which will skip the
349 * directory if it doesn't have a terminating '/') */
350 mkdir(header->name, header->mode);
351
352 /* Now set permissions etc for the new directory */
353 fixUpPermissions(header);
Erik Andersene454fb62000-03-23 04:27:58 +0000354}
355
356static void
Erik Andersen1ad302a2000-03-24 00:54:46 +0000357tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersene454fb62000-03-23 04:27:58 +0000358{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000359 if (extractFlag==FALSE || tostdoutFlag==TRUE)
360 return;
361
362 if (link(header->linkname, header->name) < 0) {
363 errorMsg("Error creating hard link '%s': %s", header->linkname, strerror(errno));
364 return;
365 }
366
367 /* Now set permissions etc for the new directory */
368 fixUpPermissions(header);
Erik Andersene454fb62000-03-23 04:27:58 +0000369}
370
371static void
Erik Andersen1ad302a2000-03-24 00:54:46 +0000372tarExtractSymLink(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersene454fb62000-03-23 04:27:58 +0000373{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000374 if (extractFlag==FALSE || tostdoutFlag==TRUE)
375 return;
376
377#ifdef S_ISLNK
378 if (symlink(header->linkname, header->name) < 0) {
379 errorMsg("Error creating symlink '%s': %s", header->linkname, strerror(errno));
380 return;
381 }
382 /* Try to change ownership of the symlink.
383 * If libs doesn't support that, don't bother.
384 * Changing the pointed-to-file is the Wrong Thing(tm).
385 */
386#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
387 lchown(header->name, header->uid, header->gid);
388#endif
389
390 /* Do not change permissions or date on symlink,
391 * since it changes the pointed to file instead. duh. */
392#else
393 fprintf(stderr, "Cannot create symbolic links\n");
394#endif
Erik Andersene454fb62000-03-23 04:27:58 +0000395}
396
397static void
Erik Andersen1ad302a2000-03-24 00:54:46 +0000398tarExtractSpecial(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersene454fb62000-03-23 04:27:58 +0000399{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000400 if (extractFlag==FALSE || tostdoutFlag==TRUE)
401 return;
402
403 if (S_ISCHR(header->mode) || S_ISBLK(header->mode) || S_ISSOCK(header->mode)) {
404 mknod(header->name, header->mode, makedev(header->devmajor, header->devminor));
405 } else if (S_ISFIFO(header->mode)) {
406 mkfifo(header->name, header->mode);
407 } else {
408 open(header->name, O_WRONLY | O_CREAT | O_TRUNC, header->mode);
409 }
410
411 /* Now set permissions etc for the new directory */
412 fixUpPermissions(header);
Erik Andersene454fb62000-03-23 04:27:58 +0000413}
414
Erik Andersen1ad302a2000-03-24 00:54:46 +0000415/* Read an octal value in a field of the specified width, with optional
Erik Andersen298854f2000-03-23 01:09:18 +0000416 * spaces on both sides of the number and with an optional null character
Erik Andersen1ad302a2000-03-24 00:54:46 +0000417 * at the end. Returns -1 on an illegal format. */
Erik Andersen298854f2000-03-23 01:09:18 +0000418static long getOctal(const char *cp, int size)
Eric Andersencc8ed391999-10-05 16:24:54 +0000419{
Erik Andersen298854f2000-03-23 01:09:18 +0000420 long val = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000421
Erik Andersen298854f2000-03-23 01:09:18 +0000422 for(;(size > 0) && (*cp == ' '); cp++, size--);
423 if ((size == 0) || !isOctal(*cp))
424 return -1;
425 for(; (size > 0) && isOctal(*cp); size--) {
426 val = val * 8 + *cp++ - '0';
Eric Andersencc8ed391999-10-05 16:24:54 +0000427 }
Erik Andersen298854f2000-03-23 01:09:18 +0000428 for (;(size > 0) && (*cp == ' '); cp++, size--);
429 if ((size > 0) && *cp)
430 return -1;
431 return val;
432}
Eric Andersencc8ed391999-10-05 16:24:54 +0000433
Erik Andersen1ad302a2000-03-24 00:54:46 +0000434
Erik Andersen298854f2000-03-23 01:09:18 +0000435/* Parse the tar header and fill in the nice struct with the details */
436static int
437parseTarHeader(struct TarHeader *rawHeader, struct TarInfo *header)
438{
Erik Andersene454fb62000-03-23 04:27:58 +0000439 int i;
440 long chksum, sum;
441 unsigned char *s = (unsigned char *)rawHeader;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000442
Erik Andersen298854f2000-03-23 01:09:18 +0000443 header->name = rawHeader->name;
444 header->mode = getOctal(rawHeader->mode, sizeof(rawHeader->mode));
445 header->uid = getOctal(rawHeader->uid, sizeof(rawHeader->uid));
446 header->gid = getOctal(rawHeader->gid, sizeof(rawHeader->gid));
447 header->size = getOctal(rawHeader->size, sizeof(rawHeader->size));
448 header->mtime = getOctal(rawHeader->mtime, sizeof(rawHeader->mtime));
449 chksum = getOctal(rawHeader->chksum, sizeof(rawHeader->chksum));
450 header->type = rawHeader->typeflag;
451 header->linkname = rawHeader->linkname;
Erik Andersen1ad302a2000-03-24 00:54:46 +0000452 header->devmajor = getOctal(rawHeader->devmajor, sizeof(rawHeader->devmajor));
453 header->devminor = getOctal(rawHeader->devminor, sizeof(rawHeader->devminor));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000454
Erik Andersen298854f2000-03-23 01:09:18 +0000455 /* Check the checksum */
456 sum = ' ' * sizeof(rawHeader->chksum);
457 for ( i = TarChecksumOffset; i > 0; i-- )
458 sum += *s++;
Erik Andersene454fb62000-03-23 04:27:58 +0000459 s += sizeof(rawHeader->chksum);
460 for ( i = (512 - TarChecksumOffset - sizeof(rawHeader->chksum)); i > 0; i-- )
Erik Andersen298854f2000-03-23 01:09:18 +0000461 sum += *s++;
Erik Andersene454fb62000-03-23 04:27:58 +0000462 if (sum == chksum )
Erik Andersen298854f2000-03-23 01:09:18 +0000463 return ( TRUE);
464 return( FALSE);
465}
Erik Andersene49d5ec2000-02-08 19:58:47 +0000466
Erik Andersene49d5ec2000-02-08 19:58:47 +0000467
Erik Andersen1ad302a2000-03-24 00:54:46 +0000468/*
469 * Read a tar file and extract or list the specified files within it.
470 * If the list is empty than all files are extracted or listed.
471 */
472static int readTarFile(const char* tarName, int extractFlag, int listFlag,
473 int tostdoutFlag, int verboseFlag)
474{
475 int status, tarFd=0;
476 int errorFlag=FALSE;
477 TarHeader rawHeader;
478 TarInfo header;
479 int alreadyWarned=FALSE;
480 //int skipFileFlag=FALSE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000481
Erik Andersen1ad302a2000-03-24 00:54:46 +0000482 /* Open the tar file for reading. */
483 if (!strcmp(tarName, "-"))
484 tarFd = fileno(stdin);
485 else
486 tarFd = open(tarName, O_RDONLY);
487 if (tarFd < 0) {
488 errorMsg( "Error opening '%s': %s", tarName, strerror(errno));
489 return ( FALSE);
Erik Andersen06936df2000-01-23 02:14:20 +0000490 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000491
Erik Andersene49d5ec2000-02-08 19:58:47 +0000492 /* Set the umask for this process so it doesn't
Erik Andersen1ad302a2000-03-24 00:54:46 +0000493 * screw up permission setting for us later. */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000494 umask(0);
Eric Andersencc8ed391999-10-05 16:24:54 +0000495
Erik Andersen1ad302a2000-03-24 00:54:46 +0000496 /* Read the tar file, and iterate over it one file at a time */
497 while ( (status = fullRead(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) {
Erik Andersene2729152000-02-18 21:34:17 +0000498
Erik Andersen1ad302a2000-03-24 00:54:46 +0000499 /* First, try to read the header */
500 if ( parseTarHeader(&rawHeader, &header) == FALSE ) {
501 close( tarFd);
502 if ( *(header.name) == '\0' ) {
503 goto endgame;
504 } else {
505 errorFlag=TRUE;
506 errorMsg("Bad tar header, skipping\n");
507 continue;
508 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000509 }
Erik Andersen1ad302a2000-03-24 00:54:46 +0000510 if ( *(header.name) == '\0' )
511 goto endgame;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000512
Erik Andersen1ad302a2000-03-24 00:54:46 +0000513 /* Check for and relativify any absolute paths */
514 if ( *(header.name) == '/' ) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000515
Erik Andersen1ad302a2000-03-24 00:54:46 +0000516 while (*(header.name) == '/')
517 ++*(header.name);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000518
Erik Andersen1ad302a2000-03-24 00:54:46 +0000519 if (alreadyWarned == FALSE) {
520 errorMsg("Absolute path detected, removing leading slashes\n");
521 alreadyWarned = TRUE;
522 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000523 }
Erik Andersen1ad302a2000-03-24 00:54:46 +0000524
525 /* Special treatment if the list (-t) flag is on */
526 if (verboseFlag == TRUE && extractFlag == FALSE) {
527 int len, len1;
528 char buf[35];
529 struct tm *tm = localtime (&(header.mtime));
530
531 len=printf("%s %d/%-d ", modeString(header.mode), header.uid, header.gid);
532 if (header.type==CHRTYPE || header.type==BLKTYPE) {
533 len1=snprintf(buf, sizeof(buf), "%ld,%-ld ",
534 header.devmajor, header.devminor);
535 } else {
536 len1=snprintf(buf, sizeof(buf), "%d ", header.size);
537 }
538 /* Jump through some hoops to make the columns match up */
539 for(;(len+len1)<31;len++)
540 printf(" ");
541 printf(buf);
542
543 /* Use ISO 8610 time format */
544 if (tm) {
545 printf ("%04d-%02d-%02d %02d:%02d:%02d ",
546 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
547 tm->tm_hour, tm->tm_min, tm->tm_sec);
548 }
549 }
550 /* List contents if we are supposed to do that */
551 if (verboseFlag == TRUE || listFlag == TRUE) {
552 /* Now the normal listing */
553 printf("%s", header.name);
554 /* If this is a link, say so */
555 if (header.type==LNKTYPE)
556 printf(" link to %s", header.linkname);
557 else if (header.type==SYMTYPE)
558 printf(" -> %s", header.linkname);
559 printf("\n");
560 }
561
562#if 0
563 /* See if we want to restore this file or not */
564 skipFileFlag=FALSE;
565 if (wantFileName(outName) == FALSE) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000566 skipFileFlag = TRUE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000567 }
Erik Andersen05100cd2000-01-16 01:30:52 +0000568#endif
Eric Andersencc8ed391999-10-05 16:24:54 +0000569
Erik Andersen1ad302a2000-03-24 00:54:46 +0000570 /* If we got here, we can be certain we have a legitimate
571 * header to work with. So work with it. */
572 switch ( header.type ) {
573 case REGTYPE:
574 case REGTYPE0:
575 /* If the name ends in a '/' then assume it is
576 * supposed to be a directory, and fall through */
577 if (header.name[strlen(header.name)-1] != '/') {
578 tarExtractRegularFile(&header, extractFlag, tostdoutFlag);
579 break;
580 }
581 case DIRTYPE:
582 tarExtractDirectory( &header, extractFlag, tostdoutFlag);
583 break;
584 case LNKTYPE:
585 tarExtractHardLink( &header, extractFlag, tostdoutFlag);
586 break;
587 case SYMTYPE:
588 tarExtractSymLink( &header, extractFlag, tostdoutFlag);
589 break;
590 case CHRTYPE:
591 case BLKTYPE:
592 case FIFOTYPE:
593 tarExtractSpecial( &header, extractFlag, tostdoutFlag);
594 break;
595 default:
596 close( tarFd);
597 return( FALSE);
598 }
599 }
600 close(tarFd);
601 if (status > 0) {
602 /* Bummer - we read a partial header */
603 errorMsg( "Error reading '%s': %s", tarName, strerror(errno));
604 return ( FALSE);
605 }
606 else
607 return( status);
608
609 /* Stuff to do when we are done */
610endgame:
611 close( tarFd);
612 if ( *(header.name) == '\0' ) {
613 if (errorFlag==FALSE)
614 return( TRUE);
615 }
616 return( FALSE);
617}
618