diff options
Diffstat (limited to 'ar.c')
-rw-r--r-- | ar.c | 384 |
1 files changed, 140 insertions, 244 deletions
@@ -4,6 +4,12 @@ | |||
4 | * | 4 | * |
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 | * | ||
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 | ||
7 | * | 13 | * |
8 | * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. | 14 | * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar. |
9 | * | 15 | * |
@@ -21,10 +27,8 @@ | |||
21 | * along with this program; if not, write to the Free Software | 27 | * along with this program; if not, write to the Free Software |
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 28 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 | * | 29 | * |
24 | * Last modified 10 June 2000 | 30 | * Last modified 25 August 2000 |
25 | */ | 31 | */ |
26 | |||
27 | |||
28 | #include <stdio.h> | 32 | #include <stdio.h> |
29 | #include <fcntl.h> | 33 | #include <fcntl.h> |
30 | #include <errno.h> | 34 | #include <errno.h> |
@@ -34,287 +38,179 @@ | |||
34 | #include <sys/types.h> | 38 | #include <sys/types.h> |
35 | #include "internal.h" | 39 | #include "internal.h" |
36 | 40 | ||
37 | #define AR_BLOCK_SIZE 60 | 41 | #define BLOCK_SIZE 60 |
38 | #define AR_PRESERVE_DATE 1 /* preserve original dates */ | 42 | #define PRESERVE_DATE 1 /* preserve original dates */ |
39 | #define AR_VERBOSE 2 /* be verbose */ | 43 | #define VERBOSE 2 /* be verbose */ |
40 | #define AR_DISPLAY 4 /* display contents */ | 44 | #define DISPLAY 4 /* display contents */ |
41 | #define AR_EXT_TO_FILE 8 /* extract contents of archive */ | 45 | #define EXT_TO_FILE 8 /* extract contents of archive */ |
42 | #define AR_EXT_TO_STDOUT 16 /* extract to stdout */ | 46 | #define EXT_TO_STDOUT 16 /* extract to stdout */ |
43 | 47 | ||
44 | #define BB_DECLARE_EXTERN | 48 | #define BB_DECLARE_EXTERN |
45 | #define bb_need_io_error | 49 | #define bb_need_io_error |
46 | #include "messages.c" | 50 | #include "messages.c" |
47 | 51 | ||
48 | struct ArHeader { /* Byte Offset */ | 52 | typedef struct rawArHeader { /* Byte Offset */ |
49 | char ar_name[16]; /* 0-15 */ | 53 | char name[16]; /* 0-15 */ |
50 | char ar_date[12]; /* 16-27 */ | 54 | char date[12]; /* 16-27 */ |
51 | char ar_uid[6], ar_gid[6]; /* 28-39 */ | 55 | char uid[6], gid[6]; /* 28-39 */ |
52 | char ar_mode[8]; /* 40-47 */ | 56 | char mode[8]; /* 40-47 */ |
53 | char ar_size[10]; /* 48-57 */ | 57 | char size[10]; /* 48-57 */ |
54 | char ar_fmag[2]; /* 58-59 */ | 58 | char fmag[2]; /* 58-59 */ |
55 | }; | 59 | } rawArHeader_t; |
56 | typedef struct ArHeader ArHeader; | 60 | |
57 | 61 | typedef struct headerL { | |
58 | struct ArInfo { | 62 | char name[16]; |
59 | char name[17]; /* File name */ | 63 | size_t size; |
60 | time_t date; /* long int, No of seconds since epoch */ | 64 | uid_t uid; |
61 | uid_t uid; /* unsigned int, Numeric UID */ | 65 | gid_t gid; |
62 | gid_t gid; /* unsigned int, Numeric GID */ | 66 | mode_t mode; |
63 | mode_t mode; /* unsigned int, Unix mode */ | 67 | time_t mtime; |
64 | size_t size; /* int, Size of the file */ | 68 | off_t offset; |
65 | }; | 69 | struct headerL *next; |
66 | typedef struct ArInfo ArInfo; | 70 | } headerL_t; |
67 | 71 | ||
68 | /* | 72 | /* |
69 | * Display details of a file, verbosly if funct=2 | 73 | * populate linked list with all ar file entries and offset |
70 | */ | 74 | */ |
71 | static void displayEntry(struct ArInfo *entry, int funct) | 75 | static int parseArArchive(int srcFd, headerL_t *current) |
72 | { | 76 | { |
73 | /* TODO convert mode to string */ | 77 | off_t lastOffset=0, thisOffset=0; |
74 | if ((funct & AR_VERBOSE) == AR_VERBOSE) | 78 | char arVersion[8]; |
75 | printf("%i %i/%i %8i %s ", entry->mode, entry->uid, entry->gid, | 79 | rawArHeader_t rawArHeader; |
76 | entry->size, timeString(entry->date)); | 80 | |
77 | printf("%s\n", entry->name); | 81 | lseek(srcFd, 0, SEEK_SET); |
78 | } | 82 | if (fullRead(srcFd, arVersion, 8) <= 0) { |
79 | 83 | errorMsg("Invalid header magic\n"); | |
80 | /* this is from tar.c remove later*/ | 84 | return (FALSE); |
81 | static long getOctal(const char *cp, int size) | ||
82 | { | ||
83 | long val = 0; | ||
84 | |||
85 | for(;(size > 0) && (*cp == ' '); cp++, size--); | ||
86 | if ((size == 0) || !isOctal(*cp)) | ||
87 | return -1; | ||
88 | for(; (size > 0) && isOctal(*cp); size--) { | ||
89 | val = val * 8 + *cp++ - '0'; | ||
90 | } | 85 | } |
91 | for (;(size > 0) && (*cp == ' '); cp++, size--); | 86 | if (strncmp(arVersion,"!<arch>",7) != 0) { |
92 | if ((size > 0) && *cp) | 87 | errorMsg("This doesnt appear to be an ar archive\n"); |
93 | return -1; | 88 | return(FALSE); |
94 | return val; | 89 | } |
95 | } | 90 | while (fullRead(srcFd, (char *) &rawArHeader, 60) == 60) { |
96 | 91 | if ( (rawArHeader.fmag[0] == '`') && | |
97 | /* | 92 | (rawArHeader.fmag[1] == '\n')) { |
98 | * Converts from the char based struct to a new struct with stricter types | 93 | sscanf(rawArHeader.name, "%s", current->name); |
99 | */ | 94 | parse_mode(rawArHeader.mode, ¤t->mode); |
100 | static int processArHeader(struct ArHeader *rawHeader, struct ArInfo *header) | 95 | current->mtime = atoi(rawArHeader.date); |
101 | { | 96 | current->uid = atoi(rawArHeader.uid); |
102 | int count2; | 97 | current->gid = atoi(rawArHeader.gid); |
103 | int count; | 98 | current->size = (size_t) atoi(rawArHeader.size); |
104 | 99 | current->offset = lseek(srcFd, 0 , SEEK_CUR); | |
105 | /* check end of header marker is valid */ | 100 | current->next = (headerL_t *) xmalloc(sizeof(headerL_t)); |
106 | if ((rawHeader->ar_fmag[0]!='`') || (rawHeader->ar_fmag[1]!='\n')) | 101 | lastOffset = lseek(srcFd, (off_t) current->size, SEEK_CUR); |
107 | return(FALSE); | 102 | current = current->next; |
108 | 103 | } | |
109 | /* convert filename */ | 104 | else { /* GNU ar has an extra char after data */ |
110 | for (count = 0; count < 16; count++) { | 105 | thisOffset=lseek(srcFd, 0, SEEK_CUR); |
111 | /* allow spaces in filename except at the end */ | 106 | if ( (thisOffset - lastOffset) > ((off_t) 61) ) |
112 | if (rawHeader->ar_name[count] == ' ') { | 107 | return(FALSE); |
113 | for (count2 = count; count2 < 16; count2++) | 108 | lseek(srcFd, thisOffset - 59, SEEK_SET); |
114 | if (!isspace(rawHeader->ar_name[count2])) | 109 | } |
115 | break; | 110 | } |
116 | if (count2 >= 16) | 111 | return(FALSE); |
117 | break; | ||
118 | } | ||
119 | /* GNU ar uses '/' as an end of filename marker */ | ||
120 | if (rawHeader->ar_name[count] == '/') | ||
121 | break; | ||
122 | header->name[count] = rawHeader->ar_name[count]; | ||
123 | } | ||
124 | header->name[count] = '\0'; | ||
125 | header->date = atoi(rawHeader->ar_date); | ||
126 | header->uid = atoi(rawHeader->ar_uid); | ||
127 | header->gid = atoi(rawHeader->ar_gid); | ||
128 | header->mode = getOctal(rawHeader->ar_mode, sizeof(rawHeader->ar_mode)); | ||
129 | header->size = atoi(rawHeader->ar_size); | ||
130 | return (TRUE); | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Copy size bytes from current position if srcFd to current position in dstFd | ||
135 | * taken from tarExtractRegularFile in tar.c, remove later | ||
136 | */ | ||
137 | static int copySubFile(int srcFd, int dstFd, int copySize) | ||
138 | { | ||
139 | int readSize, writeSize, doneSize; | ||
140 | char buffer[BUFSIZ]; | ||
141 | |||
142 | while (copySize > 0) { | ||
143 | if (copySize > BUFSIZ) | ||
144 | readSize = BUFSIZ; | ||
145 | else | ||
146 | readSize = copySize; | ||
147 | writeSize = fullRead(srcFd, buffer, readSize); | ||
148 | if (writeSize <= 0) { | ||
149 | errorMsg(io_error, "copySubFile", strerror(errno)); | ||
150 | return (FALSE); | ||
151 | } | ||
152 | doneSize = fullWrite(dstFd, buffer, writeSize); | ||
153 | if (doneSize <= 0) { | ||
154 | errorMsg(io_error, "copySubFile", strerror(errno)); | ||
155 | return (FALSE); | ||
156 | } | ||
157 | copySize -= doneSize; | ||
158 | } | ||
159 | return (TRUE); | ||
160 | } | 112 | } |
161 | 113 | ||
162 | /* | 114 | /* |
163 | * Extract the file described in ArInfo to the specified path | 115 | * return the headerL_t struct for the specified filename |
164 | * set the new files uid, gid and mode | ||
165 | */ | 116 | */ |
166 | static int extractToFile(struct ArInfo *file, int funct, int srcFd, const char *path) | 117 | static headerL_t *getSubFileHeader(int srcFd, const char filename[16]) |
167 | { | 118 | { |
168 | int dstFd, temp; | 119 | headerL_t *list; |
169 | struct stat tmpStat; | 120 | list = xmalloc(sizeof(headerL_t)); |
170 | char *pathname = NULL; | 121 | |
171 | struct utimbuf newtime; | 122 | parseArArchive(srcFd, list); |
172 | 123 | while (list->next != NULL) { | |
173 | if ((temp = isDirectory(path, TRUE, &tmpStat)) != TRUE) { | 124 | if (strncmp(list->name, filename, strlen(filename))==0) |
174 | if (!createPath(path, 0777)) { | 125 | return(list); |
175 | fatalError("Cannot extract to specified path"); | 126 | list=list->next; |
176 | return (FALSE); | 127 | } |
177 | } | 128 | return(NULL); |
178 | } | ||
179 | temp = (strlen(path) + 16); | ||
180 | pathname = (char *) xmalloc(temp); | ||
181 | pathname = strcpy(pathname, path); | ||
182 | pathname = strcat(pathname, file->name); | ||
183 | dstFd = device_open(pathname, O_WRONLY | O_CREAT); | ||
184 | temp = copySubFile(srcFd, dstFd, file->size); | ||
185 | fchown(dstFd, file->uid, file->gid); | ||
186 | fchmod(dstFd, file->mode); | ||
187 | close(dstFd); | ||
188 | if ((funct&AR_PRESERVE_DATE)==AR_PRESERVE_DATE) | ||
189 | newtime.modtime=file->date; | ||
190 | else | ||
191 | newtime.modtime=time(0); | ||
192 | newtime.actime=time(0); | ||
193 | temp = utime(pathname, &newtime); | ||
194 | return (TRUE); | ||
195 | } | 129 | } |
196 | 130 | ||
197 | /* | 131 | /* |
198 | * Return a file descriptor for the specified file and do error checks | 132 | * populate linked list with all ar file entries and offset |
199 | */ | 133 | */ |
200 | static int getArFd(char *filename) | 134 | static int displayEntry(int srcFd, const char filename[16], int funct) |
201 | { | 135 | { |
202 | int arFd; | 136 | headerL_t *file; |
203 | char arVersion[8]; | ||
204 | 137 | ||
205 | arFd = open(filename, O_RDONLY); | 138 | if ((file = getSubFileHeader(srcFd, filename)) == NULL) |
206 | if (arFd < 0) { | 139 | return(FALSE); |
207 | errorMsg("Error opening '%s': %s\n", filename, strerror(errno)); | 140 | if ((funct & VERBOSE) == VERBOSE) { |
208 | return (FALSE); | 141 | printf("%s %d/%d %8d %s ", modeString(file->mode), file->uid, file->gid, file->size, timeString(file->mtime)); |
209 | } | 142 | } |
210 | if (fullRead(arFd, arVersion, 8) <= 0) { | 143 | printf("%s\n", file->name); |
211 | errorMsg( "Unexpected EOF in archive\n"); | 144 | return(TRUE); |
212 | return (FALSE); | ||
213 | } | ||
214 | if (strncmp(arVersion,"!<arch>",7) != 0) { | ||
215 | errorMsg("ar header fails check "); | ||
216 | return(FALSE); | ||
217 | } | ||
218 | return arFd; | ||
219 | } | 145 | } |
220 | 146 | ||
221 | /* | 147 | static int extractAr(int srcFd, int dstFd, const char filename[16]) |
222 | * Step through the ar file and process it one entry at a time | ||
223 | * fileList[0] is the name of the ar archive | ||
224 | * fileList[1] and up are filenames to extract from the archive | ||
225 | * funct contains flags to specify the actions to be performed | ||
226 | */ | ||
227 | static int readArFile(char *fileList[16], int fileListSize, int funct) | ||
228 | { | 148 | { |
229 | int arFd, status, extFileFlag, i, lastOffset=0; | 149 | headerL_t *file; |
230 | ArHeader rawArHeader; | 150 | |
231 | ArInfo arEntry; | 151 | if ( (file = getSubFileHeader(srcFd, filename)) == NULL) |
232 | 152 | return(FALSE); | |
233 | /* open the ar archive */ | 153 | lseek(srcFd, file->offset, SEEK_SET); |
234 | arFd=getArFd(fileList[0]); | 154 | if (copySubFile(srcFd, dstFd, (size_t) file->size) == TRUE) |
235 | 155 | return(TRUE); | |
236 | /* read the first header, then loop until ono more headers */ | 156 | return(FALSE); |
237 | while ((status = fullRead(arFd, (char *) &rawArHeader, AR_BLOCK_SIZE)) | ||
238 | == AR_BLOCK_SIZE) { | ||
239 | |||
240 | /* check the header is valid, if not try reading the header | ||
241 | agian with an offset of 1, needed as some ar archive end | ||
242 | with a '\n' which isnt counted in specified file size */ | ||
243 | if ((status=processArHeader(&rawArHeader, &arEntry))==FALSE ) { | ||
244 | if ((i=lseek(arFd, 0, SEEK_CUR))==(lastOffset+60)) | ||
245 | lseek(arFd, lastOffset+1, SEEK_SET); | ||
246 | else | ||
247 | return(FALSE); | ||
248 | } | ||
249 | else { | ||
250 | extFileFlag=0; | ||
251 | |||
252 | if ((funct&AR_DISPLAY) || (funct&AR_VERBOSE)) | ||
253 | displayEntry(&arEntry, funct); | ||
254 | |||
255 | /* check file was specified to be extracted only if | ||
256 | some file were specified */ | ||
257 | if ((funct&AR_EXT_TO_FILE) || (funct&AR_EXT_TO_STDOUT)){ | ||
258 | if (fileListSize==1) | ||
259 | extFileFlag=1; | ||
260 | else { | ||
261 | for( i=1; i<=fileListSize; i++) | ||
262 | if ((status=(strcmp(fileList[i],arEntry.name)))==0) | ||
263 | extFileFlag=1; | ||
264 | } | ||
265 | } | ||
266 | if (extFileFlag==1) { | ||
267 | if (funct&AR_EXT_TO_FILE) | ||
268 | extractToFile(&arEntry, funct, arFd, "./"); | ||
269 | else | ||
270 | copySubFile(arFd,fileno(stdout),arEntry.size); | ||
271 | } | ||
272 | else | ||
273 | lseek(arFd, arEntry.size, SEEK_CUR); | ||
274 | lastOffset=lseek(arFd, 0, SEEK_CUR); | ||
275 | } /* if processArHeader */ | ||
276 | } /* while */ | ||
277 | return (TRUE); | ||
278 | } | 157 | } |
279 | 158 | ||
280 | extern int ar_main(int argc, char **argv) | 159 | extern int ar_main(int argc, char **argv) |
281 | { | 160 | { |
282 | int funct = 0, ret=0, i=0; | 161 | int funct = 0, opt=0; |
283 | char *fileList[16], c, *opt_ptr; | 162 | int srcFd=0, dstFd=0; |
284 | 163 | ||
285 | if (argc < 2) | 164 | while ((opt = getopt(argc, argv, "ovt:p:x:")) != -1) { |
286 | usage(ar_usage); | 165 | switch (opt) { |
287 | |||
288 | opt_ptr = argv[1]; | ||
289 | if (*opt_ptr == '-') | ||
290 | ++opt_ptr; | ||
291 | while ((c = *opt_ptr++) != '\0') { | ||
292 | switch (c) { | ||
293 | case 'o': | 166 | case 'o': |
294 | funct = funct | AR_PRESERVE_DATE; | 167 | funct = funct | PRESERVE_DATE; |
295 | break; | 168 | break; |
296 | case 'v': | 169 | case 'v': |
297 | funct = funct | AR_VERBOSE; | 170 | funct = funct | VERBOSE; |
298 | break; | 171 | break; |
299 | case 't': | 172 | case 't': |
300 | funct = funct | AR_DISPLAY; | 173 | funct = funct | DISPLAY; |
301 | break; | ||
302 | case 'x': | 174 | case 'x': |
303 | funct = funct | AR_EXT_TO_FILE; | 175 | if (opt=='x') { |
304 | break; | 176 | funct = funct | EXT_TO_FILE; |
177 | } | ||
305 | case 'p': | 178 | case 'p': |
306 | funct = funct | AR_EXT_TO_STDOUT; | 179 | if (opt=='p') { |
180 | funct = funct | EXT_TO_STDOUT; | ||
181 | } | ||
182 | /* 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) { | ||
188 | errorMsg("Cannot read %s\n", optarg); | ||
189 | return (FALSE); | ||
190 | } | ||
307 | break; | 191 | break; |
308 | default: | 192 | default: |
309 | usage(ar_usage); | 193 | usage(ar_usage); |
310 | } | 194 | } |
311 | } | 195 | } |
312 | 196 | ||
313 | for(i=0; i<(argc-2); i++) | 197 | /* check options not just preserve_dates and/or verbose */ |
314 | fileList[i]=argv[i+2]; | 198 | if (funct < 4) { |
315 | 199 | usage(ar_usage); | |
316 | if (funct > 3) | 200 | return(FALSE); |
317 | ret = readArFile(fileList, (argc-2), funct); | 201 | } |
318 | 202 | ||
319 | return (ret); | 203 | /* find files to extract */ |
204 | if (optind<argc) | ||
205 | for(; optind < argc; optind++) { | ||
206 | if ( (funct & EXT_TO_FILE) == EXT_TO_FILE) { | ||
207 | dstFd = open(argv[optind], O_WRONLY | O_CREAT); | ||
208 | extractAr(srcFd, dstFd, argv[optind]); | ||
209 | } | ||
210 | if ( (funct & EXT_TO_STDOUT) == EXT_TO_STDOUT) | ||
211 | extractAr(srcFd, fileno(stdout), argv[optind]); | ||
212 | if ( (funct & DISPLAY) == DISPLAY) | ||
213 | displayEntry(srcFd, argv[optind], funct); | ||
214 | } | ||
215 | return (TRUE); | ||
320 | } | 216 | } |