From 7c2cda2544f22add8114828535a397a2d733361d Mon Sep 17 00:00:00 2001 From: Markus Mittendrein Date: Sat, 16 Mar 2019 00:58:05 +0100 Subject: Add openMemory method with an example --- src/cc4group.c | 280 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 159 insertions(+), 121 deletions(-) (limited to 'src/cc4group.c') diff --git a/src/cc4group.c b/src/cc4group.c index 29fe0d5..f3975ee 100644 --- a/src/cc4group.c +++ b/src/cc4group.c @@ -75,8 +75,6 @@ typedef struct { struct CC4Group_t { uint8_t* uncompressedData; size_t uncompressedSize; - const char* path; - const char* subPath; C4GroupEntryData root; C4GroupEntryData realRoot; @@ -329,98 +327,45 @@ static void* cc4group_createTmpMemoryAuto(CC4Group* const this, const size_t siz static CC4Group_TmpMemoryStrategy cc4group_tmpMemoryStrategy = cc4group_createTmpMemoryAuto; -static void cc4group_uncompressGroup(CC4Group* const this) +static bool cc4group_buildGroupHierarchy(CC4Group* const this) { + this->root.data = this->uncompressedData; + bool ret = buildChildren(this, &this->root, this->uncompressedSize); + this->root.core.Directory = 1; + this->root.core.FileName[0] = '\0'; + + AddCleanupJob(deleteChildren, this->root.children); + + return ret; +} + +static bool cc4group_uncompressGroup(CC4Group* const this, const uint8_t* const compressedData, size_t const size) +{ + // only if the group is still empty + assert(this->root.children == NULL); + // declare variables here already for proper cleanup in error cases - uint8_t* retData = NULL; C4GroupHeader* header = NULL; - uint8_t* mappedFile = MAP_FAILED; - void* mapExtra; - uint8_t* mappedTmpFile = NULL; bool inflateStarted = false; + uint8_t* retData = NULL; size_t currentSize = 0; CC4Group_CleanupJob tmpCleanup; + uint8_t* mappedTmpFile = NULL; - char* path; - char* slash = NULL; - - this->path = path = strdup(this->path); - AddCleanupJob(free, (void*)this->path); - - int file = -1; - for(;;) - { - file = open(this->path, O_RDONLY | O_BINARY); - if(file != -1) - { - break; - } - - if(errno != ENOTDIR) - { - SET_ERRNO_ERROR("open: Opening group file"); - goto ret; - } - - char* oldSlash = slash; - - slash = strrchr(path, '/'); - - if(oldSlash != NULL) - { - *oldSlash = '/'; - } - - if(slash != NULL && slash != path) - { - *slash = '\0'; - this->subPath = slash + 1; - } - } - - struct stat st; - if(fstat(file, &st) == -1) - { - SET_ERRNO_ERROR("fstat: on the opened group file"); - goto ret; - } - - if(S_ISDIR(st.st_mode)) - { - SET_MESSAGE_ERROR("The specified group file is a directory"); - goto ret; - } - - off_t size = st.st_size; - mappedFile = cc4group_mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, file, 0, &mapExtra); - - if(close(file) == -1) - { - SET_ERRNO_ERROR("close: Closing the group file"); - goto ret; - } - - if(mappedFile == MAP_FAILED) - { - SET_ERRNO_ERROR("mmap: Mapping the group file"); - goto ret; - } + static uint8_t magic[] = {0x1f, 0x8b}; - if((mappedFile[0] != C4GroupMagic1 && mappedFile[0] != 0x1f) || (mappedFile[1] != C4GroupMagic2 && mappedFile[1] != 0x8b)) + if((compressedData[0] != C4GroupMagic1 && compressedData[0] != magic[0]) || (compressedData[1] != C4GroupMagic2 && compressedData[1] != magic[1])) { SET_MESSAGE_ERROR("The file is not a valid group file. Magic bytes don't match."); goto ret; } - mappedFile[0] = 0x1f; - mappedFile[1] = 0x8b; - z_stream strm = { .zalloc = NULL, .zfree = NULL, .opaque = NULL, - .next_in = mappedFile, - .avail_in = size + .next_in = magic, + .avail_in = 2 }; int ret = inflateInit2(&strm, 15 + 16); // window size 15 + automatic gzip @@ -445,7 +390,16 @@ static void cc4group_uncompressGroup(CC4Group* const this) strm.avail_out = currentSize; ret = inflate(&strm, Z_SYNC_FLUSH); + if(ret != Z_OK && (ret != Z_STREAM_END || strm.avail_in != 0)) + { + SET_ZERROR_ERROR("inflate: inflating the magic", ret); + goto ret; + } + strm.next_in = (uint8_t*)compressedData + 2; // I don't know why this must be non-const; anyway a read-only mapped input doesn't segfault. so it seems to be used read-only + strm.avail_in = size - 2; + + ret = inflate(&strm, Z_SYNC_FLUSH); if(ret != Z_OK && (ret != Z_STREAM_END || strm.avail_in != 0)) { SET_ZERROR_ERROR("inflate: inflating the group header", ret); @@ -541,14 +495,6 @@ ret: inflateEnd(&strm); } - if(mappedFile != MAP_FAILED) - { - if(cc4group_munmap(mappedFile, size, mapExtra) == -1) - { - fprintf(stderr, "WARNING: munmap: Unmapping the group file failed: %s\n", strerror(errno)); - } - } - if(retData == NULL && mappedTmpFile != NULL) { tmpCleanup.func(tmpCleanup.data); @@ -560,18 +506,137 @@ ret: this->uncompressedData = retData; this->uncompressedSize = currentSize; + + if(retData == NULL) + { + return false; + } + return cc4group_buildGroupHierarchy(this); } -static bool cc4group_buildGroupHierarchy(CC4Group* const this) +static bool cc4group_openMemory(CC4Group* const this, const uint8_t* const compressedData, size_t const size) { - this->root.data = this->uncompressedData; - bool ret = buildChildren(this, &this->root, this->uncompressedSize); - this->root.core.Directory = 1; - this->root.core.FileName[0] = '\0'; + assert(this); + assert(compressedData); + assert(size); - AddCleanupJob(deleteChildren, this->root.children); + return cc4group_uncompressGroup(this, compressedData, size); +} - return ret; +static bool cc4group_setSubRoot(CC4Group* const this, const char* const subPath) +{ + if(subPath != NULL && *subPath != '\0') + { + this->realRoot = this->root; + + const C4GroupEntryData* subRoot = cc4group_getDirectoryByPath(this, subPath); + + if(subRoot == NULL) + { + return false; + } + + this->root = *subRoot; + } + + return true; +} + +static bool cc4group_uncompressGroupFromFile(CC4Group* const this, const char* const path) +{ + uint8_t* mappedFile = MAP_FAILED; + void* mapExtra; + bool success = false; + + char* slash = NULL; + char* tmpPath = strdup(path); + + if(tmpPath == NULL) + { + SET_ERRNO_ERROR("strdup: duplicating the path"); + return false; + } + + char* subPath = NULL; + + int file = -1; + for(;;) + { + file = open(tmpPath, O_RDONLY | O_BINARY); + if(file != -1) + { + break; + } + + if(errno != ENOTDIR) + { + SET_ERRNO_ERROR("open: Opening group file"); + goto ret; + } + + char* oldSlash = slash; + + slash = strrchr(tmpPath, '/'); + + if(oldSlash != NULL) + { + *oldSlash = '/'; + } + + if(slash != NULL && slash != tmpPath) + { + *slash = '\0'; + subPath = slash + 1; + } + } + + struct stat st; + if(fstat(file, &st) == -1) + { + SET_ERRNO_ERROR("fstat: on the opened group file"); + goto ret; + } + + if(S_ISDIR(st.st_mode)) + { + SET_MESSAGE_ERROR("The specified group file is a directory"); + goto ret; + } + + off_t size = st.st_size; + mappedFile = cc4group_mmap(NULL, size, PROT_READ, MAP_PRIVATE, file, 0, &mapExtra); + + if(close(file) == -1) + { + SET_ERRNO_ERROR("close: Closing the group file"); + goto ret; + } + + if(mappedFile == MAP_FAILED) + { + SET_ERRNO_ERROR("mmap: Mapping the group file"); + goto ret; + } + + success = cc4group_uncompressGroup(this, mappedFile, size); + +ret: + if(mappedFile != MAP_FAILED) + { + if(cc4group_munmap(mappedFile, size, mapExtra) == -1) + { + fprintf(stderr, "WARNING: munmap: Unmapping the group file failed: %s\n", strerror(errno)); + } + } + + if(success) + { + success = cc4group_setSubRoot(this, subPath); + } + + free(tmpPath); + + return success; } static void cc4group_init(CC4Group* const this) @@ -580,8 +645,6 @@ static void cc4group_init(CC4Group* const this) this->uncompressedData = NULL; this->uncompressedSize = 0; - this->path = ""; - this->subPath = ""; this->root.data = NULL; this->root.freeData = false; @@ -767,33 +830,7 @@ static bool cc4group_openExisting(CC4Group* const this, const char* const path) { assert(this); - // only if the group is still empty - assert(this->root.children == NULL); - - this->path = path; - cc4group_uncompressGroup(this); - if(this->uncompressedData == NULL) - { - return false; - } - - cc4group_buildGroupHierarchy(this); - - if(*this->subPath != '\0') - { - this->realRoot = this->root; - - const C4GroupEntryData* subRoot = cc4group_getDirectoryByPath(this, this->subPath); - - if(subRoot == NULL) - { - return false; - } - - this->root = *subRoot; - } - - return true; + return cc4group_uncompressGroupFromFile(this, path); } static void cc4group_delete(CC4Group* const this) @@ -1650,6 +1687,7 @@ CC4Group_API cc4group = { .create = cc4group_create, .delete = cc4group_delete, .openExisting = cc4group_openExisting, + .openMemory = cc4group_openMemory, .save = cc4group_save, .saveOverwrite = cc4group_saveOverwrite, .extractAll = cc4group_extractAll, -- cgit v1.2.3-54-g00ecf