/** * @file ar.c AR module * * $Id: ar.c,v 1.25 2003/01/01 06:22:31 chipx86 Exp $ * * @Copyright (C) 2001-2003 The GNUpdate Project. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "ar.h" static size_t __readFunc(void *ptr, size_t size, size_t nmemb, CxFP *fp) { CxArFileData *fileData; CxFile *file; CxFP *parentFp; size_t readSize, remainingSize, result; file = fp->file; fileData = (CxArFileData *)fp->moduleData; parentFp = (CxFP *)cxGetFileArchive(file)->moduleData; if (cxTell(parentFp) != fileData->lastPos) cxSeek(parentFp, fileData->lastPos, SEEK_SET); readSize = size * nmemb; remainingSize = cxGetFileCompressedSize(file) - (fileData->lastPos - fileData->startPos); if (readSize > remainingSize) readSize = remainingSize; result = cxRead(ptr, 1, readSize, parentFp); fileData->lastPos = cxTell(parentFp); return result; } static size_t __writeFunc(const void *ptr, size_t size, size_t nmemb, CxFP *fp) { return 0; } static void __seekFunc(CxFP *fp, long offset, int whence) { CxArFileData *fileData; CxFile *file; file = fp->file; fileData = (CxArFileData *)fp->moduleData; switch (whence) { case SEEK_SET: fileData->lastPos = fileData->startPos + offset; break; case SEEK_CUR: fileData->lastPos += offset; break; case SEEK_END: fileData->lastPos = fileData->startPos + cxGetFileCompressedSize(fp->file) - offset; break; } } static void __closeFunc(CxFP *fp) { if (fp->moduleData != NULL) { free(fp->moduleData); fp->moduleData = NULL; } } static CxStatus readArchive(CxArchive *archive, CxFP *fp) { CxFile *file; CxDirectory *root; CxArHeader header; CxStatus status; char *nameTable = NULL; if ((status = cxArValidateMagic(fp)) != CX_SUCCESS) return status; root = cxGetArchiveRoot(archive); while ((status = cxArReadHeader(fp, &header)) == CX_SUCCESS) { char *filename; int size; if (header.name[0] == '/' && header.name[1] == '/') { /* Name symbol table. */ int tableSize; if (nameTable != NULL) { free(nameTable); /* * XXX Corruption, I think? I'm not 100% sure about the * ar specification, since there seems to be no real * good documents on it. */ return CX_CORRUPT; } tableSize = cxArDecToInt(header.size); MEM_CHECK(nameTable = (char *)malloc(tableSize)); /* Read in the table. */ cxRead(nameTable, tableSize, 1, fp); continue; } else if (header.name[0] == '/' && header.name[0] == ' ') { /* Symbol table. We can skip over this one. */ } else { /* Normal filename entry. */ char *c; /* See if this is an extended name. */ if (header.name[0] == '/') { /* Extended name. (>= 16 chars) */ char *start, *end; char buffer[15]; int offset, len; if (nameTable == NULL) { /* Uh oh. */ status = CX_CORRUPT; break; } /* Copy this into a buffer, and nul-terminate it. */ strncpy(buffer, header.name + 1, 15); c = strchr(buffer, ' '); *c = '\0'; /* Get the offset it represents. */ offset = cxArDecToInt(buffer); /* Find the '/' in the entry for the long name. */ start = nameTable + offset; end = strchr(start, '/'); len = end - start; MEM_CHECK(filename = (char *)malloc(len + 1)); strncpy(filename, start, len); filename[len] = '\0'; } else { /* Short name. (< 16 chars) */ filename = (char *)malloc(16); strncpy(filename, header.name, 15); filename[15] = '\0'; /* Strip off the trailing slash. */ if ((c = strchr(filename, '/')) != NULL || (c = strchr(filename, ' ')) != NULL) { *c = '\0'; } } /* Create the file structure. */ file = cxNewFile(); cxSetFileName(file, filename); free(filename); cxSetFileMode(file, cxArOctalToInt(header.mode)); cxSetFileUid(file, cxArDecToInt(header.uid)); cxSetFileGid(file, cxArDecToInt(header.gid)); cxSetFileSize(file, cxArDecToInt(header.size)); cxSetFileDate(file, (time_t)cxArDecToInt(header.date)); cxSetFileCompressedSize(file, cxGetFileSize(file)); file->moduleData = (void *)cxTell(fp); archive->archiveSize += cxGetFileSize(file); /* We don't have subdirectory support in ar files (I believe) */ cxDirAddFile(root, file); } /* Jump to the next entry. */ size = cxArDecToInt(header.size); if (size % 2 != 0) size++; cxSeek(fp, size, SEEK_CUR); } if (nameTable != NULL) free(nameTable); if (status != CX_EOF) { /* TODO: Free up memory. */ return status; } cxSetArchiveType(archive, CX_ARCHIVE_MULTI); archive->moduleData = fp; return CX_SUCCESS; } static CxStatus saveArchive(CxArchive *archive, CxFP *fp) { CxArHeader header; CxFsIterator *iter; CxFile *file; CxFP *inFp; int symPos = 0; char *symtab = NULL; size_t symtabLen = 0; size_t symtabBufSize = 0; /* Write the ! header */ cxWrite(AR_MAGIC, 1, AR_MAGIC_LEN, fp); iter = cxNewFsIterator(archive, CX_FSITER_FILES); /* Make the symbol table. */ for (file = cxGetFsIterFirst(iter); file != NULL; file = cxGetFsIterNext(iter)) { const char *filename; if (cxGetFsNodeType(file) != CX_FSNODETYPE_FILE) continue; if ((filename = cxGetFileName(file)) == NULL) continue; if (strlen(filename) >= 16) { /* Long filename. */ size_t len; len = strlen(filename) + 2; if (symtabBufSize - symtabLen < len) { char *newSymTab; size_t newSymTabBufSize; newSymTabBufSize = (symtabBufSize + len) * 2; MEM_CHECK(newSymTab = (char *)malloc(newSymTabBufSize)); memset(newSymTab, 0, newSymTabBufSize); if (symtab != NULL) { strncpy(newSymTab, symtab, symtabLen); free(symtab); } symtabBufSize = newSymTabBufSize; symtab = newSymTab; } snprintf(symtab + symtabLen, len + 1, "%s/\n", filename); symtabLen += len; } } if (symtabLen > 0) { int i; /* Write the symbol table. */ memset(&header, ' ', sizeof(CxArHeader)); strncpy(header.name, "//", 2); i = snprintf(header.size, 10, "%d", symtabLen); header.size[i] = ' '; strncpy(header.fmag, AR_FMAG, 2); cxWrite(&header, 1, AR_HEADER_LEN, fp); /* Write the entries. */ cxWrite(symtab, 1, symtabLen, fp); } /* Add the files. */ for (file = cxGetFsIterFirst(iter); file != NULL; file = cxGetFsIterNext(iter)) { if (cxGetFsNodeType(file) != CX_FSNODETYPE_FILE) continue; if (cxGetFilePhysicalPath(file) != NULL) { char buffer[4096]; size_t s; int i; inFp = cxOpenFile(cxGetFilePhysicalPath(file), CX_MODE_READ_ONLY | CX_MODE_RAW); if (inFp == NULL) continue; /* Write the file header. */ memset(&header, ' ', sizeof(CxArHeader)); if (strlen(cxGetFileName(file)) >= 16) { i = snprintf(header.name, 16, "/%d", symPos); symPos = strchr(symtab+ symPos, '\n') + 1 - symtab; } else { i = snprintf(header.name, 16, "%s/", cxGetFileName(file)); } header.name[i] = ' '; i = snprintf(header.date, 12, "%ld", cxGetFileDate(file)); header.date[i] = ' '; i = snprintf(header.uid, 6, "%u", cxGetFileUid(file)); header.uid[i] = ' '; i = snprintf(header.gid, 6, "%u", cxGetFileGid(file)); header.gid[i] = ' '; i = snprintf(header.mode, 8, "%o", cxGetFileMode(file)); header.mode[i] = ' '; i = snprintf(header.size, 20, "%u", cxGetFileSize(file)); header.size[i] = ' '; strncpy(header.fmag, AR_FMAG, 2); cxWrite(&header, 1, AR_HEADER_LEN, fp); /* Write the data */ while ((s = cxRead(buffer, 1, 4096, inFp)) > 0) cxWrite(buffer, 1, s, fp); cxClose(inFp); /* Padding */ if (cxGetFileSize(file) % 2 != 0) cxWrite("\n", 1, 1, fp); /* XXX "\n" ? */ } } cxDestroyFsIterator(iter); if (symtab != NULL) free(symtab); return CX_SUCCESS; } static void closeArchive(CxArchive *archive) { if (archive->moduleData != NULL) { archive->moduleData = NULL; } } static CxFP * openFile(CxFile *file, CxAccessMode mode) { CxArFileData *fileData; CxArchive *archive; CxFP *fp; if (!CX_IS_MODE_READ_ONLY(mode)) return NULL; archive = cxGetFileArchive(file); fp = cxNewFp(); cxSetReadFunc(fp, __readFunc); cxSetWriteFunc(fp, __writeFunc); cxSetSeekFunc(fp, __seekFunc); cxSetCloseFunc(fp, __closeFunc); MEM_CHECK(fileData = (CxArFileData *)malloc(sizeof(CxArFileData))); fileData->startPos = (long)file->moduleData; fileData->lastPos = fileData->startPos; fp->moduleData = fileData; cxSeek((CxFP *)archive->moduleData, fileData->startPos, SEEK_SET); return fp; } static void destroyFile(CxFile *file) { file->moduleData = NULL; } static char supportsExtension (const char *ext) { if (!strcasecmp(ext, "a") || !strcasecmp(ext, "deb") || !strcasecmp(ext, "ar")) { return 1; } return 0; } static CxArchiveOps ops = { readArchive, /* openArchive */ saveArchive, /* saveArchive */ closeArchive, /* closeArchive */ openFile, /* openFile */ destroyFile, /* destroyFile */ supportsExtension /* supportsExtension */ }; static void __moduleInit(CxModuleType type) { } CX_INIT_ARCHIVE_MODULE(ar, __moduleInit, ops)