summaryrefslogtreecommitdiffstats
path: root/src/cc4group.c
diff options
context:
space:
mode:
authorMarkus Mittendrein <git@maxmitti.tk>2019-03-16 00:58:05 +0100
committerMarkus Mittendrein <git@maxmitti.tk>2019-03-16 00:58:05 +0100
commit7c2cda2544f22add8114828535a397a2d733361d (patch)
tree0660bd6ad220a5171b6a4f5c5e172813afd3d52c /src/cc4group.c
parentb627615a50df6fdbf9bf574f600a5f2b6cc8eb8d (diff)
downloadcc4group-7c2cda2544f22add8114828535a397a2d733361d.tar.gz
cc4group-7c2cda2544f22add8114828535a397a2d733361d.zip
Add openMemory method with an example
Diffstat (limited to 'src/cc4group.c')
-rw-r--r--src/cc4group.c280
1 files changed, 159 insertions, 121 deletions
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,