From 4ded6e352ee0fb5d96d2e1b9f051aac1f07b4886 Mon Sep 17 00:00:00 2001 From: Markus Mittendrein Date: Mon, 20 Apr 2020 21:51:58 +0200 Subject: Rework group saving save / saveParent write back to the original group saveParent saves changes in child groups (openAsChild) by saving the top-most parent saveAs / saveAsOverwrite resembles the old save / saveOverwrite For all other saving methods, groups opend with openAsChild are saved as if there was no parent --- src/cc4group.c | 86 ++++++++++++++++++++++++++++++++++++++++++------- src/cc4group.h | 19 +++++++++-- src/cppc4group.cpp | 9 ++++-- src/cppc4group.hpp | 6 ++-- src/platform/platform.h | 7 +++- src/platform/unix.c | 5 +++ src/platform/windows.c | 6 ++++ 7 files changed, 118 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/cc4group.c b/src/cc4group.c index 0483eea..5cbce53 100644 --- a/src/cc4group.c +++ b/src/cc4group.c @@ -124,6 +124,8 @@ struct CC4Group_t { } readState; bool lazy; + const char* path; + bool subRooted; }; typedef struct { @@ -1062,6 +1064,7 @@ static bool cc4group_setSubRoot(CC4Group* const this, const char* const subPath) this->realRoot = this->root; this->root = *subRoot; + this->subRooted = true; } return true; @@ -1179,6 +1182,14 @@ ret: if(success) { success = cc4group_setSubRoot(this, subPath); + if(success && subPath != NULL && *subPath != '\0') + { + if(this->path != NULL) + { + free((void*)this->path); + } + this->path = cc4group_absolutePath(tmpPath); + } } free(tmpPath); @@ -1193,6 +1204,8 @@ static void cc4group_init(CC4Group* const this) this->referenceCounter = 1; this->parent = NULL; this->lazy = true; + this->path = NULL; + this->subRooted = false; this->uncompressedData = NULL; this->uncompressedSize = 0; @@ -1700,7 +1713,10 @@ static bool cc4group_openExisting(CC4Group* const this, const char* const path) SET_BINARY(STDIN_FILENO); return cc4group_openFd(this, STDIN_FILENO); } - else if(cc4group_isDirectory(path)) + + this->path = cc4group_absolutePath(path); + + if(cc4group_isDirectory(path)) { return cc4group_openDirectory(this, path); } @@ -1739,6 +1755,11 @@ static void cc4group_delete(CC4Group* const this) free(this->error.lastFormattedMessage); } + if(this->path != NULL) + { + free((void*)this->path); + } + free(this); } @@ -2501,12 +2522,6 @@ static bool cc4group_saveWithWriteCallback(CC4Group* const this, CC4Group_WriteC maxBlockSize = 1024 * 1024; } - if(this->parent != NULL) - { - SET_MESSAGE_ERROR("Saving is currently not implemented for groups opened with openAsChild"); - return false; - } - WriteCallback callback = { .callback = writeCallback, .arg = arg, @@ -2617,7 +2632,7 @@ static bool cc4group_saveToFd(CC4Group* const this, int fd) return cc4group_saveWithWriteCallback(this, cc4group_writeToFdCallback, &fd, 0); } -static bool cc4group_save(CC4Group* const this, const char* const path) +static bool cc4group_saveAs(CC4Group* const this, const char* const path) { assert(this); assert(path); @@ -2658,11 +2673,11 @@ static bool cc4group_save(CC4Group* const this, const char* const path) return success; } -static bool cc4group_saveOverwrite(CC4Group* const this, const char* const path) +static bool cc4group_saveAsOverwrite(CC4Group* const this, const char* const path) { if(strcmp(path, "-") == 0) { - return cc4group_save(this, path); + return cc4group_saveAs(this, path); } char tmpFileName[tmpFileNameLength]; @@ -2704,6 +2719,52 @@ ret: return success; } +static bool cc4group_save(CC4Group* const this) +{ + assert(this); + if(this->path == NULL) + { + SET_MESSAGE_ERROR("This group was not opened from an existing on disk group. cc4group.save can not be used."); + return false; + } + + if(this->parent != NULL) + { + SET_MESSAGE_ERROR("Refusing to save a group opened with openAsChild. cc4group.saveParent might be used."); + return false; + } + + if(this->subRooted) + { + SET_MESSAGE_ERROR("Refusing to save a group opened with a path inside the actual group. cc4group.saveParent might be used."); + return false; + } + + return cc4group_saveAsOverwrite(this, this->path); +} + +static bool cc4group_saveParent(CC4Group* const this) +{ + CC4Group* root = this; + while(root->parent) + { + root = root->parent; + } + + if(root->subRooted) + { + C4GroupEntryData tmp = root->root; + root->root = root->realRoot; + root->subRooted = false; + bool success = cc4group_save(root); + root->root = tmp; + root->subRooted = true; + return success; + } + + return cc4group_save(root); +} + static bool cc4group_getEntryInfoForEntry(CC4Group* const this, const C4GroupEntryData* const entry, CC4Group_EntryInfo* const info, bool const lazy) { C4GroupHeader* header = NULL; @@ -3334,9 +3395,10 @@ CC4Group_API cc4group = { .openFilePointer = cc4group_openFilePointer, .openWithReadCallback = cc4group_uncompressGroup, - .save = cc4group_save, - .saveOverwrite = cc4group_saveOverwrite, + .saveParent = cc4group_saveParent, + .saveAs = cc4group_saveAs, + .saveAsOverwrite = cc4group_saveAsOverwrite, .saveToFd = cc4group_saveToFd, .saveToFilePointer = cc4group_saveToFilePointer, .saveWithWriteCallback = cc4group_saveWithWriteCallback, diff --git a/src/cc4group.h b/src/cc4group.h index c08bad8..8de4616 100644 --- a/src/cc4group.h +++ b/src/cc4group.h @@ -247,6 +247,7 @@ typedef struct { bool (*create)(CC4Group* const this); // opens a group or a normal folder on the filesystem; path may point to a directory inside a group; path "-" can be used to read the group from stdin + // if path points to a directory inside a group, saving behaves the same as for cc4group.openAsChild bool (*openExisting)(CC4Group* const this, const char* const path); // opens a group that is stored entirely in memory @@ -272,13 +273,22 @@ typedef struct { bool (*openWithReadCallback)(CC4Group* const this, CC4Group_ReadCallback const readCallback, void* const callbackArg, CC4Group_MemoryManagement const memoryManagement, CC4Group_ReadSetupCallback const initCallback, CC4Group_ReadSetupCallback const deinitCallback); + // saves the current in-memory state of the group as a compressed c4group to disk + // overwrites the group from which this instance was opened + // works only for groups opened using cc4group.openExisting (and not from stdin via "-") + bool (*save)(CC4Group* const this); + + // same as cc4group.save, but in case of a group created using cc4group.openAsChild, saves the top-most parent instead + // also applies to groups which have been opened using a path which is a subgroup of the actual group + bool (*saveParent)(CC4Group* const this); + // saves the current in-memory state of the group as a compressed c4group to disk // fails if the given path already exists; path "-" can be used to write the group to stdout - bool (*save)(CC4Group* const this, const char* const path); + bool (*saveAs)(CC4Group* const this, const char* const path); // same as save, except that an already existing group will be overwritten // be careful, any existing file will be overwritten in-place. if any error occurs after opening the target file (e.g. out of memory, a program or system crash), the previous contents will be lost - bool (*saveOverwrite)(CC4Group* const this, const char* const path); + bool (*saveAsOverwrite)(CC4Group* const this, const char* const path); // saves the compressed group file by writing it to the file descriptor // the file descriptor must have been opened with write access and must be in blocking mode (should be default if O_NONBLOCK is not specified) @@ -415,7 +425,10 @@ typedef struct { // the returned group will share most resources with its parent (the one passed as this) // the parent may be deleted anytime, but it will stay in memory until the child is deleted as well (using reference counting) // because both share most of their data, modifications of the child will affect the parent and the opposite direction is also possible if the modifications involve the subgroup - // saving through child groups is not implemented yet, it will result in an error + // all save methods except cc4group.save and cc4group.saveParent save the child groups contents as if it didn't have a parent at all + // cc4group.save refuses to save groups from cc4group.openAsChild + // cc4group.saveParent saves the top-most parent with cc4group.save in order to save the changes in the child to disk. this will also save modifications made inside the parent group... + // ...outside of this children CC4Group* (*openAsChild)(CC4Group* const this, const char* const path); } const CC4Group_API; diff --git a/src/cppc4group.cpp b/src/cppc4group.cpp index ea24f3b..4db6153 100644 --- a/src/cppc4group.cpp +++ b/src/cppc4group.cpp @@ -239,9 +239,14 @@ bool CppC4Group::openWithReadCallback(const ReadCallback callback, void* const c return cc4group.openWithReadCallback(p->g, callback, callbackArg, convertMemoryManagement(management), initCallback, deinitCallback); } -bool CppC4Group::save(const std::string& path, const bool overwrite) +bool CppC4Group::save(const bool parent) { - return (overwrite ? cc4group.saveOverwrite : cc4group.save)(p->g, path.c_str()); + return (parent ? cc4group.saveParent : cc4group.save)(p->g); +} + +bool CppC4Group::saveAs(const std::string& path, const bool overwrite) +{ + return (overwrite ? cc4group.saveAsOverwrite : cc4group.saveAs)(p->g, path.c_str()); } bool CppC4Group::saveToFd(const int fd) diff --git a/src/cppc4group.hpp b/src/cppc4group.hpp index 0ca9eb1..33cf2cc 100644 --- a/src/cppc4group.hpp +++ b/src/cppc4group.hpp @@ -153,8 +153,10 @@ public: bool openMemory(const void* const data, const size_t size, const MemoryManagement& management = MemoryManagement::Reference); bool openWithReadCallback(const ReadCallback callback, void* const callbackArg, const MemoryManagement& management = MemoryManagement::Take, SetupCallback initCallback = nullptr, SetupCallback deinitCallback = nullptr); - // save actually maps to both cc4group.save and cc4group.saveOverwrite, thanks to default arguments (yes, thats the reason why they are separate in the C-API) - bool save(const std::string& path, const bool overwrite = false); + // save actually maps to both cc4group.save and cc4group.saveParent, thanks to default arguments (yes, thats the reason why they are separate in the C-API) + bool save(const bool parent = false); + // saveAs actually maps to both cc4group.saveAs and cc4group.saveAsOverwrite, as above + bool saveAs(const std::string& path, const bool overwrite = false); bool saveToFd(const int fd); bool saveToFilePointer(FILE* file); bool saveWithWriteCallback(const WriteCallback callback, void* const arg = nullptr, size_t bufferSize = 0); diff --git a/src/platform/platform.h b/src/platform/platform.h index 688d851..55ed242 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -24,7 +24,11 @@ #define cc4group_mkdir(path, mode) mkdir((path)) #define SET_BINARY(fd) setmode(fd, O_BINARY) #else + #ifndef _XOPEN_SOURCE + #define _XOPEN_SOURCE 700 + #endif #include + #include #define cc4group_mkdir(path, mode) mkdir((path), (mode)) #define O_BINARY 0 @@ -32,4 +36,5 @@ #endif void *cc4group_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); -int cc4group_munmap(void *addr, size_t length); \ No newline at end of file +int cc4group_munmap(void *addr, size_t length); +char* cc4group_absolutePath(const char* path); \ No newline at end of file diff --git a/src/platform/unix.c b/src/platform/unix.c index 9bd8f57..01161fc 100644 --- a/src/platform/unix.c +++ b/src/platform/unix.c @@ -9,3 +9,8 @@ int cc4group_munmap(void *addr, size_t length) { return munmap(addr, length); } + +char* cc4group_absolutePath(const char* path) +{ + return realpath(path, NULL); +} diff --git a/src/platform/windows.c b/src/platform/windows.c index 53d5038..96678ab 100644 --- a/src/platform/windows.c +++ b/src/platform/windows.c @@ -97,5 +97,11 @@ int cc4group_munmap(void *addr, size_t length) return 0; } +char* cc4group_absolutePath(const char* path) +{ + return _fullpath(NULL, path, 0); +} + + #undef DWORD_HI #undef DWORD_LO -- cgit v1.2.3-54-g00ecf