From 165a5a48963c216ac72bfb62cdf274d9ff287626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6ran=20Karl?= <3951388+JoeKar@users.noreply.github.com> Date: Tue, 22 Jul 2025 19:00:22 +0200 Subject: [PATCH] save: Use `dd` with the `notrunc` & `fsync` option Using notrunc will stop the overall truncation of the target file done by sudo. We need to do this because dd, like other coreutils, already truncates the file on open(). In case we can't store the backup file afterwards we would end up in a truncated file for which the user has no write permission by default. Instead we use a second call of `dd` to perform the necessary truncation on the command line. With the fsync option we force the dd process to synchronize the written file to the underlying device. --- internal/buffer/save.go | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/internal/buffer/save.go b/internal/buffer/save.go index 2336f9aa..349d4c66 100644 --- a/internal/buffer/save.go +++ b/internal/buffer/save.go @@ -26,6 +26,7 @@ import ( const LargeFileThreshold = 50000 type wrappedFile struct { + name string writeCloser io.WriteCloser withSudo bool screenb bool @@ -83,7 +84,13 @@ func openFile(name string, withSudo bool) (wrappedFile, error) { var sigChan chan os.Signal if withSudo { - cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "of="+name) + conv := "notrunc" + // TODO: both platforms do not support dd with conv=fsync yet + if !(runtime.GOOS == "illumos" || runtime.GOOS == "netbsd") { + conv += ",fsync" + } + + cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "conv="+conv, "of="+name) writeCloser, err = cmd.StdinPipe() if err != nil { return wrappedFile{}, err @@ -113,7 +120,18 @@ func openFile(name string, withSudo bool) (wrappedFile, error) { } } - return wrappedFile{writeCloser, withSudo, screenb, cmd, sigChan}, nil + return wrappedFile{name, writeCloser, withSudo, screenb, cmd, sigChan}, nil +} + +func (wf wrappedFile) Truncate() error { + if wf.withSudo { + // we don't need to stop the screen here, since it is still stopped + // by openFile() + // truncate might not be available on every platfom, so use dd instead + cmd := exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "count=0", "of="+wf.name) + return cmd.Run() + } + return wf.writeCloser.(*os.File).Truncate(0) } func (wf wrappedFile) Write(b *Buffer) (int, error) { @@ -134,12 +152,9 @@ func (wf wrappedFile) Write(b *Buffer) (int, error) { eol = []byte{'\n'} } - if !wf.withSudo { - f := wf.writeCloser.(*os.File) - err := f.Truncate(0) - if err != nil { - return 0, err - } + err := wf.Truncate() + if err != nil { + return 0, err } // write lines