blob: 49d4d2ecf1cc3cccee5cfb3759010e33763f032b [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 Andersen68a9ea42000-04-04 18:39:50 +00003 * Mini tar implementation for busybox
Erik Andersen6acaa402000-03-26 14:03:20 +00004 *
Erik Andersen68a9ea42000-04-04 18:39:50 +00005 * Note, that as of BusyBox 0.43 tar has been completely rewritten from the
6 * ground up. It still has remnents of the old code lying about, but it pretty
7 * different (i.e. cleaner, less global variables, etc)
Eric Andersenc4996011999-10-20 22:08:37 +00008 *
Erik Andersen68a9ea42000-04-04 18:39:50 +00009 * Copyright (C) 2000 by Lineo, inc.
Erik Andersen1ad302a2000-03-24 00:54:46 +000010 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
Eric Andersen96bcfd31999-11-12 01:30:18 +000011 *
Erik Andersen6acaa402000-03-26 14:03:20 +000012 * Based in part in the tar implementation in sash
13 * Copyright (c) 1999 by David I. Bell
14 * Permission is granted to use, distribute, or modify this source,
15 * provided that this copyright notice remains intact.
16 * Permission to distribute sash derived code under the GPL has been granted.
17 *
Erik Andersen68a9ea42000-04-04 18:39:50 +000018 * Based in part on the tar implementation from busybox-0.28
Erik Andersen6acaa402000-03-26 14:03:20 +000019 * Copyright (C) 1995 Bruce Perens
20 * This is free software under the GNU General Public License.
21 *
Eric Andersenc4996011999-10-20 22:08:37 +000022 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 * General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 *
Eric Andersencc8ed391999-10-05 16:24:54 +000036 */
37
38
39#include "internal.h"
Erik Andersen1ad302a2000-03-24 00:54:46 +000040#define BB_DECLARE_EXTERN
41#define bb_need_io_error
42#include "messages.c"
Eric Andersencc8ed391999-10-05 16:24:54 +000043#include <stdio.h>
44#include <dirent.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <signal.h>
48#include <time.h>
Erik Andersen7dc16072000-01-04 01:10:25 +000049#include <utime.h>
Eric Andersen96bcfd31999-11-12 01:30:18 +000050#include <sys/types.h>
Eric Andersen08b10341999-11-19 02:38:58 +000051#include <sys/sysmacros.h>
Erik Andersene49d5ec2000-02-08 19:58:47 +000052#include <sys/param.h> /* for PATH_MAX */
Eric Andersencc8ed391999-10-05 16:24:54 +000053
Eric Andersene77ae3a1999-10-19 20:03:34 +000054
Erik Andersen05100cd2000-01-16 01:30:52 +000055#ifdef BB_FEATURE_TAR_CREATE
56
Eric Andersene77ae3a1999-10-19 20:03:34 +000057static const char tar_usage[] =
Erik Andersenecd51242000-04-08 03:08:21 +000058 "tar -[cxtvOf] [tarFile] [-X excludeFile] [FILE] ...\n\n"
Erik Andersen5661fe02000-04-05 01:00:52 +000059 "Create, extract, or list files from a tar file. Note that\n"
60 "this version of tar packs hard links as separate files.\n\n"
Erik Andersene49d5ec2000-02-08 19:58:47 +000061 "Options:\n"
62
63 "\tc=create, x=extract, t=list contents, v=verbose,\n"
Erik Andersenecd51242000-04-08 03:08:21 +000064 "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n"
65 "\tX=exclude file\n";
Eric Andersene77ae3a1999-10-19 20:03:34 +000066
Erik Andersen05100cd2000-01-16 01:30:52 +000067#else
68
69static const char tar_usage[] =
Erik Andersenecd51242000-04-08 03:08:21 +000070 "tar -[xtvO] [-f tarFile] [-X excludeFile] [FILE] ...\n\n"
Erik Andersene49d5ec2000-02-08 19:58:47 +000071 "Extract, or list files stored in a tar file. This\n"
72 "version of tar does not support creation of tar files.\n\n"
73 "Options:\n"
74
75 "\tx=extract, t=list contents, v=verbose,\n"
Erik Andersenecd51242000-04-08 03:08:21 +000076 "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n"
77 "\tX=exclude file\n";
78
Erik Andersen05100cd2000-01-16 01:30:52 +000079
80#endif
Eric Andersene77ae3a1999-10-19 20:03:34 +000081
82
Erik Andersen298854f2000-03-23 01:09:18 +000083/* Tar file constants */
Erik Andersen1ad302a2000-03-24 00:54:46 +000084#ifndef MAJOR
85#define MAJOR(dev) (((dev)>>8)&0xff)
86#define MINOR(dev) ((dev)&0xff)
87#endif
Eric Andersencc8ed391999-10-05 16:24:54 +000088
89
Erik Andersen298854f2000-03-23 01:09:18 +000090/* POSIX tar Header Block, from POSIX 1003.1-1990 */
91struct TarHeader
92{
93 /* byte offset */
94 char name[100]; /* 0 */
95 char mode[8]; /* 100 */
96 char uid[8]; /* 108 */
97 char gid[8]; /* 116 */
98 char size[12]; /* 124 */
99 char mtime[12]; /* 136 */
100 char chksum[8]; /* 148 */
101 char typeflag; /* 156 */
102 char linkname[100]; /* 157 */
103 char magic[6]; /* 257 */
104 char version[2]; /* 263 */
105 char uname[32]; /* 265 */
106 char gname[32]; /* 297 */
107 char devmajor[8]; /* 329 */
108 char devminor[8]; /* 337 */
109 char prefix[155]; /* 345 */
110 /* padding 500 */
111};
112typedef struct TarHeader TarHeader;
Eric Andersencc8ed391999-10-05 16:24:54 +0000113
Eric Andersencc8ed391999-10-05 16:24:54 +0000114
Erik Andersen298854f2000-03-23 01:09:18 +0000115/* A few useful constants */
116#define TAR_MAGIC "ustar" /* ustar and a null */
Erik Andersen5661fe02000-04-05 01:00:52 +0000117//#define TAR_VERSION "00" /* 00 and no null */
118#define TAR_VERSION " " /* Be compatable with old GNU format */
Erik Andersen298854f2000-03-23 01:09:18 +0000119#define TAR_MAGIC_LEN 6
120#define TAR_VERSION_LEN 2
Erik Andersen298854f2000-03-23 01:09:18 +0000121#define TAR_BLOCK_SIZE 512
122
123/* A nice enum with all the possible tar file content types */
124enum TarFileType
125{
126 REGTYPE = '0', /* regular file */
127 REGTYPE0 = '\0', /* regular file (ancient bug compat)*/
128 LNKTYPE = '1', /* hard link */
129 SYMTYPE = '2', /* symbolic link */
130 CHRTYPE = '3', /* character special */
131 BLKTYPE = '4', /* block special */
132 DIRTYPE = '5', /* directory */
133 FIFOTYPE = '6', /* FIFO special */
134 CONTTYPE = '7', /* reserved */
135};
136typedef enum TarFileType TarFileType;
137
138/* This struct ignores magic, non-numeric user name,
139 * non-numeric group name, and the checksum, since
140 * these are all ignored by BusyBox tar. */
141struct TarInfo
142{
143 int tarFd; /* An open file descriptor for reading from the tarball */
144 char * name; /* File name */
145 mode_t mode; /* Unix mode, including device bits. */
146 uid_t uid; /* Numeric UID */
147 gid_t gid; /* Numeric GID */
148 size_t size; /* Size of file */
149 time_t mtime; /* Last-modified time */
150 enum TarFileType type; /* Regular, directory, link, etc */
151 char * linkname; /* Name for symbolic and hard links */
Erik Andersen1ad302a2000-03-24 00:54:46 +0000152 long devmajor; /* Major number for special device */
153 long devminor; /* Minor number for special device */
Erik Andersen298854f2000-03-23 01:09:18 +0000154};
155typedef struct TarInfo TarInfo;
156
157/* Static data */
158static const unsigned long TarChecksumOffset = (const unsigned long)&(((TarHeader *)0)->chksum);
Eric Andersencc8ed391999-10-05 16:24:54 +0000159
160
Erik Andersene454fb62000-03-23 04:27:58 +0000161/* Local procedures to restore files from a tar file. */
Erik Andersen298854f2000-03-23 01:09:18 +0000162static int readTarFile(const char* tarName, int extractFlag, int listFlag,
Erik Andersenecd51242000-04-08 03:08:21 +0000163 int tostdoutFlag, int verboseFlag, char** excludeList);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000164
165
Eric Andersencc8ed391999-10-05 16:24:54 +0000166
Erik Andersen05100cd2000-01-16 01:30:52 +0000167#ifdef BB_FEATURE_TAR_CREATE
Erik Andersen6acaa402000-03-26 14:03:20 +0000168/* Local procedures to save files into a tar file. */
Erik Andersen68a9ea42000-04-04 18:39:50 +0000169static int writeTarFile(const char* tarName, int tostdoutFlag,
Erik Andersenecd51242000-04-08 03:08:21 +0000170 int verboseFlag, int argc, char **argv, char** excludeList);
Erik Andersen05100cd2000-01-16 01:30:52 +0000171#endif
172
Eric Andersencc8ed391999-10-05 16:24:54 +0000173
Erik Andersene49d5ec2000-02-08 19:58:47 +0000174extern int tar_main(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000175{
Erik Andersenecd51242000-04-08 03:08:21 +0000176 char** excludeList=NULL;
177 int excludeListSize=0;
Erik Andersen298854f2000-03-23 01:09:18 +0000178 const char *tarName=NULL;
Erik Andersen298854f2000-03-23 01:09:18 +0000179 int listFlag = FALSE;
180 int extractFlag = FALSE;
181 int createFlag = FALSE;
182 int verboseFlag = FALSE;
183 int tostdoutFlag = FALSE;
Erik Andersenecd51242000-04-08 03:08:21 +0000184 int stopIt;
Eric Andersencc8ed391999-10-05 16:24:54 +0000185
Erik Andersenecd51242000-04-08 03:08:21 +0000186 if (argc <= 1)
Erik Andersene49d5ec2000-02-08 19:58:47 +0000187 usage(tar_usage);
Eric Andersencc8ed391999-10-05 16:24:54 +0000188
Erik Andersenecd51242000-04-08 03:08:21 +0000189 /* Parse any options */
190 while (--argc > 0 && **(++argv) == '-') {
191 stopIt=FALSE;
192 while (stopIt==FALSE && *(++(*argv))) {
193 switch (**argv) {
194 case 'f':
195 if (--argc == 0) {
196 fatalError( "Option requires an argument: No file specified\n");
197 }
198 if (tarName != NULL)
199 fatalError( "Only one 'f' option allowed\n");
200 tarName = *(++argv);
201 if (tarName == NULL)
202 fatalError( "Option requires an argument: No file specified\n");
203 stopIt=TRUE;
204 break;
Eric Andersencc8ed391999-10-05 16:24:54 +0000205
Erik Andersenecd51242000-04-08 03:08:21 +0000206 case 't':
207 if (extractFlag == TRUE || createFlag == TRUE)
208 goto flagError;
209 listFlag = TRUE;
210 break;
Erik Andersende552872000-01-23 01:34:05 +0000211
Erik Andersenecd51242000-04-08 03:08:21 +0000212 case 'x':
213 if (listFlag == TRUE || createFlag == TRUE)
214 goto flagError;
215 extractFlag = TRUE;
216 break;
217 case 'c':
218 if (extractFlag == TRUE || listFlag == TRUE)
219 goto flagError;
220 createFlag = TRUE;
221 break;
Erik Andersende552872000-01-23 01:34:05 +0000222
Erik Andersenecd51242000-04-08 03:08:21 +0000223 case 'v':
224 verboseFlag = TRUE;
225 break;
Erik Andersende552872000-01-23 01:34:05 +0000226
Erik Andersenecd51242000-04-08 03:08:21 +0000227 case 'O':
228 tostdoutFlag = TRUE;
229 tarName = "-";
230 break;
231 case 'X':
232 if (--argc == 0) {
233 fatalError( "Option requires an argument: No file specified\n");
234 }
235 excludeList=realloc( excludeList, sizeof(char**) * (excludeListSize+1));
236 excludeList[excludeListSize] = *(++argv);
237 /* Remove leading "/"s */
238 if (*excludeList[excludeListSize] =='/') {
239 excludeList[excludeListSize] = (excludeList[excludeListSize])+1;
240 }
241 if (excludeList[excludeListSize++] == NULL)
242 fatalError( "Option requires an argument: No file specified\n");
243 stopIt=TRUE;
244 break;
Erik Andersende552872000-01-23 01:34:05 +0000245
Erik Andersenecd51242000-04-08 03:08:21 +0000246 case '-':
247 usage(tar_usage);
248 break;
Erik Andersende552872000-01-23 01:34:05 +0000249
Erik Andersenecd51242000-04-08 03:08:21 +0000250 default:
251 fatalError( "Unknown tar flag '%c'\n"
252 "Try `tar --help' for more information\n", **argv);
253 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000254 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000255 }
Erik Andersenecd51242000-04-08 03:08:21 +0000256#if 0
257 for (i=0; i<excludeListSize; i++) {
258 printf( "%s\n", excludeList[i]);
259 fflush(stdout);
260 }
261#endif
262
Eric Andersencc8ed391999-10-05 16:24:54 +0000263
Erik Andersene49d5ec2000-02-08 19:58:47 +0000264 /*
265 * Do the correct type of action supplying the rest of the
266 * command line arguments as the list of files to process.
267 */
268 if (createFlag == TRUE) {
Erik Andersende552872000-01-23 01:34:05 +0000269#ifndef BB_FEATURE_TAR_CREATE
Erik Andersen298854f2000-03-23 01:09:18 +0000270 fatalError( "This version of tar was not compiled with tar creation support.\n");
Erik Andersende552872000-01-23 01:34:05 +0000271#else
Erik Andersenecd51242000-04-08 03:08:21 +0000272 exit(writeTarFile(tarName, tostdoutFlag, verboseFlag, argc, argv, excludeList));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000273#endif
274 } else {
Erik Andersenecd51242000-04-08 03:08:21 +0000275 exit(readTarFile(tarName, extractFlag, listFlag, tostdoutFlag, verboseFlag, excludeList));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000276 }
Erik Andersende552872000-01-23 01:34:05 +0000277
Erik Andersene49d5ec2000-02-08 19:58:47 +0000278 flagError:
Erik Andersen298854f2000-03-23 01:09:18 +0000279 fatalError( "Exactly one of 'c', 'x' or 't' must be specified\n");
280}
281
282static void
Erik Andersen6a34b532000-04-07 06:55:38 +0000283fixUpPermissions(TarInfo *header)
284{
285 struct utimbuf t;
286 /* Now set permissions etc for the new file */
287 chown(header->name, header->uid, header->gid);
288 chmod(header->name, header->mode);
289 /* Reset the time */
290 t.actime = time(0);
291 t.modtime = header->mtime;
292 utime(header->name, &t);
293}
294
295static int
Erik Andersen1ad302a2000-03-24 00:54:46 +0000296tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersen298854f2000-03-23 01:09:18 +0000297{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000298 size_t writeSize;
299 size_t readSize;
300 size_t actualWriteSz;
301 char buffer[BUFSIZ];
302 size_t size = header->size;
303 int outFd=fileno(stdout);
304
305 /* Open the file to be written, if a file is supposed to be written */
306 if (extractFlag==TRUE && tostdoutFlag==FALSE) {
307 if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, header->mode & ~S_IFMT)) < 0)
308 errorMsg(io_error, header->name, strerror(errno));
309 /* Create the path to the file, just in case it isn't there...
310 * This should not screw up path permissions or anything. */
311 createPath(header->name, 0777);
312 }
313
314 /* Write out the file, if we are supposed to be doing that */
315 while ( size > 0 ) {
316 actualWriteSz=0;
317 if ( size > sizeof(buffer) )
318 writeSize = readSize = sizeof(buffer);
319 else {
320 int mod = size % 512;
321 if ( mod != 0 )
322 readSize = size + (512 - mod);
323 else
324 readSize = size;
325 writeSize = size;
326 }
327 if ( (readSize = fullRead(header->tarFd, buffer, readSize)) <= 0 ) {
328 /* Tarball seems to have a problem */
Erik Andersendeea0482000-03-25 23:09:29 +0000329 errorMsg("tar: Unexpected EOF in archive\n");
Erik Andersen6a34b532000-04-07 06:55:38 +0000330 return( FALSE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000331 }
332 if ( readSize < writeSize )
333 writeSize = readSize;
334
335 /* Write out the file, if we are supposed to be doing that */
336 if (extractFlag==TRUE) {
337
338 if ((actualWriteSz=fullWrite(outFd, buffer, writeSize)) != writeSize ) {
339 /* Output file seems to have a problem */
340 errorMsg(io_error, header->name, strerror(errno));
Erik Andersen6a34b532000-04-07 06:55:38 +0000341 return( FALSE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000342 }
343 }
344
345 size -= actualWriteSz;
346 }
347
348 /* Now we are done writing the file out, so try
349 * and fix up the permissions and whatnot */
350 if (extractFlag==TRUE && tostdoutFlag==FALSE) {
Erik Andersen1ad302a2000-03-24 00:54:46 +0000351 close(outFd);
Erik Andersen6a34b532000-04-07 06:55:38 +0000352 fixUpPermissions(header);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000353 }
Erik Andersen6a34b532000-04-07 06:55:38 +0000354 return( TRUE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000355}
356
Erik Andersen6a34b532000-04-07 06:55:38 +0000357static int
Erik Andersen1ad302a2000-03-24 00:54:46 +0000358tarExtractDirectory(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersene454fb62000-03-23 04:27:58 +0000359{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000360
361 if (extractFlag==FALSE || tostdoutFlag==TRUE)
Erik Andersen6a34b532000-04-07 06:55:38 +0000362 return( TRUE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000363
364 if (createPath(header->name, header->mode) != TRUE) {
Erik Andersen6a34b532000-04-07 06:55:38 +0000365 errorMsg("tar: %s: Cannot mkdir: %s\n",
366 header->name, strerror(errno));
367 return( FALSE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000368 }
369 /* make the final component, just in case it was
370 * omitted by createPath() (which will skip the
371 * directory if it doesn't have a terminating '/') */
Erik Andersen6acaa402000-03-26 14:03:20 +0000372 if (mkdir(header->name, header->mode) == 0) {
373 fixUpPermissions(header);
374 }
Erik Andersen6a34b532000-04-07 06:55:38 +0000375 return( TRUE);
Erik Andersene454fb62000-03-23 04:27:58 +0000376}
377
Erik Andersen6a34b532000-04-07 06:55:38 +0000378static int
Erik Andersen1ad302a2000-03-24 00:54:46 +0000379tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersene454fb62000-03-23 04:27:58 +0000380{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000381 if (extractFlag==FALSE || tostdoutFlag==TRUE)
Erik Andersen6a34b532000-04-07 06:55:38 +0000382 return( TRUE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000383
384 if (link(header->linkname, header->name) < 0) {
Erik Andersen6a34b532000-04-07 06:55:38 +0000385 errorMsg("tar: %s: Cannot create hard link to '%s': %s\n",
Erik Andersen5661fe02000-04-05 01:00:52 +0000386 header->name, header->linkname, strerror(errno));
Erik Andersen6a34b532000-04-07 06:55:38 +0000387 return( FALSE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000388 }
389
390 /* Now set permissions etc for the new directory */
391 fixUpPermissions(header);
Erik Andersen6a34b532000-04-07 06:55:38 +0000392 return( TRUE);
Erik Andersene454fb62000-03-23 04:27:58 +0000393}
394
Erik Andersen6a34b532000-04-07 06:55:38 +0000395static int
Erik Andersen1ad302a2000-03-24 00:54:46 +0000396tarExtractSymLink(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersene454fb62000-03-23 04:27:58 +0000397{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000398 if (extractFlag==FALSE || tostdoutFlag==TRUE)
Erik Andersen6a34b532000-04-07 06:55:38 +0000399 return( TRUE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000400
401#ifdef S_ISLNK
402 if (symlink(header->linkname, header->name) < 0) {
Erik Andersen6a34b532000-04-07 06:55:38 +0000403 errorMsg("tar: %s: Cannot create symlink to '%s': %s\n",
Erik Andersen5661fe02000-04-05 01:00:52 +0000404 header->name, header->linkname, strerror(errno));
Erik Andersen6a34b532000-04-07 06:55:38 +0000405 return( FALSE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000406 }
407 /* Try to change ownership of the symlink.
408 * If libs doesn't support that, don't bother.
409 * Changing the pointed-to-file is the Wrong Thing(tm).
410 */
411#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
412 lchown(header->name, header->uid, header->gid);
413#endif
414
415 /* Do not change permissions or date on symlink,
416 * since it changes the pointed to file instead. duh. */
417#else
Erik Andersen6a34b532000-04-07 06:55:38 +0000418 errorMsg("tar: %s: Cannot create symlink to '%s': %s\n",
419 header->name, header->linkname,
420 "symlinks not supported");
Erik Andersen1ad302a2000-03-24 00:54:46 +0000421#endif
Erik Andersen6a34b532000-04-07 06:55:38 +0000422 return( TRUE);
Erik Andersene454fb62000-03-23 04:27:58 +0000423}
424
Erik Andersen6a34b532000-04-07 06:55:38 +0000425static int
Erik Andersen1ad302a2000-03-24 00:54:46 +0000426tarExtractSpecial(TarInfo *header, int extractFlag, int tostdoutFlag)
Erik Andersene454fb62000-03-23 04:27:58 +0000427{
Erik Andersen1ad302a2000-03-24 00:54:46 +0000428 if (extractFlag==FALSE || tostdoutFlag==TRUE)
Erik Andersen6a34b532000-04-07 06:55:38 +0000429 return( TRUE);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000430
431 if (S_ISCHR(header->mode) || S_ISBLK(header->mode) || S_ISSOCK(header->mode)) {
Erik Andersen6a34b532000-04-07 06:55:38 +0000432 if (mknod(header->name, header->mode, makedev(header->devmajor, header->devminor)) < 0) {
433 errorMsg("tar: %s: Cannot mknod: %s\n",
434 header->name, strerror(errno));
435 return( FALSE);
436 }
Erik Andersen1ad302a2000-03-24 00:54:46 +0000437 } else if (S_ISFIFO(header->mode)) {
Erik Andersen6a34b532000-04-07 06:55:38 +0000438 if (mkfifo(header->name, header->mode) < 0) {
439 errorMsg("tar: %s: Cannot mkfifo: %s\n",
440 header->name, strerror(errno));
441 return( FALSE);
442 }
Erik Andersen1ad302a2000-03-24 00:54:46 +0000443 }
444
445 /* Now set permissions etc for the new directory */
446 fixUpPermissions(header);
Erik Andersen6a34b532000-04-07 06:55:38 +0000447 return( TRUE);
Erik Andersene454fb62000-03-23 04:27:58 +0000448}
449
Erik Andersen1ad302a2000-03-24 00:54:46 +0000450/* Read an octal value in a field of the specified width, with optional
Erik Andersen298854f2000-03-23 01:09:18 +0000451 * spaces on both sides of the number and with an optional null character
Erik Andersen1ad302a2000-03-24 00:54:46 +0000452 * at the end. Returns -1 on an illegal format. */
Erik Andersen298854f2000-03-23 01:09:18 +0000453static long getOctal(const char *cp, int size)
Eric Andersencc8ed391999-10-05 16:24:54 +0000454{
Erik Andersen298854f2000-03-23 01:09:18 +0000455 long val = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000456
Erik Andersen298854f2000-03-23 01:09:18 +0000457 for(;(size > 0) && (*cp == ' '); cp++, size--);
458 if ((size == 0) || !isOctal(*cp))
459 return -1;
460 for(; (size > 0) && isOctal(*cp); size--) {
461 val = val * 8 + *cp++ - '0';
Eric Andersencc8ed391999-10-05 16:24:54 +0000462 }
Erik Andersen298854f2000-03-23 01:09:18 +0000463 for (;(size > 0) && (*cp == ' '); cp++, size--);
464 if ((size > 0) && *cp)
465 return -1;
466 return val;
467}
Eric Andersencc8ed391999-10-05 16:24:54 +0000468
Erik Andersen1ad302a2000-03-24 00:54:46 +0000469
Erik Andersen298854f2000-03-23 01:09:18 +0000470/* Parse the tar header and fill in the nice struct with the details */
471static int
Erik Andersen3364d782000-03-28 00:58:14 +0000472readTarHeader(struct TarHeader *rawHeader, struct TarInfo *header)
Erik Andersen298854f2000-03-23 01:09:18 +0000473{
Erik Andersene454fb62000-03-23 04:27:58 +0000474 int i;
475 long chksum, sum;
476 unsigned char *s = (unsigned char *)rawHeader;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000477
Erik Andersen298854f2000-03-23 01:09:18 +0000478 header->name = rawHeader->name;
479 header->mode = getOctal(rawHeader->mode, sizeof(rawHeader->mode));
480 header->uid = getOctal(rawHeader->uid, sizeof(rawHeader->uid));
481 header->gid = getOctal(rawHeader->gid, sizeof(rawHeader->gid));
482 header->size = getOctal(rawHeader->size, sizeof(rawHeader->size));
483 header->mtime = getOctal(rawHeader->mtime, sizeof(rawHeader->mtime));
484 chksum = getOctal(rawHeader->chksum, sizeof(rawHeader->chksum));
485 header->type = rawHeader->typeflag;
486 header->linkname = rawHeader->linkname;
Erik Andersen1ad302a2000-03-24 00:54:46 +0000487 header->devmajor = getOctal(rawHeader->devmajor, sizeof(rawHeader->devmajor));
488 header->devminor = getOctal(rawHeader->devminor, sizeof(rawHeader->devminor));
Erik Andersene49d5ec2000-02-08 19:58:47 +0000489
Erik Andersen298854f2000-03-23 01:09:18 +0000490 /* Check the checksum */
491 sum = ' ' * sizeof(rawHeader->chksum);
492 for ( i = TarChecksumOffset; i > 0; i-- )
493 sum += *s++;
Erik Andersene454fb62000-03-23 04:27:58 +0000494 s += sizeof(rawHeader->chksum);
495 for ( i = (512 - TarChecksumOffset - sizeof(rawHeader->chksum)); i > 0; i-- )
Erik Andersen298854f2000-03-23 01:09:18 +0000496 sum += *s++;
Erik Andersene454fb62000-03-23 04:27:58 +0000497 if (sum == chksum )
Erik Andersen298854f2000-03-23 01:09:18 +0000498 return ( TRUE);
499 return( FALSE);
500}
Erik Andersene49d5ec2000-02-08 19:58:47 +0000501
Erik Andersene49d5ec2000-02-08 19:58:47 +0000502
Erik Andersen1ad302a2000-03-24 00:54:46 +0000503/*
504 * Read a tar file and extract or list the specified files within it.
505 * If the list is empty than all files are extracted or listed.
506 */
507static int readTarFile(const char* tarName, int extractFlag, int listFlag,
Erik Andersenecd51242000-04-08 03:08:21 +0000508 int tostdoutFlag, int verboseFlag, char** excludeList)
Erik Andersen1ad302a2000-03-24 00:54:46 +0000509{
510 int status, tarFd=0;
511 int errorFlag=FALSE;
512 TarHeader rawHeader;
513 TarInfo header;
514 int alreadyWarned=FALSE;
515 //int skipFileFlag=FALSE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000516
Erik Andersen1ad302a2000-03-24 00:54:46 +0000517 /* Open the tar file for reading. */
518 if (!strcmp(tarName, "-"))
519 tarFd = fileno(stdin);
520 else
521 tarFd = open(tarName, O_RDONLY);
522 if (tarFd < 0) {
Erik Andersendeea0482000-03-25 23:09:29 +0000523 errorMsg( "Error opening '%s': %s\n", tarName, strerror(errno));
Erik Andersen1ad302a2000-03-24 00:54:46 +0000524 return ( FALSE);
Erik Andersen06936df2000-01-23 02:14:20 +0000525 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000526
Erik Andersene49d5ec2000-02-08 19:58:47 +0000527 /* Set the umask for this process so it doesn't
Erik Andersen1ad302a2000-03-24 00:54:46 +0000528 * screw up permission setting for us later. */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000529 umask(0);
Eric Andersencc8ed391999-10-05 16:24:54 +0000530
Erik Andersen1ad302a2000-03-24 00:54:46 +0000531 /* Read the tar file, and iterate over it one file at a time */
532 while ( (status = fullRead(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) {
Erik Andersene2729152000-02-18 21:34:17 +0000533
Erik Andersen1ad302a2000-03-24 00:54:46 +0000534 /* First, try to read the header */
Erik Andersen3364d782000-03-28 00:58:14 +0000535 if ( readTarHeader(&rawHeader, &header) == FALSE ) {
Erik Andersen1ad302a2000-03-24 00:54:46 +0000536 close( tarFd);
537 if ( *(header.name) == '\0' ) {
538 goto endgame;
539 } else {
540 errorFlag=TRUE;
541 errorMsg("Bad tar header, skipping\n");
542 continue;
543 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000544 }
Erik Andersen1ad302a2000-03-24 00:54:46 +0000545 if ( *(header.name) == '\0' )
546 goto endgame;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000547
Erik Andersen1ad302a2000-03-24 00:54:46 +0000548 /* Check for and relativify any absolute paths */
549 if ( *(header.name) == '/' ) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000550
Erik Andersen1ad302a2000-03-24 00:54:46 +0000551 while (*(header.name) == '/')
552 ++*(header.name);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000553
Erik Andersen1ad302a2000-03-24 00:54:46 +0000554 if (alreadyWarned == FALSE) {
555 errorMsg("Absolute path detected, removing leading slashes\n");
556 alreadyWarned = TRUE;
557 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000558 }
Erik Andersen1ad302a2000-03-24 00:54:46 +0000559
560 /* Special treatment if the list (-t) flag is on */
561 if (verboseFlag == TRUE && extractFlag == FALSE) {
562 int len, len1;
563 char buf[35];
564 struct tm *tm = localtime (&(header.mtime));
565
Erik Andersen6a34b532000-04-07 06:55:38 +0000566 len=printf("%s ", modeString(header.mode));
567 memset(buf, 0, 8*sizeof(char));
568 my_getpwuid(buf, header.uid);
569 if (! *buf)
570 len+=printf("%d", header.uid);
571 else
572 len+=printf("%s", buf);
573 memset(buf, 0, 8*sizeof(char));
574 my_getgrgid(buf, header.gid);
575 if (! *buf)
576 len+=printf("/%-d ", header.gid);
577 else
578 len+=printf("/%-s ", buf);
579
Erik Andersen1ad302a2000-03-24 00:54:46 +0000580 if (header.type==CHRTYPE || header.type==BLKTYPE) {
581 len1=snprintf(buf, sizeof(buf), "%ld,%-ld ",
582 header.devmajor, header.devminor);
583 } else {
584 len1=snprintf(buf, sizeof(buf), "%d ", header.size);
585 }
586 /* Jump through some hoops to make the columns match up */
587 for(;(len+len1)<31;len++)
588 printf(" ");
589 printf(buf);
590
591 /* Use ISO 8610 time format */
592 if (tm) {
593 printf ("%04d-%02d-%02d %02d:%02d:%02d ",
594 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
595 tm->tm_hour, tm->tm_min, tm->tm_sec);
596 }
597 }
598 /* List contents if we are supposed to do that */
599 if (verboseFlag == TRUE || listFlag == TRUE) {
600 /* Now the normal listing */
601 printf("%s", header.name);
Erik Andersen6a34b532000-04-07 06:55:38 +0000602 }
603 if (verboseFlag == TRUE && listFlag == TRUE) {
Erik Andersen1ad302a2000-03-24 00:54:46 +0000604 /* If this is a link, say so */
605 if (header.type==LNKTYPE)
606 printf(" link to %s", header.linkname);
607 else if (header.type==SYMTYPE)
608 printf(" -> %s", header.linkname);
Erik Andersen6a34b532000-04-07 06:55:38 +0000609 }
610 if (verboseFlag == TRUE || listFlag == TRUE) {
Erik Andersen1ad302a2000-03-24 00:54:46 +0000611 printf("\n");
612 }
613
614#if 0
615 /* See if we want to restore this file or not */
616 skipFileFlag=FALSE;
617 if (wantFileName(outName) == FALSE) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000618 skipFileFlag = TRUE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000619 }
Erik Andersen05100cd2000-01-16 01:30:52 +0000620#endif
Erik Andersen6a34b532000-04-07 06:55:38 +0000621 /* Remove any clutter lying in our way */
622 unlink( header.name);
Eric Andersencc8ed391999-10-05 16:24:54 +0000623
Erik Andersen1ad302a2000-03-24 00:54:46 +0000624 /* If we got here, we can be certain we have a legitimate
625 * header to work with. So work with it. */
626 switch ( header.type ) {
627 case REGTYPE:
628 case REGTYPE0:
629 /* If the name ends in a '/' then assume it is
630 * supposed to be a directory, and fall through */
631 if (header.name[strlen(header.name)-1] != '/') {
Erik Andersen6a34b532000-04-07 06:55:38 +0000632 if (tarExtractRegularFile(&header, extractFlag, tostdoutFlag)==FALSE)
633 errorFlag=TRUE;
Erik Andersen1ad302a2000-03-24 00:54:46 +0000634 break;
635 }
636 case DIRTYPE:
Erik Andersen6a34b532000-04-07 06:55:38 +0000637 if (tarExtractDirectory( &header, extractFlag, tostdoutFlag)==FALSE)
638 errorFlag=TRUE;
Erik Andersen1ad302a2000-03-24 00:54:46 +0000639 break;
640 case LNKTYPE:
Erik Andersen6a34b532000-04-07 06:55:38 +0000641 if (tarExtractHardLink( &header, extractFlag, tostdoutFlag)==FALSE)
642 errorFlag=TRUE;
Erik Andersen1ad302a2000-03-24 00:54:46 +0000643 break;
644 case SYMTYPE:
Erik Andersen6a34b532000-04-07 06:55:38 +0000645 if (tarExtractSymLink( &header, extractFlag, tostdoutFlag)==FALSE)
646 errorFlag=TRUE;
Erik Andersen1ad302a2000-03-24 00:54:46 +0000647 break;
648 case CHRTYPE:
649 case BLKTYPE:
650 case FIFOTYPE:
Erik Andersen6a34b532000-04-07 06:55:38 +0000651 if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE)
652 errorFlag=TRUE;
Erik Andersen1ad302a2000-03-24 00:54:46 +0000653 break;
654 default:
655 close( tarFd);
656 return( FALSE);
657 }
658 }
659 close(tarFd);
660 if (status > 0) {
661 /* Bummer - we read a partial header */
Erik Andersendeea0482000-03-25 23:09:29 +0000662 errorMsg( "Error reading '%s': %s\n", tarName, strerror(errno));
Erik Andersen1ad302a2000-03-24 00:54:46 +0000663 return ( FALSE);
664 }
Erik Andersen6a34b532000-04-07 06:55:38 +0000665 else if (errorFlag==TRUE) {
666 errorMsg( "tar: Error exit delayed from previous errors\n");
667 return( FALSE);
668 } else
Erik Andersen1ad302a2000-03-24 00:54:46 +0000669 return( status);
670
671 /* Stuff to do when we are done */
672endgame:
673 close( tarFd);
674 if ( *(header.name) == '\0' ) {
Erik Andersen6a34b532000-04-07 06:55:38 +0000675 if (errorFlag==TRUE)
676 errorMsg( "tar: Error exit delayed from previous errors\n");
677 else
Erik Andersen1ad302a2000-03-24 00:54:46 +0000678 return( TRUE);
679 }
680 return( FALSE);
681}
682
Erik Andersen6acaa402000-03-26 14:03:20 +0000683
684#ifdef BB_FEATURE_TAR_CREATE
685
Erik Andersen68a9ea42000-04-04 18:39:50 +0000686/* Some info to be carried along when creating a new tarball */
687struct TarBallInfo
688{
689 char* fileName; /* File name of the tarball */
690 int tarFd; /* Open-for-write file descriptor
691 for the tarball */
692 struct stat statBuf; /* Stat info for the tarball, letting
693 us know the inode and device that the
694 tarball lives, so we can avoid trying
695 to include the tarball into itself */
696 int verboseFlag; /* Whether to print extra stuff or not */
Erik Andersenecd51242000-04-08 03:08:21 +0000697 char** excludeList; /* List of files to not include */
Erik Andersen68a9ea42000-04-04 18:39:50 +0000698};
699typedef struct TarBallInfo TarBallInfo;
700
701
Erik Andersen6acaa402000-03-26 14:03:20 +0000702/* Put an octal string into the specified buffer.
703 * The number is zero and space padded and possibly null padded.
704 * Returns TRUE if successful. */
705static int putOctal (char *cp, int len, long value)
706{
707 int tempLength;
Erik Andersen6acaa402000-03-26 14:03:20 +0000708 char tempBuffer[32];
Erik Andersen5661fe02000-04-05 01:00:52 +0000709 char *tempString = tempBuffer;
Erik Andersen6acaa402000-03-26 14:03:20 +0000710
711 /* Create a string of the specified length with an initial space,
712 * leading zeroes and the octal number, and a trailing null. */
Erik Andersen5661fe02000-04-05 01:00:52 +0000713 sprintf (tempString, "%0*lo", len - 1, value);
Erik Andersen6acaa402000-03-26 14:03:20 +0000714
715 /* If the string is too large, suppress the leading space. */
Erik Andersen5661fe02000-04-05 01:00:52 +0000716 tempLength = strlen (tempString) + 1;
Erik Andersen6acaa402000-03-26 14:03:20 +0000717 if (tempLength > len) {
718 tempLength--;
719 tempString++;
720 }
721
722 /* If the string is still too large, suppress the trailing null. */
723 if (tempLength > len)
724 tempLength--;
725
726 /* If the string is still too large, fail. */
727 if (tempLength > len)
728 return FALSE;
729
730 /* Copy the string to the field. */
731 memcpy (cp, tempString, len);
732
733 return TRUE;
734}
735
Erik Andersen68a9ea42000-04-04 18:39:50 +0000736/* Write out a tar header for the specified file/directory/whatever */
Erik Andersen3364d782000-03-28 00:58:14 +0000737static int
Erik Andersen5661fe02000-04-05 01:00:52 +0000738writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *statbuf)
Erik Andersen6acaa402000-03-26 14:03:20 +0000739{
Erik Andersen5661fe02000-04-05 01:00:52 +0000740 long chksum=0;
741 struct TarHeader header;
Erik Andersenecd51242000-04-08 03:08:21 +0000742 char** tmpList;
Erik Andersen5661fe02000-04-05 01:00:52 +0000743 const unsigned char *cp = (const unsigned char *) &header;
744 ssize_t size = sizeof(struct TarHeader);
745
746 memset( &header, 0, size);
Erik Andersen3364d782000-03-28 00:58:14 +0000747
Erik Andersen68a9ea42000-04-04 18:39:50 +0000748 if (*fileName=='/') {
749 static int alreadyWarned=FALSE;
750 if (alreadyWarned==FALSE) {
751 errorMsg("tar: Removing leading '/' from member names\n");
752 alreadyWarned=TRUE;
753 }
Erik Andersen5661fe02000-04-05 01:00:52 +0000754 strcpy(header.name, fileName+1);
Erik Andersen3364d782000-03-28 00:58:14 +0000755 }
Erik Andersen68a9ea42000-04-04 18:39:50 +0000756 else {
Erik Andersen5661fe02000-04-05 01:00:52 +0000757 strcpy(header.name, fileName);
Erik Andersen68a9ea42000-04-04 18:39:50 +0000758 }
Erik Andersenecd51242000-04-08 03:08:21 +0000759#if 0
760 /* Now that leading '/''s have been removed */
761 for (tmpList=tbInfo->excludeList; tmpList && *tmpList; tmpList++) {
762 printf( "comparing '%s' and '%s'", *tmpList, header.name);
763 if (strcmp( *tmpList, header.name)==0)
764 printf( ": match\n");
765 else
766 printf( "\n");
767 }
768#endif
769
Erik Andersen5661fe02000-04-05 01:00:52 +0000770 putOctal(header.mode, sizeof(header.mode), statbuf->st_mode);
771 putOctal(header.uid, sizeof(header.uid), statbuf->st_uid);
772 putOctal(header.gid, sizeof(header.gid), statbuf->st_gid);
773 putOctal(header.size, sizeof(header.size), 0); /* Regular file size is handled later */
774 putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime);
775 strncpy(header.magic, TAR_MAGIC TAR_VERSION,
776 TAR_MAGIC_LEN + TAR_VERSION_LEN );
Erik Andersen68a9ea42000-04-04 18:39:50 +0000777
Erik Andersen5661fe02000-04-05 01:00:52 +0000778 my_getpwuid(header.uname, statbuf->st_uid);
779 /* Put some sort of sane fallback in place... */
780 if (! *header.uname)
781 strncpy(header.uname, "root", 5);
782 my_getgrgid(header.gname, statbuf->st_gid);
783 if (! *header.uname)
784 strncpy(header.uname, "root", 5);
785
786 // FIXME: (or most likely not) I break Hard Links
Erik Andersen68a9ea42000-04-04 18:39:50 +0000787 if (S_ISLNK(statbuf->st_mode)) {
Erik Andersen5661fe02000-04-05 01:00:52 +0000788 char buffer[BUFSIZ];
789 header.typeflag = SYMTYPE;
790 if ( readlink(fileName, buffer, sizeof(buffer) - 1) < 0) {
791 errorMsg("Error reading symlink '%s': %s\n", header.name, strerror(errno));
792 return ( FALSE);
793 }
794 strncpy(header.linkname, buffer, sizeof(header.linkname));
Erik Andersen68a9ea42000-04-04 18:39:50 +0000795 } else if (S_ISDIR(statbuf->st_mode)) {
Erik Andersen5661fe02000-04-05 01:00:52 +0000796 header.typeflag = DIRTYPE;
797 strncat(header.name, "/", sizeof(header.name));
Erik Andersen68a9ea42000-04-04 18:39:50 +0000798 } else if (S_ISCHR(statbuf->st_mode)) {
Erik Andersen5661fe02000-04-05 01:00:52 +0000799 header.typeflag = CHRTYPE;
800 putOctal(header.devmajor, sizeof(header.devmajor), MAJOR(statbuf->st_rdev));
801 putOctal(header.devminor, sizeof(header.devminor), MINOR(statbuf->st_rdev));
Erik Andersen68a9ea42000-04-04 18:39:50 +0000802 } else if (S_ISBLK(statbuf->st_mode)) {
Erik Andersen5661fe02000-04-05 01:00:52 +0000803 header.typeflag = BLKTYPE;
804 putOctal(header.devmajor, sizeof(header.devmajor), MAJOR(statbuf->st_rdev));
805 putOctal(header.devminor, sizeof(header.devminor), MINOR(statbuf->st_rdev));
Erik Andersen68a9ea42000-04-04 18:39:50 +0000806 } else if (S_ISFIFO(statbuf->st_mode)) {
Erik Andersen5661fe02000-04-05 01:00:52 +0000807 header.typeflag = FIFOTYPE;
808 } else if (S_ISREG(statbuf->st_mode)) {
809 header.typeflag = REGTYPE;
810 putOctal(header.size, sizeof(header.size), statbuf->st_size);
Erik Andersen68a9ea42000-04-04 18:39:50 +0000811 } else {
Erik Andersen5661fe02000-04-05 01:00:52 +0000812 errorMsg("tar: %s: Unknown file type\n", fileName);
Erik Andersen68a9ea42000-04-04 18:39:50 +0000813 return ( FALSE);
814 }
Erik Andersen68a9ea42000-04-04 18:39:50 +0000815
Erik Andersen5661fe02000-04-05 01:00:52 +0000816 /* Calculate and store the checksum (i.e. the sum of all of the bytes of
817 * the header). The checksum field must be filled with blanks for the
818 * calculation. The checksum field is formatted differently from the
819 * other fields: it has [6] digits, a null, then a space -- rather than
820 * digits, followed by a null like the other fields... */
821 memset(header.chksum, ' ', sizeof(header.chksum));
822 cp = (const unsigned char *) &header;
823 while (size-- > 0)
824 chksum += *cp++;
825 putOctal(header.chksum, 7, chksum);
826
827 /* Now write the header out to disk */
828 if ((size=fullWrite(tbInfo->tarFd, (char*)&header, sizeof(struct TarHeader))) < 0) {
829 errorMsg(io_error, fileName, strerror(errno));
830 return ( FALSE);
831 }
832 /* Pad the header up to the tar block size */
833 for (; size<TAR_BLOCK_SIZE; size++) {
834 write(tbInfo->tarFd, "\0", 1);
835 }
836 /* Now do the verbose thing (or not) */
837 if (tbInfo->verboseFlag==TRUE)
838 fprintf(stdout, "%s\n", header.name);
Erik Andersen3364d782000-03-28 00:58:14 +0000839
840 return ( TRUE);
841}
842
843
Erik Andersen68a9ea42000-04-04 18:39:50 +0000844static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData)
Erik Andersen3364d782000-03-28 00:58:14 +0000845{
Erik Andersen68a9ea42000-04-04 18:39:50 +0000846 struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData;
Erik Andersen68a9ea42000-04-04 18:39:50 +0000847
848 /* It is against the rules to archive a socket */
849 if (S_ISSOCK(statbuf->st_mode)) {
850 errorMsg("tar: %s: socket ignored\n", fileName);
851 return( TRUE);
852 }
853
854 /* It is a bad idea to store the archive we are in the process of creating,
855 * so check the device and inode to be sure that this particular file isn't
856 * the new tarball */
857 if (tbInfo->statBuf.st_dev == statbuf->st_dev &&
858 tbInfo->statBuf.st_ino == statbuf->st_ino) {
859 errorMsg("tar: %s: file is the archive; skipping\n", fileName);
860 return( TRUE);
861 }
862
Erik Andersen5661fe02000-04-05 01:00:52 +0000863 if (writeTarHeader(tbInfo, fileName, statbuf)==FALSE) {
864 return( FALSE);
Erik Andersen68a9ea42000-04-04 18:39:50 +0000865 }
Erik Andersen5661fe02000-04-05 01:00:52 +0000866
867 /* Now, if the file is a regular file, copy it out to the tarball */
868 if (S_ISREG(statbuf->st_mode)) {
869 int inputFileFd;
870 char buffer[BUFSIZ];
871 ssize_t size=0, readSize=0;
872
873 /* open the file we want to archive, and make sure all is well */
874 if ((inputFileFd = open(fileName, O_RDONLY)) < 0) {
875 errorMsg("tar: %s: Cannot open: %s\n", fileName, strerror(errno));
876 return( FALSE);
877 }
878
879 /* write the file to the archive */
880 while ( (size = fullRead(inputFileFd, buffer, sizeof(buffer))) > 0 ) {
881 if (fullWrite(tbInfo->tarFd, buffer, size) != size ) {
882 /* Output file seems to have a problem */
883 errorMsg(io_error, fileName, strerror(errno));
884 return( FALSE);
885 }
886 readSize+=size;
887 }
888 if (size == -1) {
889 errorMsg(io_error, fileName, strerror(errno));
890 return( FALSE);
891 }
892 /* Pad the file up to the tar block size */
893 for (; (readSize%TAR_BLOCK_SIZE) != 0; readSize++) {
894 write(tbInfo->tarFd, "\0", 1);
895 }
896 close( inputFileFd);
897 }
Erik Andersen68a9ea42000-04-04 18:39:50 +0000898
899 return( TRUE);
Erik Andersen6acaa402000-03-26 14:03:20 +0000900}
901
Erik Andersen68a9ea42000-04-04 18:39:50 +0000902static int writeTarFile(const char* tarName, int tostdoutFlag,
Erik Andersenecd51242000-04-08 03:08:21 +0000903 int verboseFlag, int argc, char **argv, char** excludeList)
Erik Andersen6acaa402000-03-26 14:03:20 +0000904{
Erik Andersen3364d782000-03-28 00:58:14 +0000905 int tarFd=-1;
Erik Andersen68a9ea42000-04-04 18:39:50 +0000906 int errorFlag=FALSE;
Erik Andersen5661fe02000-04-05 01:00:52 +0000907 ssize_t size;
Erik Andersen68a9ea42000-04-04 18:39:50 +0000908 struct TarBallInfo tbInfo;
909 tbInfo.verboseFlag = verboseFlag;
Erik Andersen3364d782000-03-28 00:58:14 +0000910
911 /* Make sure there is at least one file to tar up. */
912 if (argc <= 0)
913 fatalError("tar: Cowardly refusing to create an empty archive\n");
Erik Andersen6acaa402000-03-26 14:03:20 +0000914
915 /* Open the tar file for writing. */
Erik Andersen3364d782000-03-28 00:58:14 +0000916 if (tostdoutFlag == TRUE)
Erik Andersen68a9ea42000-04-04 18:39:50 +0000917 tbInfo.tarFd = fileno(stdout);
Erik Andersen6acaa402000-03-26 14:03:20 +0000918 else
Erik Andersen68a9ea42000-04-04 18:39:50 +0000919 tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644);
920 if (tbInfo.tarFd < 0) {
921 errorMsg( "tar: Error opening '%s': %s\n", tarName, strerror(errno));
Erik Andersen6acaa402000-03-26 14:03:20 +0000922 return ( FALSE);
923 }
Erik Andersenecd51242000-04-08 03:08:21 +0000924 tbInfo.excludeList=excludeList;
Erik Andersen68a9ea42000-04-04 18:39:50 +0000925 /* Store the stat info for the tarball's file, so
926 * can avoid including the tarball into itself.... */
927 if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
Erik Andersen3364d782000-03-28 00:58:14 +0000928 fatalError(io_error, tarName, strerror(errno));
Erik Andersen6acaa402000-03-26 14:03:20 +0000929
930 /* Set the umask for this process so it doesn't
931 * screw up permission setting for us later. */
932 umask(0);
933
934 /* Read the directory/files and iterate over them one at a time */
Erik Andersen3364d782000-03-28 00:58:14 +0000935 while (argc-- > 0) {
936 if (recursiveAction(*argv++, TRUE, FALSE, FALSE,
Erik Andersen68a9ea42000-04-04 18:39:50 +0000937 writeFileToTarball, writeFileToTarball,
938 (void*) &tbInfo) == FALSE) {
939 errorFlag = TRUE;
Erik Andersen3364d782000-03-28 00:58:14 +0000940 }
Erik Andersen6acaa402000-03-26 14:03:20 +0000941 }
Erik Andersen5661fe02000-04-05 01:00:52 +0000942 /* Write two empty blocks to the end of the archive */
943 for (size=0; size<(2*TAR_BLOCK_SIZE); size++) {
944 write(tbInfo.tarFd, "\0", 1);
945 }
Erik Andersen68a9ea42000-04-04 18:39:50 +0000946 /* Hang up the tools, close up shop, head home */
Erik Andersen6acaa402000-03-26 14:03:20 +0000947 close(tarFd);
Erik Andersen68a9ea42000-04-04 18:39:50 +0000948 if (errorFlag == TRUE) {
949 errorMsg("tar: Error exit delayed from previous errors\n");
950 return(FALSE);
951 }
Erik Andersen6acaa402000-03-26 14:03:20 +0000952 return( TRUE);
953}
954
955
956#endif
957