From 8324b9f1aacde5c1c80f0a94ba3d276787e14183 Mon Sep 17 00:00:00 2001 From: Glenn L McGrath Date: Sat, 9 Sep 2000 08:35:45 +0000 Subject: [PATCH] Now supports GNU style long filenames. If no filenames are specifed to be (extracted/listed) it will act on all files in the archive (i dont think the old version did this) Code is structured to better support upcoming recursive extraction functionality. The old code was a bit strange in places, dont know what i was thinking. --- ar.c | 263 ++++++++++++++++++++++++++++++++++---------------- archival/ar.c | 263 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 358 insertions(+), 168 deletions(-) diff --git a/ar.c b/ar.c index 1c598fa16..b758a6975 100644 --- a/ar.c +++ b/ar.c @@ -5,12 +5,6 @@ * Copyright (C) 2000 by Glenn McGrath * Written by Glenn McGrath 1 June 2000 * - * Modified 8 August 2000 by Glenn McGrath - * - now uses getopt - * - moved copySubFile function to utilities.c - * - creates linked list of all headers - * - easily accessable to other busybox functions - * * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. * * This program is free software; you can redistribute it and/or modify @@ -27,15 +21,20 @@ * 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 25 August 2000 + * Last modified 9 September 2000 */ #include +#include #include #include #include #include #include +#include +#include #include +#include +#include #include "internal.h" #define BLOCK_SIZE 60 @@ -45,11 +44,13 @@ #define EXT_TO_FILE 8 /* extract contents of archive */ #define EXT_TO_STDOUT 16 /* extract to stdout */ -#define BB_DECLARE_EXTERN -#define bb_need_io_error -#include "messages.c" +#define MAX_NAME_LENGTH 100 -typedef struct rawArHeader { /* Byte Offset */ +//#define BB_DECLARE_EXTERN +//#define bb_need_io_error +//#include "messages.c" + +typedef struct rawArHeader { /* Byte Offset */ char name[16]; /* 0-15 */ char date[12]; /* 16-27 */ char uid[6], gid[6]; /* 28-39 */ @@ -59,7 +60,7 @@ typedef struct rawArHeader { /* Byte Offset */ } rawArHeader_t; typedef struct headerL { - char name[16]; + char name[MAX_NAME_LENGTH]; size_t size; uid_t uid; gid_t gid; @@ -70,86 +71,162 @@ typedef struct headerL { } headerL_t; /* - * populate linked list with all ar file entries and offset + * identify Ar header (magic) and set srcFd to first header entry */ -static int parseArArchive(int srcFd, headerL_t *current) +static int checkArMagic(int srcFd) { - off_t lastOffset=0, thisOffset=0; - char arVersion[8]; - rawArHeader_t rawArHeader; - - lseek(srcFd, 0, SEEK_SET); - if (fullRead(srcFd, arVersion, 8) <= 0) { - errorMsg("Invalid header magic\n"); + char arMagic[8]; + if (fullRead(srcFd, arMagic, 8) != 8) return (FALSE); - } - if (strncmp(arVersion,"!",7) != 0) { - errorMsg("This doesnt appear to be an ar archive\n"); + + if (strncmp(arMagic,"!",7) != 0) return(FALSE); - } - while (fullRead(srcFd, (char *) &rawArHeader, 60) == 60) { - if ( (rawArHeader.fmag[0] == '`') && - (rawArHeader.fmag[1] == '\n')) { - sscanf(rawArHeader.name, "%s", current->name); - parse_mode(rawArHeader.mode, ¤t->mode); - current->mtime = atoi(rawArHeader.date); - current->uid = atoi(rawArHeader.uid); - current->gid = atoi(rawArHeader.gid); - current->size = (size_t) atoi(rawArHeader.size); - current->offset = lseek(srcFd, 0 , SEEK_CUR); - current->next = (headerL_t *) xmalloc(sizeof(headerL_t)); - lastOffset = lseek(srcFd, (off_t) current->size, SEEK_CUR); - current = current->next; - } - else { /* GNU ar has an extra char after data */ - thisOffset=lseek(srcFd, 0, SEEK_CUR); - if ( (thisOffset - lastOffset) > ((off_t) 61) ) - return(FALSE); - lseek(srcFd, thisOffset - 59, SEEK_SET); - } - } - return(FALSE); + return(TRUE); +} + +/* + * read, convert and check the raw ar header + * srcFd should be pointing to the start of header prior to entry + * srcFd will be pointing at the start of data after successful exit + * if returns FALSE srcFd is reset to initial position + */ +static int readRawArHeader(int srcFd, headerL_t *header) +{ + rawArHeader_t rawArHeader; + off_t initialOffset; + size_t nameLength; + + 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(header->name, rawArHeader.name, 16); + nameLength=strcspn(header->name, " \\"); + header->name[nameLength]='\0'; + parse_mode(rawArHeader.mode, &header->mode); + header->mtime = atoi(rawArHeader.date); + header->uid = atoi(rawArHeader.uid); + header->gid = atoi(rawArHeader.gid); + header->size = (size_t) atoi(rawArHeader.size); + header->offset = initialOffset + (off_t) 60; + return(TRUE); +} + +/* + * get, check and correct the converted header + */ +static int readArEntry(int srcFd, headerL_t *newEntry) +{ + size_t nameLength; + + if(readRawArHeader(srcFd, newEntry)==FALSE) + return(FALSE); + + nameLength = strcspn(newEntry->name, "/"); + + /* handle GNU style short filenames, strip trailing '/' */ + if (nameLength > 0) + newEntry->name[nameLength]='\0'; + + /* handle GNU style long filenames */ + if (nameLength == 0) { + /* escape from recursive call */ + if (newEntry->name[1]=='0') + return(TRUE); + + /* the data section contains the real filename */ + if (newEntry->name[1]=='/') { + char tempName[MAX_NAME_LENGTH]; + + if (newEntry->size > MAX_NAME_LENGTH) + newEntry->size = MAX_NAME_LENGTH; + fullRead(srcFd, tempName, newEntry->size); + tempName[newEntry->size-3]='\0'; + + /* read the second header for this entry */ + /* be carefull, this is recursive */ + if (readArEntry(srcFd, newEntry)==FALSE) + return(FALSE); + + if ((newEntry->name[0]='/') && (newEntry->name[1]='0')) + strcpy(newEntry->name, tempName); + else { + errorMsg("Invalid long filename\n"); + return(FALSE); + } + } + } + return(TRUE); } /* * return the headerL_t struct for the specified filename */ -static headerL_t *getSubFileHeader(int srcFd, const char filename[16]) +static headerL_t *getHeaders(int srcFd, headerL_t *head) { - headerL_t *list; - list = xmalloc(sizeof(headerL_t)); + int arEntry=FALSE; + headerL_t *list; + list = (headerL_t *) malloc(sizeof(headerL_t)); - parseArArchive(srcFd, list); - while (list->next != NULL) { - if (strncmp(list->name, filename, strlen(filename))==0) - return(list); - list=list->next; - } - return(NULL); + if (checkArMagic(srcFd)==TRUE) + arEntry=TRUE; + else + errorMsg("isnt an ar archive\n"); + + if (arEntry==TRUE) { + while(readArEntry(srcFd, list) == TRUE) { + list->next = (headerL_t *) malloc(sizeof(headerL_t)); + *list->next = *head; + *head = *list; + + /* recursive check for sub-archives */ + lseek(srcFd, list->size, SEEK_CUR); +/* printf("checking for sub headers\n"); + if ((subList = getHeaders(srcFd, list->next)) != NULL) { + printf("found a sub archive !\n"); + } + else + printf("didnt find a sub header\n"); */ + } + } + + return(head); +} + +/* + * find an entry in the linked list matching the filename + */ +static headerL_t *findEntry(headerL_t *head, const char *filename) +{ + while(head->next != NULL) { + if (strcmp(filename, head->name)==0) + return(head); + head=head->next; + } + return(NULL); } /* * populate linked list with all ar file entries and offset */ -static int displayEntry(int srcFd, const char filename[16], int funct) +static int displayEntry(headerL_t *head, int funct) { - headerL_t *file; - - if ((file = getSubFileHeader(srcFd, filename)) == NULL) - return(FALSE); if ((funct & VERBOSE) == VERBOSE) { - printf("%s %d/%d %8d %s ", modeString(file->mode), file->uid, file->gid, file->size, timeString(file->mtime)); + printf("%s %d/%d %8d %s ", modeString(head->mode), head->uid, head->gid, head->size, timeString(head->mtime)); } - printf("%s\n", file->name); + printf("%s\n", head->name); + head = head->next; return(TRUE); } -static int extractAr(int srcFd, int dstFd, const char filename[16]) +static int extractAr(int srcFd, int dstFd, headerL_t *file) { - headerL_t *file; - - if ( (file = getSubFileHeader(srcFd, filename)) == NULL) - return(FALSE); lseek(srcFd, file->offset, SEEK_SET); if (copySubFile(srcFd, dstFd, (size_t) file->size) == TRUE) return(TRUE); @@ -160,7 +237,8 @@ extern int ar_main(int argc, char **argv) { int funct = 0, opt=0; int srcFd=0, dstFd=0; - + headerL_t *header, *entry, *extractList; + while ((opt = getopt(argc, argv, "ovt:p:x:")) != -1) { switch (opt) { case 'o': @@ -180,10 +258,6 @@ extern int ar_main(int argc, char **argv) funct = funct | EXT_TO_STDOUT; } /* following is common to 't','x' and 'p' */ - if (optarg == NULL) { - printf("expected a filename\n"); - return(FALSE); - } if ( (srcFd = open(optarg, O_RDONLY)) < 0) { errorMsg("Cannot read %s\n", optarg); return (FALSE); @@ -200,17 +274,38 @@ extern int ar_main(int argc, char **argv) return(FALSE); } - /* find files to extract */ - if (optindnext = (headerL_t *) malloc(sizeof(headerL_t)); + *entry->next = *extractList; + *extractList = *entry; } - if ( (funct & EXT_TO_STDOUT) == EXT_TO_STDOUT) - extractAr(srcFd, fileno(stdout), argv[optind]); - if ( (funct & DISPLAY) == DISPLAY) - displayEntry(srcFd, argv[optind], funct); + optind++; + } + } + else + /* extract everything */ + extractList = header; + + while(extractList->next != NULL) { + if ( (funct & EXT_TO_FILE) == EXT_TO_FILE) { + dstFd = open(extractList->name, O_WRONLY | O_CREAT); + extractAr(srcFd, dstFd, extractList); } + if ( (funct & EXT_TO_STDOUT) == EXT_TO_STDOUT) + extractAr(srcFd, fileno(stdout), extractList); + if ( (funct & DISPLAY) == DISPLAY) + displayEntry(extractList, funct); + extractList=extractList->next; + } return (TRUE); } diff --git a/archival/ar.c b/archival/ar.c index 1c598fa16..b758a6975 100644 --- a/archival/ar.c +++ b/archival/ar.c @@ -5,12 +5,6 @@ * Copyright (C) 2000 by Glenn McGrath * Written by Glenn McGrath 1 June 2000 * - * Modified 8 August 2000 by Glenn McGrath - * - now uses getopt - * - moved copySubFile function to utilities.c - * - creates linked list of all headers - * - easily accessable to other busybox functions - * * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. * * This program is free software; you can redistribute it and/or modify @@ -27,15 +21,20 @@ * 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 25 August 2000 + * Last modified 9 September 2000 */ #include +#include #include #include #include #include #include +#include +#include #include +#include +#include #include "internal.h" #define BLOCK_SIZE 60 @@ -45,11 +44,13 @@ #define EXT_TO_FILE 8 /* extract contents of archive */ #define EXT_TO_STDOUT 16 /* extract to stdout */ -#define BB_DECLARE_EXTERN -#define bb_need_io_error -#include "messages.c" +#define MAX_NAME_LENGTH 100 -typedef struct rawArHeader { /* Byte Offset */ +//#define BB_DECLARE_EXTERN +//#define bb_need_io_error +//#include "messages.c" + +typedef struct rawArHeader { /* Byte Offset */ char name[16]; /* 0-15 */ char date[12]; /* 16-27 */ char uid[6], gid[6]; /* 28-39 */ @@ -59,7 +60,7 @@ typedef struct rawArHeader { /* Byte Offset */ } rawArHeader_t; typedef struct headerL { - char name[16]; + char name[MAX_NAME_LENGTH]; size_t size; uid_t uid; gid_t gid; @@ -70,86 +71,162 @@ typedef struct headerL { } headerL_t; /* - * populate linked list with all ar file entries and offset + * identify Ar header (magic) and set srcFd to first header entry */ -static int parseArArchive(int srcFd, headerL_t *current) +static int checkArMagic(int srcFd) { - off_t lastOffset=0, thisOffset=0; - char arVersion[8]; - rawArHeader_t rawArHeader; - - lseek(srcFd, 0, SEEK_SET); - if (fullRead(srcFd, arVersion, 8) <= 0) { - errorMsg("Invalid header magic\n"); + char arMagic[8]; + if (fullRead(srcFd, arMagic, 8) != 8) return (FALSE); - } - if (strncmp(arVersion,"!",7) != 0) { - errorMsg("This doesnt appear to be an ar archive\n"); + + if (strncmp(arMagic,"!",7) != 0) return(FALSE); - } - while (fullRead(srcFd, (char *) &rawArHeader, 60) == 60) { - if ( (rawArHeader.fmag[0] == '`') && - (rawArHeader.fmag[1] == '\n')) { - sscanf(rawArHeader.name, "%s", current->name); - parse_mode(rawArHeader.mode, ¤t->mode); - current->mtime = atoi(rawArHeader.date); - current->uid = atoi(rawArHeader.uid); - current->gid = atoi(rawArHeader.gid); - current->size = (size_t) atoi(rawArHeader.size); - current->offset = lseek(srcFd, 0 , SEEK_CUR); - current->next = (headerL_t *) xmalloc(sizeof(headerL_t)); - lastOffset = lseek(srcFd, (off_t) current->size, SEEK_CUR); - current = current->next; - } - else { /* GNU ar has an extra char after data */ - thisOffset=lseek(srcFd, 0, SEEK_CUR); - if ( (thisOffset - lastOffset) > ((off_t) 61) ) - return(FALSE); - lseek(srcFd, thisOffset - 59, SEEK_SET); - } - } - return(FALSE); + return(TRUE); +} + +/* + * read, convert and check the raw ar header + * srcFd should be pointing to the start of header prior to entry + * srcFd will be pointing at the start of data after successful exit + * if returns FALSE srcFd is reset to initial position + */ +static int readRawArHeader(int srcFd, headerL_t *header) +{ + rawArHeader_t rawArHeader; + off_t initialOffset; + size_t nameLength; + + 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(header->name, rawArHeader.name, 16); + nameLength=strcspn(header->name, " \\"); + header->name[nameLength]='\0'; + parse_mode(rawArHeader.mode, &header->mode); + header->mtime = atoi(rawArHeader.date); + header->uid = atoi(rawArHeader.uid); + header->gid = atoi(rawArHeader.gid); + header->size = (size_t) atoi(rawArHeader.size); + header->offset = initialOffset + (off_t) 60; + return(TRUE); +} + +/* + * get, check and correct the converted header + */ +static int readArEntry(int srcFd, headerL_t *newEntry) +{ + size_t nameLength; + + if(readRawArHeader(srcFd, newEntry)==FALSE) + return(FALSE); + + nameLength = strcspn(newEntry->name, "/"); + + /* handle GNU style short filenames, strip trailing '/' */ + if (nameLength > 0) + newEntry->name[nameLength]='\0'; + + /* handle GNU style long filenames */ + if (nameLength == 0) { + /* escape from recursive call */ + if (newEntry->name[1]=='0') + return(TRUE); + + /* the data section contains the real filename */ + if (newEntry->name[1]=='/') { + char tempName[MAX_NAME_LENGTH]; + + if (newEntry->size > MAX_NAME_LENGTH) + newEntry->size = MAX_NAME_LENGTH; + fullRead(srcFd, tempName, newEntry->size); + tempName[newEntry->size-3]='\0'; + + /* read the second header for this entry */ + /* be carefull, this is recursive */ + if (readArEntry(srcFd, newEntry)==FALSE) + return(FALSE); + + if ((newEntry->name[0]='/') && (newEntry->name[1]='0')) + strcpy(newEntry->name, tempName); + else { + errorMsg("Invalid long filename\n"); + return(FALSE); + } + } + } + return(TRUE); } /* * return the headerL_t struct for the specified filename */ -static headerL_t *getSubFileHeader(int srcFd, const char filename[16]) +static headerL_t *getHeaders(int srcFd, headerL_t *head) { - headerL_t *list; - list = xmalloc(sizeof(headerL_t)); + int arEntry=FALSE; + headerL_t *list; + list = (headerL_t *) malloc(sizeof(headerL_t)); - parseArArchive(srcFd, list); - while (list->next != NULL) { - if (strncmp(list->name, filename, strlen(filename))==0) - return(list); - list=list->next; - } - return(NULL); + if (checkArMagic(srcFd)==TRUE) + arEntry=TRUE; + else + errorMsg("isnt an ar archive\n"); + + if (arEntry==TRUE) { + while(readArEntry(srcFd, list) == TRUE) { + list->next = (headerL_t *) malloc(sizeof(headerL_t)); + *list->next = *head; + *head = *list; + + /* recursive check for sub-archives */ + lseek(srcFd, list->size, SEEK_CUR); +/* printf("checking for sub headers\n"); + if ((subList = getHeaders(srcFd, list->next)) != NULL) { + printf("found a sub archive !\n"); + } + else + printf("didnt find a sub header\n"); */ + } + } + + return(head); +} + +/* + * find an entry in the linked list matching the filename + */ +static headerL_t *findEntry(headerL_t *head, const char *filename) +{ + while(head->next != NULL) { + if (strcmp(filename, head->name)==0) + return(head); + head=head->next; + } + return(NULL); } /* * populate linked list with all ar file entries and offset */ -static int displayEntry(int srcFd, const char filename[16], int funct) +static int displayEntry(headerL_t *head, int funct) { - headerL_t *file; - - if ((file = getSubFileHeader(srcFd, filename)) == NULL) - return(FALSE); if ((funct & VERBOSE) == VERBOSE) { - printf("%s %d/%d %8d %s ", modeString(file->mode), file->uid, file->gid, file->size, timeString(file->mtime)); + printf("%s %d/%d %8d %s ", modeString(head->mode), head->uid, head->gid, head->size, timeString(head->mtime)); } - printf("%s\n", file->name); + printf("%s\n", head->name); + head = head->next; return(TRUE); } -static int extractAr(int srcFd, int dstFd, const char filename[16]) +static int extractAr(int srcFd, int dstFd, headerL_t *file) { - headerL_t *file; - - if ( (file = getSubFileHeader(srcFd, filename)) == NULL) - return(FALSE); lseek(srcFd, file->offset, SEEK_SET); if (copySubFile(srcFd, dstFd, (size_t) file->size) == TRUE) return(TRUE); @@ -160,7 +237,8 @@ extern int ar_main(int argc, char **argv) { int funct = 0, opt=0; int srcFd=0, dstFd=0; - + headerL_t *header, *entry, *extractList; + while ((opt = getopt(argc, argv, "ovt:p:x:")) != -1) { switch (opt) { case 'o': @@ -180,10 +258,6 @@ extern int ar_main(int argc, char **argv) funct = funct | EXT_TO_STDOUT; } /* following is common to 't','x' and 'p' */ - if (optarg == NULL) { - printf("expected a filename\n"); - return(FALSE); - } if ( (srcFd = open(optarg, O_RDONLY)) < 0) { errorMsg("Cannot read %s\n", optarg); return (FALSE); @@ -200,17 +274,38 @@ extern int ar_main(int argc, char **argv) return(FALSE); } - /* find files to extract */ - if (optindnext = (headerL_t *) malloc(sizeof(headerL_t)); + *entry->next = *extractList; + *extractList = *entry; } - if ( (funct & EXT_TO_STDOUT) == EXT_TO_STDOUT) - extractAr(srcFd, fileno(stdout), argv[optind]); - if ( (funct & DISPLAY) == DISPLAY) - displayEntry(srcFd, argv[optind], funct); + optind++; + } + } + else + /* extract everything */ + extractList = header; + + while(extractList->next != NULL) { + if ( (funct & EXT_TO_FILE) == EXT_TO_FILE) { + dstFd = open(extractList->name, O_WRONLY | O_CREAT); + extractAr(srcFd, dstFd, extractList); } + if ( (funct & EXT_TO_STDOUT) == EXT_TO_STDOUT) + extractAr(srcFd, fileno(stdout), extractList); + if ( (funct & DISPLAY) == DISPLAY) + displayEntry(extractList, funct); + extractList=extractList->next; + } return (TRUE); }