From 852ff13fc43e99737c89ced1485c19bfbe0714bb Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Fri, 16 Jun 2000 04:56:40 +0000 Subject: [PATCH] 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 ? --- ar.c | 229 +++++++++++++++++++++++++++++++------------------- archival/ar.c | 229 +++++++++++++++++++++++++++++++------------------- 2 files changed, 284 insertions(+), 174 deletions(-) diff --git a/ar.c b/ar.c index 253f3348a..e58b7395d 100644 --- a/ar.c +++ b/ar.c @@ -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 #include #include +#include +#include +#include #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,"!",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, "!", 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); } diff --git a/archival/ar.c b/archival/ar.c index 253f3348a..e58b7395d 100644 --- a/archival/ar.c +++ b/archival/ar.c @@ -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 #include #include +#include +#include +#include #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,"!",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, "!", 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); }