save: Use dd with the notrunc & fsync and postpone truncation#3814
save: Use dd with the notrunc & fsync and postpone truncation#3814JoeKar merged 2 commits intomicro-editor:masterfrom
dd with the notrunc & fsync and postpone truncation#3814Conversation
|
Erm... Try opening a file which is bigger than 4K, delete all the text in it and replace it with a single line saying: EDIT: not even necessarily bigger than 4K, just any file bigger than this single line. |
Holy, I knew that I had forgotten something (just used a smaller one for testing). 🤦♂️ We still could use |
dd with the notrunc and fsync optiondd with the notrunc option
dd with the notrunc optiondd with the notrunc & fsync option and truncate as command
internal/buffer/save.go
Outdated
| if wf.withSudo { | ||
| // we don't need to stop the screen here, since it is still stopped | ||
| // by openFile() | ||
| cmd := exec.Command(config.GlobalSettings["sucmd"].(string), "truncate", "-s", strconv.FormatInt(size, 10), wf.name) |
There was a problem hiding this comment.
I checked all Unix platforms in cross-compile.sh and only 3 platforms don't have truncate as a command, which are Illumos, NetBSD, and OpenBSD.
If we will replace the truncate command, dd count=0 of=path may work on all such platforms but could only truncate to 0 bytes. There might be a difference in behavior, since the command may open the file with O_TRUNC (and closes it after) instead of just using the truncate system call, but I doubt it would be different.
There was a problem hiding this comment.
Thanks a lot for testing this on other platforms! 👍
So exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "count=0", "if=/dev/zero", "of="+wf.name) indeed looks like the better option then.
Great to have such a fragmentation with the coreutils too.
There was a problem hiding this comment.
Great to have such a fragmentation with the coreutils too.
Maybe not really, since it's hard to ensure compatibility of shell scripts without reading the documentation of each platform.
I currently find POSIX 2017 (command list) to be most reliable when strictly writing portable commands, but it takes a while to read and understand.
dd with the notrunc & fsync option and truncate as commanddd with the notrunc & fsync and postpone truncation
internal/buffer/save.go
Outdated
| cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "of="+name) | ||
| cmd = exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "conv=notrunc,fsync", "of="+name) |
There was a problem hiding this comment.
Damn...
Maybe run
ddwithoutconv=fsyncon both platforms and add a TODO comment?
Looks like the only option right now. I've to better idea so far. 🤔
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.
This will stop the overall truncation of the target file.
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.