diff options
author | bug1 <bug1@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2000-09-09 08:35:45 +0000 |
---|---|---|
committer | bug1 <bug1@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2000-09-09 08:35:45 +0000 |
commit | faa718403b49d227ceab550845253a2eff15823a (patch) | |
tree | ba69840f3c18da79be76d730ca570f5411a2f1df /ar.c | |
parent | e7decde03cb840ac3fc584db42638537fe711d2e (diff) | |
download | busybox-w32-faa718403b49d227ceab550845253a2eff15823a.tar.gz busybox-w32-faa718403b49d227ceab550845253a2eff15823a.tar.bz2 busybox-w32-faa718403b49d227ceab550845253a2eff15823a.zip |
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.
git-svn-id: svn://busybox.net/trunk/busybox@1018 69ca8d6d-28ef-0310-b511-8ec308f3f277
Diffstat (limited to 'ar.c')
-rw-r--r-- | ar.c | 265 |
1 files changed, 180 insertions, 85 deletions
@@ -5,12 +5,6 @@ | |||
5 | * Copyright (C) 2000 by Glenn McGrath | 5 | * Copyright (C) 2000 by Glenn McGrath |
6 | * Written by Glenn McGrath <bug1@netconnect.com.au> 1 June 2000 | 6 | * Written by Glenn McGrath <bug1@netconnect.com.au> 1 June 2000 |
7 | * | 7 | * |
8 | * Modified 8 August 2000 by Glenn McGrath | ||
9 | * - now uses getopt | ||
10 | * - moved copySubFile function to utilities.c | ||
11 | * - creates linked list of all headers | ||
12 | * - easily accessable to other busybox functions | ||
13 | * | ||
14 | * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. | 8 | * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. |
15 | * | 9 | * |
16 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
@@ -27,15 +21,20 @@ | |||
27 | * along with this program; if not, write to the Free Software | 21 | * along with this program; if not, write to the Free Software |
28 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
29 | * | 23 | * |
30 | * Last modified 25 August 2000 | 24 | * Last modified 9 September 2000 |
31 | */ | 25 | */ |
32 | #include <stdio.h> | 26 | #include <stdio.h> |
27 | #include <string.h> | ||
33 | #include <fcntl.h> | 28 | #include <fcntl.h> |
34 | #include <errno.h> | 29 | #include <errno.h> |
35 | #include <ctype.h> | 30 | #include <ctype.h> |
36 | #include <time.h> | 31 | #include <time.h> |
37 | #include <utime.h> | 32 | #include <utime.h> |
33 | #include <unistd.h> | ||
34 | #include <stdlib.h> | ||
38 | #include <sys/types.h> | 35 | #include <sys/types.h> |
36 | #include <sys/stat.h> | ||
37 | #include <malloc.h> | ||
39 | #include "internal.h" | 38 | #include "internal.h" |
40 | 39 | ||
41 | #define BLOCK_SIZE 60 | 40 | #define BLOCK_SIZE 60 |
@@ -45,11 +44,13 @@ | |||
45 | #define EXT_TO_FILE 8 /* extract contents of archive */ | 44 | #define EXT_TO_FILE 8 /* extract contents of archive */ |
46 | #define EXT_TO_STDOUT 16 /* extract to stdout */ | 45 | #define EXT_TO_STDOUT 16 /* extract to stdout */ |
47 | 46 | ||
48 | #define BB_DECLARE_EXTERN | 47 | #define MAX_NAME_LENGTH 100 |
49 | #define bb_need_io_error | 48 | |
50 | #include "messages.c" | 49 | //#define BB_DECLARE_EXTERN |
50 | //#define bb_need_io_error | ||
51 | //#include "messages.c" | ||
51 | 52 | ||
52 | typedef struct rawArHeader { /* Byte Offset */ | 53 | typedef struct rawArHeader { /* Byte Offset */ |
53 | char name[16]; /* 0-15 */ | 54 | char name[16]; /* 0-15 */ |
54 | char date[12]; /* 16-27 */ | 55 | char date[12]; /* 16-27 */ |
55 | char uid[6], gid[6]; /* 28-39 */ | 56 | char uid[6], gid[6]; /* 28-39 */ |
@@ -59,7 +60,7 @@ typedef struct rawArHeader { /* Byte Offset */ | |||
59 | } rawArHeader_t; | 60 | } rawArHeader_t; |
60 | 61 | ||
61 | typedef struct headerL { | 62 | typedef struct headerL { |
62 | char name[16]; | 63 | char name[MAX_NAME_LENGTH]; |
63 | size_t size; | 64 | size_t size; |
64 | uid_t uid; | 65 | uid_t uid; |
65 | gid_t gid; | 66 | gid_t gid; |
@@ -70,86 +71,162 @@ typedef struct headerL { | |||
70 | } headerL_t; | 71 | } headerL_t; |
71 | 72 | ||
72 | /* | 73 | /* |
73 | * populate linked list with all ar file entries and offset | 74 | * identify Ar header (magic) and set srcFd to first header entry |
74 | */ | 75 | */ |
75 | static int parseArArchive(int srcFd, headerL_t *current) | 76 | static int checkArMagic(int srcFd) |
76 | { | 77 | { |
77 | off_t lastOffset=0, thisOffset=0; | 78 | char arMagic[8]; |
78 | char arVersion[8]; | 79 | if (fullRead(srcFd, arMagic, 8) != 8) |
79 | rawArHeader_t rawArHeader; | ||
80 | |||
81 | lseek(srcFd, 0, SEEK_SET); | ||
82 | if (fullRead(srcFd, arVersion, 8) <= 0) { | ||
83 | errorMsg("Invalid header magic\n"); | ||
84 | return (FALSE); | 80 | return (FALSE); |
85 | } | 81 | |
86 | if (strncmp(arVersion,"!<arch>",7) != 0) { | 82 | if (strncmp(arMagic,"!<arch>",7) != 0) |
87 | errorMsg("This doesnt appear to be an ar archive\n"); | ||
88 | return(FALSE); | 83 | return(FALSE); |
89 | } | 84 | return(TRUE); |
90 | while (fullRead(srcFd, (char *) &rawArHeader, 60) == 60) { | 85 | } |
91 | if ( (rawArHeader.fmag[0] == '`') && | 86 | |
92 | (rawArHeader.fmag[1] == '\n')) { | 87 | /* |
93 | sscanf(rawArHeader.name, "%s", current->name); | 88 | * read, convert and check the raw ar header |
94 | parse_mode(rawArHeader.mode, ¤t->mode); | 89 | * srcFd should be pointing to the start of header prior to entry |
95 | current->mtime = atoi(rawArHeader.date); | 90 | * srcFd will be pointing at the start of data after successful exit |
96 | current->uid = atoi(rawArHeader.uid); | 91 | * if returns FALSE srcFd is reset to initial position |
97 | current->gid = atoi(rawArHeader.gid); | 92 | */ |
98 | current->size = (size_t) atoi(rawArHeader.size); | 93 | static int readRawArHeader(int srcFd, headerL_t *header) |
99 | current->offset = lseek(srcFd, 0 , SEEK_CUR); | 94 | { |
100 | current->next = (headerL_t *) xmalloc(sizeof(headerL_t)); | 95 | rawArHeader_t rawArHeader; |
101 | lastOffset = lseek(srcFd, (off_t) current->size, SEEK_CUR); | 96 | off_t initialOffset; |
102 | current = current->next; | 97 | size_t nameLength; |
103 | } | 98 | |
104 | else { /* GNU ar has an extra char after data */ | 99 | initialOffset = lseek(srcFd, 0, SEEK_CUR); |
105 | thisOffset=lseek(srcFd, 0, SEEK_CUR); | 100 | if (fullRead(srcFd, (char *) &rawArHeader, 60) != 60) { |
106 | if ( (thisOffset - lastOffset) > ((off_t) 61) ) | 101 | lseek(srcFd, initialOffset, SEEK_SET); |
107 | return(FALSE); | 102 | return(FALSE); |
108 | lseek(srcFd, thisOffset - 59, SEEK_SET); | 103 | } |
109 | } | 104 | if ((rawArHeader.fmag[0]!='`') || (rawArHeader.fmag[1]!='\n')) { |
110 | } | 105 | lseek(srcFd, initialOffset, SEEK_SET); |
111 | return(FALSE); | 106 | return(FALSE); |
107 | } | ||
108 | |||
109 | strncpy(header->name, rawArHeader.name, 16); | ||
110 | nameLength=strcspn(header->name, " \\"); | ||
111 | header->name[nameLength]='\0'; | ||
112 | parse_mode(rawArHeader.mode, &header->mode); | ||
113 | header->mtime = atoi(rawArHeader.date); | ||
114 | header->uid = atoi(rawArHeader.uid); | ||
115 | header->gid = atoi(rawArHeader.gid); | ||
116 | header->size = (size_t) atoi(rawArHeader.size); | ||
117 | header->offset = initialOffset + (off_t) 60; | ||
118 | return(TRUE); | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * get, check and correct the converted header | ||
123 | */ | ||
124 | static int readArEntry(int srcFd, headerL_t *newEntry) | ||
125 | { | ||
126 | size_t nameLength; | ||
127 | |||
128 | if(readRawArHeader(srcFd, newEntry)==FALSE) | ||
129 | return(FALSE); | ||
130 | |||
131 | nameLength = strcspn(newEntry->name, "/"); | ||
132 | |||
133 | /* handle GNU style short filenames, strip trailing '/' */ | ||
134 | if (nameLength > 0) | ||
135 | newEntry->name[nameLength]='\0'; | ||
136 | |||
137 | /* handle GNU style long filenames */ | ||
138 | if (nameLength == 0) { | ||
139 | /* escape from recursive call */ | ||
140 | if (newEntry->name[1]=='0') | ||
141 | return(TRUE); | ||
142 | |||
143 | /* the data section contains the real filename */ | ||
144 | if (newEntry->name[1]=='/') { | ||
145 | char tempName[MAX_NAME_LENGTH]; | ||
146 | |||
147 | if (newEntry->size > MAX_NAME_LENGTH) | ||
148 | newEntry->size = MAX_NAME_LENGTH; | ||
149 | fullRead(srcFd, tempName, newEntry->size); | ||
150 | tempName[newEntry->size-3]='\0'; | ||
151 | |||
152 | /* read the second header for this entry */ | ||
153 | /* be carefull, this is recursive */ | ||
154 | if (readArEntry(srcFd, newEntry)==FALSE) | ||
155 | return(FALSE); | ||
156 | |||
157 | if ((newEntry->name[0]='/') && (newEntry->name[1]='0')) | ||
158 | strcpy(newEntry->name, tempName); | ||
159 | else { | ||
160 | errorMsg("Invalid long filename\n"); | ||
161 | return(FALSE); | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | return(TRUE); | ||
112 | } | 166 | } |
113 | 167 | ||
114 | /* | 168 | /* |
115 | * return the headerL_t struct for the specified filename | 169 | * return the headerL_t struct for the specified filename |
116 | */ | 170 | */ |
117 | static headerL_t *getSubFileHeader(int srcFd, const char filename[16]) | 171 | static headerL_t *getHeaders(int srcFd, headerL_t *head) |
118 | { | 172 | { |
119 | headerL_t *list; | 173 | int arEntry=FALSE; |
120 | list = xmalloc(sizeof(headerL_t)); | 174 | headerL_t *list; |
121 | 175 | list = (headerL_t *) malloc(sizeof(headerL_t)); | |
122 | parseArArchive(srcFd, list); | 176 | |
123 | while (list->next != NULL) { | 177 | if (checkArMagic(srcFd)==TRUE) |
124 | if (strncmp(list->name, filename, strlen(filename))==0) | 178 | arEntry=TRUE; |
125 | return(list); | 179 | else |
126 | list=list->next; | 180 | errorMsg("isnt an ar archive\n"); |
127 | } | 181 | |
128 | return(NULL); | 182 | if (arEntry==TRUE) { |
183 | while(readArEntry(srcFd, list) == TRUE) { | ||
184 | list->next = (headerL_t *) malloc(sizeof(headerL_t)); | ||
185 | *list->next = *head; | ||
186 | *head = *list; | ||
187 | |||
188 | /* recursive check for sub-archives */ | ||
189 | lseek(srcFd, list->size, SEEK_CUR); | ||
190 | /* printf("checking for sub headers\n"); | ||
191 | if ((subList = getHeaders(srcFd, list->next)) != NULL) { | ||
192 | printf("found a sub archive !\n"); | ||
193 | } | ||
194 | else | ||
195 | printf("didnt find a sub header\n"); */ | ||
196 | } | ||
197 | } | ||
198 | |||
199 | return(head); | ||
129 | } | 200 | } |
130 | 201 | ||
131 | /* | 202 | /* |
132 | * populate linked list with all ar file entries and offset | 203 | * find an entry in the linked list matching the filename |
133 | */ | 204 | */ |
134 | static int displayEntry(int srcFd, const char filename[16], int funct) | 205 | static headerL_t *findEntry(headerL_t *head, const char *filename) |
135 | { | 206 | { |
136 | headerL_t *file; | 207 | while(head->next != NULL) { |
208 | if (strcmp(filename, head->name)==0) | ||
209 | return(head); | ||
210 | head=head->next; | ||
211 | } | ||
212 | return(NULL); | ||
213 | } | ||
137 | 214 | ||
138 | if ((file = getSubFileHeader(srcFd, filename)) == NULL) | 215 | /* |
139 | return(FALSE); | 216 | * populate linked list with all ar file entries and offset |
217 | */ | ||
218 | static int displayEntry(headerL_t *head, int funct) | ||
219 | { | ||
140 | if ((funct & VERBOSE) == VERBOSE) { | 220 | if ((funct & VERBOSE) == VERBOSE) { |
141 | printf("%s %d/%d %8d %s ", modeString(file->mode), file->uid, file->gid, file->size, timeString(file->mtime)); | 221 | printf("%s %d/%d %8d %s ", modeString(head->mode), head->uid, head->gid, head->size, timeString(head->mtime)); |
142 | } | 222 | } |
143 | printf("%s\n", file->name); | 223 | printf("%s\n", head->name); |
224 | head = head->next; | ||
144 | return(TRUE); | 225 | return(TRUE); |
145 | } | 226 | } |
146 | 227 | ||
147 | static int extractAr(int srcFd, int dstFd, const char filename[16]) | 228 | static int extractAr(int srcFd, int dstFd, headerL_t *file) |
148 | { | 229 | { |
149 | headerL_t *file; | ||
150 | |||
151 | if ( (file = getSubFileHeader(srcFd, filename)) == NULL) | ||
152 | return(FALSE); | ||
153 | lseek(srcFd, file->offset, SEEK_SET); | 230 | lseek(srcFd, file->offset, SEEK_SET); |
154 | if (copySubFile(srcFd, dstFd, (size_t) file->size) == TRUE) | 231 | if (copySubFile(srcFd, dstFd, (size_t) file->size) == TRUE) |
155 | return(TRUE); | 232 | return(TRUE); |
@@ -160,7 +237,8 @@ extern int ar_main(int argc, char **argv) | |||
160 | { | 237 | { |
161 | int funct = 0, opt=0; | 238 | int funct = 0, opt=0; |
162 | int srcFd=0, dstFd=0; | 239 | int srcFd=0, dstFd=0; |
163 | 240 | headerL_t *header, *entry, *extractList; | |
241 | |||
164 | while ((opt = getopt(argc, argv, "ovt:p:x:")) != -1) { | 242 | while ((opt = getopt(argc, argv, "ovt:p:x:")) != -1) { |
165 | switch (opt) { | 243 | switch (opt) { |
166 | case 'o': | 244 | case 'o': |
@@ -180,10 +258,6 @@ extern int ar_main(int argc, char **argv) | |||
180 | funct = funct | EXT_TO_STDOUT; | 258 | funct = funct | EXT_TO_STDOUT; |
181 | } | 259 | } |
182 | /* following is common to 't','x' and 'p' */ | 260 | /* following is common to 't','x' and 'p' */ |
183 | if (optarg == NULL) { | ||
184 | printf("expected a filename\n"); | ||
185 | return(FALSE); | ||
186 | } | ||
187 | if ( (srcFd = open(optarg, O_RDONLY)) < 0) { | 261 | if ( (srcFd = open(optarg, O_RDONLY)) < 0) { |
188 | errorMsg("Cannot read %s\n", optarg); | 262 | errorMsg("Cannot read %s\n", optarg); |
189 | return (FALSE); | 263 | return (FALSE); |
@@ -200,17 +274,38 @@ extern int ar_main(int argc, char **argv) | |||
200 | return(FALSE); | 274 | return(FALSE); |
201 | } | 275 | } |
202 | 276 | ||
203 | /* find files to extract */ | 277 | entry = (headerL_t *) malloc(sizeof(headerL_t)); |
204 | if (optind<argc) | 278 | header = (headerL_t *) malloc(sizeof(headerL_t)); |
205 | for(; optind < argc; optind++) { | 279 | extractList = (headerL_t *) malloc(sizeof(headerL_t)); |
206 | if ( (funct & EXT_TO_FILE) == EXT_TO_FILE) { | 280 | |
207 | dstFd = open(argv[optind], O_WRONLY | O_CREAT); | 281 | header = getHeaders(srcFd, header); |
208 | extractAr(srcFd, dstFd, argv[optind]); | 282 | |
283 | /* find files to extract or display */ | ||
284 | if (optind<argc) { | ||
285 | /* only handle specified files */ | ||
286 | while(optind < argc) { | ||
287 | if ( (entry = findEntry(header, argv[optind])) != NULL) { | ||
288 | entry->next = (headerL_t *) malloc(sizeof(headerL_t)); | ||
289 | *entry->next = *extractList; | ||
290 | *extractList = *entry; | ||
209 | } | 291 | } |
210 | if ( (funct & EXT_TO_STDOUT) == EXT_TO_STDOUT) | 292 | optind++; |
211 | extractAr(srcFd, fileno(stdout), argv[optind]); | 293 | } |
212 | if ( (funct & DISPLAY) == DISPLAY) | 294 | } |
213 | displayEntry(srcFd, argv[optind], funct); | 295 | else |
296 | /* extract everything */ | ||
297 | extractList = header; | ||
298 | |||
299 | while(extractList->next != NULL) { | ||
300 | if ( (funct & EXT_TO_FILE) == EXT_TO_FILE) { | ||
301 | dstFd = open(extractList->name, O_WRONLY | O_CREAT); | ||
302 | extractAr(srcFd, dstFd, extractList); | ||
214 | } | 303 | } |
304 | if ( (funct & EXT_TO_STDOUT) == EXT_TO_STDOUT) | ||
305 | extractAr(srcFd, fileno(stdout), extractList); | ||
306 | if ( (funct & DISPLAY) == DISPLAY) | ||
307 | displayEntry(extractList, funct); | ||
308 | extractList=extractList->next; | ||
309 | } | ||
215 | return (TRUE); | 310 | return (TRUE); |
216 | } | 311 | } |