aboutsummaryrefslogtreecommitdiff
path: root/text.c
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2018-04-25 19:22:18 +0200
committerMarc André Tanner <mat@brain-dump.org>2018-05-16 13:24:01 +0200
commit5b609d292642a9146a64f60ff75e3bdc44c8d14a (patch)
treeee978ab2ac0682ef861a3bbb046a5c5c272dabd8 /text.c
parentb23116112e21fbdecc51e2029bfe01c0f19b5267 (diff)
downloadvis-5b609d292642a9146a64f60ff75e3bdc44c8d14a.tar.gz
vis-5b609d292642a9146a64f60ff75e3bdc44c8d14a.tar.xz
text: use mkstemp(3) for temporary file creation in atomic saves
Instead of simply appending a tilde to the original file name, we now create an unique temporary file based on the pattern `.filename.vis.XXXXXX`. In case the file does not yet exist, we use 0666 & ~umask as permission, (this should match the previous `open(2)` based behavior).
Diffstat (limited to 'text.c')
-rw-r--r--text.c31
1 files changed, 25 insertions, 6 deletions
diff --git a/text.c b/text.c
index 9d24e5a..5d5f814 100644
--- a/text.c
+++ b/text.c
@@ -815,7 +815,8 @@ static bool preserve_selinux_context(int src, int dest) {
return true;
}
-/* Create a new file named `filename~` and try to preserve all important
+/* Create a new file named `.filename.vis.XXXXXX` (where `XXXXXX` is a
+ * randomly generated, unique suffix) and try to preserve all important
* meta data. After the file content has been written to this temporary
* file, text_save_commit_atomic will atomically move it to its final
* (possibly already existing) destination using rename(2).
@@ -844,14 +845,32 @@ static bool text_save_begin_atomic(TextSave *ctx) {
goto err;
}
- size_t namelen = strlen(ctx->filename) + 1 /* ~ */ + 1 /* \0 */;
- if (!(ctx->tmpname = calloc(1, namelen)))
+ char suffix[] = ".vis.XXXXXX";
+ size_t len = strlen(ctx->filename) + sizeof("./.") + sizeof(suffix);
+ char *dir = strdup(ctx->filename);
+ char *base = strdup(ctx->filename);
+
+ if (!(ctx->tmpname = malloc(len)) || !dir || !base) {
+ free(dir);
+ free(base);
goto err;
- snprintf(ctx->tmpname, namelen, "%s~", ctx->filename);
+ }
- if ((ctx->fd = open(ctx->tmpname, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC, oldfd == -1 ? 0666 : oldmeta.st_mode)) == -1)
+ snprintf(ctx->tmpname, len, "%s/.%s%s", dirname(dir), basename(base), suffix);
+ free(dir);
+ free(base);
+
+ if ((ctx->fd = mkstemp(ctx->tmpname)) == -1)
goto err;
- if (oldfd != -1) {
+
+ if (oldfd == -1) {
+ mode_t mask = umask(0);
+ umask(mask);
+ if (fchmod(ctx->fd, 0666 & ~mask) == -1)
+ goto err;
+ } else {
+ if (fchmod(ctx->fd, oldmeta.st_mode) == -1)
+ goto err;
if (!preserve_acl(oldfd, ctx->fd) || !preserve_selinux_context(oldfd, ctx->fd))
goto err;
/* change owner if necessary */