static char const rcsid[] = "$Id: untar.c,v 1.3 2004/11/30 17:30:00 dasenbro Exp $";
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/param.h>
#include "clamav.h"
#include "others.h"
#include "untar.h"
#include "mbox.h"
#include "blob.h"
#define BLOCKSIZE 512
#ifndef NAME_MAX
#ifdef MAXNAMELEN
#define NAME_MAX MAXNAMELEN
#else
#ifdef FILENAME_MAX
#define NAME_MAX FILENAME_MAX
#endif
#endif
#endif
#ifndef O_BINARY
#define O_BINARY 0
#endif
static int
octal(const char *str)
{
int ret = -1;
sscanf(str, "%o", &ret);
return ret;
}
int
cli_untar(const char *dir, int desc)
{
int size = 0;
int in_block = 0;
char fullname[NAME_MAX + 1];
FILE *outfile = NULL;
cli_dbgmsg("In untar(%s, %d)\n", dir ? dir : "", desc);
for(;;) {
char block[BLOCKSIZE];
const int nread = cli_readn(desc, block, sizeof(block));
if(!in_block && nread == 0)
break;
if(nread < 0) {
if(outfile)
fclose(outfile);
cli_errmsg("cli_untar: block read error\n");
return CL_EIO;
}
if(!in_block) {
char type;
const char *suffix;
size_t suffixLen = 0;
int fd, directory;
char magic[7], name[101], osize[13];
if(outfile) {
if(fclose(outfile)) {
cli_errmsg("cli_untar: cannot close file %s\n",
fullname);
return CL_EIO;
}
outfile = (FILE*)0;
}
if(block[0] == '\0')
break;
strncpy(magic, block+257, 5);
magic[5] = '\0';
if(strcmp(magic, "ustar") != 0) {
cli_dbgmsg("Incorrect magic string '%s' in tar header\n", magic);
return CL_EFORMAT;
}
type = block[156];
switch(type) {
case '0':
case '\0':
case '7':
directory = 0;
break;
case '5':
case '2':
case '3':
case '4':
case '6':
directory = 1;
break;
default:
cli_errmsg("cli_untar: unknown type flag %c\n", type);
return CL_EIO;
}
if(directory) {
in_block = 0;
continue;
}
strncpy(name, block, 100);
name[100] = '\0';
sanitiseName(name);
suffix = strrchr(name, '.');
if(suffix == NULL)
suffix = "";
else {
suffixLen = strlen(suffix);
if(suffixLen > 4) {
suffix = "";
suffixLen = 0;
}
}
snprintf(fullname, sizeof(fullname) - 1 - suffixLen, "%s/%.*sXXXXXX", dir,
(int)(sizeof(fullname) - 9 - suffixLen - strlen(dir)), name);
#if defined(C_LINUX) || defined(C_BSD) || defined(HAVE_MKSTEMP) || defined(C_SOLARIS) || defined(C_CYGWIN)
fd = mkstemp(fullname);
#else
(void)mktemp(fullname);
fd = open(fullname, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600);
#endif
if(fd < 0) {
cli_errmsg("Can't create temporary file %s: %s\n", fullname, strerror(errno));
cli_dbgmsg("%lu %d %d\n", suffixLen, sizeof(fullname), strlen(fullname));
return CL_ETMPFILE;
}
cli_dbgmsg("cli_untar: extracting %s\n", fullname);
in_block = 1;
if((outfile = fdopen(fd, "wb")) == NULL) {
cli_errmsg("cli_untar: cannot create file %s\n",
fullname);
close(fd);
return CL_ETMPFILE;
}
strncpy(osize, block+124, 12);
osize[12] = '\0';
size = octal(osize);
if(size < 0) {
cli_errmsg("Invalid size in tar header\n");
fclose(outfile);
return CL_EFORMAT;
}
cli_dbgmsg("cli_untar: size = %d\n", size);
} else {
const int nbytes = size>512? 512:size;
const int nwritten = fwrite(block, 1, nbytes, outfile);
if(nwritten != nbytes) {
cli_errmsg("cli_untar: only wrote %d bytes to file %s\n",
nwritten, fullname);
}
size -= nbytes;
}
if (size == 0)
in_block = 0;
}
if(outfile)
fclose(outfile);
return 0;
}