Implemented new ar functionality unique to busybox ar (i think), the -R
[oweals/busybox.git] / ar.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini ar implementation for busybox 
4  *
5  * Copyright (C) 2000 by Glenn McGrath
6  * Written by Glenn McGrath <bug1@netconnect.com.au> 1 June 2000
7  *              
8  * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  *
24  * Last modified 9 September 2000
25  */
26 #include <stdio.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <time.h>
32 #include <utime.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <malloc.h>
38 #include "internal.h"
39
40 #define BLOCK_SIZE 60
41 #define PRESERVE_DATE 1 /* preserve original dates */
42 #define VERBOSE       2 /* be verbose */
43 #define DISPLAY       4 /* display contents */
44 #define EXT_TO_FILE   8 /* extract contents of archive */
45 #define EXT_TO_STDOUT 16        /* extract to stdout */
46 #define RECURSIVE     32         
47
48 #define MAX_NAME_LENGTH 100
49
50 //#define BB_DECLARE_EXTERN
51 //#define bb_need_io_error
52 //#include "messages.c"
53
54 typedef struct rawArHeader {    /* Byte Offset */
55         char name[16];          /*  0-15 */
56         char date[12];          /* 16-27 */
57         char uid[6], gid[6];    /* 28-39 */
58         char mode[8];           /* 40-47 */
59         char size[10];          /* 48-57 */
60         char fmag[2];           /* 58-59 */
61 } rawArHeader_t;
62
63 typedef struct headerL {
64         char name[MAX_NAME_LENGTH];
65         size_t size;
66         uid_t uid;
67         gid_t gid;
68         mode_t mode;
69         time_t mtime;
70         off_t offset;
71         struct headerL *next;
72 } headerL_t;
73
74 /*
75  * identify Ar header (magic) and set srcFd to first header entry 
76  */
77 static int checkArMagic(int srcFd)
78 {
79         char arMagic[8];
80         if (fullRead(srcFd, arMagic, 8) != 8)
81                 return (FALSE);
82         
83         if (strncmp(arMagic,"!<arch>",7) != 0)
84                 return(FALSE);
85         return(TRUE);
86 }
87
88 /*
89  * read, convert and check the raw ar header
90  * srcFd should be pointing to the start of header prior to entry
91  * srcFd will be pointing at the start of data after successful exit
92  * if returns FALSE srcFd is reset to initial position
93  */
94 static int readRawArHeader(int srcFd, headerL_t *header)
95 {
96         rawArHeader_t rawArHeader;
97         off_t   initialOffset;
98         size_t nameLength;
99         
100         initialOffset = lseek(srcFd, 0, SEEK_CUR);
101         if (fullRead(srcFd, (char *) &rawArHeader, 60) != 60) {
102                 lseek(srcFd, initialOffset, SEEK_SET);
103                 return(FALSE);
104         }
105         if ((rawArHeader.fmag[0]!='`') || (rawArHeader.fmag[1]!='\n')) {
106                 lseek(srcFd, initialOffset, SEEK_SET);
107                 return(FALSE);
108         }
109
110         strncpy(header->name, rawArHeader.name, 16);
111         nameLength=strcspn(header->name, " \\");
112         header->name[nameLength]='\0';
113         parse_mode(rawArHeader.mode, &header->mode);
114         header->mtime = atoi(rawArHeader.date);
115         header->uid = atoi(rawArHeader.uid);
116         header->gid = atoi(rawArHeader.gid);
117         header->size = (size_t) atoi(rawArHeader.size);
118         header->offset = initialOffset + (off_t) 60;
119         return(TRUE); 
120 }
121
122 /*
123  * get, check and correct the converted header
124  */ 
125 static int readArEntry(int srcFd, headerL_t *newEntry)
126 {
127         size_t nameLength;
128
129         if(readRawArHeader(srcFd, newEntry)==FALSE)
130                 return(FALSE);
131         
132         nameLength = strcspn(newEntry->name, "/");
133         
134         /* handle GNU style short filenames, strip trailing '/' */
135         if (nameLength > 0)
136                 newEntry->name[nameLength]='\0';
137         
138         /* handle GNU style long filenames */ 
139         if (nameLength == 0) {
140                 /* escape from recursive call */
141                 if (newEntry->name[1]=='0') 
142                         return(TRUE);
143
144                 /* the data section contains the real filename */
145                 if (newEntry->name[1]=='/') {
146                         char tempName[MAX_NAME_LENGTH];
147
148                         if (newEntry->size > MAX_NAME_LENGTH)
149                                 newEntry->size = MAX_NAME_LENGTH;
150                         fullRead(srcFd, tempName, newEntry->size);
151                         tempName[newEntry->size-3]='\0';
152                         
153                         /* read the second header for this entry */
154                         /* be carefull, this is recursive */
155                         if (readArEntry(srcFd, newEntry)==FALSE)
156                                 return(FALSE);
157                 
158                         if ((newEntry->name[0]='/') && (newEntry->name[1]='0'))
159                                 strcpy(newEntry->name, tempName);
160                         else {
161                                 errorMsg("Invalid long filename\n");
162                                 return(FALSE);
163                         }
164                 }
165         }
166         return(TRUE);   
167 }
168
169 /*
170  * return the headerL_t struct for the specified filename
171  */
172 static headerL_t *getHeaders(int srcFd, headerL_t *head, int funct)
173 {
174         headerL_t *list;
175         list = (headerL_t *) malloc(sizeof(headerL_t));
176
177         if (checkArMagic(srcFd)==TRUE) {
178                 printf("found ar header ");
179                 while(readArEntry(srcFd, list) == TRUE) {
180                         list->next = (headerL_t *) malloc(sizeof(headerL_t));
181                         *list->next = *head;
182                         *head = *list;
183                 
184                         /* recursive check for sub-archives */
185                         if ((funct & RECURSIVE) == RECURSIVE) 
186                                 head = getHeaders(srcFd, head, funct);
187                         lseek(srcFd, head->offset + head->size, SEEK_SET);
188                 }
189         }
190         else 
191                 printf("not an ar header\n");
192         return(head);
193 }
194
195 /*
196  * find an entry in the linked list matching the filename
197  */
198 static headerL_t *findEntry(headerL_t *head, const char *filename)
199 {
200         while(head->next != NULL) {
201                 if (strcmp(filename, head->name)==0) 
202                         return(head);
203                 head=head->next;
204         }
205         return(NULL);
206 }
207
208 /*
209  * populate linked list with all ar file entries and offset 
210  */
211 static int displayEntry(headerL_t *head, int funct)
212 {
213         if ((funct & VERBOSE) == VERBOSE) {
214                 printf("%s %d/%d %8d %s ", modeString(head->mode), head->uid, head->gid, head->size, timeString(head->mtime));
215         }
216         printf("%s\n", head->name);
217         head = head->next;
218         return(TRUE);
219 }
220
221 static int extractAr(int srcFd, int dstFd, headerL_t *file)
222 {
223         lseek(srcFd, file->offset, SEEK_SET);
224         if (copySubFile(srcFd, dstFd, (size_t) file->size) == TRUE)
225                 return(TRUE);   
226         return(FALSE);
227 }
228
229 extern int ar_main(int argc, char **argv)
230 {
231         int funct = 0, opt=0;
232         int srcFd=0, dstFd=0;
233         headerL_t *header, *entry, *extractList;
234
235         while ((opt = getopt(argc, argv, "ovtpxR")) != -1) {
236                 switch (opt) {
237                 case 'o':
238                         funct = funct | PRESERVE_DATE;
239                         break;
240                 case 'v':
241                         funct = funct | VERBOSE;
242                         break;
243                 case 't':
244                         funct = funct | DISPLAY;
245                         break;
246                 case 'x':
247                         funct = funct | EXT_TO_FILE;
248                         break;
249                 case 'p':
250                         funct = funct | EXT_TO_STDOUT;
251                         break;
252                 case 'R':
253                         funct = funct | RECURSIVE;
254                         break;
255                 default:
256                         usage(ar_usage);
257                 }
258         }
259  
260         /* check the src filename was specified */
261         if (optind == argc) {
262                 usage(ar_usage);
263                 return(FALSE);
264         }
265         
266         if ( (srcFd = open(argv[optind], O_RDONLY)) < 0) {
267                 errorMsg("Cannot read %s\n", optarg);
268                 return (FALSE);
269         }
270         optind++;       
271         entry = (headerL_t *) malloc(sizeof(headerL_t));
272         header = (headerL_t *) malloc(sizeof(headerL_t));
273         extractList = (headerL_t *) malloc(sizeof(headerL_t));  
274
275         header = getHeaders(srcFd, header, funct);
276         
277         /* find files to extract or display */
278         if (optind<argc) {
279                 /* only handle specified files */
280                 while(optind < argc) { 
281                         if ( (entry = findEntry(header, argv[optind])) != NULL) {
282                                 entry->next = (headerL_t *) malloc(sizeof(headerL_t));
283                                 *entry->next = *extractList;
284                                 *extractList = *entry;
285                         }
286                         optind++;
287                 }       
288         }
289         else 
290                 /* extract everything */
291                 extractList = header;
292         
293        while(extractList->next != NULL) {       
294                 if ( (funct & EXT_TO_FILE) == EXT_TO_FILE) {
295                         dstFd = open(extractList->name, O_WRONLY | O_CREAT, extractList->mode);
296                         
297                         extractAr(srcFd, dstFd, extractList);
298                 }
299                 if ( (funct & EXT_TO_STDOUT) == EXT_TO_STDOUT)  
300                         extractAr(srcFd, fileno(stdout), extractList);  
301                 if ( (funct & DISPLAY) == DISPLAY)
302                         displayEntry(extractList, funct);
303                 extractList=extractList->next;
304         }
305         return (TRUE);
306 }