hush/tar.c

1169 lines
24 KiB
C
Raw Normal View History

1999-10-05 16:24:54 +00:00
/*
1999-10-20 22:08:37 +00:00
* Mini tar implementation for busybox based on code taken from sash.
*
1999-10-05 16:24:54 +00:00
* Copyright (c) 1999 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
*
* Permission to distribute this code under the GPL has been granted.
1999-10-20 22:08:37 +00:00
*
1999-10-12 22:26:06 +00:00
* Modified for busybox by Erik Andersen <andersee@debian.org>
1999-10-20 22:08:37 +00:00
* Adjusted to grok stdin/stdout options.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
1999-10-05 16:24:54 +00:00
*/
#include "internal.h"
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
1999-10-05 16:24:54 +00:00
static const char tar_usage[] =
1999-10-12 22:26:06 +00:00
"Create, extract, or list files from a TAR file\n\n"
"usage: tar -[cxtvOf] [tarFileName] [FILE] ...\n"
"\tc=create, x=extract, t=list contents, v=verbose,\n"
"\tO=extract to stdout, f=tarfile or \"-\" for stdin\n";
1999-10-05 16:24:54 +00:00
/*
* Tar file constants.
*/
#define TAR_BLOCK_SIZE 512
#define TAR_NAME_SIZE 100
/*
* The POSIX (and basic GNU) tar header format.
* This structure is always embedded in a TAR_BLOCK_SIZE sized block
* with zero padding. We only process this information minimally.
*/
1999-10-12 22:26:06 +00:00
typedef struct {
char name[TAR_NAME_SIZE];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char checkSum[8];
char typeFlag;
char linkName[TAR_NAME_SIZE];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devMajor[8];
char devMinor[8];
char prefix[155];
1999-10-05 16:24:54 +00:00
} TarHeader;
#define TAR_MAGIC "ustar"
#define TAR_VERSION "00"
#define TAR_TYPE_REGULAR '0'
#define TAR_TYPE_HARD_LINK '1'
#define TAR_TYPE_SOFT_LINK '2'
/*
* Static data.
*/
1999-10-12 22:26:06 +00:00
static int listFlag;
static int extractFlag;
static int createFlag;
static int verboseFlag;
static int tostdoutFlag;
static int inHeader;
static int badHeader;
static int errorFlag;
static int skipFileFlag;
static int warnedRoot;
static int eofFlag;
static long dataCc;
static int outFd;
static char outName[TAR_NAME_SIZE];
1999-10-05 16:24:54 +00:00
/*
* Static data associated with the tar file.
*/
1999-10-12 22:26:06 +00:00
static const char *tarName;
static int tarFd;
static dev_t tarDev;
static ino_t tarInode;
1999-10-05 16:24:54 +00:00
/*
* Local procedures to restore files from a tar file.
*/
1999-10-12 22:26:06 +00:00
static void readTarFile (int fileCount, char **fileTable);
static void readData (const char *cp, int count);
static long getOctal (const char *cp, int len);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
static void readHeader (const TarHeader * hp,
int fileCount, char **fileTable);
1999-10-05 16:24:54 +00:00
/*
* Local procedures to save files into a tar file.
*/
1999-10-12 22:26:06 +00:00
static void saveFile (const char *fileName, int seeLinks);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
static void saveRegularFile (const char *fileName,
const struct stat *statbuf);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
static void saveDirectory (const char *fileName,
const struct stat *statbuf);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
static int wantFileName (const char *fileName,
int fileCount, char **fileTable);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
static void writeHeader (const char *fileName, const struct stat *statbuf);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
static void writeTarFile (int fileCount, char **fileTable);
static void writeTarBlock (const char *buf, int len);
static int putOctal (char *cp, int len, long value);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
extern int tar_main (int argc, char **argv)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
const char *options;
argc--;
argv++;
if (argc < 1) {
fprintf (stderr, "%s", tar_usage);
exit (FALSE);
}
errorFlag = FALSE;
extractFlag = FALSE;
createFlag = FALSE;
listFlag = FALSE;
verboseFlag = FALSE;
tostdoutFlag = FALSE;
tarName = NULL;
tarDev = 0;
tarInode = 0;
tarFd = -1;
/*
* Parse the options.
*/
options = *argv++;
argc--;
if (**argv == '-') {
for (; *options; options++) {
switch (*options) {
case 'f':
if (tarName != NULL) {
fprintf (stderr, "Only one 'f' option allowed\n");
exit (FALSE);
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
tarName = *argv++;
argc--;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
break;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
case 't':
listFlag = TRUE;
break;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
case 'x':
extractFlag = TRUE;
break;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
case 'c':
createFlag = TRUE;
break;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
case 'v':
verboseFlag = TRUE;
break;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
case 'O':
tostdoutFlag = TRUE;
break;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
case '-':
break;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
default:
fprintf (stderr, "Unknown tar flag '%c'\n", *options);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
exit (FALSE);
}
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
}
/*
* Validate the options.
*/
if (extractFlag + listFlag + createFlag != 1) {
fprintf (stderr,
"Exactly one of 'c', 'x' or 't' must be specified\n");
exit (FALSE);
}
/*
* Do the correct type of action supplying the rest of the
* command line arguments as the list of files to process.
*/
if (createFlag)
writeTarFile (argc, argv);
else
readTarFile (argc, argv);
if (errorFlag)
fprintf (stderr, "\n");
exit (errorFlag);
1999-10-05 16:24:54 +00:00
}
/*
* Read a tar file and extract or list the specified files within it.
* If the list is empty than all files are extracted or listed.
*/
1999-10-12 22:26:06 +00:00
static void readTarFile (int fileCount, char **fileTable)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
const char *cp;
int cc;
int inCc;
int blockSize;
char buf[BUF_SIZE];
skipFileFlag = FALSE;
badHeader = FALSE;
warnedRoot = FALSE;
eofFlag = FALSE;
inHeader = TRUE;
inCc = 0;
dataCc = 0;
outFd = -1;
blockSize = sizeof (buf);
cp = buf;
/*
* Open the tar file for reading.
*/
if ((tarName == NULL) || !strcmp (tarName, "-")) {
tarFd = STDIN;
} else
tarFd = open (tarName, O_RDONLY);
if (tarFd < 0) {
perror (tarName);
errorFlag = TRUE;
return;
}
/*
* Read blocks from the file until an end of file header block
* has been seen. (A real end of file from a read is an error.)
*/
while (!eofFlag) {
/*
* Read the next block of data if necessary.
* This will be a large block if possible, which we will
* then process in the small tar blocks.
1999-10-05 16:24:54 +00:00
*/
1999-10-12 22:26:06 +00:00
if (inCc <= 0) {
cp = buf;
inCc = fullRead (tarFd, buf, blockSize);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (inCc < 0) {
perror (tarName);
1999-10-05 16:24:54 +00:00
errorFlag = TRUE;
1999-10-12 22:26:06 +00:00
goto done;
}
if (inCc == 0) {
fprintf (stderr,
"Unexpected end of file from \"%s\"", tarName);
errorFlag = TRUE;
goto done;
}
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
/*
* If we are expecting a header block then examine it.
1999-10-05 16:24:54 +00:00
*/
1999-10-12 22:26:06 +00:00
if (inHeader) {
readHeader ((const TarHeader *) cp, fileCount, fileTable);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
cp += TAR_BLOCK_SIZE;
inCc -= TAR_BLOCK_SIZE;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
continue;
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
/*
* We are currently handling the data for a file.
* Process the minimum of the amount of data we have available
* and the amount left to be processed for the file.
1999-10-05 16:24:54 +00:00
*/
1999-10-12 22:26:06 +00:00
cc = inCc;
if (cc > dataCc)
cc = dataCc;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
readData (cp, cc);
/*
* If the amount left isn't an exact multiple of the tar block
* size then round it up to the next block boundary since there
* is padding at the end of the file.
1999-10-05 16:24:54 +00:00
*/
1999-10-12 22:26:06 +00:00
if (cc % TAR_BLOCK_SIZE)
cc += TAR_BLOCK_SIZE - (cc % TAR_BLOCK_SIZE);
cp += cc;
inCc -= cc;
}
done:
/*
* Close the tar file if needed.
*/
if ((tarFd >= 0) && (close (tarFd) < 0))
perror (tarName);
/*
* Close the output file if needed.
* This is only done here on a previous error and so no
* message is required on errors.
*/
if (tostdoutFlag == FALSE) {
if (outFd >= 0)
(void) close (outFd);
}
1999-10-05 16:24:54 +00:00
}
/*
* Examine the header block that was just read.
* This can specify the information for another file, or it can mark
* the end of the tar file.
*/
static void
1999-10-12 22:26:06 +00:00
readHeader (const TarHeader * hp, int fileCount, char **fileTable)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
int mode;
int uid;
int gid;
int checkSum;
long size;
time_t mtime;
const char *name;
int cc;
int hardLink;
int softLink;
/*
* If the block is completely empty, then this is the end of the
* archive file. If the name is null, then just skip this header.
*/
name = hp->name;
if (*name == '\0') {
for (cc = TAR_BLOCK_SIZE; cc > 0; cc--) {
if (*name++)
1999-10-05 16:24:54 +00:00
return;
}
1999-10-12 22:26:06 +00:00
eofFlag = TRUE;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* There is another file in the archive to examine.
* Extract the encoded information and check it.
*/
mode = getOctal (hp->mode, sizeof (hp->mode));
uid = getOctal (hp->uid, sizeof (hp->uid));
gid = getOctal (hp->gid, sizeof (hp->gid));
size = getOctal (hp->size, sizeof (hp->size));
mtime = getOctal (hp->mtime, sizeof (hp->mtime));
checkSum = getOctal (hp->checkSum, sizeof (hp->checkSum));
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0)) {
if (!badHeader)
fprintf (stderr, "Bad tar header, skipping\n");
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
badHeader = TRUE;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
badHeader = FALSE;
skipFileFlag = FALSE;
/*
* Check for the file modes.
*/
hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) ||
1999-10-05 16:24:54 +00:00
(hp->typeFlag == TAR_TYPE_HARD_LINK - '0'));
1999-10-12 22:26:06 +00:00
softLink = ((hp->typeFlag == TAR_TYPE_SOFT_LINK) ||
1999-10-05 16:24:54 +00:00
(hp->typeFlag == TAR_TYPE_SOFT_LINK - '0'));
1999-10-12 22:26:06 +00:00
/*
* Check for a directory or a regular file.
*/
if (name[strlen (name) - 1] == '/')
mode |= S_IFDIR;
else if ((mode & S_IFMT) == 0)
mode |= S_IFREG;
/*
* Check for absolute paths in the file.
* If we find any, then warn the user and make them relative.
*/
if (*name == '/') {
while (*name == '/')
name++;
if (!warnedRoot) {
fprintf (stderr,
"Absolute path detected, removing leading slashes\n");
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
warnedRoot = TRUE;
}
/*
* See if we want this file to be restored.
* If not, then set up to skip it.
*/
if (!wantFileName (name, fileCount, fileTable)) {
if (!hardLink && !softLink && S_ISREG (mode)) {
inHeader = (size == 0);
dataCc = size;
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
skipFileFlag = TRUE;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* This file is to be handled.
* If we aren't extracting then just list information about the file.
*/
if (!extractFlag) {
if (verboseFlag) {
printf ("%s %3d/%-d %9ld %s %s", modeString (mode),
uid, gid, size, timeString (mtime), name);
} else
printf ("%s", name);
1999-10-05 16:24:54 +00:00
if (hardLink)
1999-10-12 22:26:06 +00:00
printf (" (link to \"%s\")", hp->linkName);
else if (softLink)
printf (" (symlink to \"%s\")", hp->linkName);
else if (S_ISREG (mode)) {
inHeader = (size == 0);
dataCc = size;
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
printf ("\n");
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* We really want to extract the file.
*/
if (verboseFlag)
printf ("x %s\n", name);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (hardLink) {
if (link (hp->linkName, name) < 0)
perror (name);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (softLink) {
#ifdef S_ISLNK
if (symlink (hp->linkName, name) < 0)
perror (name);
#else
fprintf (stderr, "Cannot create symbolic links\n");
#endif
return;
}
/*
* If the file is a directory, then just create the path.
*/
if (S_ISDIR (mode)) {
createPath (name, mode);
return;
}
/*
* There is a file to write.
* First create the path to it if necessary with a default permission.
*/
createPath (name, 0777);
inHeader = (size == 0);
dataCc = size;
/*
* Start the output file.
*/
if (tostdoutFlag == TRUE)
outFd = STDOUT;
else
outFd = open (name, O_WRONLY | O_CREAT | O_TRUNC, mode);
if (outFd < 0) {
perror (name);
skipFileFlag = TRUE;
return;
}
/*
* If the file is empty, then that's all we need to do.
*/
if (size == 0 && tostdoutFlag == FALSE) {
(void) close (outFd);
outFd = -1;
}
1999-10-05 16:24:54 +00:00
}
/*
* Handle a data block of some specified size that was read.
*/
1999-10-12 22:26:06 +00:00
static void readData (const char *cp, int count)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
/*
* Reduce the amount of data left in this file.
* If there is no more data left, then we need to read
* the header again.
*/
dataCc -= count;
if (dataCc <= 0)
inHeader = TRUE;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* If we aren't extracting files or this file is being
* skipped then do nothing more.
*/
if (!extractFlag || skipFileFlag)
return;
/*
* Write the data to the output file.
*/
if (fullWrite (outFd, cp, count) < 0) {
perror (outName);
if (tostdoutFlag == FALSE) {
(void) close (outFd);
outFd = -1;
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
skipFileFlag = TRUE;
return;
}
/*
* If the write failed, close the file and disable further
* writes to this file.
*/
if (dataCc <= 0 && tostdoutFlag == FALSE) {
if (close (outFd))
perror (outName);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
outFd = -1;
}
1999-10-05 16:24:54 +00:00
}
/*
* Write a tar file containing the specified files.
*/
1999-10-12 22:26:06 +00:00
static void writeTarFile (int fileCount, char **fileTable)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
struct stat statbuf;
/*
* Make sure there is at least one file specified.
*/
if (fileCount <= 0) {
fprintf (stderr, "No files specified to be saved\n");
errorFlag = TRUE;
}
/*
* Create the tar file for writing.
*/
if ((tarName == NULL) || !strcmp (tarName, "-")) {
tostdoutFlag = TRUE;
tarFd = STDOUT;
} else
tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (tarFd < 0) {
perror (tarName);
errorFlag = TRUE;
return;
}
/*
* Get the device and inode of the tar file for checking later.
*/
if (fstat (tarFd, &statbuf) < 0) {
perror (tarName);
errorFlag = TRUE;
goto done;
}
tarDev = statbuf.st_dev;
tarInode = statbuf.st_ino;
/*
* Append each file name into the archive file.
* Follow symbolic links for these top level file names.
*/
while (!errorFlag && (fileCount-- > 0)) {
saveFile (*fileTable++, FALSE);
}
/*
* Now write an empty block of zeroes to end the archive.
*/
writeTarBlock ("", 1);
done:
/*
* Close the tar file and check for errors if it was opened.
*/
if ((tostdoutFlag == FALSE) && (tarFd >= 0) && (close (tarFd) < 0))
perror (tarName);
1999-10-05 16:24:54 +00:00
}
/*
* Save one file into the tar file.
* If the file is a directory, then this will recursively save all of
* the files and directories within the directory. The seeLinks
* flag indicates whether or not we want to see symbolic links as
* they really are, instead of blindly following them.
*/
1999-10-12 22:26:06 +00:00
static void saveFile (const char *fileName, int seeLinks)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
int status;
int mode;
struct stat statbuf;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (verboseFlag)
printf ("a %s\n", fileName);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* Check that the file name will fit in the header.
*/
if (strlen (fileName) >= TAR_NAME_SIZE) {
fprintf (stderr, "%s: File name is too long\n", fileName);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* Find out about the file.
*/
1999-10-05 16:24:54 +00:00
#ifdef S_ISLNK
1999-10-12 22:26:06 +00:00
if (seeLinks)
status = lstat (fileName, &statbuf);
else
1999-10-05 16:24:54 +00:00
#endif
1999-10-12 22:26:06 +00:00
status = stat (fileName, &statbuf);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (status < 0) {
perror (fileName);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* Make sure we aren't trying to save our file into itself.
*/
if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode)) {
fprintf (stderr, "Skipping saving of archive file itself\n");
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* Check the type of file.
*/
mode = statbuf.st_mode;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (S_ISDIR (mode)) {
saveDirectory (fileName, &statbuf);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (S_ISREG (mode)) {
saveRegularFile (fileName, &statbuf);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* The file is a strange type of file, ignore it.
*/
fprintf (stderr, "%s: not a directory or regular file\n", fileName);
1999-10-05 16:24:54 +00:00
}
/*
* Save a regular file to the tar file.
*/
static void
1999-10-12 22:26:06 +00:00
saveRegularFile (const char *fileName, const struct stat *statbuf)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
int sawEof;
int fileFd;
int cc;
int dataCount;
long fullDataCount;
char data[TAR_BLOCK_SIZE * 16];
/*
* Open the file for reading.
*/
fileFd = open (fileName, O_RDONLY);
if (fileFd < 0) {
perror (fileName);
return;
}
/*
* Write out the header for the file.
*/
writeHeader (fileName, statbuf);
/*
* Write the data blocks of the file.
* We must be careful to write the amount of data that the stat
* buffer indicated, even if the file has changed size. Otherwise
* the tar file will be incorrect.
*/
fullDataCount = statbuf->st_size;
sawEof = FALSE;
while (fullDataCount > 0) {
/*
* Get the amount to write this iteration which is
* the minumum of the amount left to write and the
* buffer size.
1999-10-05 16:24:54 +00:00
*/
1999-10-12 22:26:06 +00:00
dataCount = sizeof (data);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (dataCount > fullDataCount)
dataCount = (int) fullDataCount;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* Read the data from the file if we haven't seen the
* end of file yet.
1999-10-05 16:24:54 +00:00
*/
1999-10-12 22:26:06 +00:00
cc = 0;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (!sawEof) {
cc = fullRead (fileFd, data, dataCount);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (cc < 0) {
perror (fileName);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
(void) close (fileFd);
errorFlag = TRUE;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return;
}
/*
* If the file ended too soon, complain and set
* a flag so we will zero fill the rest of it.
*/
if (cc < dataCount) {
fprintf (stderr,
"%s: Short read - zero filling", fileName);
sawEof = TRUE;
}
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
/*
* Zero fill the rest of the data if necessary.
*/
if (cc < dataCount)
memset (data + cc, 0, dataCount - cc);
/*
* Write the buffer to the TAR file.
1999-10-05 16:24:54 +00:00
*/
1999-10-12 22:26:06 +00:00
writeTarBlock (data, dataCount);
fullDataCount -= dataCount;
}
/*
* Close the file.
*/
if ((tostdoutFlag == FALSE) && close (fileFd) < 0)
fprintf (stderr, "%s: close: %s\n", fileName, strerror (errno));
1999-10-05 16:24:54 +00:00
}
/*
* Save a directory and all of its files to the tar file.
*/
1999-10-12 22:26:06 +00:00
static void saveDirectory (const char *dirName, const struct stat *statbuf)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
DIR *dir;
struct dirent *entry;
int needSlash;
1999-10-13 18:56:42 +00:00
char fullName[NAME_MAX];
1999-10-12 22:26:06 +00:00
/*
* Construct the directory name as used in the tar file by appending
* a slash character to it.
*/
strcpy (fullName, dirName);
strcat (fullName, "/");
/*
* Write out the header for the directory entry.
*/
writeHeader (fullName, statbuf);
/*
* Open the directory.
*/
dir = opendir (dirName);
if (dir == NULL) {
fprintf (stderr, "Cannot read directory \"%s\": %s\n",
dirName, strerror (errno));
return;
}
/*
* See if a slash is needed.
*/
needSlash = (*dirName && (dirName[strlen (dirName) - 1] != '/'));
/*
* Read all of the directory entries and check them,
* except for the current and parent directory entries.
*/
while (!errorFlag && ((entry = readdir (dir)) != NULL)) {
if ((strcmp (entry->d_name, ".") == 0) ||
(strcmp (entry->d_name, "..") == 0)) {
continue;
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
/*
* Build the full path name to the file.
1999-10-05 16:24:54 +00:00
*/
1999-10-12 22:26:06 +00:00
strcpy (fullName, dirName);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (needSlash)
strcat (fullName, "/");
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
strcat (fullName, entry->d_name);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* Write this file to the tar file, noticing whether or not
* the file is a symbolic link.
1999-10-05 16:24:54 +00:00
*/
1999-10-12 22:26:06 +00:00
saveFile (fullName, TRUE);
}
/*
* All done, close the directory.
*/
closedir (dir);
1999-10-05 16:24:54 +00:00
}
/*
* Write a tar header for the specified file name and status.
* It is assumed that the file name fits.
*/
1999-10-12 22:26:06 +00:00
static void writeHeader (const char *fileName, const struct stat *statbuf)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
long checkSum;
const unsigned char *cp;
int len;
TarHeader header;
/*
* Zero the header block in preparation for filling it in.
*/
memset ((char *) &header, 0, sizeof (header));
/*
* Fill in the header.
*/
strcpy (header.name, fileName);
strncpy (header.magic, TAR_MAGIC, sizeof (header.magic));
strncpy (header.version, TAR_VERSION, sizeof (header.version));
putOctal (header.mode, sizeof (header.mode), statbuf->st_mode & 0777);
putOctal (header.uid, sizeof (header.uid), statbuf->st_uid);
putOctal (header.gid, sizeof (header.gid), statbuf->st_gid);
putOctal (header.size, sizeof (header.size), statbuf->st_size);
putOctal (header.mtime, sizeof (header.mtime), statbuf->st_mtime);
header.typeFlag = TAR_TYPE_REGULAR;
/*
* Calculate and store the checksum.
* This is the sum of all of the bytes of the header,
* with the checksum field itself treated as blanks.
*/
memset (header.checkSum, ' ', sizeof (header.checkSum));
cp = (const unsigned char *) &header;
len = sizeof (header);
checkSum = 0;
while (len-- > 0)
checkSum += *cp++;
putOctal (header.checkSum, sizeof (header.checkSum), checkSum);
/*
* Write the tar header.
*/
writeTarBlock ((const char *) &header, sizeof (header));
1999-10-05 16:24:54 +00:00
}
/*
* Write data to one or more blocks of the tar file.
* The data is always padded out to a multiple of TAR_BLOCK_SIZE.
* The errorFlag static variable is set on an error.
*/
1999-10-12 22:26:06 +00:00
static void writeTarBlock (const char *buf, int len)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
int partialLength;
int completeLength;
char fullBlock[TAR_BLOCK_SIZE];
/*
* If we had a write error before, then do nothing more.
*/
if (errorFlag)
return;
/*
* Get the amount of complete and partial blocks.
*/
partialLength = len % TAR_BLOCK_SIZE;
completeLength = len - partialLength;
/*
* Write all of the complete blocks.
*/
if ((completeLength > 0) && !fullWrite (tarFd, buf, completeLength)) {
perror (tarName);
errorFlag = TRUE;
return;
}
/*
* If there are no partial blocks left, we are done.
*/
if (partialLength == 0)
return;
/*
* Copy the partial data into a complete block, and pad the rest
* of it with zeroes.
*/
memcpy (fullBlock, buf + completeLength, partialLength);
memset (fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength);
/*
* Write the last complete block.
*/
if (!fullWrite (tarFd, fullBlock, TAR_BLOCK_SIZE)) {
perror (tarName);
errorFlag = TRUE;
}
1999-10-05 16:24:54 +00:00
}
/*
* Read an octal value in a field of the specified width, with optional
* spaces on both sides of the number and with an optional null character
* at the end. Returns -1 on an illegal format.
*/
1999-10-12 22:26:06 +00:00
static long getOctal (const char *cp, int len)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
long val;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
while ((len > 0) && (*cp == ' ')) {
cp++;
len--;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if ((len == 0) || !isOctal (*cp))
return -1;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
val = 0;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
while ((len > 0) && isOctal (*cp)) {
val = val * 8 + *cp++ - '0';
len--;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
while ((len > 0) && (*cp == ' ')) {
cp++;
len--;
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if ((len > 0) && *cp)
return -1;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return val;
1999-10-05 16:24:54 +00:00
}
/*
* Put an octal string into the specified buffer.
* The number is zero and space padded and possibly null padded.
* Returns TRUE if successful.
*/
1999-10-12 22:26:06 +00:00
static int putOctal (char *cp, int len, long value)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
int tempLength;
char *tempString;
char tempBuffer[32];
/*
* Create a string of the specified length with an initial space,
* leading zeroes and the octal number, and a trailing null.
*/
tempString = tempBuffer;
sprintf (tempString, " %0*lo", len - 2, value);
tempLength = strlen (tempString) + 1;
/*
* If the string is too large, suppress the leading space.
*/
if (tempLength > len) {
tempLength--;
tempString++;
}
/*
* If the string is still too large, suppress the trailing null.
*/
if (tempLength > len)
tempLength--;
/*
* If the string is still too large, fail.
*/
if (tempLength > len)
return FALSE;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* Copy the string to the field.
*/
memcpy (cp, tempString, len);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return TRUE;
1999-10-05 16:24:54 +00:00
}
/*
* See if the specified file name belongs to one of the specified list
* of path prefixes. An empty list implies that all files are wanted.
* Returns TRUE if the file is selected.
*/
1999-10-09 00:25:00 +00:00
static int
1999-10-12 22:26:06 +00:00
wantFileName (const char *fileName, int fileCount, char **fileTable)
1999-10-05 16:24:54 +00:00
{
1999-10-12 22:26:06 +00:00
const char *pathName;
int fileLength;
int pathLength;
/*
* If there are no files in the list, then the file is wanted.
*/
if (fileCount == 0)
return TRUE;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
fileLength = strlen (fileName);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
/*
* Check each of the test paths.
*/
while (fileCount-- > 0) {
pathName = *fileTable++;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
pathLength = strlen (pathName);
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (fileLength < pathLength)
continue;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if (memcmp (fileName, pathName, pathLength) != 0)
continue;
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
if ((fileLength == pathLength) || (fileName[pathLength] == '/')) {
return TRUE;
1999-10-05 16:24:54 +00:00
}
1999-10-12 22:26:06 +00:00
}
1999-10-05 16:24:54 +00:00
1999-10-12 22:26:06 +00:00
return FALSE;
1999-10-05 16:24:54 +00:00
}
/* END CODE */