github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/persist/persist.go (about) 1 package persist 2 3 import ( 4 "crypto/rand" 5 "encoding/base32" 6 "errors" 7 "os" 8 "path/filepath" 9 ) 10 11 const ( 12 // persistDir defines the folder that is used for testing the persist 13 // package. 14 persistDir = "persist" 15 ) 16 17 var ( 18 // ErrBadVersion indicates that the version number of the file is not 19 // compatible with the current codebase. 20 ErrBadVersion = errors.New("incompatible version") 21 22 // ErrBadHeader indicates that the file opened is not the file that was 23 // expected. 24 ErrBadHeader = errors.New("wrong header") 25 ) 26 27 // Metadata contains the header and version of the data being stored. 28 type Metadata struct { 29 Header, Version string 30 } 31 32 // RandomSuffix returns a 20 character base32 suffix for a filename. There are 33 // 100 bits of entropy, and a very low probability of colliding with existing 34 // files unintentionally. 35 func RandomSuffix() string { 36 randBytes := make([]byte, 20) 37 rand.Read(randBytes) 38 str := base32.StdEncoding.EncodeToString(randBytes) 39 return str[:20] 40 } 41 42 // A safeFile is a file that is stored under a temporary filename. When Commit 43 // is called, the file is renamed to its "final" filename. This allows for 44 // atomic updating of files; otherwise, an unexpected shutdown could leave a 45 // valuable file in a corrupted state. Callers must still Close the file handle 46 // as usual. 47 type safeFile struct { 48 *os.File 49 finalName string 50 } 51 52 // Commit closes the file, and then renames it to the intended final filename. 53 // Commit should not be called from a defer if the function it is being called 54 // from can return an error. 55 func (sf *safeFile) Commit() error { 56 if err := sf.Close(); err != nil { 57 return err 58 } 59 return os.Rename(sf.finalName+"_temp", sf.finalName) 60 } 61 62 // CommitSync syncs the file, closes it, and then renames it to the intended 63 // final filename. CommitSync should not be called from a defer if the 64 // function it is being called from can return an error. 65 func (sf *safeFile) CommitSync() error { 66 if err := sf.Sync(); err != nil { 67 return err 68 } 69 return sf.Commit() 70 } 71 72 // NewSafeFile returns a file that can atomically be written to disk, 73 // minimizing the risk of corruption. 74 func NewSafeFile(filename string) (*safeFile, error) { 75 file, err := os.Create(filename + "_temp") 76 if err != nil { 77 return nil, err 78 } 79 80 // Get the absolute path of the filename so that calling os.Chdir in 81 // between calling NewSafeFile and calling safeFile.Commit does not change 82 // the final file path. 83 absFilename, err := filepath.Abs(filename) 84 if err != nil { 85 return nil, err 86 } 87 88 return &safeFile{file, absFilename}, nil 89 }