@@ -25,7 +25,6 @@ import (
2525const LargeFileThreshold = 50000
2626
2727type wrappedFile struct {
28- name string
2928 writeCloser io.WriteCloser
3029 withSudo bool
3130 screenb bool
@@ -82,13 +81,13 @@ func openFile(name string, withSudo bool) (wrappedFile, error) {
8281 var sigChan chan os.Signal
8382
8483 if withSudo {
85- conv := "notrunc"
84+ args := [] string { "dd" , "bs=4k" , "of=" + name }
8685 // TODO: both platforms do not support dd with conv=fsync yet
8786 if ! (runtime .GOOS == "illumos" || runtime .GOOS == "netbsd" ) {
88- conv += ", fsync"
87+ args = append ( args , "conv= fsync")
8988 }
9089
91- cmd = exec .Command (config .GlobalSettings ["sucmd" ].(string ), "dd" , "bs=4k" , "conv=" + conv , "of=" + name )
90+ cmd = exec .Command (config .GlobalSettings ["sucmd" ].(string ), args ... )
9291 writeCloser , err = cmd .StdinPipe ()
9392 if err != nil {
9493 return wrappedFile {}, err
@@ -99,16 +98,16 @@ func openFile(name string, withSudo bool) (wrappedFile, error) {
9998 signal .Notify (sigChan , os .Interrupt )
10099
101100 screenb = screen .TempFini ()
101+
102102 // need to start the process now, otherwise when we flush the file
103103 // contents to its stdin it might hang because the kernel's pipe size
104104 // is too small to handle the full file contents all at once
105105 err = cmd .Start ()
106106 if err != nil {
107+ writeCloser .Close ()
107108 screen .TempStart (screenb )
108-
109109 signal .Notify (util .Sigterm , os .Interrupt )
110110 signal .Stop (sigChan )
111-
112111 return wrappedFile {}, err
113112 }
114113 } else {
@@ -118,18 +117,7 @@ func openFile(name string, withSudo bool) (wrappedFile, error) {
118117 }
119118 }
120119
121- return wrappedFile {name , writeCloser , withSudo , screenb , cmd , sigChan }, nil
122- }
123-
124- func (wf wrappedFile ) Truncate () error {
125- if wf .withSudo {
126- // we don't need to stop the screen here, since it is still stopped
127- // by openFile()
128- // truncate might not be available on every platfom, so use dd instead
129- cmd := exec .Command (config .GlobalSettings ["sucmd" ].(string ), "dd" , "count=0" , "of=" + wf .name )
130- return cmd .Run ()
131- }
132- return wf .writeCloser .(* os.File ).Truncate (0 )
120+ return wrappedFile {writeCloser , withSudo , screenb , cmd , sigChan }, nil
133121}
134122
135123func (wf wrappedFile ) Write (b * SharedBuffer ) (int , error ) {
@@ -150,9 +138,12 @@ func (wf wrappedFile) Write(b *SharedBuffer) (int, error) {
150138 eol = []byte {'\n' }
151139 }
152140
153- err := wf .Truncate ()
154- if err != nil {
155- return 0 , err
141+ if ! wf .withSudo {
142+ f := wf .writeCloser .(* os.File )
143+ err := f .Truncate (0 )
144+ if err != nil {
145+ return 0 , err
146+ }
156147 }
157148
158149 // write lines
@@ -247,6 +238,13 @@ func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error
247238 return errors .New ("Cannot save scratch buffer" )
248239 }
249240
241+ if withSudo {
242+ _ , err := exec .LookPath (config .GlobalSettings ["sucmd" ].(string ))
243+ if err != nil {
244+ return err
245+ }
246+ }
247+
250248 if ! autoSave && b .Settings ["rmtrailingws" ].(bool ) {
251249 for i , l := range b .lines {
252250 leftover := util .CharacterCount (bytes .TrimRightFunc (l .data , unicode .IsSpace ))
@@ -354,26 +352,44 @@ func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error
354352// This means that the file is not overwritten directly but by writing to the
355353// backup file first.
356354func (b * SharedBuffer ) safeWrite (path string , withSudo bool , newFile bool ) (int , error ) {
357- file , err := openFile (path , withSudo )
358- if err != nil {
359- return 0 , err
360- }
355+ var err error
361356
362357 defer func () {
363358 if newFile && err != nil {
364359 os .Remove (path )
365360 }
366361 }()
367362
368- // Try to backup first before writing
369- backupName , resolveName , err := b .writeBackup (path )
363+ // When using sudo, create the backup before opening the file because
364+ // openFile() truncates the target immediately. For non-sudo saves,
365+ // openFile() does not truncate on open, i.e. open is non-destructive,
366+ // so only create the backup after open succeeds.
367+ var backupName , resolveName string
368+ if withSudo {
369+ backupName , resolveName , err = b .writeBackup (path )
370+ if err != nil {
371+ return 0 , err
372+ }
373+ delete (requestedBackups , b )
374+ }
375+
376+ file , err := openFile (path , withSudo )
370377 if err != nil {
371- file .Close ()
378+ if withSudo {
379+ return 0 , util.OverwriteError {err , backupName }
380+ }
372381 return 0 , err
373382 }
374383
375- // Backup saved, so cancel pending periodic backup, if any
376- delete (requestedBackups , b )
384+ if ! withSudo {
385+ backupName , resolveName , err = b .writeBackup (path )
386+ if err != nil {
387+ file .Close ()
388+ return 0 , err
389+ }
390+ // Backup saved, so cancel pending periodic backup, if any
391+ delete (requestedBackups , b )
392+ }
377393
378394 b .forceKeepBackup = true
379395 size := 0
0 commit comments