/** * @file tar.c Tar module * * $Id: tar.c,v 1.23 2003/01/01 06:22:33 chipx86 Exp $ * * @Copyright (C) 1999-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 "tar.h" typedef struct { long startPos; long lastPos; } CxTarFileData; static size_t __readFunc(void *ptr, size_t size, size_t nmemb, CxFP *fp) { CxTarFileData *fileData; CxFile *file; CxFP *parentFp; size_t readSize, remainingSize, result; file = fp->file; fileData = (CxTarFileData *)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) { CxTarFileData *fileData; CxFile *file; file = fp->file; fileData = (CxTarFileData *)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) { TarBlock block; CxStatus status; CxDirectory *root; char foundBlock = 0; int i; root = cxGetArchiveRoot(archive); while ((status = cxTarReadBlock(fp, &block)) == CX_SUCCESS) { size_t size; CxDirectory *parentDir; const char *blockName; char *baseName = NULL, *basePath = NULL; size_t blockNameLen; foundBlock = 1; if (*block.header.name == '.' && block.header.name[1] == '/') blockName = block.header.name + 1; else blockName = block.header.name; if (!strcmp(blockName, "/")) continue; blockNameLen = strlen(blockName); if (block.header.typeFlag == DIRECTORY) { CxDirectory *dir; char *fullPath; fullPath = strdup(blockName); if (fullPath[blockNameLen - 1] == '/') fullPath[blockNameLen - 1] = '\0'; cxSplitPath(fullPath, &basePath, &baseName); if (baseName != NULL && baseName[0] == '.' && baseName[1] == '\0') { free(baseName); if (basePath != NULL) free(basePath); continue; } dir = cxNewDirectory(); cxSetDirName(dir, baseName); free(baseName); free(fullPath); /* Find the appropriate parent directory. */ if (basePath != NULL) { parentDir = cxGetDirectory(root, basePath); free(basePath); } else parentDir = root; cxDirAddSubDir(parentDir, dir); /* Clean up. */ if (block.header.gnu_longname) free(block.header.gnu_longname); if (block.header.gnu_longlink) free(block.header.gnu_longlink); } else if (block.header.typeFlag == NORMAL_FILE_0 || block.header.typeFlag == NORMAL_FILE_1) { CxFile *file; /* Build the file. */ file = cxNewFile(); baseName = cxGetBaseName(blockName); cxSetFileName(file, baseName); free(baseName); #if 0 switch (block.header.typeFlag) { case NORMAL_FILE_0: case NORMAL_FILE_1: type = CX_FILETYPE_NORMAL; break; case SYMLINK: type = CX_FILETYPE_SYMLINK; break; case HARDLINK: type = CX_FILETYPE_HARDLINK; break; case CHAR_DEVICE: type = CX_FILETYPE_CHAR_DEVICE; break; case BLOCK_DEVICE: type = CX_FILETYPE_BLOCK_DEVICE; break; case FIFO: type = CX_FILETYPE_FIFO; break; default: fprintf(stderr, "Warning: Unknown file type in tar.\n"); type = CX_FILETYPE_UNKNOWN; break; } #endif cxSetFileMode(file, (mode_t)cxTarOctalToInt(block.header.mode)); cxSetFileUid(file, (uid_t)cxTarOctalToInt(block.header.uid)); cxSetFileGid(file, (gid_t)cxTarOctalToInt(block.header.gid)); cxSetFileSize(file, (size_t)cxTarOctalToInt(block.header.size)); cxSetFileDate(file, (time_t)cxTarOctalToInt(block.header.mtime)); cxSetFileCompressedSize(file, cxGetFileSize(file)); archive->archiveSize += cxGetFileCompressedSize(file); file->moduleData = (void *)cxTell(fp); /* Find the appropriate parent directory. */ basePath = cxGetBasePath(blockName); /* * |\ /| * | \/\/\/\/\/ | * | . , /o\ .| * \ .- \_/ / * \ . , , / * / .. .. \ * /.~~**..**.\ */ if (basePath != NULL) { parentDir = cxGetDirectory(root, basePath); free(basePath); } else parentDir = root; cxDirAddFile(parentDir, file); /* Clean up. */ if (block.header.gnu_longname) free(block.header.gnu_longname); if (block.header.gnu_longlink) free(block.header.gnu_longlink); /* Jump to the next one. */ if (block.header.typeFlag == NORMAL_FILE_0 || block.header.typeFlag == NORMAL_FILE_1 || block.header.typeFlag == CONTIGUOUS) { size = cxGetFileSize(file); for (i = size; i > 0; i -= TAR_BLOCK_SIZE) { char buffer[TAR_BLOCK_SIZE]; int k = cxRead(buffer, 1, TAR_BLOCK_SIZE, fp); if (k != TAR_BLOCK_SIZE) { if (k != -1) errno = EINVAL; return CX_ERROR; /* XXX */ } } } } else { fprintf(stderr, "Warning: Unknown file type in tar: '%c'.\n", block.header.typeFlag); } } if (status != CX_EOF) { /* TODO: Free up memory. */ return status; } else if (foundBlock == 0) { return CX_EOF; } cxSetArchiveType(archive, CX_ARCHIVE_MULTI); archive->moduleData = fp; return CX_SUCCESS; } static CxStatus saveArchive(CxArchive *archive, CxFP *fp) { return CX_NOT_SUPPORTED; #if 0 CxFsIterator *iter; CxFP *inFp; CxFsNode *node; iter = cxNewFsIterator(archive, CX_FSITER_FILES_DIRS); for (node = cxGetFsIterFirst(iter); node != NULL; node = cxGetFsIterNext(iter)) { UstarHeader header; char *filename; size_t nameSize; if (cxGetFsNodeType(node) == CX_FSNODETYPE_DIRECTORY) { nameSize = strlen(cxGetDirPath(node)) + 2; MEM_CHECK(filename = (char *)malloc(nameSize)); snprintf(filename, nameSize, "%s/"); } else { filename = strdup(cxGetFilePath(node)); nameSize = strlen(filename); } if (strlen(cxGetFilePath(node)) <= 100) { strncpy(header.name, cxGetFilePath(node), nameSize); } else { } } return CX_SUCCESS; #endif } static void closeArchive(CxArchive *archive) { archive->moduleData = NULL; } static CxFP * openFile(CxFile *file, CxAccessMode mode) { CxTarFileData *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 = (CxTarFileData *)malloc(sizeof(CxTarFileData))); 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, "tar")) 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(tar, __moduleInit, ops)