blob: a53370e85d1f364b00829cf5ac8c1b6a6e4b14ac [file] [log] [blame]
Eric Andersencc8ed391999-10-05 16:24:54 +00001/*
Eric Andersenc4996011999-10-20 22:08:37 +00002 * Mini tar implementation for busybox based on code taken from sash.
3 *
Eric Andersencc8ed391999-10-05 16:24:54 +00004 * Copyright (c) 1999 by David I. Bell
5 * Permission is granted to use, distribute, or modify this source,
6 * provided that this copyright notice remains intact.
7 *
Eric Andersencc8ed391999-10-05 16:24:54 +00008 * Permission to distribute this code under the GPL has been granted.
Eric Andersenc4996011999-10-20 22:08:37 +00009 *
Eric Andersen3cf52d11999-10-12 22:26:06 +000010 * Modified for busybox by Erik Andersen <andersee@debian.org>
Eric Andersenc4996011999-10-20 22:08:37 +000011 * Adjusted to grok stdin/stdout options.
12 *
Eric Andersen96bcfd31999-11-12 01:30:18 +000013 * Modified to handle device special files by Matt Porter
14 * <porter@debian.org>
15 *
Eric Andersenc4996011999-10-20 22:08:37 +000016 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 *
Eric Andersencc8ed391999-10-05 16:24:54 +000030 */
31
32
33#include "internal.h"
Eric Andersencc8ed391999-10-05 16:24:54 +000034#include <stdio.h>
35#include <dirent.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <signal.h>
39#include <time.h>
Erik Andersen7dc16072000-01-04 01:10:25 +000040#include <utime.h>
Eric Andersen96bcfd31999-11-12 01:30:18 +000041#include <sys/types.h>
Eric Andersen08b10341999-11-19 02:38:58 +000042#include <sys/sysmacros.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000043
Eric Andersene77ae3a1999-10-19 20:03:34 +000044
45static const char tar_usage[] =
Eric Andersend73dc5b1999-11-10 23:13:02 +000046"tar -[cxtvOf] [tarFileName] [FILE] ...\n\n"
47"Create, extract, or list files from a tar file\n\n"
48"Options:\n"
49"\tc=create, x=extract, t=list contents, v=verbose,\n"
50"\tO=extract to stdout, f=tarfile or \"-\" for stdin\n";
Eric Andersene77ae3a1999-10-19 20:03:34 +000051
52
53
Eric Andersencc8ed391999-10-05 16:24:54 +000054/*
55 * Tar file constants.
56 */
57#define TAR_BLOCK_SIZE 512
58#define TAR_NAME_SIZE 100
59
60
61/*
62 * The POSIX (and basic GNU) tar header format.
63 * This structure is always embedded in a TAR_BLOCK_SIZE sized block
64 * with zero padding. We only process this information minimally.
65 */
Eric Andersen3cf52d11999-10-12 22:26:06 +000066typedef struct {
67 char name[TAR_NAME_SIZE];
68 char mode[8];
69 char uid[8];
70 char gid[8];
71 char size[12];
72 char mtime[12];
73 char checkSum[8];
74 char typeFlag;
75 char linkName[TAR_NAME_SIZE];
76 char magic[6];
77 char version[2];
78 char uname[32];
79 char gname[32];
80 char devMajor[8];
81 char devMinor[8];
82 char prefix[155];
Eric Andersencc8ed391999-10-05 16:24:54 +000083} TarHeader;
84
85#define TAR_MAGIC "ustar"
86#define TAR_VERSION "00"
87
88#define TAR_TYPE_REGULAR '0'
89#define TAR_TYPE_HARD_LINK '1'
90#define TAR_TYPE_SOFT_LINK '2'
91
92
93/*
94 * Static data.
95 */
Eric Andersend73dc5b1999-11-10 23:13:02 +000096static int listFlag;
97static int extractFlag;
98static int createFlag;
99static int verboseFlag;
100static int tostdoutFlag;
Eric Andersencc8ed391999-10-05 16:24:54 +0000101
Eric Andersen5556c181999-11-10 19:27:58 +0000102static int inHeader; // <- check me
Eric Andersend73dc5b1999-11-10 23:13:02 +0000103static int badHeader;
104static int errorFlag;
105static int skipFileFlag;
106static int warnedRoot;
107static int eofFlag;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000108static long dataCc;
109static int outFd;
Erik Andersen7dc16072000-01-04 01:10:25 +0000110static const char *outName;
Eric Andersencc8ed391999-10-05 16:24:54 +0000111
Erik Andersen7dc16072000-01-04 01:10:25 +0000112static int mode;
113static int uid;
114static int gid;
115static time_t mtime;
Eric Andersencc8ed391999-10-05 16:24:54 +0000116
117/*
118 * Static data associated with the tar file.
119 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000120static const char *tarName;
121static int tarFd;
122static dev_t tarDev;
123static ino_t tarInode;
Eric Andersencc8ed391999-10-05 16:24:54 +0000124
125
126/*
127 * Local procedures to restore files from a tar file.
128 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000129static void readTarFile (int fileCount, char **fileTable);
130static void readData (const char *cp, int count);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000131static long getOctal (const char *cp, int len);
Eric Andersencc8ed391999-10-05 16:24:54 +0000132
Eric Andersen3cf52d11999-10-12 22:26:06 +0000133static void readHeader (const TarHeader * hp,
134 int fileCount, char **fileTable);
Eric Andersencc8ed391999-10-05 16:24:54 +0000135
136
137/*
138 * Local procedures to save files into a tar file.
139 */
Eric Andersend73dc5b1999-11-10 23:13:02 +0000140static void saveFile (const char *fileName, int seeLinks);
Eric Andersencc8ed391999-10-05 16:24:54 +0000141
Eric Andersen3cf52d11999-10-12 22:26:06 +0000142static void saveRegularFile (const char *fileName,
143 const struct stat *statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000144
Eric Andersen3cf52d11999-10-12 22:26:06 +0000145static void saveDirectory (const char *fileName,
146 const struct stat *statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000147
Eric Andersen3cf52d11999-10-12 22:26:06 +0000148static int wantFileName (const char *fileName,
Eric Andersend73dc5b1999-11-10 23:13:02 +0000149 int fileCount, char **fileTable);
Eric Andersencc8ed391999-10-05 16:24:54 +0000150
Eric Andersen3cf52d11999-10-12 22:26:06 +0000151static void writeHeader (const char *fileName, const struct stat *statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000152
Eric Andersen3cf52d11999-10-12 22:26:06 +0000153static void writeTarFile (int fileCount, char **fileTable);
154static void writeTarBlock (const char *buf, int len);
Eric Andersend73dc5b1999-11-10 23:13:02 +0000155static int putOctal (char *cp, int len, long value);
Eric Andersencc8ed391999-10-05 16:24:54 +0000156
157
Eric Andersen3cf52d11999-10-12 22:26:06 +0000158extern int tar_main (int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000159{
Eric Andersen3cf52d11999-10-12 22:26:06 +0000160 const char *options;
Eric Andersencc8ed391999-10-05 16:24:54 +0000161
Eric Andersen3cf52d11999-10-12 22:26:06 +0000162 argc--;
163 argv++;
Eric Andersencc8ed391999-10-05 16:24:54 +0000164
Eric Andersenbe971d61999-11-03 16:52:50 +0000165 if (argc < 1)
166 usage( tar_usage);
Eric Andersencc8ed391999-10-05 16:24:54 +0000167
168
Eric Andersen3cf52d11999-10-12 22:26:06 +0000169 errorFlag = FALSE;
170 extractFlag = FALSE;
171 createFlag = FALSE;
172 listFlag = FALSE;
173 verboseFlag = FALSE;
174 tostdoutFlag = FALSE;
175 tarName = NULL;
176 tarDev = 0;
177 tarInode = 0;
178 tarFd = -1;
Eric Andersencc8ed391999-10-05 16:24:54 +0000179
Eric Andersen3cf52d11999-10-12 22:26:06 +0000180 /*
181 * Parse the options.
182 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000183 if (**argv == '-') {
Eric Andersen50d63601999-11-09 01:47:36 +0000184 options = (*argv++) + 1;
185 argc--;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000186 for (; *options; options++) {
187 switch (*options) {
188 case 'f':
189 if (tarName != NULL) {
190 fprintf (stderr, "Only one 'f' option allowed\n");
Eric Andersencc8ed391999-10-05 16:24:54 +0000191
Eric Andersen3cf52d11999-10-12 22:26:06 +0000192 exit (FALSE);
Eric Andersencc8ed391999-10-05 16:24:54 +0000193 }
Eric Andersen3cf52d11999-10-12 22:26:06 +0000194
195 tarName = *argv++;
196 argc--;
197
198 break;
199
200 case 't':
201 listFlag = TRUE;
202 break;
203
204 case 'x':
205 extractFlag = TRUE;
206 break;
207
208 case 'c':
209 createFlag = TRUE;
210 break;
211
212 case 'v':
213 verboseFlag = TRUE;
214 break;
215
216 case 'O':
217 tostdoutFlag = TRUE;
218 break;
219
220 case '-':
Eric Andersend73dc5b1999-11-10 23:13:02 +0000221 usage( tar_usage);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000222 break;
223
224 default:
Eric Andersend73dc5b1999-11-10 23:13:02 +0000225 fprintf (stderr, "Unknown tar flag '%c'\n"
226 "Try `tar --help' for more information\n",
227 *options);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000228
229 exit (FALSE);
230 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000231 }
Eric Andersen3cf52d11999-10-12 22:26:06 +0000232 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000233
Eric Andersen3cf52d11999-10-12 22:26:06 +0000234 /*
235 * Validate the options.
236 */
Eric Andersen5556c181999-11-10 19:27:58 +0000237 if (extractFlag + listFlag + createFlag != (TRUE+FALSE+FALSE)) {
Eric Andersen3cf52d11999-10-12 22:26:06 +0000238 fprintf (stderr,
239 "Exactly one of 'c', 'x' or 't' must be specified\n");
Eric Andersencc8ed391999-10-05 16:24:54 +0000240
Eric Andersen3cf52d11999-10-12 22:26:06 +0000241 exit (FALSE);
242 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000243
Eric Andersen3cf52d11999-10-12 22:26:06 +0000244 /*
245 * Do the correct type of action supplying the rest of the
246 * command line arguments as the list of files to process.
247 */
Eric Andersen5556c181999-11-10 19:27:58 +0000248 if (createFlag==TRUE)
Eric Andersen3cf52d11999-10-12 22:26:06 +0000249 writeTarFile (argc, argv);
250 else
251 readTarFile (argc, argv);
Eric Andersen5556c181999-11-10 19:27:58 +0000252 if (errorFlag==TRUE)
Eric Andersen3cf52d11999-10-12 22:26:06 +0000253 fprintf (stderr, "\n");
Eric Andersen5556c181999-11-10 19:27:58 +0000254 exit (!errorFlag);
Eric Andersencc8ed391999-10-05 16:24:54 +0000255}
256
257
258/*
259 * Read a tar file and extract or list the specified files within it.
260 * If the list is empty than all files are extracted or listed.
261 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000262static void readTarFile (int fileCount, char **fileTable)
Eric Andersencc8ed391999-10-05 16:24:54 +0000263{
Eric Andersen3cf52d11999-10-12 22:26:06 +0000264 const char *cp;
265 int cc;
266 int inCc;
267 int blockSize;
268 char buf[BUF_SIZE];
Eric Andersencc8ed391999-10-05 16:24:54 +0000269
Eric Andersen3cf52d11999-10-12 22:26:06 +0000270 skipFileFlag = FALSE;
271 badHeader = FALSE;
272 warnedRoot = FALSE;
273 eofFlag = FALSE;
274 inHeader = TRUE;
275 inCc = 0;
276 dataCc = 0;
277 outFd = -1;
278 blockSize = sizeof (buf);
279 cp = buf;
Eric Andersencc8ed391999-10-05 16:24:54 +0000280
Eric Andersen3cf52d11999-10-12 22:26:06 +0000281 /*
282 * Open the tar file for reading.
283 */
284 if ((tarName == NULL) || !strcmp (tarName, "-")) {
Eric Andersen08b10341999-11-19 02:38:58 +0000285 tarFd = fileno(stdin);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000286 } else
287 tarFd = open (tarName, O_RDONLY);
288
289 if (tarFd < 0) {
290 perror (tarName);
291 errorFlag = TRUE;
292 return;
293 }
294
295 /*
296 * Read blocks from the file until an end of file header block
297 * has been seen. (A real end of file from a read is an error.)
298 */
Eric Andersen5556c181999-11-10 19:27:58 +0000299 while (eofFlag==FALSE) {
Eric Andersen3cf52d11999-10-12 22:26:06 +0000300 /*
301 * Read the next block of data if necessary.
302 * This will be a large block if possible, which we will
303 * then process in the small tar blocks.
Eric Andersencc8ed391999-10-05 16:24:54 +0000304 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000305 if (inCc <= 0) {
306 cp = buf;
307 inCc = fullRead (tarFd, buf, blockSize);
Eric Andersencc8ed391999-10-05 16:24:54 +0000308
Eric Andersen3cf52d11999-10-12 22:26:06 +0000309 if (inCc < 0) {
310 perror (tarName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000311 errorFlag = TRUE;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000312 goto done;
313 }
314
315 if (inCc == 0) {
316 fprintf (stderr,
317 "Unexpected end of file from \"%s\"", tarName);
318 errorFlag = TRUE;
319 goto done;
320 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000321 }
322
Eric Andersen3cf52d11999-10-12 22:26:06 +0000323 /*
324 * If we are expecting a header block then examine it.
Eric Andersencc8ed391999-10-05 16:24:54 +0000325 */
Eric Andersen5556c181999-11-10 19:27:58 +0000326 if (inHeader==TRUE) {
Eric Andersen3cf52d11999-10-12 22:26:06 +0000327 readHeader ((const TarHeader *) cp, fileCount, fileTable);
Eric Andersencc8ed391999-10-05 16:24:54 +0000328
Eric Andersen3cf52d11999-10-12 22:26:06 +0000329 cp += TAR_BLOCK_SIZE;
330 inCc -= TAR_BLOCK_SIZE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000331
Eric Andersen3cf52d11999-10-12 22:26:06 +0000332 continue;
Eric Andersencc8ed391999-10-05 16:24:54 +0000333 }
334
Eric Andersen3cf52d11999-10-12 22:26:06 +0000335 /*
336 * We are currently handling the data for a file.
337 * Process the minimum of the amount of data we have available
338 * and the amount left to be processed for the file.
Eric Andersencc8ed391999-10-05 16:24:54 +0000339 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000340 cc = inCc;
Eric Andersencc8ed391999-10-05 16:24:54 +0000341
Eric Andersen3cf52d11999-10-12 22:26:06 +0000342 if (cc > dataCc)
343 cc = dataCc;
344
345 readData (cp, cc);
346
347 /*
348 * If the amount left isn't an exact multiple of the tar block
349 * size then round it up to the next block boundary since there
350 * is padding at the end of the file.
Eric Andersencc8ed391999-10-05 16:24:54 +0000351 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000352 if (cc % TAR_BLOCK_SIZE)
353 cc += TAR_BLOCK_SIZE - (cc % TAR_BLOCK_SIZE);
354
355 cp += cc;
356 inCc -= cc;
357 }
358
359 done:
360 /*
361 * Close the tar file if needed.
362 */
363 if ((tarFd >= 0) && (close (tarFd) < 0))
364 perror (tarName);
365
366 /*
367 * Close the output file if needed.
368 * This is only done here on a previous error and so no
369 * message is required on errors.
370 */
371 if (tostdoutFlag == FALSE) {
Erik Andersen7dc16072000-01-04 01:10:25 +0000372 if (outFd >= 0) {
373 close (outFd);
374 }
Eric Andersen3cf52d11999-10-12 22:26:06 +0000375 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000376}
377
378
379/*
380 * Examine the header block that was just read.
381 * This can specify the information for another file, or it can mark
382 * the end of the tar file.
383 */
384static void
Eric Andersen3cf52d11999-10-12 22:26:06 +0000385readHeader (const TarHeader * hp, int fileCount, char **fileTable)
Eric Andersencc8ed391999-10-05 16:24:54 +0000386{
Eric Andersen3cf52d11999-10-12 22:26:06 +0000387 int checkSum;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000388 int cc;
389 int hardLink;
390 int softLink;
Eric Andersen96bcfd31999-11-12 01:30:18 +0000391 int devFileFlag;
Erik Andersen7dc16072000-01-04 01:10:25 +0000392 unsigned int major;
393 unsigned int minor;
394 long size;
395 struct utimbuf utb;
Eric Andersencc8ed391999-10-05 16:24:54 +0000396
Eric Andersen3cf52d11999-10-12 22:26:06 +0000397 /*
398 * If the block is completely empty, then this is the end of the
399 * archive file. If the name is null, then just skip this header.
400 */
Erik Andersen7dc16072000-01-04 01:10:25 +0000401 outName = hp->name;
Eric Andersencc8ed391999-10-05 16:24:54 +0000402
Erik Andersen7dc16072000-01-04 01:10:25 +0000403 if (*outName == '\0') {
Eric Andersen3cf52d11999-10-12 22:26:06 +0000404 for (cc = TAR_BLOCK_SIZE; cc > 0; cc--) {
Erik Andersen7dc16072000-01-04 01:10:25 +0000405 if (*outName++)
Eric Andersencc8ed391999-10-05 16:24:54 +0000406 return;
407 }
408
Eric Andersen3cf52d11999-10-12 22:26:06 +0000409 eofFlag = TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000410
Eric Andersen3cf52d11999-10-12 22:26:06 +0000411 return;
412 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000413
Eric Andersen3cf52d11999-10-12 22:26:06 +0000414 /*
415 * There is another file in the archive to examine.
416 * Extract the encoded information and check it.
417 */
418 mode = getOctal (hp->mode, sizeof (hp->mode));
419 uid = getOctal (hp->uid, sizeof (hp->uid));
420 gid = getOctal (hp->gid, sizeof (hp->gid));
421 size = getOctal (hp->size, sizeof (hp->size));
422 mtime = getOctal (hp->mtime, sizeof (hp->mtime));
423 checkSum = getOctal (hp->checkSum, sizeof (hp->checkSum));
Eric Andersen96bcfd31999-11-12 01:30:18 +0000424 major = getOctal (hp->devMajor, sizeof (hp->devMajor));
425 minor = getOctal (hp->devMinor, sizeof (hp->devMinor));
Eric Andersencc8ed391999-10-05 16:24:54 +0000426
Eric Andersen3cf52d11999-10-12 22:26:06 +0000427 if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0)) {
Eric Andersen5556c181999-11-10 19:27:58 +0000428 if (badHeader==FALSE)
Eric Andersen3cf52d11999-10-12 22:26:06 +0000429 fprintf (stderr, "Bad tar header, skipping\n");
Eric Andersencc8ed391999-10-05 16:24:54 +0000430
Eric Andersen3cf52d11999-10-12 22:26:06 +0000431 badHeader = TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000432
Eric Andersen3cf52d11999-10-12 22:26:06 +0000433 return;
434 }
435
436 badHeader = FALSE;
437 skipFileFlag = FALSE;
Eric Andersen96bcfd31999-11-12 01:30:18 +0000438 devFileFlag = FALSE;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000439
440 /*
441 * Check for the file modes.
442 */
443 hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) ||
Eric Andersencc8ed391999-10-05 16:24:54 +0000444 (hp->typeFlag == TAR_TYPE_HARD_LINK - '0'));
445
Eric Andersen3cf52d11999-10-12 22:26:06 +0000446 softLink = ((hp->typeFlag == TAR_TYPE_SOFT_LINK) ||
Eric Andersencc8ed391999-10-05 16:24:54 +0000447 (hp->typeFlag == TAR_TYPE_SOFT_LINK - '0'));
448
Eric Andersen3cf52d11999-10-12 22:26:06 +0000449 /*
Eric Andersen96bcfd31999-11-12 01:30:18 +0000450 * Check for a directory.
Eric Andersen3cf52d11999-10-12 22:26:06 +0000451 */
Erik Andersen7dc16072000-01-04 01:10:25 +0000452 if (outName[strlen (outName) - 1] == '/')
Eric Andersen3cf52d11999-10-12 22:26:06 +0000453 mode |= S_IFDIR;
Eric Andersencc8ed391999-10-05 16:24:54 +0000454
Eric Andersen3cf52d11999-10-12 22:26:06 +0000455 /*
456 * Check for absolute paths in the file.
457 * If we find any, then warn the user and make them relative.
458 */
Erik Andersen7dc16072000-01-04 01:10:25 +0000459 if (*outName == '/') {
460 while (*outName == '/')
461 outName++;
Eric Andersencc8ed391999-10-05 16:24:54 +0000462
Eric Andersen5556c181999-11-10 19:27:58 +0000463 if (warnedRoot==FALSE) {
Eric Andersen3cf52d11999-10-12 22:26:06 +0000464 fprintf (stderr,
465 "Absolute path detected, removing leading slashes\n");
Eric Andersencc8ed391999-10-05 16:24:54 +0000466 }
467
Eric Andersen3cf52d11999-10-12 22:26:06 +0000468 warnedRoot = TRUE;
469 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000470
Eric Andersen3cf52d11999-10-12 22:26:06 +0000471 /*
472 * See if we want this file to be restored.
473 * If not, then set up to skip it.
474 */
Erik Andersen7dc16072000-01-04 01:10:25 +0000475 if (wantFileName (outName, fileCount, fileTable) == FALSE) {
Eric Andersen96bcfd31999-11-12 01:30:18 +0000476 if ( !hardLink && !softLink && (S_ISREG (mode) || S_ISCHR (mode)
477 || S_ISBLK (mode) || S_ISSOCK(mode) || S_ISFIFO(mode) ) ) {
Eric Andersen5556c181999-11-10 19:27:58 +0000478 inHeader = (size == 0)? TRUE : FALSE;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000479 dataCc = size;
Eric Andersencc8ed391999-10-05 16:24:54 +0000480 }
481
Eric Andersen3cf52d11999-10-12 22:26:06 +0000482 skipFileFlag = TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000483
Eric Andersen3cf52d11999-10-12 22:26:06 +0000484 return;
485 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000486
Eric Andersen3cf52d11999-10-12 22:26:06 +0000487 /*
488 * This file is to be handled.
489 * If we aren't extracting then just list information about the file.
490 */
Eric Andersen5556c181999-11-10 19:27:58 +0000491 if (extractFlag==FALSE) {
492 if (verboseFlag==TRUE) {
Eric Andersen03018f71999-11-29 04:29:13 +0000493 printf ("%s %3d/%-d ", modeString (mode), uid, gid);
494 if( S_ISCHR (mode) || S_ISBLK (mode) )
495 printf ("%4d,%4d %s ", major,minor, timeString (mtime));
496 else
497 printf ("%9ld %s ", size, timeString (mtime));
498 }
Erik Andersen7dc16072000-01-04 01:10:25 +0000499 printf ("%s", outName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000500
501 if (hardLink)
Eric Andersen3cf52d11999-10-12 22:26:06 +0000502 printf (" (link to \"%s\")", hp->linkName);
503 else if (softLink)
504 printf (" (symlink to \"%s\")", hp->linkName);
Eric Andersen96bcfd31999-11-12 01:30:18 +0000505 else if (S_ISREG (mode) || S_ISCHR (mode) || S_ISBLK (mode) ||
506 S_ISSOCK(mode) || S_ISFIFO(mode) ) {
Eric Andersen5556c181999-11-10 19:27:58 +0000507 inHeader = (size == 0)? TRUE : FALSE;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000508 dataCc = size;
Eric Andersencc8ed391999-10-05 16:24:54 +0000509 }
510
Eric Andersen3cf52d11999-10-12 22:26:06 +0000511 printf ("\n");
512
513 return;
514 }
515
516 /*
517 * We really want to extract the file.
518 */
Eric Andersen5556c181999-11-10 19:27:58 +0000519 if (verboseFlag==TRUE)
Erik Andersen7dc16072000-01-04 01:10:25 +0000520 printf ("x %s\n", outName);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000521
522 if (hardLink) {
Erik Andersen7dc16072000-01-04 01:10:25 +0000523 if (link (hp->linkName, outName) < 0)
524 perror (outName);
525 /* Set the file time */
526 utb.actime = mtime;
527 utb.modtime = mtime;
528 utime (outName, &utb);
529 /* Set the file permissions */
530 chown(outName, uid, gid);
531 chmod(outName, mode);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000532 return;
533 }
534
535 if (softLink) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000536#ifdef S_ISLNK
Erik Andersen7dc16072000-01-04 01:10:25 +0000537 if (symlink (hp->linkName, outName) < 0)
538 perror (outName);
539 /* Try to change ownership of the symlink.
540 * If libs doesn't support that, don't bother.
541 * Changing the pointed-to file is the Wrong Thing(tm).
542 */
543#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
544 lchown(outName, uid, gid);
545#endif
546
547 /* Do not change permissions or date on symlink,
548 * since it changes the pointed to file instead. duh. */
Eric Andersencc8ed391999-10-05 16:24:54 +0000549#else
Eric Andersen3cf52d11999-10-12 22:26:06 +0000550 fprintf (stderr, "Cannot create symbolic links\n");
Eric Andersencc8ed391999-10-05 16:24:54 +0000551#endif
Eric Andersen3cf52d11999-10-12 22:26:06 +0000552 return;
553 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000554
Eric Andersen03018f71999-11-29 04:29:13 +0000555 /* Set the umask for this process so it doesn't
556 * screw things up. */
557 umask(0);
558
Eric Andersen3cf52d11999-10-12 22:26:06 +0000559 /*
560 * If the file is a directory, then just create the path.
561 */
562 if (S_ISDIR (mode)) {
Erik Andersen7dc16072000-01-04 01:10:25 +0000563 createPath (outName, mode);
564 /* Set the file time */
565 utb.actime = mtime;
566 utb.modtime = mtime;
567 utime (outName, &utb);
568 /* Set the file permissions */
569 chown(outName, uid, gid);
570 chmod(outName, mode);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000571 return;
572 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000573
Eric Andersen3cf52d11999-10-12 22:26:06 +0000574 /*
575 * There is a file to write.
Eric Andersen03018f71999-11-29 04:29:13 +0000576 * First create the path to it if necessary with default permissions.
Eric Andersen3cf52d11999-10-12 22:26:06 +0000577 */
Erik Andersen7dc16072000-01-04 01:10:25 +0000578 createPath (outName, 0777);
Eric Andersencc8ed391999-10-05 16:24:54 +0000579
Eric Andersen5556c181999-11-10 19:27:58 +0000580 inHeader = (size == 0)? TRUE : FALSE;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000581 dataCc = size;
Eric Andersencc8ed391999-10-05 16:24:54 +0000582
Eric Andersen3cf52d11999-10-12 22:26:06 +0000583 /*
584 * Start the output file.
585 */
586 if (tostdoutFlag == TRUE)
Eric Andersen08b10341999-11-19 02:38:58 +0000587 outFd = fileno(stdout);
Eric Andersen96bcfd31999-11-12 01:30:18 +0000588 else {
589 if ( S_ISCHR(mode) || S_ISBLK(mode) || S_ISSOCK(mode) ) {
590 devFileFlag = TRUE;
Erik Andersen7dc16072000-01-04 01:10:25 +0000591 outFd = mknod (outName, mode, makedev(major, minor) );
Eric Andersen96bcfd31999-11-12 01:30:18 +0000592 }
593 else if (S_ISFIFO(mode) ) {
Eric Andersenb6a44b81999-11-13 04:47:09 +0000594 devFileFlag = TRUE;
Erik Andersen7dc16072000-01-04 01:10:25 +0000595 outFd = mkfifo(outName, mode);
Eric Andersen96bcfd31999-11-12 01:30:18 +0000596 } else {
Erik Andersen7dc16072000-01-04 01:10:25 +0000597 outFd = open (outName, O_WRONLY | O_CREAT | O_TRUNC, mode);
Eric Andersen96bcfd31999-11-12 01:30:18 +0000598 }
Erik Andersen00266d31999-12-28 00:17:46 +0000599 if (outFd < 0) {
Erik Andersen7dc16072000-01-04 01:10:25 +0000600 perror (outName);
Erik Andersen00266d31999-12-28 00:17:46 +0000601 skipFileFlag = TRUE;
602 return;
603 }
Erik Andersen7dc16072000-01-04 01:10:25 +0000604 /* Set the file time */
605 utb.actime = mtime;
606 utb.modtime = mtime;
607 utime (outName, &utb);
608 /* Set the file permissions */
609 chown(outName, uid, gid);
610 chmod(outName, mode);
Eric Andersen96bcfd31999-11-12 01:30:18 +0000611 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000612
Eric Andersencc8ed391999-10-05 16:24:54 +0000613
Eric Andersen3cf52d11999-10-12 22:26:06 +0000614 /*
615 * If the file is empty, then that's all we need to do.
616 */
Eric Andersen96bcfd31999-11-12 01:30:18 +0000617 if (size == 0 && (tostdoutFlag == FALSE) && (devFileFlag == FALSE)) {
Erik Andersen7dc16072000-01-04 01:10:25 +0000618 close (outFd);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000619 outFd = -1;
620 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000621}
622
623
624/*
625 * Handle a data block of some specified size that was read.
626 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000627static void readData (const char *cp, int count)
Eric Andersencc8ed391999-10-05 16:24:54 +0000628{
Eric Andersen3cf52d11999-10-12 22:26:06 +0000629 /*
630 * Reduce the amount of data left in this file.
631 * If there is no more data left, then we need to read
632 * the header again.
633 */
634 dataCc -= count;
Eric Andersencc8ed391999-10-05 16:24:54 +0000635
Eric Andersen3cf52d11999-10-12 22:26:06 +0000636 if (dataCc <= 0)
637 inHeader = TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000638
Eric Andersen3cf52d11999-10-12 22:26:06 +0000639 /*
640 * If we aren't extracting files or this file is being
641 * skipped then do nothing more.
642 */
Eric Andersen5556c181999-11-10 19:27:58 +0000643 if (extractFlag==FALSE || skipFileFlag==TRUE)
Eric Andersen3cf52d11999-10-12 22:26:06 +0000644 return;
Eric Andersencc8ed391999-10-05 16:24:54 +0000645
Eric Andersen3cf52d11999-10-12 22:26:06 +0000646 /*
647 * Write the data to the output file.
648 */
649 if (fullWrite (outFd, cp, count) < 0) {
650 perror (outName);
651 if (tostdoutFlag == FALSE) {
Erik Andersen7dc16072000-01-04 01:10:25 +0000652 close (outFd);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000653 outFd = -1;
Eric Andersencc8ed391999-10-05 16:24:54 +0000654 }
Eric Andersen3cf52d11999-10-12 22:26:06 +0000655 skipFileFlag = TRUE;
656 return;
657 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000658
Eric Andersen3cf52d11999-10-12 22:26:06 +0000659 /*
Erik Andersen7dc16072000-01-04 01:10:25 +0000660 * Check if we are done writing to the file now.
Eric Andersen3cf52d11999-10-12 22:26:06 +0000661 */
662 if (dataCc <= 0 && tostdoutFlag == FALSE) {
Erik Andersen7dc16072000-01-04 01:10:25 +0000663 struct utimbuf utb;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000664 if (close (outFd))
665 perror (outName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000666
Erik Andersen7dc16072000-01-04 01:10:25 +0000667 /* Set the file time */
668 utb.actime = mtime;
669 utb.modtime = mtime;
670 utime (outName, &utb);
671 /* Set the file permissions */
672 chown(outName, uid, gid);
673 chmod(outName, mode);
674
Eric Andersen3cf52d11999-10-12 22:26:06 +0000675 outFd = -1;
676 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000677}
678
679
680/*
681 * Write a tar file containing the specified files.
682 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000683static void writeTarFile (int fileCount, char **fileTable)
Eric Andersencc8ed391999-10-05 16:24:54 +0000684{
Eric Andersen3cf52d11999-10-12 22:26:06 +0000685 struct stat statbuf;
Eric Andersencc8ed391999-10-05 16:24:54 +0000686
Eric Andersen3cf52d11999-10-12 22:26:06 +0000687 /*
688 * Make sure there is at least one file specified.
689 */
690 if (fileCount <= 0) {
691 fprintf (stderr, "No files specified to be saved\n");
692 errorFlag = TRUE;
693 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000694
Eric Andersen3cf52d11999-10-12 22:26:06 +0000695 /*
696 * Create the tar file for writing.
697 */
698 if ((tarName == NULL) || !strcmp (tarName, "-")) {
699 tostdoutFlag = TRUE;
Eric Andersen08b10341999-11-19 02:38:58 +0000700 tarFd = fileno(stdout);
Eric Andersen3cf52d11999-10-12 22:26:06 +0000701 } else
702 tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
Eric Andersencc8ed391999-10-05 16:24:54 +0000703
Eric Andersen3cf52d11999-10-12 22:26:06 +0000704 if (tarFd < 0) {
705 perror (tarName);
706 errorFlag = TRUE;
707 return;
708 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000709
Eric Andersen3cf52d11999-10-12 22:26:06 +0000710 /*
711 * Get the device and inode of the tar file for checking later.
712 */
713 if (fstat (tarFd, &statbuf) < 0) {
714 perror (tarName);
715 errorFlag = TRUE;
716 goto done;
717 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000718
Eric Andersen3cf52d11999-10-12 22:26:06 +0000719 tarDev = statbuf.st_dev;
720 tarInode = statbuf.st_ino;
Eric Andersen03018f71999-11-29 04:29:13 +0000721
Eric Andersen3cf52d11999-10-12 22:26:06 +0000722 /*
723 * Append each file name into the archive file.
724 * Follow symbolic links for these top level file names.
725 */
Eric Andersen5556c181999-11-10 19:27:58 +0000726 while (errorFlag==FALSE && (fileCount-- > 0)) {
Eric Andersen3cf52d11999-10-12 22:26:06 +0000727 saveFile (*fileTable++, FALSE);
728 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000729
Eric Andersen3cf52d11999-10-12 22:26:06 +0000730 /*
731 * Now write an empty block of zeroes to end the archive.
732 */
733 writeTarBlock ("", 1);
Eric Andersencc8ed391999-10-05 16:24:54 +0000734
735
Eric Andersen3cf52d11999-10-12 22:26:06 +0000736 done:
737 /*
738 * Close the tar file and check for errors if it was opened.
739 */
740 if ((tostdoutFlag == FALSE) && (tarFd >= 0) && (close (tarFd) < 0))
741 perror (tarName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000742}
743
744
745/*
746 * Save one file into the tar file.
747 * If the file is a directory, then this will recursively save all of
748 * the files and directories within the directory. The seeLinks
749 * flag indicates whether or not we want to see symbolic links as
750 * they really are, instead of blindly following them.
751 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000752static void saveFile (const char *fileName, int seeLinks)
Eric Andersencc8ed391999-10-05 16:24:54 +0000753{
Eric Andersen3cf52d11999-10-12 22:26:06 +0000754 int status;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000755 struct stat statbuf;
Eric Andersencc8ed391999-10-05 16:24:54 +0000756
Eric Andersen5556c181999-11-10 19:27:58 +0000757 if (verboseFlag==TRUE)
Eric Andersen3cf52d11999-10-12 22:26:06 +0000758 printf ("a %s\n", fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000759
Eric Andersen3cf52d11999-10-12 22:26:06 +0000760 /*
761 * Check that the file name will fit in the header.
762 */
763 if (strlen (fileName) >= TAR_NAME_SIZE) {
764 fprintf (stderr, "%s: File name is too long\n", fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000765
Eric Andersen3cf52d11999-10-12 22:26:06 +0000766 return;
767 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000768
Eric Andersen3cf52d11999-10-12 22:26:06 +0000769 /*
770 * Find out about the file.
771 */
Eric Andersencc8ed391999-10-05 16:24:54 +0000772#ifdef S_ISLNK
Eric Andersen5556c181999-11-10 19:27:58 +0000773 if (seeLinks==TRUE)
Eric Andersen3cf52d11999-10-12 22:26:06 +0000774 status = lstat (fileName, &statbuf);
775 else
Eric Andersencc8ed391999-10-05 16:24:54 +0000776#endif
Eric Andersen3cf52d11999-10-12 22:26:06 +0000777 status = stat (fileName, &statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000778
Eric Andersen3cf52d11999-10-12 22:26:06 +0000779 if (status < 0) {
780 perror (fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000781
Eric Andersen3cf52d11999-10-12 22:26:06 +0000782 return;
783 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000784
Eric Andersen3cf52d11999-10-12 22:26:06 +0000785 /*
786 * Make sure we aren't trying to save our file into itself.
787 */
788 if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode)) {
789 fprintf (stderr, "Skipping saving of archive file itself\n");
Eric Andersencc8ed391999-10-05 16:24:54 +0000790
Eric Andersen3cf52d11999-10-12 22:26:06 +0000791 return;
792 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000793
Eric Andersen3cf52d11999-10-12 22:26:06 +0000794 /*
795 * Check the type of file.
796 */
797 mode = statbuf.st_mode;
Eric Andersencc8ed391999-10-05 16:24:54 +0000798
Eric Andersen3cf52d11999-10-12 22:26:06 +0000799 if (S_ISDIR (mode)) {
800 saveDirectory (fileName, &statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000801
Eric Andersen3cf52d11999-10-12 22:26:06 +0000802 return;
803 }
Eric Andersen3cf52d11999-10-12 22:26:06 +0000804 if (S_ISREG (mode)) {
805 saveRegularFile (fileName, &statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000806
Eric Andersen3cf52d11999-10-12 22:26:06 +0000807 return;
808 }
Eric Andersen96bcfd31999-11-12 01:30:18 +0000809
810 /* Some day add support for tarring these up... but not today. :) */
811// if (S_ISLNK(mode) || S_ISFIFO(mode) || S_ISBLK(mode) || S_ISCHR (mode) ) {
812// fprintf (stderr, "%s: This version of tar can't store this type of file\n", fileName);
813// }
Eric Andersencc8ed391999-10-05 16:24:54 +0000814
Eric Andersen3cf52d11999-10-12 22:26:06 +0000815 /*
816 * The file is a strange type of file, ignore it.
817 */
818 fprintf (stderr, "%s: not a directory or regular file\n", fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000819}
820
821
822/*
823 * Save a regular file to the tar file.
824 */
825static void
Eric Andersen3cf52d11999-10-12 22:26:06 +0000826saveRegularFile (const char *fileName, const struct stat *statbuf)
Eric Andersencc8ed391999-10-05 16:24:54 +0000827{
Eric Andersen3cf52d11999-10-12 22:26:06 +0000828 int sawEof;
829 int fileFd;
830 int cc;
831 int dataCount;
832 long fullDataCount;
833 char data[TAR_BLOCK_SIZE * 16];
Eric Andersencc8ed391999-10-05 16:24:54 +0000834
Eric Andersen3cf52d11999-10-12 22:26:06 +0000835 /*
836 * Open the file for reading.
837 */
838 fileFd = open (fileName, O_RDONLY);
839
840 if (fileFd < 0) {
841 perror (fileName);
842
843 return;
844 }
845
846 /*
847 * Write out the header for the file.
848 */
849 writeHeader (fileName, statbuf);
850
851 /*
852 * Write the data blocks of the file.
853 * We must be careful to write the amount of data that the stat
854 * buffer indicated, even if the file has changed size. Otherwise
855 * the tar file will be incorrect.
856 */
857 fullDataCount = statbuf->st_size;
858 sawEof = FALSE;
859
860 while (fullDataCount > 0) {
861 /*
862 * Get the amount to write this iteration which is
863 * the minumum of the amount left to write and the
864 * buffer size.
Eric Andersencc8ed391999-10-05 16:24:54 +0000865 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000866 dataCount = sizeof (data);
Eric Andersencc8ed391999-10-05 16:24:54 +0000867
Eric Andersen3cf52d11999-10-12 22:26:06 +0000868 if (dataCount > fullDataCount)
869 dataCount = (int) fullDataCount;
870
871 /*
872 * Read the data from the file if we haven't seen the
873 * end of file yet.
874 */
875 cc = 0;
876
Eric Andersen5556c181999-11-10 19:27:58 +0000877 if (sawEof==FALSE) {
Eric Andersen3cf52d11999-10-12 22:26:06 +0000878 cc = fullRead (fileFd, data, dataCount);
879
880 if (cc < 0) {
881 perror (fileName);
882
883 (void) close (fileFd);
884 errorFlag = TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000885
886 return;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000887 }
888
889 /*
890 * If the file ended too soon, complain and set
891 * a flag so we will zero fill the rest of it.
892 */
893 if (cc < dataCount) {
894 fprintf (stderr,
895 "%s: Short read - zero filling", fileName);
896
897 sawEof = TRUE;
898 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000899 }
900
Eric Andersen3cf52d11999-10-12 22:26:06 +0000901 /*
902 * Zero fill the rest of the data if necessary.
Eric Andersencc8ed391999-10-05 16:24:54 +0000903 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000904 if (cc < dataCount)
905 memset (data + cc, 0, dataCount - cc);
Eric Andersencc8ed391999-10-05 16:24:54 +0000906
Eric Andersen3cf52d11999-10-12 22:26:06 +0000907 /*
908 * Write the buffer to the TAR file.
Eric Andersencc8ed391999-10-05 16:24:54 +0000909 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000910 writeTarBlock (data, dataCount);
Eric Andersencc8ed391999-10-05 16:24:54 +0000911
Eric Andersen3cf52d11999-10-12 22:26:06 +0000912 fullDataCount -= dataCount;
913 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000914
Eric Andersen3cf52d11999-10-12 22:26:06 +0000915 /*
916 * Close the file.
917 */
918 if ((tostdoutFlag == FALSE) && close (fileFd) < 0)
919 fprintf (stderr, "%s: close: %s\n", fileName, strerror (errno));
Eric Andersencc8ed391999-10-05 16:24:54 +0000920}
921
922
923/*
924 * Save a directory and all of its files to the tar file.
925 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000926static void saveDirectory (const char *dirName, const struct stat *statbuf)
Eric Andersencc8ed391999-10-05 16:24:54 +0000927{
Eric Andersen3cf52d11999-10-12 22:26:06 +0000928 DIR *dir;
929 struct dirent *entry;
930 int needSlash;
Eric Andersen1b61f411999-10-13 18:56:42 +0000931 char fullName[NAME_MAX];
Eric Andersencc8ed391999-10-05 16:24:54 +0000932
Eric Andersen3cf52d11999-10-12 22:26:06 +0000933 /*
934 * Construct the directory name as used in the tar file by appending
935 * a slash character to it.
936 */
937 strcpy (fullName, dirName);
938 strcat (fullName, "/");
Eric Andersencc8ed391999-10-05 16:24:54 +0000939
Eric Andersen3cf52d11999-10-12 22:26:06 +0000940 /*
941 * Write out the header for the directory entry.
942 */
943 writeHeader (fullName, statbuf);
Eric Andersencc8ed391999-10-05 16:24:54 +0000944
Eric Andersen3cf52d11999-10-12 22:26:06 +0000945 /*
946 * Open the directory.
947 */
948 dir = opendir (dirName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000949
Eric Andersen3cf52d11999-10-12 22:26:06 +0000950 if (dir == NULL) {
951 fprintf (stderr, "Cannot read directory \"%s\": %s\n",
952 dirName, strerror (errno));
Eric Andersencc8ed391999-10-05 16:24:54 +0000953
Eric Andersen3cf52d11999-10-12 22:26:06 +0000954 return;
955 }
956
957 /*
958 * See if a slash is needed.
959 */
960 needSlash = (*dirName && (dirName[strlen (dirName) - 1] != '/'));
961
962 /*
963 * Read all of the directory entries and check them,
964 * except for the current and parent directory entries.
965 */
Eric Andersen5556c181999-11-10 19:27:58 +0000966 while (errorFlag==FALSE && ((entry = readdir (dir)) != NULL)) {
Eric Andersen3cf52d11999-10-12 22:26:06 +0000967 if ((strcmp (entry->d_name, ".") == 0) ||
968 (strcmp (entry->d_name, "..") == 0)) {
969 continue;
Eric Andersencc8ed391999-10-05 16:24:54 +0000970 }
971
Eric Andersen3cf52d11999-10-12 22:26:06 +0000972 /*
973 * Build the full path name to the file.
Eric Andersencc8ed391999-10-05 16:24:54 +0000974 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000975 strcpy (fullName, dirName);
Eric Andersencc8ed391999-10-05 16:24:54 +0000976
Eric Andersen3cf52d11999-10-12 22:26:06 +0000977 if (needSlash)
978 strcat (fullName, "/");
979
980 strcat (fullName, entry->d_name);
981
982 /*
983 * Write this file to the tar file, noticing whether or not
984 * the file is a symbolic link.
Eric Andersencc8ed391999-10-05 16:24:54 +0000985 */
Eric Andersen3cf52d11999-10-12 22:26:06 +0000986 saveFile (fullName, TRUE);
987 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000988
Eric Andersen3cf52d11999-10-12 22:26:06 +0000989 /*
990 * All done, close the directory.
991 */
992 closedir (dir);
Eric Andersencc8ed391999-10-05 16:24:54 +0000993}
994
995
996/*
997 * Write a tar header for the specified file name and status.
998 * It is assumed that the file name fits.
999 */
Eric Andersen3cf52d11999-10-12 22:26:06 +00001000static void writeHeader (const char *fileName, const struct stat *statbuf)
Eric Andersencc8ed391999-10-05 16:24:54 +00001001{
Eric Andersen3cf52d11999-10-12 22:26:06 +00001002 long checkSum;
1003 const unsigned char *cp;
1004 int len;
1005 TarHeader header;
Eric Andersencc8ed391999-10-05 16:24:54 +00001006
Eric Andersen3cf52d11999-10-12 22:26:06 +00001007 /*
1008 * Zero the header block in preparation for filling it in.
1009 */
1010 memset ((char *) &header, 0, sizeof (header));
Eric Andersencc8ed391999-10-05 16:24:54 +00001011
Eric Andersen3cf52d11999-10-12 22:26:06 +00001012 /*
1013 * Fill in the header.
1014 */
1015 strcpy (header.name, fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +00001016
Eric Andersen3cf52d11999-10-12 22:26:06 +00001017 strncpy (header.magic, TAR_MAGIC, sizeof (header.magic));
1018 strncpy (header.version, TAR_VERSION, sizeof (header.version));
Eric Andersencc8ed391999-10-05 16:24:54 +00001019
Eric Andersen3cf52d11999-10-12 22:26:06 +00001020 putOctal (header.mode, sizeof (header.mode), statbuf->st_mode & 0777);
1021 putOctal (header.uid, sizeof (header.uid), statbuf->st_uid);
1022 putOctal (header.gid, sizeof (header.gid), statbuf->st_gid);
1023 putOctal (header.size, sizeof (header.size), statbuf->st_size);
1024 putOctal (header.mtime, sizeof (header.mtime), statbuf->st_mtime);
Eric Andersencc8ed391999-10-05 16:24:54 +00001025
Eric Andersen3cf52d11999-10-12 22:26:06 +00001026 header.typeFlag = TAR_TYPE_REGULAR;
Eric Andersencc8ed391999-10-05 16:24:54 +00001027
Eric Andersen3cf52d11999-10-12 22:26:06 +00001028 /*
1029 * Calculate and store the checksum.
1030 * This is the sum of all of the bytes of the header,
1031 * with the checksum field itself treated as blanks.
1032 */
1033 memset (header.checkSum, ' ', sizeof (header.checkSum));
Eric Andersencc8ed391999-10-05 16:24:54 +00001034
Eric Andersen3cf52d11999-10-12 22:26:06 +00001035 cp = (const unsigned char *) &header;
1036 len = sizeof (header);
1037 checkSum = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +00001038
Eric Andersen3cf52d11999-10-12 22:26:06 +00001039 while (len-- > 0)
1040 checkSum += *cp++;
Eric Andersencc8ed391999-10-05 16:24:54 +00001041
Eric Andersen3cf52d11999-10-12 22:26:06 +00001042 putOctal (header.checkSum, sizeof (header.checkSum), checkSum);
Eric Andersencc8ed391999-10-05 16:24:54 +00001043
Eric Andersen3cf52d11999-10-12 22:26:06 +00001044 /*
1045 * Write the tar header.
1046 */
1047 writeTarBlock ((const char *) &header, sizeof (header));
Eric Andersencc8ed391999-10-05 16:24:54 +00001048}
1049
1050
1051/*
1052 * Write data to one or more blocks of the tar file.
1053 * The data is always padded out to a multiple of TAR_BLOCK_SIZE.
1054 * The errorFlag static variable is set on an error.
1055 */
Eric Andersen3cf52d11999-10-12 22:26:06 +00001056static void writeTarBlock (const char *buf, int len)
Eric Andersencc8ed391999-10-05 16:24:54 +00001057{
Eric Andersen3cf52d11999-10-12 22:26:06 +00001058 int partialLength;
1059 int completeLength;
1060 char fullBlock[TAR_BLOCK_SIZE];
Eric Andersencc8ed391999-10-05 16:24:54 +00001061
Eric Andersen3cf52d11999-10-12 22:26:06 +00001062 /*
1063 * If we had a write error before, then do nothing more.
1064 */
Eric Andersen5556c181999-11-10 19:27:58 +00001065 if (errorFlag==TRUE)
Eric Andersen3cf52d11999-10-12 22:26:06 +00001066 return;
Eric Andersencc8ed391999-10-05 16:24:54 +00001067
Eric Andersen3cf52d11999-10-12 22:26:06 +00001068 /*
1069 * Get the amount of complete and partial blocks.
1070 */
1071 partialLength = len % TAR_BLOCK_SIZE;
1072 completeLength = len - partialLength;
Eric Andersencc8ed391999-10-05 16:24:54 +00001073
Eric Andersen3cf52d11999-10-12 22:26:06 +00001074 /*
1075 * Write all of the complete blocks.
1076 */
1077 if ((completeLength > 0) && !fullWrite (tarFd, buf, completeLength)) {
1078 perror (tarName);
Eric Andersencc8ed391999-10-05 16:24:54 +00001079
Eric Andersen3cf52d11999-10-12 22:26:06 +00001080 errorFlag = TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +00001081
Eric Andersen3cf52d11999-10-12 22:26:06 +00001082 return;
1083 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001084
Eric Andersen3cf52d11999-10-12 22:26:06 +00001085 /*
1086 * If there are no partial blocks left, we are done.
1087 */
1088 if (partialLength == 0)
1089 return;
Eric Andersencc8ed391999-10-05 16:24:54 +00001090
Eric Andersen3cf52d11999-10-12 22:26:06 +00001091 /*
1092 * Copy the partial data into a complete block, and pad the rest
1093 * of it with zeroes.
1094 */
1095 memcpy (fullBlock, buf + completeLength, partialLength);
1096 memset (fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength);
Eric Andersencc8ed391999-10-05 16:24:54 +00001097
Eric Andersen3cf52d11999-10-12 22:26:06 +00001098 /*
1099 * Write the last complete block.
1100 */
1101 if (!fullWrite (tarFd, fullBlock, TAR_BLOCK_SIZE)) {
1102 perror (tarName);
Eric Andersencc8ed391999-10-05 16:24:54 +00001103
Eric Andersen3cf52d11999-10-12 22:26:06 +00001104 errorFlag = TRUE;
1105 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001106}
1107
1108
1109/*
Eric Andersencc8ed391999-10-05 16:24:54 +00001110 * Read an octal value in a field of the specified width, with optional
1111 * spaces on both sides of the number and with an optional null character
1112 * at the end. Returns -1 on an illegal format.
1113 */
Eric Andersen3cf52d11999-10-12 22:26:06 +00001114static long getOctal (const char *cp, int len)
Eric Andersencc8ed391999-10-05 16:24:54 +00001115{
Eric Andersen3cf52d11999-10-12 22:26:06 +00001116 long val;
Eric Andersencc8ed391999-10-05 16:24:54 +00001117
Eric Andersen3cf52d11999-10-12 22:26:06 +00001118 while ((len > 0) && (*cp == ' ')) {
1119 cp++;
1120 len--;
1121 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001122
Eric Andersen3cf52d11999-10-12 22:26:06 +00001123 if ((len == 0) || !isOctal (*cp))
1124 return -1;
Eric Andersencc8ed391999-10-05 16:24:54 +00001125
Eric Andersen3cf52d11999-10-12 22:26:06 +00001126 val = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +00001127
Eric Andersen3cf52d11999-10-12 22:26:06 +00001128 while ((len > 0) && isOctal (*cp)) {
1129 val = val * 8 + *cp++ - '0';
1130 len--;
1131 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001132
Eric Andersen3cf52d11999-10-12 22:26:06 +00001133 while ((len > 0) && (*cp == ' ')) {
1134 cp++;
1135 len--;
1136 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001137
Eric Andersen3cf52d11999-10-12 22:26:06 +00001138 if ((len > 0) && *cp)
1139 return -1;
Eric Andersencc8ed391999-10-05 16:24:54 +00001140
Eric Andersen3cf52d11999-10-12 22:26:06 +00001141 return val;
Eric Andersencc8ed391999-10-05 16:24:54 +00001142}
1143
1144
1145/*
1146 * Put an octal string into the specified buffer.
1147 * The number is zero and space padded and possibly null padded.
1148 * Returns TRUE if successful.
1149 */
Eric Andersen3cf52d11999-10-12 22:26:06 +00001150static int putOctal (char *cp, int len, long value)
Eric Andersencc8ed391999-10-05 16:24:54 +00001151{
Eric Andersen3cf52d11999-10-12 22:26:06 +00001152 int tempLength;
1153 char *tempString;
1154 char tempBuffer[32];
Eric Andersencc8ed391999-10-05 16:24:54 +00001155
Eric Andersen3cf52d11999-10-12 22:26:06 +00001156 /*
1157 * Create a string of the specified length with an initial space,
1158 * leading zeroes and the octal number, and a trailing null.
1159 */
1160 tempString = tempBuffer;
Eric Andersencc8ed391999-10-05 16:24:54 +00001161
Eric Andersen3cf52d11999-10-12 22:26:06 +00001162 sprintf (tempString, " %0*lo", len - 2, value);
Eric Andersencc8ed391999-10-05 16:24:54 +00001163
Eric Andersen3cf52d11999-10-12 22:26:06 +00001164 tempLength = strlen (tempString) + 1;
Eric Andersencc8ed391999-10-05 16:24:54 +00001165
Eric Andersen3cf52d11999-10-12 22:26:06 +00001166 /*
1167 * If the string is too large, suppress the leading space.
1168 */
1169 if (tempLength > len) {
1170 tempLength--;
1171 tempString++;
1172 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001173
Eric Andersen3cf52d11999-10-12 22:26:06 +00001174 /*
1175 * If the string is still too large, suppress the trailing null.
1176 */
1177 if (tempLength > len)
1178 tempLength--;
Eric Andersencc8ed391999-10-05 16:24:54 +00001179
Eric Andersen3cf52d11999-10-12 22:26:06 +00001180 /*
1181 * If the string is still too large, fail.
1182 */
1183 if (tempLength > len)
1184 return FALSE;
Eric Andersencc8ed391999-10-05 16:24:54 +00001185
Eric Andersen3cf52d11999-10-12 22:26:06 +00001186 /*
1187 * Copy the string to the field.
1188 */
1189 memcpy (cp, tempString, len);
Eric Andersencc8ed391999-10-05 16:24:54 +00001190
Eric Andersen3cf52d11999-10-12 22:26:06 +00001191 return TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +00001192}
1193
1194
1195/*
1196 * See if the specified file name belongs to one of the specified list
1197 * of path prefixes. An empty list implies that all files are wanted.
1198 * Returns TRUE if the file is selected.
1199 */
Eric Andersenf811e071999-10-09 00:25:00 +00001200static int
Eric Andersen3cf52d11999-10-12 22:26:06 +00001201wantFileName (const char *fileName, int fileCount, char **fileTable)
Eric Andersencc8ed391999-10-05 16:24:54 +00001202{
Eric Andersen3cf52d11999-10-12 22:26:06 +00001203 const char *pathName;
1204 int fileLength;
1205 int pathLength;
Eric Andersencc8ed391999-10-05 16:24:54 +00001206
Eric Andersen3cf52d11999-10-12 22:26:06 +00001207 /*
1208 * If there are no files in the list, then the file is wanted.
1209 */
1210 if (fileCount == 0)
1211 return TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +00001212
Eric Andersen3cf52d11999-10-12 22:26:06 +00001213 fileLength = strlen (fileName);
Eric Andersencc8ed391999-10-05 16:24:54 +00001214
Eric Andersen3cf52d11999-10-12 22:26:06 +00001215 /*
1216 * Check each of the test paths.
1217 */
1218 while (fileCount-- > 0) {
1219 pathName = *fileTable++;
Eric Andersencc8ed391999-10-05 16:24:54 +00001220
Eric Andersen3cf52d11999-10-12 22:26:06 +00001221 pathLength = strlen (pathName);
Eric Andersencc8ed391999-10-05 16:24:54 +00001222
Eric Andersen3cf52d11999-10-12 22:26:06 +00001223 if (fileLength < pathLength)
1224 continue;
Eric Andersencc8ed391999-10-05 16:24:54 +00001225
Eric Andersen3cf52d11999-10-12 22:26:06 +00001226 if (memcmp (fileName, pathName, pathLength) != 0)
1227 continue;
Eric Andersencc8ed391999-10-05 16:24:54 +00001228
Eric Andersen3cf52d11999-10-12 22:26:06 +00001229 if ((fileLength == pathLength) || (fileName[pathLength] == '/')) {
1230 return TRUE;
Eric Andersencc8ed391999-10-05 16:24:54 +00001231 }
Eric Andersen3cf52d11999-10-12 22:26:06 +00001232 }
Eric Andersencc8ed391999-10-05 16:24:54 +00001233
Eric Andersen3cf52d11999-10-12 22:26:06 +00001234 return FALSE;
Eric Andersencc8ed391999-10-05 16:24:54 +00001235}
1236
1237
1238
Eric Andersencc8ed391999-10-05 16:24:54 +00001239/* END CODE */