aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2023-07-30 20:39:54 +0200
committerRobin Jarry <robin@jarry.cc>2023-08-02 19:26:24 +0200
commite32cf3d478e746e1a83a1289e47399087fe1fda3 (patch)
tree2906b3c3373a69f6dadd41e23bd04ec989853006
parentdc5c35758dca410350ff36f9bf5c9090236e6043 (diff)
downloadaerc-e32cf3d478e746e1a83a1289e47399087fe1fda3.tar.gz
composer: reopen the email file when editor is closed
Some text editors save changes in a temp file before moving it over to the destination instead of rewriting the original file in place. This protects against all sorts of corruption, like writing partial files in disk-full scenarios for example. When this is the case, aerc's open file descriptor points to a different file, one that was never written to by the editor. For text editors that *do* overwrite the file (i.e. the same filesystem inode id), depending on timing, kernel version, operating system, filesystem options, caching, etc. Reading from aerc's file descriptor after the text editor has modified the same file on disk may not return the updated data. This behaviour can be easily observed with the following C program: #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(void) { int fd; ssize_t n; char buf[BUFSIZ]; unlink("test.txt"); fd = open("test.txt", O_CREAT|O_RDWR, 0644); write(fd, "Bonjour\n", strlen("Bonjour\n")); fsync(fd); lseek(fd, 0, SEEK_SET); n = read(fd, buf, sizeof(buf)); buf[n] = '\0'; printf("before: %s\n", buf); system("vim test.txt"); lseek(fd, 0, SEEK_SET); n = read(fd, buf, sizeof(buf)); buf[n] = '\0'; printf("after: %s\n", buf); return 0; } Most of the time, before and after texts will be identical even if you modified the test.txt file contents in the editor. The only reliable way to ensure to read the latest contents from the disk is to close and reopen the file after the text editor has exited. Do exactly that in termClosed before anything else. Fixes: 801caf812377 ("compose: factorize body read in a method") Fixes: https://todo.sr.ht/~rjarry/aerc/184 Signed-off-by: Robin Jarry <robin@jarry.cc> Tested-by: Ronald Evers <ronald@ch10.nl>
-rw-r--r--widgets/compose.go14
1 files changed, 14 insertions, 0 deletions
diff --git a/widgets/compose.go b/widgets/compose.go
index fa88b0f0..281a35cc 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -1144,12 +1144,26 @@ func (c *Composer) termEvent(event tcell.Event) bool {
return false
}
+func (c *Composer) reopenEmailFile() error {
+ name := c.email.Name()
+ f, err := os.OpenFile(name, os.O_RDWR, 0o600)
+ if err != nil {
+ return err
+ }
+ err = c.email.Close()
+ c.email = f
+ return err
+}
+
func (c *Composer) termClosed(err error) {
c.Lock()
defer c.Unlock()
if c.editor == nil {
return
}
+ if e := c.reopenEmailFile(); e != nil {
+ c.aerc.PushError("Failed to reopen email file: " + e.Error())
+ }
editor := c.editor
defer editor.Destroy()
c.editor = nil