vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
commit fcd06b3d31af9c149dc8b73efe12d1efee0b0179 parent 3ba0be3dd450744b6b9513603c9bd3d774fa67b4 Author: Marc André Tanner <mat@brain-dump.org> Date: Wed, 25 Apr 2018 19:22:18 +0200 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:
| M | text.c | | | 31 | +++++++++++++++++++++++++------ |
| M | text.h | | | 7 | ++++--- |
2 files changed, 29 insertions(+), 9 deletions(-)
diff --git 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 */ diff --git a/text.h b/text.h @@ -280,9 +280,10 @@ enum TextSaveMethod { /** * Save file atomically using `rename(2)`. * - * Creates a new file named `filename~` and tries to restore all important - * meta data. After which it is atomically moved to its final - * (possibly already existing) destination using `rename(2)`. + * Creates a temporary file, restores all important meta data, + * before moving it atomically to its final (possibly already + * existing) destination using `rename(2)`. For new files, + * permissions are set to `0666 & ~umask`. * * @rst * .. warning:: This approach does not work if: