blob: 99f166c3ead02936e7d4bbf4e6fe9525bb31a44e [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/*
Eric Andersenc4996011999-10-20 22:08:37 +00003 * Mini tar implementation for busybox based on code taken from sash.
4 *
Eric Andersencc8ed391999-10-05 16:24:54 +00005 * Copyright (c) 1999 by David I. Bell
6 * Permission is granted to use, distribute, or modify this source,
7 * provided that this copyright notice remains intact.
8 *
Eric Andersencc8ed391999-10-05 16:24:54 +00009 * Permission to distribute this code under the GPL has been granted.
Eric Andersenc4996011999-10-20 22:08:37 +000010 *
Eric Andersen3cf52d11999-10-12 22:26:06 +000011 * Modified for busybox by Erik Andersen <andersee@debian.org>
Eric Andersenc4996011999-10-20 22:08:37 +000012 * Adjusted to grok stdin/stdout options.
13 *
Eric Andersen96bcfd31999-11-12 01:30:18 +000014 * Modified to handle device special files by Matt Porter
15 * <porter@debian.org>
16 *
Eric Andersenc4996011999-10-20 22:08:37 +000017 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 *
Eric Andersencc8ed391999-10-05 16:24:54 +000031 */
32
33
34#include "internal.h"
Eric Andersencc8ed391999-10-05 16:24:54 +000035#include <stdio.h>
36#include <dirent.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <signal.h>
40#include <time.h>
Erik Andersen7dc16072000-01-04 01:10:25 +000041#include <utime.h>
Eric Andersen96bcfd31999-11-12 01:30:18 +000042#include <sys/types.h>
Eric Andersen08b10341999-11-19 02:38:58 +000043#include <sys/sysmacros.h>
Erik Andersene49d5ec2000-02-08 19:58:47 +000044#include <sys/param.h> /* for PATH_MAX */
Eric Andersencc8ed391999-10-05 16:24:54 +000045
Eric Andersene77ae3a1999-10-19 20:03:34 +000046
Erik Andersen05100cd2000-01-16 01:30:52 +000047#ifdef BB_FEATURE_TAR_CREATE
48
Eric Andersene77ae3a1999-10-19 20:03:34 +000049static const char tar_usage[] =
Erik Andersene49d5ec2000-02-08 19:58:47 +000050 "tar -[cxtvOf] [tarFileName] [FILE] ...\n\n"
51 "Create, extract, or list files from a tar file.\n\n"
52 "Options:\n"
53
54 "\tc=create, x=extract, t=list contents, v=verbose,\n"
55 "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n";
Eric Andersene77ae3a1999-10-19 20:03:34 +000056
Erik Andersen05100cd2000-01-16 01:30:52 +000057#else
58
59static const char tar_usage[] =
Erik Andersene49d5ec2000-02-08 19:58:47 +000060 "tar -[xtvOf] [tarFileName] [FILE] ...\n\n"
61 "Extract, or list files stored in a tar file. This\n"
62 "version of tar does not support creation of tar files.\n\n"
63 "Options:\n"
64
65 "\tx=extract, t=list contents, v=verbose,\n"
66 "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n";
Erik Andersen05100cd2000-01-16 01:30:52 +000067
68#endif
Eric Andersene77ae3a1999-10-19 20:03:34 +000069
70
Erik Andersen298854f2000-03-23 01:09:18 +000071/* Tar file constants */
Eric Andersencc8ed391999-10-05 16:24:54 +000072
73
Erik Andersen298854f2000-03-23 01:09:18 +000074/* POSIX tar Header Block, from POSIX 1003.1-1990 */
75struct TarHeader
76{
77 /* byte offset */
78 char name[100]; /* 0 */
79 char mode[8]; /* 100 */
80 char uid[8]; /* 108 */
81 char gid[8]; /* 116 */
82 char size[12]; /* 124 */
83 char mtime[12]; /* 136 */
84 char chksum[8]; /* 148 */
85 char typeflag; /* 156 */
86 char linkname[100]; /* 157 */
87 char magic[6]; /* 257 */
88 char version[2]; /* 263 */
89 char uname[32]; /* 265 */
90 char gname[32]; /* 297 */
91 char devmajor[8]; /* 329 */
92 char devminor[8]; /* 337 */
93 char prefix[155]; /* 345 */
94 /* padding 500 */
95};
96typedef struct TarHeader TarHeader;
Eric Andersencc8ed391999-10-05 16:24:54 +000097
Eric Andersencc8ed391999-10-05 16:24:54 +000098
Erik Andersen298854f2000-03-23 01:09:18 +000099/* A few useful constants */
100#define TAR_MAGIC "ustar" /* ustar and a null */
101#define TAR_VERSION "00" /* 00 and no null */
102#define TAR_MAGIC_LEN 6
103#define TAR_VERSION_LEN 2
104#define TAR_NAME_LEN 100
105#define TAR_BLOCK_SIZE 512
106
107/* A nice enum with all the possible tar file content types */
108enum TarFileType
109{
110 REGTYPE = '0', /* regular file */
111 REGTYPE0 = '\0', /* regular file (ancient bug compat)*/
112 LNKTYPE = '1', /* hard link */
113 SYMTYPE = '2', /* symbolic link */
114 CHRTYPE = '3', /* character special */
115 BLKTYPE = '4', /* block special */
116 DIRTYPE = '5', /* directory */
117 FIFOTYPE = '6', /* FIFO special */
118 CONTTYPE = '7', /* reserved */
119};
120typedef enum TarFileType TarFileType;
121
122/* This struct ignores magic, non-numeric user name,
123 * non-numeric group name, and the checksum, since
124 * these are all ignored by BusyBox tar. */
125struct TarInfo
126{
127 int tarFd; /* An open file descriptor for reading from the tarball */
128 char * name; /* File name */
129 mode_t mode; /* Unix mode, including device bits. */
130 uid_t uid; /* Numeric UID */
131 gid_t gid; /* Numeric GID */
132 size_t size; /* Size of file */
133 time_t mtime; /* Last-modified time */
134 enum TarFileType type; /* Regular, directory, link, etc */
135 char * linkname; /* Name for symbolic and hard links */
136 dev_t device; /* Special device for mknod() */
137};
138typedef struct TarInfo TarInfo;
139
140/* Static data */
141static const unsigned long TarChecksumOffset = (const unsigned long)&(((TarHeader *)0)->chksum);
Eric Andersencc8ed391999-10-05 16:24:54 +0000142
143
Erik Andersene454fb62000-03-23 04:27:58 +0000144/* Local procedures to restore files from a tar file. */
Erik Andersen298854f2000-03-23 01:09:18 +0000145static int readTarFile(const char* tarName, int extractFlag, int listFlag,
146 int tostdoutFlag, int verboseFlag);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000147static long getOctal(const char *cp, int len);
Erik Andersen298854f2000-03-23 01:09:18 +0000148static int parseTarHeader(struct TarHeader *rawHeader, struct TarInfo *header);
Eric Andersencc8ed391999-10-05 16:24:54 +0000149
Erik Andersen05100cd2000-01-16 01:30:52 +0000150#ifdef BB_FEATURE_TAR_CREATE
Eric Andersencc8ed391999-10-05 16:24:54 +0000151/*
152 * Local procedures to save files into a tar file.
153 */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000154static void saveFile(const char *fileName, int seeLinks);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000155static void saveRegularFile(const char *fileName,
Erik Andersene49d5ec2000-02-08 19:58:47 +0000156 const struct stat *statbuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000157static void saveDirectory(const char *fileName,
Erik Andersene49d5ec2000-02-08 19:58:47 +0000158 const struct stat *statbuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000159static void writeHeader(const char *fileName, const struct stat *statbuf);
Erik Andersen298854f2000-03-23 01:09:18 +0000160static void writeTarFile(int argc, char **argv);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000161static void writeTarBlock(const char *buf, int len);
162static int putOctal(char *cp, int len, long value);
Eric Andersencc8ed391999-10-05 16:24:54 +0000163
Erik Andersen05100cd2000-01-16 01:30:52 +0000164#endif
165
Eric Andersencc8ed391999-10-05 16:24:54 +0000166
Erik Andersene49d5ec2000-02-08 19:58:47 +0000167extern int tar_main(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000168{
Erik Andersen298854f2000-03-23 01:09:18 +0000169 const char *tarName=NULL;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000170 const char *options;
Erik Andersen298854f2000-03-23 01:09:18 +0000171 int listFlag = FALSE;
172 int extractFlag = FALSE;
173 int createFlag = FALSE;
174 int verboseFlag = FALSE;
175 int tostdoutFlag = FALSE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000176
Erik Andersene49d5ec2000-02-08 19:58:47 +0000177 argc--;
178 argv++;
Eric Andersencc8ed391999-10-05 16:24:54 +0000179
Erik Andersene49d5ec2000-02-08 19:58:47 +0000180 if (argc < 1)
181 usage(tar_usage);
Eric Andersencc8ed391999-10-05 16:24:54 +0000182
Erik Andersen298854f2000-03-23 01:09:18 +0000183 /* Parse options */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000184 if (**argv == '-')
185 options = (*argv++) + 1;
186 else
187 options = (*argv++);
188 argc--;
Eric Andersencc8ed391999-10-05 16:24:54 +0000189
Erik Andersene49d5ec2000-02-08 19:58:47 +0000190 for (; *options; options++) {
191 switch (*options) {
192 case 'f':
Erik Andersen298854f2000-03-23 01:09:18 +0000193 if (tarName != NULL)
194 fatalError( "Only one 'f' option allowed\n");
Erik Andersende552872000-01-23 01:34:05 +0000195
Erik Andersene49d5ec2000-02-08 19:58:47 +0000196 tarName = *argv++;
Erik Andersen298854f2000-03-23 01:09:18 +0000197 if (tarName == NULL)
198 fatalError( "Option requires an argument: No file specified\n");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000199 argc--;
Erik Andersende552872000-01-23 01:34:05 +0000200
Erik Andersene49d5ec2000-02-08 19:58:47 +0000201 break;
Erik Andersende552872000-01-23 01:34:05 +0000202
Erik Andersene49d5ec2000-02-08 19:58:47 +0000203 case 't':
204 if (extractFlag == TRUE || createFlag == TRUE)
205 goto flagError;
206 listFlag = TRUE;
207 break;
Erik Andersende552872000-01-23 01:34:05 +0000208
Erik Andersene49d5ec2000-02-08 19:58:47 +0000209 case 'x':
210 if (listFlag == TRUE || createFlag == TRUE)
211 goto flagError;
212 extractFlag = TRUE;
213 break;
214 case 'c':
215 if (extractFlag == TRUE || listFlag == TRUE)
216 goto flagError;
217 createFlag = TRUE;
218 break;
Erik Andersende552872000-01-23 01:34:05 +0000219
Erik Andersene49d5ec2000-02-08 19:58:47 +0000220 case 'v':
221 verboseFlag = TRUE;
222 break;
Erik Andersende552872000-01-23 01:34:05 +0000223
Erik Andersene49d5ec2000-02-08 19:58:47 +0000224 case 'O':
225 tostdoutFlag = TRUE;
226 break;
Erik Andersende552872000-01-23 01:34:05 +0000227
Erik Andersene49d5ec2000-02-08 19:58:47 +0000228 case '-':
229 usage(tar_usage);
230 break;
Erik Andersende552872000-01-23 01:34:05 +0000231
Erik Andersene49d5ec2000-02-08 19:58:47 +0000232 default:
Erik Andersen298854f2000-03-23 01:09:18 +0000233 fatalError( "Unknown tar flag '%c'\n"
Erik Andersene49d5ec2000-02-08 19:58:47 +0000234 "Try `tar --help' for more information\n", *options);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000235 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000236 }
237
Erik Andersene49d5ec2000-02-08 19:58:47 +0000238 /*
239 * Do the correct type of action supplying the rest of the
240 * command line arguments as the list of files to process.
241 */
242 if (createFlag == TRUE) {
Erik Andersende552872000-01-23 01:34:05 +0000243#ifndef BB_FEATURE_TAR_CREATE
Erik Andersen298854f2000-03-23 01:09:18 +0000244 fatalError( "This version of tar was not compiled with tar creation support.\n");
Erik Andersende552872000-01-23 01:34:05 +0000245#else
Erik Andersen298854f2000-03-23 01:09:18 +0000246 exit(writeTarFile(argc, argv));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000247#endif
248 } else {
Erik Andersen298854f2000-03-23 01:09:18 +0000249 exit(readTarFile(tarName, extractFlag, listFlag, tostdoutFlag, verboseFlag));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000250 }
Erik Andersende552872000-01-23 01:34:05 +0000251
Erik Andersene49d5ec2000-02-08 19:58:47 +0000252 flagError:
Erik Andersen298854f2000-03-23 01:09:18 +0000253 fatalError( "Exactly one of 'c', 'x' or 't' must be specified\n");
254}
255
256static void
257tarExtractRegularFile(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag)
258{
Erik Andersene454fb62000-03-23 04:27:58 +0000259 return;
Eric Andersencc8ed391999-10-05 16:24:54 +0000260}
Erik Andersene454fb62000-03-23 04:27:58 +0000261
262static void
263tarExtractDirectory(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag)
264{
265 return;
266}
267
268static void
269tarExtractHardLink(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag)
270{
271 return;
272}
273
274static void
275tarExtractSymLink(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag)
276{
277 return;
278}
279
280static void
281tarExtractSpecial(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag)
282{
283 return;
284}
285
Eric Andersencc8ed391999-10-05 16:24:54 +0000286
287
288/*
289 * Read a tar file and extract or list the specified files within it.
290 * If the list is empty than all files are extracted or listed.
291 */
Erik Andersen298854f2000-03-23 01:09:18 +0000292static int readTarFile(const char* tarName, int extractFlag, int listFlag,
293 int tostdoutFlag, int verboseFlag)
Eric Andersencc8ed391999-10-05 16:24:54 +0000294{
Erik Andersen298854f2000-03-23 01:09:18 +0000295 int status, tarFd=0;
296 int errorFlag=FALSE;
297 TarHeader rawHeader;
298 TarInfo header;
Eric Andersencc8ed391999-10-05 16:24:54 +0000299
Erik Andersen298854f2000-03-23 01:09:18 +0000300 /* Open the tar file for reading. */
301 if (!strcmp(tarName, "-"))
Erik Andersene49d5ec2000-02-08 19:58:47 +0000302 tarFd = fileno(stdin);
Erik Andersen298854f2000-03-23 01:09:18 +0000303 else
Erik Andersene49d5ec2000-02-08 19:58:47 +0000304 tarFd = open(tarName, O_RDONLY);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000305 if (tarFd < 0) {
Erik Andersen298854f2000-03-23 01:09:18 +0000306 errorMsg( "Error opening '%s': %s", tarName, strerror(errno));
307 return ( FALSE);
Eric Andersencc8ed391999-10-05 16:24:54 +0000308 }
309
Erik Andersen298854f2000-03-23 01:09:18 +0000310 /* Read the tar file */
311 while ( (status = fullRead(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) {
312 /* Now see if the header looks ok */
313 if ( parseTarHeader(&rawHeader, &header) == FALSE ) {
314 close( tarFd);
315 if ( *(header.name) == '\0' ) {
316 goto endgame;
317 } else {
318 errorFlag=TRUE;
319 errorMsg("Bad tar header, skipping\n");
320 continue;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000321 }
322 }
Erik Andersen298854f2000-03-23 01:09:18 +0000323 if ( *(header.name) == '\0' )
324 goto endgame;
Erik Andersene454fb62000-03-23 04:27:58 +0000325
326 if (extractFlag == FALSE) {
327 if (verboseFlag == TRUE) {
328 printf("%s %3d/%-d ", modeString(header.mode), header.uid, header.gid);
329 if (header.type==CHRTYPE || header.type==BLKTYPE)
330 printf("%4d,%4d %s ", MAJOR(header.device),
331 MINOR(header.device), timeString(header.mtime));
332 else
333 printf("%9ld %s ", header.size, timeString(header.mtime));
334 }
335 printf("%s", header.name);
336
337 if (header.type==LNKTYPE)
338 printf(" (link to \"%s\")", hp->linkName);
339 else if (header.type==SYMTYPE)
340 printf(" (symlink to \"%s\")", hp->linkName);
341 printf("\n");
342 continue;
343 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000344
Erik Andersen298854f2000-03-23 01:09:18 +0000345 /* If we got here, we can be certain we have a legitimate
346 * header to work with. So work with it. */
347 switch ( header.type ) {
348 case REGTYPE:
349 case REGTYPE0:
350 /* If the name ends in a '/' then assume it is
351 * supposed to be a directory, and fall through */
352 if (header.name[strlen(header.name)-1] != '/') {
353 tarExtractRegularFile(&header, extractFlag, listFlag, tostdoutFlag, verboseFlag);
354 break;
355 }
Erik Andersene454fb62000-03-23 04:27:58 +0000356 case DIRTYPE:
Erik Andersen298854f2000-03-23 01:09:18 +0000357 tarExtractDirectory( &header, extractFlag, listFlag, tostdoutFlag, verboseFlag);
358 break;
Erik Andersene454fb62000-03-23 04:27:58 +0000359 case LNKTYPE:
Erik Andersen298854f2000-03-23 01:09:18 +0000360 tarExtractHardLink( &header, extractFlag, listFlag, tostdoutFlag, verboseFlag);
361 break;
Erik Andersene454fb62000-03-23 04:27:58 +0000362 case SYMTYPE:
Erik Andersen298854f2000-03-23 01:09:18 +0000363 tarExtractSymLink( &header, extractFlag, listFlag, tostdoutFlag, verboseFlag);
364 break;
Erik Andersene454fb62000-03-23 04:27:58 +0000365 case CHRTYPE:
366 case BLKTYPE:
367 case FIFOTYPE:
Erik Andersen298854f2000-03-23 01:09:18 +0000368 tarExtractSpecial( &header, extractFlag, listFlag, tostdoutFlag, verboseFlag);
369 break;
Erik Andersen298854f2000-03-23 01:09:18 +0000370 default:
371 close( tarFd);
372 return( FALSE);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000373 }
Erik Andersen7dc16072000-01-04 01:10:25 +0000374 }
Erik Andersen298854f2000-03-23 01:09:18 +0000375
376 close(tarFd);
377 if (status > 0) {
378 /* Bummer - we read a partial header */
379 errorMsg( "Error reading '%s': %s", tarName, strerror(errno));
380 return ( FALSE);
381 }
382 else
383 return( status);
384
385 /* Stuff we do when we know we are done with the file */
386endgame:
387 close( tarFd);
388 if ( *(header.name) == '\0' ) {
389 if (errorFlag==FALSE)
390 return( TRUE);
391 }
392 return( FALSE);
Eric Andersencc8ed391999-10-05 16:24:54 +0000393}
394
Eric Andersencc8ed391999-10-05 16:24:54 +0000395/*
Erik Andersen298854f2000-03-23 01:09:18 +0000396 * Read an octal value in a field of the specified width, with optional
397 * spaces on both sides of the number and with an optional null character
398 * at the end. Returns -1 on an illegal format.
Eric Andersencc8ed391999-10-05 16:24:54 +0000399 */
Erik Andersen298854f2000-03-23 01:09:18 +0000400static long getOctal(const char *cp, int size)
Eric Andersencc8ed391999-10-05 16:24:54 +0000401{
Erik Andersen298854f2000-03-23 01:09:18 +0000402 long val = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000403
Erik Andersen298854f2000-03-23 01:09:18 +0000404 for(;(size > 0) && (*cp == ' '); cp++, size--);
405 if ((size == 0) || !isOctal(*cp))
406 return -1;
407 for(; (size > 0) && isOctal(*cp); size--) {
408 val = val * 8 + *cp++ - '0';
Eric Andersencc8ed391999-10-05 16:24:54 +0000409 }
Erik Andersen298854f2000-03-23 01:09:18 +0000410 for (;(size > 0) && (*cp == ' '); cp++, size--);
411 if ((size > 0) && *cp)
412 return -1;
413 return val;
414}
Eric Andersencc8ed391999-10-05 16:24:54 +0000415
Erik Andersen298854f2000-03-23 01:09:18 +0000416/* Parse the tar header and fill in the nice struct with the details */
417static int
418parseTarHeader(struct TarHeader *rawHeader, struct TarInfo *header)
419{
Erik Andersene454fb62000-03-23 04:27:58 +0000420 int i;
421 long chksum, sum;
422 unsigned char *s = (unsigned char *)rawHeader;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000423
Erik Andersen298854f2000-03-23 01:09:18 +0000424 header->name = rawHeader->name;
425 header->mode = getOctal(rawHeader->mode, sizeof(rawHeader->mode));
426 header->uid = getOctal(rawHeader->uid, sizeof(rawHeader->uid));
427 header->gid = getOctal(rawHeader->gid, sizeof(rawHeader->gid));
428 header->size = getOctal(rawHeader->size, sizeof(rawHeader->size));
429 header->mtime = getOctal(rawHeader->mtime, sizeof(rawHeader->mtime));
430 chksum = getOctal(rawHeader->chksum, sizeof(rawHeader->chksum));
431 header->type = rawHeader->typeflag;
432 header->linkname = rawHeader->linkname;
433 header->device = MAJOR(getOctal(rawHeader->devmajor, sizeof(rawHeader->devmajor))) |
434 MINOR(getOctal(rawHeader->devminor, sizeof(rawHeader->devminor)));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000435
Erik Andersen298854f2000-03-23 01:09:18 +0000436 /* Check the checksum */
437 sum = ' ' * sizeof(rawHeader->chksum);
438 for ( i = TarChecksumOffset; i > 0; i-- )
439 sum += *s++;
Erik Andersene454fb62000-03-23 04:27:58 +0000440 s += sizeof(rawHeader->chksum);
441 for ( i = (512 - TarChecksumOffset - sizeof(rawHeader->chksum)); i > 0; i-- )
Erik Andersen298854f2000-03-23 01:09:18 +0000442 sum += *s++;
Erik Andersene454fb62000-03-23 04:27:58 +0000443 if (sum == chksum )
Erik Andersen298854f2000-03-23 01:09:18 +0000444 return ( TRUE);
445 return( FALSE);
446}
Erik Andersene49d5ec2000-02-08 19:58:47 +0000447
Erik Andersen298854f2000-03-23 01:09:18 +0000448#if 0
449 if ((header->mode < 0) || (header->uid < 0) ||
450 (header->gid < 0) || (header->size < 0)) {
451 errorMsg(stderr, "Bad tar header, skipping\n");
452 return( FALSE);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000453 }
454
455 badHeader = FALSE;
456 skipFileFlag = FALSE;
457 devFileFlag = FALSE;
458
459 /*
460 * Check for the file modes.
461 */
462 hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) ||
463 (hp->typeFlag == TAR_TYPE_HARD_LINK - '0'));
464
465 softLink = ((hp->typeFlag == TAR_TYPE_SOFT_LINK) ||
466 (hp->typeFlag == TAR_TYPE_SOFT_LINK - '0'));
467
468 /*
469 * Check for a directory.
470 */
471 if (outName[strlen(outName) - 1] == '/')
472 mode |= S_IFDIR;
473
474 /*
475 * Check for absolute paths in the file.
476 * If we find any, then warn the user and make them relative.
477 */
478 if (*outName == '/') {
479 while (*outName == '/')
480 outName++;
481
482 if (warnedRoot == FALSE) {
483 fprintf(stderr,
484 "Absolute path detected, removing leading slashes\n");
485 }
486
487 warnedRoot = TRUE;
488 }
489
490 /*
491 * See if we want this file to be restored.
492 * If not, then set up to skip it.
493 */
Erik Andersen298854f2000-03-23 01:09:18 +0000494 if (wantFileName(outName, argc, argv) == FALSE) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000495 if (!hardLink && !softLink && (S_ISREG(mode) || S_ISCHR(mode)
496 || S_ISBLK(mode) || S_ISSOCK(mode)
497 || S_ISFIFO(mode))) {
498 inHeader = (size == 0) ? TRUE : FALSE;
499 dataCc = size;
500 }
501
502 skipFileFlag = TRUE;
503
504 return;
505 }
506
507 /*
508 * This file is to be handled.
509 * If we aren't extracting then just list information about the file.
510 */
511 if (extractFlag == FALSE) {
512 if (verboseFlag == TRUE) {
513 printf("%s %3d/%-d ", modeString(mode), uid, gid);
514 if (S_ISCHR(mode) || S_ISBLK(mode))
515 printf("%4d,%4d %s ", major, minor, timeString(mtime));
516 else
517 printf("%9ld %s ", size, timeString(mtime));
518 }
519 printf("%s", outName);
520
521 if (hardLink)
522 printf(" (link to \"%s\")", hp->linkName);
523 else if (softLink)
524 printf(" (symlink to \"%s\")", hp->linkName);
525 else if (S_ISREG(mode) || S_ISCHR(mode) || S_ISBLK(mode) ||
526 S_ISSOCK(mode) || S_ISFIFO(mode)) {
527 inHeader = (size == 0) ? TRUE : FALSE;
528 dataCc = size;
529 }
530
531 printf("\n");
532
533 return;
534 }
535
536 /*
537 * We really want to extract the file.
538 */
539 if (verboseFlag == TRUE)
540 printf("x %s\n", outName);
541
542 if (hardLink) {
543 if (link(hp->linkName, outName) < 0) {
544 perror(outName);
545 return;
546 }
547 /* Set the file time */
548 utb.actime = mtime;
549 utb.modtime = mtime;
550 utime(outName, &utb);
551 /* Set the file permissions */
552 chown(outName, uid, gid);
553 chmod(outName, mode);
554 return;
555 }
556
557 if (softLink) {
558#ifdef S_ISLNK
559 if (symlink(hp->linkName, outName) < 0) {
560 perror(outName);
561 return;
562 }
563 /* Try to change ownership of the symlink.
564 * If libs doesn't support that, don't bother.
565 * Changing the pointed-to file is the Wrong Thing(tm).
566 */
Erik Andersen7dc16072000-01-04 01:10:25 +0000567#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
Erik Andersene49d5ec2000-02-08 19:58:47 +0000568 lchown(outName, uid, gid);
Erik Andersen7dc16072000-01-04 01:10:25 +0000569#endif
570
Erik Andersene49d5ec2000-02-08 19:58:47 +0000571 /* Do not change permissions or date on symlink,
572 * since it changes the pointed to file instead. duh. */
Eric Andersencc8ed391999-10-05 16:24:54 +0000573#else
Erik Andersene49d5ec2000-02-08 19:58:47 +0000574 fprintf(stderr, "Cannot create symbolic links\n");
Eric Andersencc8ed391999-10-05 16:24:54 +0000575#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +0000576 return;
Erik Andersen06936df2000-01-23 02:14:20 +0000577 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000578
Erik Andersene49d5ec2000-02-08 19:58:47 +0000579 /* Set the umask for this process so it doesn't
580 * screw things up. */
581 umask(0);
Eric Andersencc8ed391999-10-05 16:24:54 +0000582
Erik Andersene49d5ec2000-02-08 19:58:47 +0000583 /*
584 * If the file is a directory, then just create the path.
585 */
586 if (S_ISDIR(mode)) {
587 if (createPath(outName, mode) == TRUE) {
Erik Andersene2729152000-02-18 21:34:17 +0000588 /* make the final component, just in case it was
589 * omitted by createPath() (which will skip the
590 * directory if it doesn't have a terminating '/')
591 */
592 mkdir(outName, mode);
593
Erik Andersene49d5ec2000-02-08 19:58:47 +0000594 /* Set the file time */
595 utb.actime = mtime;
596 utb.modtime = mtime;
597 utime(outName, &utb);
598 /* Set the file permissions */
599 chown(outName, uid, gid);
600 chmod(outName, mode);
601 return;
602 }
603 return;
Eric Andersen96bcfd31999-11-12 01:30:18 +0000604 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000605
606 /*
607 * There is a file to write.
608 * First create the path to it if necessary with default permissions.
609 */
610 createPath(outName, 0777);
611
612 inHeader = (size == 0) ? TRUE : FALSE;
613 dataCc = size;
614
615 /*
616 * Start the output file.
617 */
618 if (tostdoutFlag == TRUE)
619 outFd = fileno(stdout);
620 else {
621 if (S_ISCHR(mode) || S_ISBLK(mode) || S_ISSOCK(mode)) {
622 devFileFlag = TRUE;
623 outFd = mknod(outName, mode, makedev(major, minor));
624 } else if (S_ISFIFO(mode)) {
625 devFileFlag = TRUE;
626 outFd = mkfifo(outName, mode);
627 } else {
628 outFd = open(outName, O_WRONLY | O_CREAT | O_TRUNC, mode);
629 }
630 if (outFd < 0) {
631 perror(outName);
632 skipFileFlag = TRUE;
633 return;
634 }
635 /* Set the file time */
636 utb.actime = mtime;
637 utb.modtime = mtime;
638 utime(outName, &utb);
639 /* Set the file permissions */
640 chown(outName, uid, gid);
641 chmod(outName, mode);
Eric Andersen96bcfd31999-11-12 01:30:18 +0000642 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000643
Eric Andersencc8ed391999-10-05 16:24:54 +0000644
Erik Andersene49d5ec2000-02-08 19:58:47 +0000645 /*
646 * If the file is empty, then that's all we need to do.
647 */
648 if (size == 0 && (tostdoutFlag == FALSE) && (devFileFlag == FALSE)) {
649 close(outFd);
650 outFd = -1;
651 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000652}
653
654
655/*
656 * Handle a data block of some specified size that was read.
657 */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000658static void readData(const char *cp, int count)
Eric Andersencc8ed391999-10-05 16:24:54 +0000659{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000660 /*
661 * Reduce the amount of data left in this file.
662 * If there is no more data left, then we need to read
663 * the header again.
664 */
665 dataCc -= count;
Eric Andersencc8ed391999-10-05 16:24:54 +0000666
Erik Andersene49d5ec2000-02-08 19:58:47 +0000667 if (dataCc <= 0)
668 inHeader = TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000669
Erik Andersene49d5ec2000-02-08 19:58:47 +0000670 /*
671 * If we aren't extracting files or this file is being
672 * skipped then do nothing more.
673 */
674 if (extractFlag == FALSE || skipFileFlag == TRUE)
675 return;
Eric Andersencc8ed391999-10-05 16:24:54 +0000676
Erik Andersene49d5ec2000-02-08 19:58:47 +0000677 /*
678 * Write the data to the output file.
679 */
680 if (fullWrite(outFd, cp, count) < 0) {
681 perror(outName);
682 if (tostdoutFlag == FALSE) {
683 close(outFd);
684 outFd = -1;
685 }
686 skipFileFlag = TRUE;
687 return;
Eric Andersencc8ed391999-10-05 16:24:54 +0000688 }
689
Erik Andersene49d5ec2000-02-08 19:58:47 +0000690 /*
691 * Check if we are done writing to the file now.
692 */
693 if (dataCc <= 0 && tostdoutFlag == FALSE) {
694 struct utimbuf utb;
Eric Andersencc8ed391999-10-05 16:24:54 +0000695
Erik Andersene49d5ec2000-02-08 19:58:47 +0000696 if (close(outFd))
697 perror(outName);
Erik Andersen7dc16072000-01-04 01:10:25 +0000698
Erik Andersene49d5ec2000-02-08 19:58:47 +0000699 /* Set the file time */
700 utb.actime = mtime;
701 utb.modtime = mtime;
702 utime(outName, &utb);
703 /* Set the file permissions */
704 chown(outName, uid, gid);
705 chmod(outName, mode);
706
707 outFd = -1;
708 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000709}
710
711
712/*
Erik Andersen05100cd2000-01-16 01:30:52 +0000713 * See if the specified file name belongs to one of the specified list
714 * of path prefixes. An empty list implies that all files are wanted.
715 * Returns TRUE if the file is selected.
716 */
717static int
Erik Andersen298854f2000-03-23 01:09:18 +0000718wantFileName(const char *fileName, int argc, char **argv)
Erik Andersen05100cd2000-01-16 01:30:52 +0000719{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000720 const char *pathName;
721 int fileLength;
722 int pathLength;
Erik Andersen05100cd2000-01-16 01:30:52 +0000723
Erik Andersene49d5ec2000-02-08 19:58:47 +0000724 /*
725 * If there are no files in the list, then the file is wanted.
726 */
Erik Andersen298854f2000-03-23 01:09:18 +0000727 if (argc == 0)
Erik Andersene49d5ec2000-02-08 19:58:47 +0000728 return TRUE;
Erik Andersen05100cd2000-01-16 01:30:52 +0000729
Erik Andersene49d5ec2000-02-08 19:58:47 +0000730 fileLength = strlen(fileName);
Erik Andersen05100cd2000-01-16 01:30:52 +0000731
Erik Andersene49d5ec2000-02-08 19:58:47 +0000732 /*
733 * Check each of the test paths.
734 */
Erik Andersen298854f2000-03-23 01:09:18 +0000735 while (argc-- > 0) {
736 pathName = *argv++;
Erik Andersen05100cd2000-01-16 01:30:52 +0000737
Erik Andersene49d5ec2000-02-08 19:58:47 +0000738 pathLength = strlen(pathName);
Erik Andersen05100cd2000-01-16 01:30:52 +0000739
Erik Andersene49d5ec2000-02-08 19:58:47 +0000740 if (fileLength < pathLength)
741 continue;
Erik Andersen05100cd2000-01-16 01:30:52 +0000742
Erik Andersene49d5ec2000-02-08 19:58:47 +0000743 if (memcmp(fileName, pathName, pathLength) != 0)
744 continue;
Erik Andersen05100cd2000-01-16 01:30:52 +0000745
Erik Andersene49d5ec2000-02-08 19:58:47 +0000746 if ((fileLength == pathLength) || (fileName[pathLength] == '/')) {
747 return TRUE;
748 }
Erik Andersen05100cd2000-01-16 01:30:52 +0000749 }
Erik Andersen05100cd2000-01-16 01:30:52 +0000750
Erik Andersene49d5ec2000-02-08 19:58:47 +0000751 return FALSE;
Erik Andersen05100cd2000-01-16 01:30:52 +0000752}
753
Erik Andersen05100cd2000-01-16 01:30:52 +0000754
755/* From here to the end of the file is the tar writing stuff.
756 * If you do not have BB_FEATURE_TAR_CREATE defined, this will
757 * not be built.
758 * */
759#ifdef BB_FEATURE_TAR_CREATE
760
761/*
Eric Andersencc8ed391999-10-05 16:24:54 +0000762 * Write a tar file containing the specified files.
763 */
Erik Andersen298854f2000-03-23 01:09:18 +0000764static void writeTarFile(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000765{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000766 struct stat statbuf;
Eric Andersencc8ed391999-10-05 16:24:54 +0000767
Erik Andersene49d5ec2000-02-08 19:58:47 +0000768 /*
769 * Make sure there is at least one file specified.
770 */
Erik Andersen298854f2000-03-23 01:09:18 +0000771 if (argc <= 0) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000772 fprintf(stderr, "No files specified to be saved\n");
773 errorFlag = TRUE;
774 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000775
Erik Andersene49d5ec2000-02-08 19:58:47 +0000776 /*
777 * Create the tar file for writing.
778 */
779 if ((tarName == NULL) || !strcmp(tarName, "-")) {
780 tostdoutFlag = TRUE;
781 tarFd = fileno(stdout);
782 } else
783 tarFd = open(tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
Eric Andersencc8ed391999-10-05 16:24:54 +0000784
Erik Andersene49d5ec2000-02-08 19:58:47 +0000785 if (tarFd < 0) {
786 perror(tarName);
787 errorFlag = TRUE;
788 return;
789 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000790
Erik Andersene49d5ec2000-02-08 19:58:47 +0000791 /*
792 * Get the device and inode of the tar file for checking later.
793 */
794 if (fstat(tarFd, &statbuf) < 0) {
795 perror(tarName);
796 errorFlag = TRUE;
797 goto done;
798 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000799
Erik Andersene49d5ec2000-02-08 19:58:47 +0000800 tarDev = statbuf.st_dev;
801 tarInode = statbuf.st_ino;
Eric Andersencc8ed391999-10-05 16:24:54 +0000802
Erik Andersene49d5ec2000-02-08 19:58:47 +0000803 /*
804 * Append each file name into the archive file.
805 * Follow symbolic links for these top level file names.
806 */
Erik Andersen298854f2000-03-23 01:09:18 +0000807 while (errorFlag == FALSE && (argc-- > 0)) {
808 saveFile(*argv++, FALSE);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000809 }
810
811 /*
812 * Now write an empty block of zeroes to end the archive.
813 */
814 writeTarBlock("", 1);
Eric Andersencc8ed391999-10-05 16:24:54 +0000815
816
Eric Andersen3cf52d11999-10-12 22:26:06 +0000817 done:
Erik Andersene49d5ec2000-02-08 19:58:47 +0000818 /*
819 * Close the tar file and check for errors if it was opened.
820 */
821 if ((tostdoutFlag == FALSE) && (tarFd >= 0) && (close(tarFd) < 0))
822 perror(tarName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000823}
824
Eric Andersencc8ed391999-10-05 16:24:54 +0000825/*
826 * Save one file into the tar file.
827 * If the file is a directory, then this will recursively save all of
828 * the files and directories within the directory. The seeLinks
829 * flag indicates whether or not we want to see symbolic links as
830 * they really are, instead of blindly following them.
831 */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000832static void saveFile(const char *fileName, int seeLinks)
Eric Andersencc8ed391999-10-05 16:24:54 +0000833{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000834 int status;
835 struct stat statbuf;
Eric Andersencc8ed391999-10-05 16:24:54 +0000836
Erik Andersene49d5ec2000-02-08 19:58:47 +0000837 if (verboseFlag == TRUE)
838 printf("a %s\n", fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000839
Erik Andersene49d5ec2000-02-08 19:58:47 +0000840 /*
841 * Check that the file name will fit in the header.
842 */
843 if (strlen(fileName) >= TAR_NAME_SIZE) {
844 fprintf(stderr, "%s: File name is too long\n", fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000845
Erik Andersene49d5ec2000-02-08 19:58:47 +0000846 return;
847 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000848
Erik Andersene49d5ec2000-02-08 19:58:47 +0000849 /*
850 * Find out about the file.
851 */
Eric Andersencc8ed391999-10-05 16:24:54 +0000852#ifdef S_ISLNK
Erik Andersene49d5ec2000-02-08 19:58:47 +0000853 if (seeLinks == TRUE)
854 status = lstat(fileName, &statbuf);
855 else
Eric Andersencc8ed391999-10-05 16:24:54 +0000856#endif
Erik Andersene49d5ec2000-02-08 19:58:47 +0000857 status = stat(fileName, &statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000858
Erik Andersene49d5ec2000-02-08 19:58:47 +0000859 if (status < 0) {
860 perror(fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000861
Erik Andersene49d5ec2000-02-08 19:58:47 +0000862 return;
863 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000864
Erik Andersene49d5ec2000-02-08 19:58:47 +0000865 /*
866 * Make sure we aren't trying to save our file into itself.
867 */
868 if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode)) {
869 fprintf(stderr, "Skipping saving of archive file itself\n");
Eric Andersencc8ed391999-10-05 16:24:54 +0000870
Erik Andersene49d5ec2000-02-08 19:58:47 +0000871 return;
872 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000873
Erik Andersene49d5ec2000-02-08 19:58:47 +0000874 /*
875 * Check the type of file.
876 */
877 mode = statbuf.st_mode;
Eric Andersencc8ed391999-10-05 16:24:54 +0000878
Erik Andersene49d5ec2000-02-08 19:58:47 +0000879 if (S_ISDIR(mode)) {
880 saveDirectory(fileName, &statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000881
Erik Andersene49d5ec2000-02-08 19:58:47 +0000882 return;
883 }
884 if (S_ISREG(mode)) {
885 saveRegularFile(fileName, &statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000886
Erik Andersene49d5ec2000-02-08 19:58:47 +0000887 return;
888 }
889
890 /* Some day add support for tarring these up... but not today. :) */
Eric Andersen96bcfd31999-11-12 01:30:18 +0000891// if (S_ISLNK(mode) || S_ISFIFO(mode) || S_ISBLK(mode) || S_ISCHR (mode) ) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000892// fprintf (stderr, "%s: This version of tar can't store this type of file\n", fileName);
Eric Andersen96bcfd31999-11-12 01:30:18 +0000893// }
Eric Andersencc8ed391999-10-05 16:24:54 +0000894
Erik Andersene49d5ec2000-02-08 19:58:47 +0000895 /*
896 * The file is a strange type of file, ignore it.
897 */
898 fprintf(stderr, "%s: not a directory or regular file\n", fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000899}
900
901
902/*
903 * Save a regular file to the tar file.
904 */
905static void
Erik Andersene49d5ec2000-02-08 19:58:47 +0000906saveRegularFile(const char *fileName, const struct stat *statbuf)
Eric Andersencc8ed391999-10-05 16:24:54 +0000907{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000908 int sawEof;
909 int fileFd;
910 int cc;
911 int dataCount;
912 long fullDataCount;
913 char data[TAR_BLOCK_SIZE * 16];
Eric Andersen3cf52d11999-10-12 22:26:06 +0000914
915 /*
Erik Andersene49d5ec2000-02-08 19:58:47 +0000916 * Open the file for reading.
Eric Andersen3cf52d11999-10-12 22:26:06 +0000917 */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000918 fileFd = open(fileName, O_RDONLY);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000919
Erik Andersene49d5ec2000-02-08 19:58:47 +0000920 if (fileFd < 0) {
921 perror(fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000922
923 return;
924 }
925
Eric Andersen3cf52d11999-10-12 22:26:06 +0000926 /*
Erik Andersene49d5ec2000-02-08 19:58:47 +0000927 * Write out the header for the file.
Eric Andersencc8ed391999-10-05 16:24:54 +0000928 */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000929 writeHeader(fileName, statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000930
Eric Andersen3cf52d11999-10-12 22:26:06 +0000931 /*
Erik Andersene49d5ec2000-02-08 19:58:47 +0000932 * Write the data blocks of the file.
933 * We must be careful to write the amount of data that the stat
934 * buffer indicated, even if the file has changed size. Otherwise
935 * the tar file will be incorrect.
Eric Andersencc8ed391999-10-05 16:24:54 +0000936 */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000937 fullDataCount = statbuf->st_size;
938 sawEof = FALSE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000939
Erik Andersene49d5ec2000-02-08 19:58:47 +0000940 while (fullDataCount > 0) {
941 /*
942 * Get the amount to write this iteration which is
943 * the minumum of the amount left to write and the
944 * buffer size.
945 */
946 dataCount = sizeof(data);
Eric Andersencc8ed391999-10-05 16:24:54 +0000947
Erik Andersene49d5ec2000-02-08 19:58:47 +0000948 if (dataCount > fullDataCount)
949 dataCount = (int) fullDataCount;
950
951 /*
952 * Read the data from the file if we haven't seen the
953 * end of file yet.
954 */
955 cc = 0;
956
957 if (sawEof == FALSE) {
958 cc = fullRead(fileFd, data, dataCount);
959
960 if (cc < 0) {
961 perror(fileName);
962
963 (void) close(fileFd);
964 errorFlag = TRUE;
965
966 return;
967 }
968
969 /*
970 * If the file ended too soon, complain and set
971 * a flag so we will zero fill the rest of it.
972 */
973 if (cc < dataCount) {
974 fprintf(stderr, "%s: Short read - zero filling", fileName);
975
976 sawEof = TRUE;
977 }
978 }
979
980 /*
981 * Zero fill the rest of the data if necessary.
982 */
983 if (cc < dataCount)
984 memset(data + cc, 0, dataCount - cc);
985
986 /*
987 * Write the buffer to the TAR file.
988 */
989 writeTarBlock(data, dataCount);
990
991 fullDataCount -= dataCount;
992 }
993
994 /*
995 * Close the file.
996 */
997 if ((tostdoutFlag == FALSE) && close(fileFd) < 0)
998 fprintf(stderr, "%s: close: %s\n", fileName, strerror(errno));
Eric Andersencc8ed391999-10-05 16:24:54 +0000999}
1000
1001
1002/*
1003 * Save a directory and all of its files to the tar file.
1004 */
Erik Andersene49d5ec2000-02-08 19:58:47 +00001005static void saveDirectory(const char *dirName, const struct stat *statbuf)
Eric Andersencc8ed391999-10-05 16:24:54 +00001006{
Erik Andersene49d5ec2000-02-08 19:58:47 +00001007 DIR *dir;
1008 struct dirent *entry;
1009 int needSlash;
1010 char fullName[PATH_MAX + 1];
Eric Andersencc8ed391999-10-05 16:24:54 +00001011
Erik Andersene49d5ec2000-02-08 19:58:47 +00001012 /*
1013 * Construct the directory name as used in the tar file by appending
1014 * a slash character to it.
1015 */
1016 strcpy(fullName, dirName);
1017 strcat(fullName, "/");
Eric Andersencc8ed391999-10-05 16:24:54 +00001018
Erik Andersene49d5ec2000-02-08 19:58:47 +00001019 /*
1020 * Write out the header for the directory entry.
1021 */
1022 writeHeader(fullName, statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +00001023
Erik Andersene49d5ec2000-02-08 19:58:47 +00001024 /*
1025 * Open the directory.
1026 */
1027 dir = opendir(dirName);
Eric Andersencc8ed391999-10-05 16:24:54 +00001028
Erik Andersene49d5ec2000-02-08 19:58:47 +00001029 if (dir == NULL) {
1030 fprintf(stderr, "Cannot read directory \"%s\": %s\n",
1031 dirName, strerror(errno));
Eric Andersencc8ed391999-10-05 16:24:54 +00001032
Erik Andersene49d5ec2000-02-08 19:58:47 +00001033 return;
Eric Andersencc8ed391999-10-05 16:24:54 +00001034 }
1035
Eric Andersen3cf52d11999-10-12 22:26:06 +00001036 /*
Erik Andersene49d5ec2000-02-08 19:58:47 +00001037 * See if a slash is needed.
Eric Andersencc8ed391999-10-05 16:24:54 +00001038 */
Erik Andersene49d5ec2000-02-08 19:58:47 +00001039 needSlash = (*dirName && (dirName[strlen(dirName) - 1] != '/'));
Eric Andersen3cf52d11999-10-12 22:26:06 +00001040
1041 /*
Erik Andersene49d5ec2000-02-08 19:58:47 +00001042 * Read all of the directory entries and check them,
1043 * except for the current and parent directory entries.
Eric Andersencc8ed391999-10-05 16:24:54 +00001044 */
Erik Andersene49d5ec2000-02-08 19:58:47 +00001045 while (errorFlag == FALSE && ((entry = readdir(dir)) != NULL)) {
1046 if ((strcmp(entry->d_name, ".") == 0) ||
1047 (strcmp(entry->d_name, "..") == 0)) {
1048 continue;
1049 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001050
Erik Andersene49d5ec2000-02-08 19:58:47 +00001051 /*
1052 * Build the full path name to the file.
1053 */
1054 strcpy(fullName, dirName);
1055
1056 if (needSlash)
1057 strcat(fullName, "/");
1058
1059 strcat(fullName, entry->d_name);
1060
1061 /*
1062 * Write this file to the tar file, noticing whether or not
1063 * the file is a symbolic link.
1064 */
1065 saveFile(fullName, TRUE);
1066 }
1067
1068 /*
1069 * All done, close the directory.
1070 */
1071 closedir(dir);
Eric Andersencc8ed391999-10-05 16:24:54 +00001072}
1073
1074
1075/*
1076 * Write a tar header for the specified file name and status.
1077 * It is assumed that the file name fits.
1078 */
Erik Andersene49d5ec2000-02-08 19:58:47 +00001079static void writeHeader(const char *fileName, const struct stat *statbuf)
Eric Andersencc8ed391999-10-05 16:24:54 +00001080{
Erik Andersene49d5ec2000-02-08 19:58:47 +00001081 long checkSum;
1082 const unsigned char *cp;
1083 int len;
1084 TarHeader header;
Eric Andersencc8ed391999-10-05 16:24:54 +00001085
Erik Andersene49d5ec2000-02-08 19:58:47 +00001086 /*
1087 * Zero the header block in preparation for filling it in.
1088 */
1089 memset((char *) &header, 0, sizeof(header));
Eric Andersencc8ed391999-10-05 16:24:54 +00001090
Erik Andersene49d5ec2000-02-08 19:58:47 +00001091 /*
1092 * Fill in the header.
1093 */
1094 strcpy(header.name, fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +00001095
Erik Andersene49d5ec2000-02-08 19:58:47 +00001096 strncpy(header.magic, TAR_MAGIC, sizeof(header.magic));
1097 strncpy(header.version, TAR_VERSION, sizeof(header.version));
Eric Andersencc8ed391999-10-05 16:24:54 +00001098
Erik Andersene49d5ec2000-02-08 19:58:47 +00001099 putOctal(header.mode, sizeof(header.mode), statbuf->st_mode & 0777);
1100 putOctal(header.uid, sizeof(header.uid), statbuf->st_uid);
1101 putOctal(header.gid, sizeof(header.gid), statbuf->st_gid);
1102 putOctal(header.size, sizeof(header.size), statbuf->st_size);
1103 putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime);
Eric Andersencc8ed391999-10-05 16:24:54 +00001104
Erik Andersene49d5ec2000-02-08 19:58:47 +00001105 header.typeFlag = TAR_TYPE_REGULAR;
Eric Andersencc8ed391999-10-05 16:24:54 +00001106
Erik Andersene49d5ec2000-02-08 19:58:47 +00001107 /*
1108 * Calculate and store the checksum.
1109 * This is the sum of all of the bytes of the header,
1110 * with the checksum field itself treated as blanks.
1111 */
1112 memset(header.checkSum, ' ', sizeof(header.checkSum));
Eric Andersencc8ed391999-10-05 16:24:54 +00001113
Erik Andersene49d5ec2000-02-08 19:58:47 +00001114 cp = (const unsigned char *) &header;
1115 len = sizeof(header);
1116 checkSum = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +00001117
Erik Andersene49d5ec2000-02-08 19:58:47 +00001118 while (len-- > 0)
1119 checkSum += *cp++;
Eric Andersencc8ed391999-10-05 16:24:54 +00001120
Erik Andersene49d5ec2000-02-08 19:58:47 +00001121 putOctal(header.checkSum, sizeof(header.checkSum), checkSum);
Eric Andersencc8ed391999-10-05 16:24:54 +00001122
Erik Andersene49d5ec2000-02-08 19:58:47 +00001123 /*
1124 * Write the tar header.
1125 */
1126 writeTarBlock((const char *) &header, sizeof(header));
Eric Andersencc8ed391999-10-05 16:24:54 +00001127}
1128
1129
1130/*
1131 * Write data to one or more blocks of the tar file.
1132 * The data is always padded out to a multiple of TAR_BLOCK_SIZE.
1133 * The errorFlag static variable is set on an error.
1134 */
Erik Andersene49d5ec2000-02-08 19:58:47 +00001135static void writeTarBlock(const char *buf, int len)
Eric Andersencc8ed391999-10-05 16:24:54 +00001136{
Erik Andersene49d5ec2000-02-08 19:58:47 +00001137 int partialLength;
1138 int completeLength;
1139 char fullBlock[TAR_BLOCK_SIZE];
Eric Andersencc8ed391999-10-05 16:24:54 +00001140
Erik Andersene49d5ec2000-02-08 19:58:47 +00001141 /*
1142 * If we had a write error before, then do nothing more.
1143 */
1144 if (errorFlag == TRUE)
1145 return;
Eric Andersencc8ed391999-10-05 16:24:54 +00001146
Erik Andersene49d5ec2000-02-08 19:58:47 +00001147 /*
1148 * Get the amount of complete and partial blocks.
1149 */
1150 partialLength = len % TAR_BLOCK_SIZE;
1151 completeLength = len - partialLength;
Eric Andersencc8ed391999-10-05 16:24:54 +00001152
Erik Andersene49d5ec2000-02-08 19:58:47 +00001153 /*
1154 * Write all of the complete blocks.
1155 */
1156 if ((completeLength > 0) && !fullWrite(tarFd, buf, completeLength)) {
1157 perror(tarName);
Eric Andersencc8ed391999-10-05 16:24:54 +00001158
Erik Andersene49d5ec2000-02-08 19:58:47 +00001159 errorFlag = TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +00001160
Erik Andersene49d5ec2000-02-08 19:58:47 +00001161 return;
1162 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001163
Erik Andersene49d5ec2000-02-08 19:58:47 +00001164 /*
1165 * If there are no partial blocks left, we are done.
1166 */
1167 if (partialLength == 0)
1168 return;
Eric Andersencc8ed391999-10-05 16:24:54 +00001169
Erik Andersene49d5ec2000-02-08 19:58:47 +00001170 /*
1171 * Copy the partial data into a complete block, and pad the rest
1172 * of it with zeroes.
1173 */
1174 memcpy(fullBlock, buf + completeLength, partialLength);
1175 memset(fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength);
Eric Andersencc8ed391999-10-05 16:24:54 +00001176
Erik Andersene49d5ec2000-02-08 19:58:47 +00001177 /*
1178 * Write the last complete block.
1179 */
1180 if (!fullWrite(tarFd, fullBlock, TAR_BLOCK_SIZE)) {
1181 perror(tarName);
Eric Andersencc8ed391999-10-05 16:24:54 +00001182
Erik Andersene49d5ec2000-02-08 19:58:47 +00001183 errorFlag = TRUE;
1184 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001185}
1186
1187
1188/*
Eric Andersencc8ed391999-10-05 16:24:54 +00001189 * Put an octal string into the specified buffer.
1190 * The number is zero and space padded and possibly null padded.
1191 * Returns TRUE if successful.
1192 */
Erik Andersene49d5ec2000-02-08 19:58:47 +00001193static int putOctal(char *cp, int len, long value)
Eric Andersencc8ed391999-10-05 16:24:54 +00001194{
Erik Andersene49d5ec2000-02-08 19:58:47 +00001195 int tempLength;
1196 char *tempString;
1197 char tempBuffer[32];
Eric Andersencc8ed391999-10-05 16:24:54 +00001198
Erik Andersene49d5ec2000-02-08 19:58:47 +00001199 /*
1200 * Create a string of the specified length with an initial space,
1201 * leading zeroes and the octal number, and a trailing null.
1202 */
1203 tempString = tempBuffer;
Eric Andersencc8ed391999-10-05 16:24:54 +00001204
Erik Andersene49d5ec2000-02-08 19:58:47 +00001205 sprintf(tempString, " %0*lo", len - 2, value);
Eric Andersencc8ed391999-10-05 16:24:54 +00001206
Erik Andersene49d5ec2000-02-08 19:58:47 +00001207 tempLength = strlen(tempString) + 1;
Eric Andersencc8ed391999-10-05 16:24:54 +00001208
Erik Andersene49d5ec2000-02-08 19:58:47 +00001209 /*
1210 * If the string is too large, suppress the leading space.
1211 */
1212 if (tempLength > len) {
1213 tempLength--;
1214 tempString++;
1215 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001216
Erik Andersene49d5ec2000-02-08 19:58:47 +00001217 /*
1218 * If the string is still too large, suppress the trailing null.
1219 */
1220 if (tempLength > len)
1221 tempLength--;
Eric Andersencc8ed391999-10-05 16:24:54 +00001222
Erik Andersene49d5ec2000-02-08 19:58:47 +00001223 /*
1224 * If the string is still too large, fail.
1225 */
1226 if (tempLength > len)
1227 return FALSE;
Eric Andersencc8ed391999-10-05 16:24:54 +00001228
Erik Andersene49d5ec2000-02-08 19:58:47 +00001229 /*
1230 * Copy the string to the field.
1231 */
1232 memcpy(cp, tempString, len);
Eric Andersencc8ed391999-10-05 16:24:54 +00001233
Erik Andersene49d5ec2000-02-08 19:58:47 +00001234 return TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +00001235}
Erik Andersen05100cd2000-01-16 01:30:52 +00001236#endif
Eric Andersencc8ed391999-10-05 16:24:54 +00001237
Eric Andersencc8ed391999-10-05 16:24:54 +00001238/* END CODE */
Erik Andersen298854f2000-03-23 01:09:18 +00001239#endif