github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/atomic.go (about) 1 package filesystem 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 ) 8 9 const ( 10 // atomicWriteTemporaryNamePrefix is the file name prefix to use for 11 // intermediate temporary files used in atomic writes. 12 atomicWriteTemporaryNamePrefix = TemporaryNamePrefix + "atomic-write" 13 ) 14 15 // WriteFileAtomic writes a file to disk in an atomic fashion by using an 16 // intermediate temporary file that is swapped in place using a rename 17 // operation. 18 func WriteFileAtomic(path string, data []byte, permissions os.FileMode) error { 19 // Create a temporary file. The os package already uses secure permissions 20 // for creating temporary files, so we don't need to change them. 21 temporary, err := os.CreateTemp(filepath.Dir(path), atomicWriteTemporaryNamePrefix) 22 if err != nil { 23 return fmt.Errorf("unable to create temporary file: %w", err) 24 } 25 26 // Write data. 27 if _, err = temporary.Write(data); err != nil { 28 temporary.Close() 29 os.Remove(temporary.Name()) 30 return fmt.Errorf("unable to write data to temporary file: %w", err) 31 } 32 33 // Close out the file. 34 if err = temporary.Close(); err != nil { 35 os.Remove(temporary.Name()) 36 return fmt.Errorf("unable to close temporary file: %w", err) 37 } 38 39 // Set the file's permissions. 40 if err = os.Chmod(temporary.Name(), permissions); err != nil { 41 os.Remove(temporary.Name()) 42 return fmt.Errorf("unable to change file permissions: %w", err) 43 } 44 45 // Rename the file. 46 if err = Rename(nil, temporary.Name(), nil, path, true); err != nil { 47 os.Remove(temporary.Name()) 48 return fmt.Errorf("unable to rename file: %w", err) 49 } 50 51 // Success. 52 return nil 53 }