github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/pkg/ioutils/fswriters.go (about) 1 package ioutils // import "github.com/Prakhar-Agarwal-byte/moby/pkg/ioutils" 2 3 import ( 4 "io" 5 "os" 6 "path/filepath" 7 ) 8 9 // NewAtomicFileWriter returns WriteCloser so that writing to it writes to a 10 // temporary file and closing it atomically changes the temporary file to 11 // destination path. Writing and closing concurrently is not allowed. 12 func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) { 13 f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) 14 if err != nil { 15 return nil, err 16 } 17 18 abspath, err := filepath.Abs(filename) 19 if err != nil { 20 return nil, err 21 } 22 return &atomicFileWriter{ 23 f: f, 24 fn: abspath, 25 perm: perm, 26 }, nil 27 } 28 29 // AtomicWriteFile atomically writes data to a file named by filename. 30 func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { 31 f, err := NewAtomicFileWriter(filename, perm) 32 if err != nil { 33 return err 34 } 35 n, err := f.Write(data) 36 if err == nil && n < len(data) { 37 err = io.ErrShortWrite 38 f.(*atomicFileWriter).writeErr = err 39 } 40 if err1 := f.Close(); err == nil { 41 err = err1 42 } 43 return err 44 } 45 46 type atomicFileWriter struct { 47 f *os.File 48 fn string 49 writeErr error 50 perm os.FileMode 51 } 52 53 func (w *atomicFileWriter) Write(dt []byte) (int, error) { 54 n, err := w.f.Write(dt) 55 if err != nil { 56 w.writeErr = err 57 } 58 return n, err 59 } 60 61 func (w *atomicFileWriter) Close() (retErr error) { 62 defer func() { 63 if retErr != nil || w.writeErr != nil { 64 os.Remove(w.f.Name()) 65 } 66 }() 67 if err := w.f.Sync(); err != nil { 68 w.f.Close() 69 return err 70 } 71 if err := w.f.Close(); err != nil { 72 return err 73 } 74 if err := os.Chmod(w.f.Name(), w.perm); err != nil { 75 return err 76 } 77 if w.writeErr == nil { 78 return os.Rename(w.f.Name(), w.fn) 79 } 80 return nil 81 } 82 83 // AtomicWriteSet is used to atomically write a set 84 // of files and ensure they are visible at the same time. 85 // Must be committed to a new directory. 86 type AtomicWriteSet struct { 87 root string 88 } 89 90 // NewAtomicWriteSet creates a new atomic write set to 91 // atomically create a set of files. The given directory 92 // is used as the base directory for storing files before 93 // commit. If no temporary directory is given the system 94 // default is used. 95 func NewAtomicWriteSet(tmpDir string) (*AtomicWriteSet, error) { 96 td, err := os.MkdirTemp(tmpDir, "write-set-") 97 if err != nil { 98 return nil, err 99 } 100 101 return &AtomicWriteSet{ 102 root: td, 103 }, nil 104 } 105 106 // WriteFile writes a file to the set, guaranteeing the file 107 // has been synced. 108 func (ws *AtomicWriteSet) WriteFile(filename string, data []byte, perm os.FileMode) error { 109 f, err := ws.FileWriter(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) 110 if err != nil { 111 return err 112 } 113 n, err := f.Write(data) 114 if err == nil && n < len(data) { 115 err = io.ErrShortWrite 116 } 117 if err1 := f.Close(); err == nil { 118 err = err1 119 } 120 return err 121 } 122 123 type syncFileCloser struct { 124 *os.File 125 } 126 127 func (w syncFileCloser) Close() error { 128 err := w.File.Sync() 129 if err1 := w.File.Close(); err == nil { 130 err = err1 131 } 132 return err 133 } 134 135 // FileWriter opens a file writer inside the set. The file 136 // should be synced and closed before calling commit. 137 func (ws *AtomicWriteSet) FileWriter(name string, flag int, perm os.FileMode) (io.WriteCloser, error) { 138 f, err := os.OpenFile(filepath.Join(ws.root, name), flag, perm) 139 if err != nil { 140 return nil, err 141 } 142 return syncFileCloser{f}, nil 143 } 144 145 // Cancel cancels the set and removes all temporary data 146 // created in the set. 147 func (ws *AtomicWriteSet) Cancel() error { 148 return os.RemoveAll(ws.root) 149 } 150 151 // Commit moves all created files to the target directory. The 152 // target directory must not exist and the parent of the target 153 // directory must exist. 154 func (ws *AtomicWriteSet) Commit(target string) error { 155 return os.Rename(ws.root, target) 156 } 157 158 // String returns the location the set is writing to. 159 func (ws *AtomicWriteSet) String() string { 160 return ws.root 161 }