summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cc4group.c123
1 files changed, 112 insertions, 11 deletions
diff --git a/src/cc4group.c b/src/cc4group.c
index c8dde19..bd0ea1a 100644
--- a/src/cc4group.c
+++ b/src/cc4group.c
@@ -389,7 +389,7 @@ static void deleteChildren(GroupEntryList* const entries)
GroupEntryListDestroy(entries);
}
-static void* cc4group_mapSizedWriteFd(CC4Group* const this, int fd, size_t size)
+static void* cc4group_mapSizedWriteFd(CC4Group* const this, int fd, size_t size, bool allowRead)
{
// allocate file size
// https://gist.github.com/marcetcheverry/991042
@@ -405,7 +405,12 @@ static void* cc4group_mapSizedWriteFd(CC4Group* const this, int fd, size_t size)
return MAP_FAILED;
}
- return cc4group_mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ void* result = cc4group_mmap(NULL, size, (allowRead ? PROT_READ : 0) | PROT_WRITE, MAP_SHARED, fd, 0);
+ if(result == MAP_FAILED)
+ {
+ SET_ERRNO_ERROR("mmap: mapping the file failed");
+ }
+ return result;
}
static const char tmpFileTemplate[] = "cc4grouptmpXXXXXX";
@@ -475,7 +480,7 @@ static void* cc4group_createTmpMemoryFile(CC4Group* const this, const size_t siz
unmapData->unlinkLater = unlink(unmapData->fileName) == -1; // in case it can't be deleted now (Windows -.-), delete it when cleaning up
- ret = cc4group_mapSizedWriteFd(this, tmpFile, size);
+ ret = cc4group_mapSizedWriteFd(this, tmpFile, size, true);
if(ret == MAP_FAILED)
{
// error message is set in the method
@@ -2897,6 +2902,109 @@ static bool cc4group_saveAs(CC4Group* const this, const char* const path)
return success;
}
+static bool cc4group_move(CC4Group* const this, const char* src, const char* dst, bool overwrite)
+{
+ assert(!cc4group_isDirectory(src));
+
+ if(rename(src, dst) != 0)
+ {
+ if(errno == EXDEV)
+ {
+ bool success = false;
+ void* srcMapped = MAP_FAILED;
+ void* targetMapped = MAP_FAILED;
+ int targetFile = -1;
+ int sourceFile = open(src, O_RDONLY, O_BINARY);
+ if(sourceFile == -1)
+ {
+ SET_ERRNO_ERROR("open: Opening the source file failed");
+ goto ret;
+ }
+
+ struct stat st;
+ if(fstat(sourceFile, &st) != 0)
+ {
+ SET_ERRNO_ERROR("stat: Stating the source file failed");
+ goto ret;
+ }
+
+ srcMapped = cc4group_mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, sourceFile, 0);
+ if(srcMapped == MAP_FAILED)
+ {
+ SET_ERRNO_ERROR("mmap: Mapping the source file failed");
+ goto ret;
+ }
+
+ if(close(sourceFile) == 0)
+ {
+ sourceFile = -1;
+ }
+
+ targetFile = open(dst, O_RDWR | O_BINARY | O_CREAT | (overwrite ? O_TRUNC : O_EXCL));
+ if(targetFile == -1)
+ {
+ SET_ERRNO_ERROR("open: Opening the target file failed");
+ goto ret;
+ }
+
+ targetMapped = cc4group_mapSizedWriteFd(this, targetFile, st.st_size, false);
+ if(targetMapped == MAP_FAILED)
+ {
+ goto ret;
+ }
+
+ if(close(targetFile) == 0)
+ {
+ targetFile = -1;
+ }
+
+ memcpy(targetMapped, srcMapped, st.st_size);
+
+ cc4group_tryWarnUnlink(this, src);
+
+ success = true;
+
+ ret:
+ if(sourceFile != -1)
+ {
+ if(close(sourceFile) == -1)
+ {
+ cc4group_warn(this, "cc4group_move: Closing the source file \"%s\" failed: %s", src, strerror(errno));
+ }
+ }
+ if(targetFile != -1)
+ {
+ if(close(targetFile) == -1)
+ {
+ cc4group_warn(this, "cc4group_move: Closing the destination file \"%s\" failed: %s", dst, strerror(errno));
+ }
+ }
+ if(srcMapped != MAP_FAILED)
+ {
+ if(cc4group_munmap(srcMapped, st.st_size) != 0)
+ {
+ cc4group_warn(this, "cc4group_move: Unmapping the source file \"%s\" failed: %s", src, strerror(errno));
+ }
+ }
+ if(targetMapped != MAP_FAILED)
+ {
+ if(cc4group_munmap(targetMapped, st.st_size) != 0)
+ {
+ cc4group_warn(this, "cc4group_move: Unmapping the destination file \"%s\" failed: %s", dst, strerror(errno));
+ }
+ }
+ return success;
+ }
+ else
+ {
+ SET_ERRNO_ERROR("rename: Moving the file failed");
+ }
+ return false;
+ }
+
+ return true;
+}
+
static bool cc4group_saveAsOverwrite(CC4Group* const this, const char* const path)
{
if(strcmp(path, "-") == 0)
@@ -2918,14 +3026,7 @@ static bool cc4group_saveAsOverwrite(CC4Group* const this, const char* const pat
goto ret;
}
- if(rename(tmpFileName, path) == 0)
- {
- success = true;
- }
- else
- {
- SET_ERRNO_ERROR("rename: can't rename the temporary file to the target path");
- }
+ success = cc4group_move(this, tmpFileName, path, true);
if(close(tmpFile) == -1)
{