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