1 /**
2 * @file ar.c AR module
3 *
4 * $Id: ar.c,v 1.25 2003/01/01 06:22:31 chipx86 Exp $
5 *
6 * @Copyright (C) 2001-2003 The GNUpdate Project.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23 #include "ar.h"
24
25 static size_t
__readFunc(void * ptr,size_t size,size_t nmemb,CxFP * fp)26 __readFunc(void *ptr, size_t size, size_t nmemb, CxFP *fp)
27 {
28 CxArFileData *fileData;
29 CxFile *file;
30 CxFP *parentFp;
31 size_t readSize, remainingSize, result;
32
33 file = fp->file;
34 fileData = (CxArFileData *)fp->moduleData;
35
36 parentFp = (CxFP *)cxGetFileArchive(file)->moduleData;
37
38 if (cxTell(parentFp) != fileData->lastPos)
39 cxSeek(parentFp, fileData->lastPos, SEEK_SET);
40
41 readSize = size * nmemb;
42 remainingSize = cxGetFileCompressedSize(file) - (fileData->lastPos -
43 fileData->startPos);
44
45 if (readSize > remainingSize)
46 readSize = remainingSize;
47
48 result = cxRead(ptr, 1, readSize, parentFp);
49
50 fileData->lastPos = cxTell(parentFp);
51
52 return result;
53 }
54
55 static size_t
__writeFunc(const void * ptr,size_t size,size_t nmemb,CxFP * fp)56 __writeFunc(const void *ptr, size_t size, size_t nmemb, CxFP *fp)
57 {
58 return 0;
59 }
60
61 static void
__seekFunc(CxFP * fp,long offset,int whence)62 __seekFunc(CxFP *fp, long offset, int whence)
63 {
64 CxArFileData *fileData;
65 CxFile *file;
66
67 file = fp->file;
68 fileData = (CxArFileData *)fp->moduleData;
69
70 switch (whence)
71 {
72 case SEEK_SET: fileData->lastPos = fileData->startPos + offset; break;
73 case SEEK_CUR: fileData->lastPos += offset; break;
74 case SEEK_END:
75 fileData->lastPos = fileData->startPos +
76 cxGetFileCompressedSize(fp->file) - offset;
77 break;
78 }
79 }
80
81 static void
__closeFunc(CxFP * fp)82 __closeFunc(CxFP *fp)
83 {
84 if (fp->moduleData != NULL)
85 {
86 free(fp->moduleData);
87
88 fp->moduleData = NULL;
89 }
90 }
91
92 static CxStatus
readArchive(CxArchive * archive,CxFP * fp)93 readArchive(CxArchive *archive, CxFP *fp)
94 {
95 CxFile *file;
96 CxDirectory *root;
97 CxArHeader header;
98 CxStatus status;
99 char *nameTable = NULL;
100
101 if ((status = cxArValidateMagic(fp)) != CX_SUCCESS)
102 return status;
103
104 root = cxGetArchiveRoot(archive);
105
106 while ((status = cxArReadHeader(fp, &header)) == CX_SUCCESS)
107 {
108 char *filename;
109 int size;
110
111 if (header.name[0] == '/' && header.name[1] == '/')
112 {
113 /* Name symbol table. */
114
115 int tableSize;
116
117 if (nameTable != NULL)
118 {
119 free(nameTable);
120
121 /*
122 * XXX Corruption, I think? I'm not 100% sure about the
123 * ar specification, since there seems to be no real
124 * good documents on it.
125 */
126 return CX_CORRUPT;
127 }
128
129 tableSize = cxArDecToInt(header.size);
130
131 MEM_CHECK(nameTable = (char *)malloc(tableSize));
132
133 /* Read in the table. */
134 cxRead(nameTable, tableSize, 1, fp);
135
136 continue;
137 }
138 else if (header.name[0] == '/' && header.name[0] == ' ')
139 {
140 /* Symbol table. We can skip over this one. */
141 }
142 else
143 {
144 /* Normal filename entry. */
145 char *c;
146
147 /* See if this is an extended name. */
148 if (header.name[0] == '/')
149 {
150 /* Extended name. (>= 16 chars) */
151 char *start, *end;
152 char buffer[15];
153 int offset, len;
154
155 if (nameTable == NULL)
156 {
157 /* Uh oh. */
158 status = CX_CORRUPT;
159
160 break;
161 }
162
163 /* Copy this into a buffer, and nul-terminate it. */
164 strncpy(buffer, header.name + 1, 15);
165
166 c = strchr(buffer, ' ');
167 *c = '\0';
168
169 /* Get the offset it represents. */
170 offset = cxArDecToInt(buffer);
171
172 /* Find the '/' in the entry for the long name. */
173 start = nameTable + offset;
174 end = strchr(start, '/');
175
176 len = end - start;
177
178 MEM_CHECK(filename = (char *)malloc(len + 1));
179
180 strncpy(filename, start, len);
181 filename[len] = '\0';
182 }
183 else
184 {
185 /* Short name. (< 16 chars) */
186 filename = (char *)malloc(16);
187 strncpy(filename, header.name, 15);
188
189 filename[15] = '\0';
190
191 /* Strip off the trailing slash. */
192 if ((c = strchr(filename, '/')) != NULL ||
193 (c = strchr(filename, ' ')) != NULL)
194 {
195 *c = '\0';
196 }
197 }
198
199 /* Create the file structure. */
200 file = cxNewFile();
201
202 cxSetFileName(file, filename);
203 free(filename);
204
205 cxSetFileMode(file, cxArOctalToInt(header.mode));
206 cxSetFileUid(file, cxArDecToInt(header.uid));
207 cxSetFileGid(file, cxArDecToInt(header.gid));
208 cxSetFileSize(file, cxArDecToInt(header.size));
209 cxSetFileDate(file, (time_t)cxArDecToInt(header.date));
210 cxSetFileCompressedSize(file, cxGetFileSize(file));
211
212 file->moduleData = (void *)cxTell(fp);
213
214 archive->archiveSize += cxGetFileSize(file);
215
216 /* We don't have subdirectory support in ar files (I believe) */
217 cxDirAddFile(root, file);
218 }
219
220 /* Jump to the next entry. */
221 size = cxArDecToInt(header.size);
222
223 if (size % 2 != 0)
224 size++;
225
226 cxSeek(fp, size, SEEK_CUR);
227 }
228
229 if (nameTable != NULL)
230 free(nameTable);
231
232 if (status != CX_EOF)
233 {
234 /* TODO: Free up memory. */
235 return status;
236 }
237
238 cxSetArchiveType(archive, CX_ARCHIVE_MULTI);
239
240 archive->moduleData = fp;
241
242 return CX_SUCCESS;
243 }
244
245 static CxStatus
saveArchive(CxArchive * archive,CxFP * fp)246 saveArchive(CxArchive *archive, CxFP *fp)
247 {
248 CxArHeader header;
249 CxFsIterator *iter;
250 CxFile *file;
251 CxFP *inFp;
252 int symPos = 0;
253 char *symtab = NULL;
254 size_t symtabLen = 0;
255 size_t symtabBufSize = 0;
256
257 /* Write the !<arch> header */
258 cxWrite(AR_MAGIC, 1, AR_MAGIC_LEN, fp);
259
260 iter = cxNewFsIterator(archive, CX_FSITER_FILES);
261
262 /* Make the symbol table. */
263 for (file = cxGetFsIterFirst(iter);
264 file != NULL;
265 file = cxGetFsIterNext(iter))
266 {
267 const char *filename;
268
269 if (cxGetFsNodeType(file) != CX_FSNODETYPE_FILE)
270 continue;
271
272 if ((filename = cxGetFileName(file)) == NULL)
273 continue;
274
275 if (strlen(filename) >= 16)
276 {
277 /* Long filename. */
278 size_t len;
279
280 len = strlen(filename) + 2;
281
282 if (symtabBufSize - symtabLen < len)
283 {
284 char *newSymTab;
285 size_t newSymTabBufSize;
286
287 newSymTabBufSize = (symtabBufSize + len) * 2;
288
289 MEM_CHECK(newSymTab = (char *)malloc(newSymTabBufSize));
290
291 memset(newSymTab, 0, newSymTabBufSize);
292
293 if (symtab != NULL)
294 {
295 strncpy(newSymTab, symtab, symtabLen);
296
297 free(symtab);
298 }
299
300 symtabBufSize = newSymTabBufSize;
301 symtab = newSymTab;
302 }
303
304 snprintf(symtab + symtabLen, len + 1, "%s/\n", filename);
305
306 symtabLen += len;
307 }
308 }
309
310 if (symtabLen > 0)
311 {
312 int i;
313
314 /* Write the symbol table. */
315 memset(&header, ' ', sizeof(CxArHeader));
316
317 strncpy(header.name, "//", 2);
318
319 i = snprintf(header.size, 10, "%d", symtabLen);
320 header.size[i] = ' ';
321
322 strncpy(header.fmag, AR_FMAG, 2);
323
324 cxWrite(&header, 1, AR_HEADER_LEN, fp);
325
326 /* Write the entries. */
327 cxWrite(symtab, 1, symtabLen, fp);
328 }
329
330 /* Add the files. */
331 for (file = cxGetFsIterFirst(iter);
332 file != NULL;
333 file = cxGetFsIterNext(iter))
334 {
335 if (cxGetFsNodeType(file) != CX_FSNODETYPE_FILE)
336 continue;
337
338 if (cxGetFilePhysicalPath(file) != NULL)
339 {
340 char buffer[4096];
341 size_t s;
342 int i;
343
344 inFp = cxOpenFile(cxGetFilePhysicalPath(file),
345 CX_MODE_READ_ONLY | CX_MODE_RAW);
346
347 if (inFp == NULL)
348 continue;
349
350 /* Write the file header. */
351 memset(&header, ' ', sizeof(CxArHeader));
352
353 if (strlen(cxGetFileName(file)) >= 16)
354 {
355 i = snprintf(header.name, 16, "/%d", symPos);
356 symPos = strchr(symtab+ symPos, '\n') + 1 - symtab;
357 }
358 else
359 {
360 i = snprintf(header.name, 16, "%s/", cxGetFileName(file));
361 }
362
363 header.name[i] = ' ';
364
365 i = snprintf(header.date, 12, "%ld", cxGetFileDate(file));
366 header.date[i] = ' ';
367 i = snprintf(header.uid, 6, "%u", cxGetFileUid(file));
368 header.uid[i] = ' ';
369 i = snprintf(header.gid, 6, "%u", cxGetFileGid(file));
370 header.gid[i] = ' ';
371 i = snprintf(header.mode, 8, "%o", cxGetFileMode(file));
372 header.mode[i] = ' ';
373 i = snprintf(header.size, 20, "%u", cxGetFileSize(file));
374 header.size[i] = ' ';
375
376 strncpy(header.fmag, AR_FMAG, 2);
377
378 cxWrite(&header, 1, AR_HEADER_LEN, fp);
379
380 /* Write the data */
381 while ((s = cxRead(buffer, 1, 4096, inFp)) > 0)
382 cxWrite(buffer, 1, s, fp);
383
384 cxClose(inFp);
385
386 /* Padding */
387 if (cxGetFileSize(file) % 2 != 0)
388 cxWrite("\n", 1, 1, fp); /* XXX "\n" ? */
389 }
390 }
391
392 cxDestroyFsIterator(iter);
393
394 if (symtab != NULL)
395 free(symtab);
396
397 return CX_SUCCESS;
398 }
399
400 static void
closeArchive(CxArchive * archive)401 closeArchive(CxArchive *archive)
402 {
403 if (archive->moduleData != NULL)
404 {
405 archive->moduleData = NULL;
406 }
407 }
408
409 static CxFP *
openFile(CxFile * file,CxAccessMode mode)410 openFile(CxFile *file, CxAccessMode mode)
411 {
412 CxArFileData *fileData;
413 CxArchive *archive;
414 CxFP *fp;
415
416 if (!CX_IS_MODE_READ_ONLY(mode))
417 return NULL;
418
419 archive = cxGetFileArchive(file);
420
421 fp = cxNewFp();
422
423 cxSetReadFunc(fp, __readFunc);
424 cxSetWriteFunc(fp, __writeFunc);
425 cxSetSeekFunc(fp, __seekFunc);
426 cxSetCloseFunc(fp, __closeFunc);
427
428 MEM_CHECK(fileData = (CxArFileData *)malloc(sizeof(CxArFileData)));
429
430 fileData->startPos = (long)file->moduleData;
431 fileData->lastPos = fileData->startPos;
432
433 fp->moduleData = fileData;
434
435 cxSeek((CxFP *)archive->moduleData, fileData->startPos, SEEK_SET);
436
437 return fp;
438 }
439
440 static void
destroyFile(CxFile * file)441 destroyFile(CxFile *file)
442 {
443 file->moduleData = NULL;
444 }
445
446 static char
supportsExtension(const char * ext)447 supportsExtension (const char *ext)
448 {
449 if (!strcasecmp(ext, "a") ||
450 !strcasecmp(ext, "deb") ||
451 !strcasecmp(ext, "ar"))
452 {
453 return 1;
454 }
455
456 return 0;
457 }
458
459 static CxArchiveOps ops =
460 {
461 readArchive, /* openArchive */
462 saveArchive, /* saveArchive */
463 closeArchive, /* closeArchive */
464 openFile, /* openFile */
465 destroyFile, /* destroyFile */
466 supportsExtension /* supportsExtension */
467 };
468
469 static void
__moduleInit(CxModuleType type)470 __moduleInit(CxModuleType type)
471 {
472 }
473
474 CX_INIT_ARCHIVE_MODULE(ar, __moduleInit, ops)
475
476