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