Skip to content

Commit 165a5a4

Browse files
committed
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.
1 parent 87ce573 commit 165a5a4

File tree

1 file changed

+23
-8
lines changed

1 file changed

+23
-8
lines changed

internal/buffer/save.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
const LargeFileThreshold = 50000
2727

2828
type wrappedFile struct {
29+
name string
2930
writeCloser io.WriteCloser
3031
withSudo bool
3132
screenb bool
@@ -83,7 +84,13 @@ func openFile(name string, withSudo bool) (wrappedFile, error) {
8384
var sigChan chan os.Signal
8485

8586
if withSudo {
86-
cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "of="+name)
87+
conv := "notrunc"
88+
// TODO: both platforms do not support dd with conv=fsync yet
89+
if !(runtime.GOOS == "illumos" || runtime.GOOS == "netbsd") {
90+
conv += ",fsync"
91+
}
92+
93+
cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "conv="+conv, "of="+name)
8794
writeCloser, err = cmd.StdinPipe()
8895
if err != nil {
8996
return wrappedFile{}, err
@@ -113,7 +120,18 @@ func openFile(name string, withSudo bool) (wrappedFile, error) {
113120
}
114121
}
115122

116-
return wrappedFile{writeCloser, withSudo, screenb, cmd, sigChan}, nil
123+
return wrappedFile{name, writeCloser, withSudo, screenb, cmd, sigChan}, nil
124+
}
125+
126+
func (wf wrappedFile) Truncate() error {
127+
if wf.withSudo {
128+
// we don't need to stop the screen here, since it is still stopped
129+
// by openFile()
130+
// truncate might not be available on every platfom, so use dd instead
131+
cmd := exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "count=0", "of="+wf.name)
132+
return cmd.Run()
133+
}
134+
return wf.writeCloser.(*os.File).Truncate(0)
117135
}
118136

119137
func (wf wrappedFile) Write(b *Buffer) (int, error) {
@@ -134,12 +152,9 @@ func (wf wrappedFile) Write(b *Buffer) (int, error) {
134152
eol = []byte{'\n'}
135153
}
136154

137-
if !wf.withSudo {
138-
f := wf.writeCloser.(*os.File)
139-
err := f.Truncate(0)
140-
if err != nil {
141-
return 0, err
142-
}
155+
err := wf.Truncate()
156+
if err != nil {
157+
return 0, err
143158
}
144159

145160
// write lines

0 commit comments

Comments
 (0)