X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=ar.c;h=241c1fac238a810c13292e6e68a745598abd8dea;hb=322ae93a5e0b78b65831f9fd87fd456eb84d21a1;hp=27dfadd1db62cb3a6bf426957c0e41297556d86f;hpb=57ebebfb01a9a29378b2f0179724661bfc5402e9;p=oweals%2Fbusybox.git diff --git a/ar.c b/ar.c index 27dfadd1d..241c1fac2 100644 --- a/ar.c +++ b/ar.c @@ -4,7 +4,7 @@ * * Copyright (C) 2000 by Glenn McGrath * Written by Glenn McGrath 1 June 2000 - * + * * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. * * This program is free software; you can redistribute it and/or modify @@ -21,312 +21,374 @@ * 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 + * Last modified 9 September 2000 */ - - #include +#include #include #include #include #include #include +#include +#include #include +#include +#include #include "internal.h" -#define AR_BLOCK_SIZE 60 -#define AR_PRESERVE_DATE 1 /* preserve original dates */ -#define AR_VERBOSE 2 /* be verbose */ -#define AR_DISPLAY 4 /* display contents */ -#define AR_EXT_TO_FILE 8 /* extract contents of archive */ -#define AR_EXT_TO_STDOUT 16 /* extract to stdout */ - -#define BB_DECLARE_EXTERN -#define bb_need_io_error -#include "messages.c" - -struct ArHeader { /* Byte Offset */ - char ar_name[16]; /* 0-15 */ - char ar_date[12]; /* 16-27 */ - char ar_uid[6], ar_gid[6]; /* 28-39 */ - char ar_mode[8]; /* 40-47 */ - char ar_size[10]; /* 48-57 */ - char ar_fmag[2]; /* 58-59 */ -}; -typedef struct ArHeader ArHeader; - -struct ArInfo { - char name[17]; /* File name */ - time_t date; /* long int, No of seconds since epoch */ - uid_t uid; /* unsigned int, Numeric UID */ - gid_t gid; /* unsigned int, Numeric GID */ - mode_t mode; /* unsigned int, Unix mode */ - size_t size; /* int, Size of the file */ -}; -typedef struct ArInfo ArInfo; - -static const char ar_usage[] = "ar [optxvV] archive [filenames] \n" -#ifndef BB_FEATURE_TRIVIAL_HELP - "\nExtract or list files from an ar archive.\n\n" - "Options:\n" - "\to\t\tpreserve original dates\n" - "\tp\t\textract to stdout\n" - "\tt\t\tlist\n" - "\tx\t\textract\n" - "\tv\t\tverbosely list files processed\n" +#define BLOCK_SIZE 60 +#define PRESERVE_DATE 1 /* preserve original dates */ +#define VERBOSE 2 /* be verbose */ +#define DISPLAY 4 /* display contents */ +#define EXT_TO_FILE 8 /* extract contents of archive */ +#define EXT_TO_STDOUT 16 /* extract to stdout */ +#define RECURSIVE 32 + +#define MAX_NAME_LENGTH 100 + +//#define BB_DECLARE_EXTERN +//#define bb_need_io_error +//#include "messages.c" + +//#define BB_AR_EXPERIMENTAL_UNTAR + +#if defined BB_AR_EXPERIMENTAL_UNTAR +typedef struct rawTarHeader { + char name[100]; /* 0-99 */ + char mode[8]; /* 100-107 */ + char uid[8]; /* 108-115 */ + char gid[8]; /* 116-123 */ + char size[12]; /* 124-135 */ + char mtime[12]; /* 136-147 */ + char chksum[8]; /* 148-155 */ + char typeflag; /* 156-156 */ + char linkname[100]; /* 157-256 */ + char magic[6]; /* 257-262 */ + char version[2]; /* 263-264 */ + char uname[32]; /* 265-296 */ + char gname[32]; /* 297-328 */ + char devmajor[8]; /* 329-336 */ + char devminor[8]; /* 337-344 */ + char prefix[155]; /* 345-499 */ + char padding[12]; /* 500-512 */ +} rawTarHeader_t; #endif - ; +typedef struct rawArHeader { /* Byte Offset */ + char name[16]; /* 0-15 */ + char date[12]; /* 16-27 */ + char uid[6], gid[6]; /* 28-39 */ + char mode[8]; /* 40-47 */ + char size[10]; /* 48-57 */ + char fmag[2]; /* 58-59 */ +} rawArHeader_t; + +typedef struct headerL { + char name[MAX_NAME_LENGTH]; + size_t size; + uid_t uid; + gid_t gid; + mode_t mode; + time_t mtime; + off_t offset; + struct headerL *next; +} headerL_t; + +#if defined BB_AR_EXPERIMENTAL_UNTAR /* - * Display details of a file, verbosly if funct=2 + * identify Tar header (magic field) and reset srcFd to entry position */ -static void displayEntry(struct ArInfo *entry, int funct) +static int checkTarMagic(int srcFd) { - /* TODO convert mode to string */ - 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); + off_t headerStart; + char magic[6]; + + headerStart = lseek(srcFd, 0, SEEK_CUR); + lseek(srcFd, (off_t) 257, SEEK_CUR); + fullRead(srcFd, magic, 6); + lseek(srcFd, headerStart, SEEK_SET); + if (strncmp(magic, "ustar", 5)!=0) + return(FALSE); + return(TRUE); } -/* this is from tar.c remove later*/ -static long getOctal(const char *cp, int size) + +static int readTarHeader(int srcFd, headerL_t *current) { - long val = 0; + rawTarHeader_t rawTarHeader; + unsigned char *temp = (unsigned char *) &rawTarHeader; + long sum = 0; + int i; + off_t initialOffset; - for(;(size > 0) && (*cp == ' '); cp++, size--); - if ((size == 0) || !isOctal(*cp)) - return -1; - for(; (size > 0) && isOctal(*cp); size--) { - val = val * 8 + *cp++ - '0'; + initialOffset = lseek(srcFd, 0, SEEK_CUR); + if (fullRead(srcFd, (char *) &rawTarHeader, 512) != 512) { + lseek(srcFd, initialOffset, SEEK_SET); + return(FALSE); } - for (;(size > 0) && (*cp == ' '); cp++, size--); - if ((size > 0) && *cp) - return -1; - return val; -} + for (i = 0; i < 148 ; i++) + sum += temp[i]; + sum += ' ' * 8; + for (i = 156; i < 512 ; i++) + sum += temp[i]; + if (sum!= strtol(rawTarHeader.chksum, NULL, 8)) + return(FALSE); + sscanf(rawTarHeader.name, "%s", current->name); + current->size = strtol(rawTarHeader.size, NULL, 8); + current->uid = strtol(rawTarHeader.uid, NULL, 8); + current->gid = strtol(rawTarHeader.gid, NULL, 8); + current->mode = strtol(rawTarHeader.mode, NULL, 8); + current->mtime = strtol(rawTarHeader.mtime, NULL, 8); + current->offset = lseek(srcFd, 0 , SEEK_CUR); -/* - * 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])) - break; - 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]; - } - header->name[count] = '\0'; - header->date = atoi(rawHeader->ar_date); - header->uid = atoi(rawHeader->ar_uid); - header->gid = atoi(rawHeader->ar_gid); - header->mode = getOctal(rawHeader->ar_mode, sizeof(rawHeader->ar_mode)); - header->size = atoi(rawHeader->ar_size); - return (TRUE); + current->next = (headerL_t *) xmalloc(sizeof(headerL_t)); + current = current->next; + return(TRUE); } +#endif /* - * Copy size bytes from current position if srcFd to current position in dstFd - * taken from tarExtractRegularFile in tar.c, remove later + * identify Ar header (magic) and reset srcFd to entry position */ -static int copySubFile(int srcFd, int dstFd, int copySize) +static int checkArMagic(int srcFd) { - int readSize, writeSize, doneSize; - char buffer[BUFSIZ]; - - while (copySize > 0) { - if (copySize > BUFSIZ) - readSize = BUFSIZ; - else - readSize = copySize; - writeSize = fullRead(srcFd, buffer, readSize); - if (writeSize <= 0) { - errorMsg(io_error, "copySubFile :", strerror(errno)); - return (FALSE); - } - doneSize = fullWrite(dstFd, buffer, writeSize); - if (doneSize <= 0) { - errorMsg(io_error, "copySubFile :", strerror(errno)); - return (FALSE); - } - copySize -= doneSize; - } - return (TRUE); + off_t headerStart; + char arMagic[8]; + + headerStart = lseek(srcFd, 0, SEEK_CUR); + if (fullRead(srcFd, arMagic, 8) != 8) { + printf("fatal error/n"); + return (FALSE); + } + lseek(srcFd, headerStart, SEEK_SET); + + if (strncmp(arMagic,"!",7) != 0) + return(FALSE); + return(TRUE); } /* - * Extract the file described in ArInfo to the specified path - * set the new files uid, gid and mode - */ -static int extractToFile(struct ArInfo *file, int funct, int srcFd, const char *path) + * get, check and correct the converted header + */ +static int readArEntry(int srcFd, headerL_t *entry) { - int dstFd, temp; - struct stat tmpStat; - char *pathname = NULL; - struct utimbuf newtime; + size_t nameLength; + rawArHeader_t rawArHeader; + off_t initialOffset; + + initialOffset = lseek(srcFd, 0, SEEK_CUR); + if (fullRead(srcFd, (char *) &rawArHeader, 60) != 60) { + lseek(srcFd, initialOffset, SEEK_SET); + return(FALSE); + } + if ((rawArHeader.fmag[0]!='`') || (rawArHeader.fmag[1]!='\n')) { + lseek(srcFd, initialOffset, SEEK_SET); + return(FALSE); + } + + strncpy(entry->name, rawArHeader.name, 16); + nameLength=strcspn(entry->name, " \\"); + entry->name[nameLength]='\0'; + parse_mode(rawArHeader.mode, &entry->mode); + entry->mtime = atoi(rawArHeader.date); + entry->uid = atoi(rawArHeader.uid); + entry->gid = atoi(rawArHeader.gid); + entry->size = (size_t) atoi(rawArHeader.size); + entry->offset = initialOffset + (off_t) 60; + + nameLength = strcspn(entry->name, "/"); + + /* handle GNU style short filenames, strip trailing '/' */ + if (nameLength > 0) + entry->name[nameLength]='\0'; - if ((temp = isDirectory(path, TRUE, &tmpStat)) != TRUE) { - if (!createPath(path, 0777)) { - fatalError("Cannot extract to specified path"); - return (FALSE); + /* handle GNU style long filenames */ + if (nameLength == 0) { + /* escape from recursive call */ + if (entry->name[1]=='0') + return(TRUE); + + /* the data section contains the real filename */ + if (entry->name[1]=='/') { + char tempName[MAX_NAME_LENGTH]; + + if (entry->size > MAX_NAME_LENGTH) + entry->size = MAX_NAME_LENGTH; + fullRead(srcFd, tempName, entry->size); + tempName[entry->size-3]='\0'; + + /* read the second header for this entry */ + /* be carefull, this is recursive */ + if (readArEntry(srcFd, entry)==FALSE) + return(FALSE); + + if ((entry->name[0]='/') && (entry->name[1]='0')) + strcpy(entry->name, tempName); + else { + errorMsg("Invalid long filename\n"); + return(FALSE); + } } } - temp = (strlen(path) + 16); - pathname = (char *) xmalloc(temp); - pathname = strcpy(pathname, path); - pathname = strcat(pathname, file->name); - dstFd = device_open(pathname, O_WRONLY | O_CREAT); - 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); + return(TRUE); } /* - * Return a file descriptor for the specified file and do error checks + * return the headerL_t struct for the specified filename */ -static int getArFd(char *filename) +static headerL_t *getHeaders(int srcFd, headerL_t *head, int funct) { - int arFd; - char arVersion[8]; +#if defined BB_AR_EXPERIMENTAL_UNTAR + int tar=FALSE; +#endif + int ar=FALSE; + headerL_t *list; + off_t initialOffset; - 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); + list = (headerL_t *) xmalloc(sizeof(headerL_t)); + initialOffset=lseek(srcFd, 0, SEEK_CUR); + if (checkArMagic(srcFd)==TRUE) + ar=TRUE; + +#if defined BB_AR_EXPERIMENTAL_UNTAR + if (checkTarMagic(srcFd)==TRUE) + tar=TRUE; + + if (tar==TRUE) { + while(readTarHeader(srcFd, list)==TRUE) { + off_t tarOffset; + list->next = (headerL_t *) xmalloc(sizeof(headerL_t)); + *list->next = *head; + *head = *list; + + /* recursive check for sub-archives */ + if ((funct & RECURSIVE) == RECURSIVE) + head = getHeaders(srcFd, head, funct); + tarOffset = (off_t) head->size/512; + if ( head->size % 512 > 0) + tarOffset++; + tarOffset=tarOffset*512; + lseek(srcFd, head->offset + tarOffset, SEEK_SET); + } } - return arFd; +#endif + + if (ar==TRUE) { + lseek(srcFd, 8, SEEK_CUR); + while(1) { + if (readArEntry(srcFd, list) == FALSE) { + lseek(srcFd, ++initialOffset, SEEK_CUR); + if (readArEntry(srcFd, list) == FALSE) + return(head); + } + list->next = (headerL_t *) xmalloc(sizeof(headerL_t)); + *list->next = *head; + *head = *list; + /* recursive check for sub-archives */ + if (funct & RECURSIVE) + head = getHeaders(srcFd, head, funct); + lseek(srcFd, head->offset + head->size, SEEK_SET); + } + } + return(head); } /* - * 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 + * find an entry in the linked list matching the filename */ -static int readArFile(char *fileList[16], int fileListSize, int funct) +static headerL_t *findEntry(headerL_t *head, const char *filename) { - int arFd, status, extFileFlag, i, lastOffset=0; - ArHeader rawArHeader; - ArInfo arEntry; - - /* 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) { - - /* 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 (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); + while(head->next != NULL) { + if (strcmp(filename, head->name)==0) + return(head); + head=head->next; + } + return(NULL); } extern int ar_main(int argc, char **argv) { - int funct = 0, ret=0, i=0; - char *fileList[16], c, *opt_ptr; + int funct = 0, opt=0; + int srcFd=0, dstFd=0; + headerL_t *header, *entry, *extractList; - if (argc < 2) - usage(ar_usage); - - opt_ptr = argv[1]; - if (*opt_ptr == '-') - ++opt_ptr; - while ((c = *opt_ptr++) != '\0') { - switch (c) { + while ((opt = getopt(argc, argv, "ovtpxR")) != -1) { + switch (opt) { case 'o': - funct = funct | AR_PRESERVE_DATE; + funct |= PRESERVE_DATE; break; case 'v': - funct = funct | AR_VERBOSE; + funct |= VERBOSE; break; case 't': - funct = funct | AR_DISPLAY; + funct |= DISPLAY; break; case 'x': - funct = funct | AR_EXT_TO_FILE; + funct |= EXT_TO_FILE; break; case 'p': - funct = funct | AR_EXT_TO_STDOUT; + funct |= EXT_TO_STDOUT; + break; + case 'R': + funct |= RECURSIVE; break; default: usage(ar_usage); } } - - for(i=0; i<(argc-2); i++) - fileList[i]=argv[i+2]; + + /* check the src filename was specified */ + if (optind == argc) { + usage(ar_usage); + return(FALSE); + } - if (funct > 3) - ret = readArFile(fileList, (argc-2), funct); + if ( (srcFd = open(argv[optind], O_RDONLY)) < 0) { + errorMsg("Cannot read %s\n", optarg); + return (FALSE); + } + optind++; + entry = (headerL_t *) xmalloc(sizeof(headerL_t)); + header = (headerL_t *) xmalloc(sizeof(headerL_t)); + extractList = (headerL_t *) xmalloc(sizeof(headerL_t)); + + header = getHeaders(srcFd, header, funct); + /* find files to extract or display */ + if (optindnext = (headerL_t *) xmalloc(sizeof(headerL_t)); + *entry->next = *extractList; + *extractList = *entry; + } + optind++; + } + } + else + extractList = header; - return (ret); + while(extractList->next != NULL) { + if (funct & EXT_TO_FILE) { + if (isDirectory(extractList->name, TRUE, NULL)==FALSE) + createPath(extractList->name, 0666); + dstFd = open(extractList->name, O_WRONLY | O_CREAT, extractList->mode); + lseek(srcFd, extractList->offset, SEEK_SET); + copySubFile(srcFd, dstFd, (size_t) extractList->size); + } + if (funct & EXT_TO_STDOUT) { + lseek(srcFd, extractList->offset, SEEK_SET); + copySubFile(srcFd, fileno(stdout), (size_t) extractList->size); + } + if ( (funct & DISPLAY) || (funct & VERBOSE)) { + if (funct & VERBOSE) + printf("%s %d/%d %8d %s ", modeString(extractList->mode), + extractList->uid, extractList->gid, + extractList->size, timeString(extractList->mtime)); + printf("%s\n", extractList->name); + } + extractList=extractList->next; + } + return (TRUE); }