github.com/gernest/nezuko@v0.1.2/internal/renameio/renameio.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package renameio writes files atomically by renaming temporary files. 6 package renameio 7 8 import ( 9 "bytes" 10 "io" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 ) 15 16 const patternSuffix = "*.tmp" 17 18 // Pattern returns a glob pattern that matches the unrenamed temporary files 19 // created when writing to filename. 20 func Pattern(filename string) string { 21 return filepath.Join(filepath.Dir(filename), filepath.Base(filename)+patternSuffix) 22 } 23 24 // WriteFile is like ioutil.WriteFile, but first writes data to an arbitrary 25 // file in the same directory as filename, then renames it atomically to the 26 // final name. 27 // 28 // That ensures that the final location, if it exists, is always a complete file. 29 func WriteFile(filename string, data []byte) (err error) { 30 return WriteToFile(filename, bytes.NewReader(data)) 31 } 32 33 // WriteToFile is a variant of WriteFile that accepts the data as an io.Reader 34 // instead of a slice. 35 func WriteToFile(filename string, data io.Reader) (err error) { 36 f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)+patternSuffix) 37 if err != nil { 38 return err 39 } 40 defer func() { 41 // Only call os.Remove on f.Name() if we failed to rename it: otherwise, 42 // some other process may have created a new file with the same name after 43 // that. 44 if err != nil { 45 f.Close() 46 os.Remove(f.Name()) 47 } 48 }() 49 50 if _, err := io.Copy(f, data); err != nil { 51 return err 52 } 53 // Sync the file before renaming it: otherwise, after a crash the reader may 54 // observe a 0-length file instead of the actual contents. 55 // See https://golang.org/issue/22397#issuecomment-380831736. 56 if err := f.Sync(); err != nil { 57 return err 58 } 59 if err := f.Close(); err != nil { 60 return err 61 } 62 return os.Rename(f.Name(), filename) 63 }