summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2024-02-13 09:24:43 -0500
committerpommicket <pommicket@gmail.com>2024-02-13 09:24:43 -0500
commit6d85a7e605011d16018fdf202e40e59e2f916b63 (patch)
tree8ce9a180f24b0b2d5465475b199713589f96b68b
parent12622d7fd3f19936f2d235d8965491eb2adbae80 (diff)
better backups!
- they are now created with 600 permissions on unix to prevent leaking file contents to other users - they use the ~ suffix used by emacs etc for better compatibility with gitignores, etc. - we now call fdatasync / _commit to ensure that even if power goes out halfway through saving, no data can be lost
-rw-r--r--buffer.c29
1 files changed, 26 insertions, 3 deletions
diff --git a/buffer.c b/buffer.c
index 497afa8..9ef2467 100644
--- a/buffer.c
+++ b/buffer.c
@@ -6,6 +6,11 @@
#include <sys/stat.h>
+#if __unix__
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+
/// A single line in a buffer
typedef struct Line Line;
@@ -3196,6 +3201,13 @@ static bool buffer_write_to_file(TextBuffer *buffer, const char *path) {
buffer_error(buffer, "Couldn't write to %s.", path);
success = false;
}
+ fflush(out);
+ // make sure data is on disk before returning from this function
+ #if __unix__
+ fdatasync(fileno(out));
+ #elif _WIN32
+ _commit(_fileno(out));
+ #endif
if (fclose(out) != 0) {
if (!buffer_has_error(buffer))
buffer_error(buffer, "Couldn't close file %s.", path);
@@ -3221,19 +3233,30 @@ bool buffer_save(TextBuffer *buffer) {
*backup_path = '\0';
if (settings->save_backup) {
- strbuf_printf(backup_path, "%s.ted-bk", buffer->path);
- if (strlen(backup_path) != strlen(buffer->path) + 7) {
+ strbuf_printf(backup_path, "%s~", buffer->path);
+ if (strlen(backup_path) < strlen(buffer->path) + 1) {
buffer_error(buffer, "File name too long.");
return false;
}
-
+ #if __unix__
+ // create backup file with 600 permissions so we don't leak file data to other users.
+ int fd = open(backup_path, O_CREAT|O_TRUNC|O_WRONLY, 0600);
+ if (fd == -1) {
+ buffer_error(buffer, "Error creating %s: %s",
+ backup_path, strerror(errno));
+ return false;
+ }
+ close(fd);
+ #endif
}
bool success = true;
+ // first save backup so if writing main file fails halfway through user's data won't be lost
if (*backup_path)
success &= buffer_write_to_file(buffer, backup_path);
if (success)
success &= buffer_write_to_file(buffer, buffer->path);
+ // now if writing the main file succeeded we can safely delete the backup
if (success && *backup_path)
remove(backup_path);
buffer->last_write_time = timespec_to_seconds(time_last_modified(buffer->path));