Update to ar.c from Glenn McGrath. His comments follow:

------------------------------

the attached diff applies to the ar.c i originally submitted

Changes

Preserve dates now works.
Extracted files are set to the uid, gid and mode specified in the
archive.
Checks for valid end of header marker.
Correctly unpacks newer debian files. (has a '\n' character between
entries)
Added more comments.
Cleaned up code.
Added a last modified date to help keep track of versions.


TODO

Common functionality in ar.c are in tar.c should be merged, in
particular getOctal and extractSubFile.

Should all functions that are used by multiple file go in utilities.c ?
This commit is contained in:
Eric Andersen 2000-06-16 04:56:40 +00:00
parent b991395405
commit 852ff13fc4
2 changed files with 284 additions and 174 deletions

229
ar.c
View File

@ -21,6 +21,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Last modified 10 June 2000
*/
@ -28,9 +29,18 @@
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <utime.h>
#include <sys/types.h>
#include "internal.h"
#define AR_BLOCK_SIZE 60
#define AR_PRESERVE_DATE 1
#define AR_VERBOSE 2
#define AR_DISPLAY 4
#define AR_EXT_TO_FILE 8
#define AR_EXT_TO_STDOUT 16
#define BB_DECLARE_EXTERN
#define bb_need_io_error
#include "messages.c"
@ -67,22 +77,50 @@ static const char ar_usage[] = "ar [optxvV] archive [filenames] \n"
#endif
;
static void displayContents(struct ArInfo *entry, int funct)
/*
* Display details of a file, verbosly if funct=2
*/
static void displayEntry(struct ArInfo *entry, int funct)
{
/* TODO convert mode to string */
if ((funct & 2) == 2)
printf("%i %i/%i %6i %s ", entry->mode, entry->uid, entry->gid,
if ((funct & AR_VERBOSE) == AR_VERBOSE)
printf("%i %i/%i %8i %s ", entry->mode, entry->uid, entry->gid,
entry->size, timeString(entry->date));
printf("%s\n", entry->name);
}
/* Converts to new typed struct */
static int readArHeader(struct ArHeader *rawHeader, struct ArInfo *header)
/* this is from tar.c remove later*/
static long getOctal(const char *cp, int size)
{
long val = 0;
for(;(size > 0) && (*cp == ' '); cp++, size--);
if ((size == 0) || !isOctal(*cp))
return -1;
for(; (size > 0) && isOctal(*cp); size--) {
val = val * 8 + *cp++ - '0';
}
for (;(size > 0) && (*cp == ' '); cp++, size--);
if ((size > 0) && *cp)
return -1;
return val;
}
/*
* Converts from the char based struct to a new struct with stricter types
*/
static int processArHeader(struct ArHeader *rawHeader, struct ArInfo *header)
{
int count2;
int count;
/* check end of header marker is valid */
if ((rawHeader->ar_fmag[0]!='`') || (rawHeader->ar_fmag[1]!='\n'))
return(FALSE);
/* convert filename */
for (count = 0; count < 16; count++) {
/* allow spaces in filename except at the end */
if (rawHeader->ar_name[count] == ' ') {
for (count2 = count; count2 < 16; count2++)
if (!isspace(rawHeader->ar_name[count2]))
@ -90,6 +128,7 @@ static int readArHeader(struct ArHeader *rawHeader, struct ArInfo *header)
if (count2 >= 16)
break;
}
/* GNU ar uses '/' as an end of filename marker */
if (rawHeader->ar_name[count] == '/')
break;
header->name[count] = rawHeader->ar_name[count];
@ -98,15 +137,14 @@ static int readArHeader(struct ArHeader *rawHeader, struct ArInfo *header)
header->date = atoi(rawHeader->ar_date);
header->uid = atoi(rawHeader->ar_uid);
header->gid = atoi(rawHeader->ar_gid);
header->mode = atoi(rawHeader->ar_mode);
header->mode = getOctal(rawHeader->ar_mode, sizeof(rawHeader->ar_mode));
header->size = atoi(rawHeader->ar_size);
return (TRUE);
}
/*
* Copy size bytes from current position if srcFd to current position in dstFd
* taken from tarExtractRegularFile in tar.c
* could be used for ar, tar and copyFile .
* taken from tarExtractRegularFile in tar.c, remove later
*/
static int copySubFile(int srcFd, int dstFd, int copySize)
{
@ -134,17 +172,16 @@ static int copySubFile(int srcFd, int dstFd, int copySize)
}
/*
* Need to add checks, stat newfile before creation,change mode from 0777
* extract to current dir
* dstStat.st_size copied from current position of file pointed to by srcFd
* Extract the file described in ArInfo to the specified path
* set the new files uid, gid and mode
*/
static int extractToFile(int srcFd, const char *path, const char *name,
int size)
static int extractToFile(struct ArInfo *file, int funct, int srcFd, const char *path)
{
int dstFd, temp;
struct stat tmpStat;
char *pathname = NULL;
struct utimbuf newtime;
if ((temp = isDirectory(path, TRUE, &tmpStat)) != TRUE) {
if (!createPath(path, 0777)) {
fatalError("Cannot extract to specified path");
@ -154,95 +191,108 @@ static int extractToFile(int srcFd, const char *path, const char *name,
temp = (strlen(path) + 16);
pathname = (char *) xmalloc(temp);
pathname = strcpy(pathname, path);
pathname = strcat(pathname, &name[0]);
pathname = strcat(pathname, file->name);
dstFd = device_open(pathname, O_WRONLY | O_CREAT);
pathname = NULL;
temp = copySubFile(srcFd, dstFd, size);
temp = copySubFile(srcFd, dstFd, file->size);
fchown(dstFd, file->uid, file->gid);
fchmod(dstFd, file->mode);
close(dstFd);
if ((funct&AR_PRESERVE_DATE)==AR_PRESERVE_DATE)
newtime.modtime=file->date;
else
newtime.modtime=time(0);
newtime.actime=time(0);
temp = utime(pathname, &newtime);
return (TRUE);
}
/* Step through the ar file entries */
static int readArFile(char **fileNames, int argc, int funct)
/*
* Return a file descriptor for the specified file and do error checks
*/
static int getArFd(char *filename)
{
int arFd = 0;
int pdates = 0;
int verbose = 0;
int display = 0;
int extract = 0;
int extToStdout = 0;
int status = 0;
int found = 0;
int arFd;
char arVersion[8];
arFd = open(filename, O_RDONLY);
if (arFd < 0) {
errorMsg("Error opening '%s': %s\n", filename, strerror(errno));
return (FALSE);
}
if (fullRead(arFd, arVersion, 8) <= 0) {
errorMsg( "ar: Unexpected EOF in archive\n");
return (FALSE);
}
if (strncmp(arVersion,"!<arch>",7) != 0) {
errorMsg("ar header fails check ");
return(FALSE);
}
return arFd;
}
/*
* Step through the ar file and process it one entry at a time
* fileList[0] is the name of the ar archive
* fileList[1] and up are filenames to extract from the archive
* funct contains flags to specify the actions to be performed
*/
static int readArFile(char *fileList[16], int fileListSize, int funct)
{
int arFd, status, extFileFlag, i, lastOffset=0;
ArHeader rawArHeader;
ArInfo arEntry;
char arVersion[8];
char *arName;
char *selName[argc - 2];
int i;
if ((funct & 1) == 1)
pdates = 1;
if ((funct & 2) == 2)
verbose = 1;
if ((funct & 4) == 4)
display = 1;
if ((funct & 16) == 16) { /* extract to stdout */
extract = 1;
extToStdout = 1;
}
if ((funct & 8) == 8) { /* extract to file */
extract = 1;
}
arName = fileNames[2];
for (i = 0; i < (argc - 3); i++)
selName[i] = fileNames[i + 3];
arFd = open(arName, O_RDONLY);
if (arFd < 0) {
errorMsg("Error opening '%s': %s\n", arName, strerror(errno));
return (FALSE);
}
if (fullRead(arFd, arVersion, 8) <= 0) {
errorMsg("ar: Unexpected EOF in archive\n");
return (FALSE);
}
if (strncmp(arVersion, "!<arch>", 7) != 0) {
errorMsg("ar header fails check ");
return (FALSE);
}
/* open the ar archive */
arFd=getArFd(fileList[0]);
/* read the first header, then loop until ono more headers */
while ((status = fullRead(arFd, (char *) &rawArHeader, AR_BLOCK_SIZE))
== AR_BLOCK_SIZE) {
readArHeader(&rawArHeader, &arEntry);
if (display == 1) {
displayContents(&arEntry, funct);
}
if (argc == 3)
found = 1;
else {
found = 0;
for (i = 0; i < (argc - 3); i++) {
if ((status = (strcmp(selName[i], arEntry.name))) == 0)
found = 1;
/* check the header is valid, if not try reading the header
agian with an offset of 1, needed as some ar archive end
with a '\n' which isnt counted in specified file size */
if ((status=processArHeader(&rawArHeader, &arEntry))==FALSE ) {
if ((i=lseek(arFd, 0, SEEK_CUR))==(lastOffset+60))
lseek(arFd, lastOffset+1, SEEK_SET);
else
return(FALSE);
}
else {
extFileFlag=0;
if (funct&AR_DISPLAY)
displayEntry(&arEntry, funct);
/* check file was specified to be extracted only if
some file were specified */
if ((funct&AR_EXT_TO_FILE) || (funct&AR_EXT_TO_STDOUT)){
if (fileListSize==1)
extFileFlag=1;
else {
for( i=1; i<=fileListSize; i++)
if ((status=(strcmp(fileList[i],arEntry.name)))==0)
extFileFlag=1;
}
}
}
if ((extract == 1) && (found == 1)) {
if (extToStdout == 1) {
copySubFile(arFd, fileno(stdout), arEntry.size);
} else {
extractToFile(arFd, "./", arEntry.name, arEntry.size);
}
} else
lseek(arFd, arEntry.size, SEEK_CUR);
}
return (0);
if (extFileFlag==1) {
if (funct&AR_EXT_TO_FILE)
extractToFile(&arEntry, funct, arFd, "./");
else
copySubFile(arFd,fileno(stdout),arEntry.size);
}
else
lseek(arFd, arEntry.size, SEEK_CUR);
lastOffset=lseek(arFd, 0, SEEK_CUR);
} /* if processArHeader */
} /* while */
return (TRUE);
}
extern int ar_main(int argc, char **argv)
{
int ret = 0;
char *opt_ptr;
char c;
int funct = 0;
int funct = 0, ret=0, i=0;
char *fileList[16], c, *opt_ptr;
if (argc < 2)
usage(ar_usage);
@ -271,7 +321,12 @@ extern int ar_main(int argc, char **argv)
usage(ar_usage);
}
}
for(i=0; i<(argc-2); i++)
fileList[i]=argv[i+2];
if (funct > 3)
ret = readArFile(argv, argc, funct);
ret = readArFile(fileList, (argc-2), funct);
return (ret);
}

View File

@ -21,6 +21,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Last modified 10 June 2000
*/
@ -28,9 +29,18 @@
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <utime.h>
#include <sys/types.h>
#include "internal.h"
#define AR_BLOCK_SIZE 60
#define AR_PRESERVE_DATE 1
#define AR_VERBOSE 2
#define AR_DISPLAY 4
#define AR_EXT_TO_FILE 8
#define AR_EXT_TO_STDOUT 16
#define BB_DECLARE_EXTERN
#define bb_need_io_error
#include "messages.c"
@ -67,22 +77,50 @@ static const char ar_usage[] = "ar [optxvV] archive [filenames] \n"
#endif
;
static void displayContents(struct ArInfo *entry, int funct)
/*
* Display details of a file, verbosly if funct=2
*/
static void displayEntry(struct ArInfo *entry, int funct)
{
/* TODO convert mode to string */
if ((funct & 2) == 2)
printf("%i %i/%i %6i %s ", entry->mode, entry->uid, entry->gid,
if ((funct & AR_VERBOSE) == AR_VERBOSE)
printf("%i %i/%i %8i %s ", entry->mode, entry->uid, entry->gid,
entry->size, timeString(entry->date));
printf("%s\n", entry->name);
}
/* Converts to new typed struct */
static int readArHeader(struct ArHeader *rawHeader, struct ArInfo *header)
/* this is from tar.c remove later*/
static long getOctal(const char *cp, int size)
{
long val = 0;
for(;(size > 0) && (*cp == ' '); cp++, size--);
if ((size == 0) || !isOctal(*cp))
return -1;
for(; (size > 0) && isOctal(*cp); size--) {
val = val * 8 + *cp++ - '0';
}
for (;(size > 0) && (*cp == ' '); cp++, size--);
if ((size > 0) && *cp)
return -1;
return val;
}
/*
* Converts from the char based struct to a new struct with stricter types
*/
static int processArHeader(struct ArHeader *rawHeader, struct ArInfo *header)
{
int count2;
int count;
/* check end of header marker is valid */
if ((rawHeader->ar_fmag[0]!='`') || (rawHeader->ar_fmag[1]!='\n'))
return(FALSE);
/* convert filename */
for (count = 0; count < 16; count++) {
/* allow spaces in filename except at the end */
if (rawHeader->ar_name[count] == ' ') {
for (count2 = count; count2 < 16; count2++)
if (!isspace(rawHeader->ar_name[count2]))
@ -90,6 +128,7 @@ static int readArHeader(struct ArHeader *rawHeader, struct ArInfo *header)
if (count2 >= 16)
break;
}
/* GNU ar uses '/' as an end of filename marker */
if (rawHeader->ar_name[count] == '/')
break;
header->name[count] = rawHeader->ar_name[count];
@ -98,15 +137,14 @@ static int readArHeader(struct ArHeader *rawHeader, struct ArInfo *header)
header->date = atoi(rawHeader->ar_date);
header->uid = atoi(rawHeader->ar_uid);
header->gid = atoi(rawHeader->ar_gid);
header->mode = atoi(rawHeader->ar_mode);
header->mode = getOctal(rawHeader->ar_mode, sizeof(rawHeader->ar_mode));
header->size = atoi(rawHeader->ar_size);
return (TRUE);
}
/*
* Copy size bytes from current position if srcFd to current position in dstFd
* taken from tarExtractRegularFile in tar.c
* could be used for ar, tar and copyFile .
* taken from tarExtractRegularFile in tar.c, remove later
*/
static int copySubFile(int srcFd, int dstFd, int copySize)
{
@ -134,17 +172,16 @@ static int copySubFile(int srcFd, int dstFd, int copySize)
}
/*
* Need to add checks, stat newfile before creation,change mode from 0777
* extract to current dir
* dstStat.st_size copied from current position of file pointed to by srcFd
* Extract the file described in ArInfo to the specified path
* set the new files uid, gid and mode
*/
static int extractToFile(int srcFd, const char *path, const char *name,
int size)
static int extractToFile(struct ArInfo *file, int funct, int srcFd, const char *path)
{
int dstFd, temp;
struct stat tmpStat;
char *pathname = NULL;
struct utimbuf newtime;
if ((temp = isDirectory(path, TRUE, &tmpStat)) != TRUE) {
if (!createPath(path, 0777)) {
fatalError("Cannot extract to specified path");
@ -154,95 +191,108 @@ static int extractToFile(int srcFd, const char *path, const char *name,
temp = (strlen(path) + 16);
pathname = (char *) xmalloc(temp);
pathname = strcpy(pathname, path);
pathname = strcat(pathname, &name[0]);
pathname = strcat(pathname, file->name);
dstFd = device_open(pathname, O_WRONLY | O_CREAT);
pathname = NULL;
temp = copySubFile(srcFd, dstFd, size);
temp = copySubFile(srcFd, dstFd, file->size);
fchown(dstFd, file->uid, file->gid);
fchmod(dstFd, file->mode);
close(dstFd);
if ((funct&AR_PRESERVE_DATE)==AR_PRESERVE_DATE)
newtime.modtime=file->date;
else
newtime.modtime=time(0);
newtime.actime=time(0);
temp = utime(pathname, &newtime);
return (TRUE);
}
/* Step through the ar file entries */
static int readArFile(char **fileNames, int argc, int funct)
/*
* Return a file descriptor for the specified file and do error checks
*/
static int getArFd(char *filename)
{
int arFd = 0;
int pdates = 0;
int verbose = 0;
int display = 0;
int extract = 0;
int extToStdout = 0;
int status = 0;
int found = 0;
int arFd;
char arVersion[8];
arFd = open(filename, O_RDONLY);
if (arFd < 0) {
errorMsg("Error opening '%s': %s\n", filename, strerror(errno));
return (FALSE);
}
if (fullRead(arFd, arVersion, 8) <= 0) {
errorMsg( "ar: Unexpected EOF in archive\n");
return (FALSE);
}
if (strncmp(arVersion,"!<arch>",7) != 0) {
errorMsg("ar header fails check ");
return(FALSE);
}
return arFd;
}
/*
* Step through the ar file and process it one entry at a time
* fileList[0] is the name of the ar archive
* fileList[1] and up are filenames to extract from the archive
* funct contains flags to specify the actions to be performed
*/
static int readArFile(char *fileList[16], int fileListSize, int funct)
{
int arFd, status, extFileFlag, i, lastOffset=0;
ArHeader rawArHeader;
ArInfo arEntry;
char arVersion[8];
char *arName;
char *selName[argc - 2];
int i;
if ((funct & 1) == 1)
pdates = 1;
if ((funct & 2) == 2)
verbose = 1;
if ((funct & 4) == 4)
display = 1;
if ((funct & 16) == 16) { /* extract to stdout */
extract = 1;
extToStdout = 1;
}
if ((funct & 8) == 8) { /* extract to file */
extract = 1;
}
arName = fileNames[2];
for (i = 0; i < (argc - 3); i++)
selName[i] = fileNames[i + 3];
arFd = open(arName, O_RDONLY);
if (arFd < 0) {
errorMsg("Error opening '%s': %s\n", arName, strerror(errno));
return (FALSE);
}
if (fullRead(arFd, arVersion, 8) <= 0) {
errorMsg("ar: Unexpected EOF in archive\n");
return (FALSE);
}
if (strncmp(arVersion, "!<arch>", 7) != 0) {
errorMsg("ar header fails check ");
return (FALSE);
}
/* open the ar archive */
arFd=getArFd(fileList[0]);
/* read the first header, then loop until ono more headers */
while ((status = fullRead(arFd, (char *) &rawArHeader, AR_BLOCK_SIZE))
== AR_BLOCK_SIZE) {
readArHeader(&rawArHeader, &arEntry);
if (display == 1) {
displayContents(&arEntry, funct);
}
if (argc == 3)
found = 1;
else {
found = 0;
for (i = 0; i < (argc - 3); i++) {
if ((status = (strcmp(selName[i], arEntry.name))) == 0)
found = 1;
/* check the header is valid, if not try reading the header
agian with an offset of 1, needed as some ar archive end
with a '\n' which isnt counted in specified file size */
if ((status=processArHeader(&rawArHeader, &arEntry))==FALSE ) {
if ((i=lseek(arFd, 0, SEEK_CUR))==(lastOffset+60))
lseek(arFd, lastOffset+1, SEEK_SET);
else
return(FALSE);
}
else {
extFileFlag=0;
if (funct&AR_DISPLAY)
displayEntry(&arEntry, funct);
/* check file was specified to be extracted only if
some file were specified */
if ((funct&AR_EXT_TO_FILE) || (funct&AR_EXT_TO_STDOUT)){
if (fileListSize==1)
extFileFlag=1;
else {
for( i=1; i<=fileListSize; i++)
if ((status=(strcmp(fileList[i],arEntry.name)))==0)
extFileFlag=1;
}
}
}
if ((extract == 1) && (found == 1)) {
if (extToStdout == 1) {
copySubFile(arFd, fileno(stdout), arEntry.size);
} else {
extractToFile(arFd, "./", arEntry.name, arEntry.size);
}
} else
lseek(arFd, arEntry.size, SEEK_CUR);
}
return (0);
if (extFileFlag==1) {
if (funct&AR_EXT_TO_FILE)
extractToFile(&arEntry, funct, arFd, "./");
else
copySubFile(arFd,fileno(stdout),arEntry.size);
}
else
lseek(arFd, arEntry.size, SEEK_CUR);
lastOffset=lseek(arFd, 0, SEEK_CUR);
} /* if processArHeader */
} /* while */
return (TRUE);
}
extern int ar_main(int argc, char **argv)
{
int ret = 0;
char *opt_ptr;
char c;
int funct = 0;
int funct = 0, ret=0, i=0;
char *fileList[16], c, *opt_ptr;
if (argc < 2)
usage(ar_usage);
@ -271,7 +321,12 @@ extern int ar_main(int argc, char **argv)
usage(ar_usage);
}
}
for(i=0; i<(argc-2); i++)
fileList[i]=argv[i+2];
if (funct > 3)
ret = readArFile(argv, argc, funct);
ret = readArFile(fileList, (argc-2), funct);
return (ret);
}