package common import ( "io" "io/ioutil" "os" "path/filepath" ) // WriteFileAtomic creates a temporary file with data and the perm given and // swaps it atomically with filename if successful. func WriteFileAtomic(filename string, data []byte, perm os.FileMode) error { var ( dir = filepath.Dir(filename) // Create in case it doesn't exist and force kernel // flush, which still leaves the potential of lingering disk cache. // Never overwrites files flag = os.O_WRONLY | os.O_CREATE | os.O_SYNC | os.O_TRUNC | os.O_EXCL ) tempFile := filepath.Join(dir, "write-file-atomic-"+RandStr(32)) f, err := os.OpenFile(tempFile, flag, perm) if err != nil { return err } // Clean up in any case. Defer stacking order is last-in-first-out. defer os.Remove(f.Name()) defer f.Close() if n, err := f.Write(data); err != nil { return err } else if n < len(data) { return io.ErrShortWrite } // Close the file before renaming it, otherwise it will cause "The process // cannot access the file because it is being used by another process." on windows. f.Close() return os.Rename(f.Name(), filename) } //-------------------------------------------------------------------------------- func Tempfile(prefix string) (*os.File, string) { file, err := ioutil.TempFile("", prefix) if err != nil { PanicCrisis(err) } return file, file.Name() } func Tempdir(prefix string) (*os.File, string) { tempDir := os.TempDir() + "/" + prefix + RandStr(12) err := EnsureDir(tempDir, 0700) if err != nil { panic(Fmt("Error creating temp dir: %v", err)) } dir, err := os.Open(tempDir) if err != nil { panic(Fmt("Error opening temp dir: %v", err)) } return dir, tempDir }