github.com/MRtecno98/afero@v1.9.3/unionFile.go (about) 1 package afero 2 3 import ( 4 "io" 5 "os" 6 "path/filepath" 7 "syscall" 8 ) 9 10 // The UnionFile implements the afero.File interface and will be returned 11 // when reading a directory present at least in the overlay or opening a file 12 // for writing. 13 // 14 // The calls to 15 // Readdir() and Readdirnames() merge the file os.FileInfo / names from the 16 // base and the overlay - for files present in both layers, only those 17 // from the overlay will be used. 18 // 19 // When opening files for writing (Create() / OpenFile() with the right flags) 20 // the operations will be done in both layers, starting with the overlay. A 21 // successful read in the overlay will move the cursor position in the base layer 22 // by the number of bytes read. 23 type UnionFile struct { 24 Base File 25 Layer File 26 Merger DirsMerger 27 off int 28 files []os.FileInfo 29 } 30 31 func (f *UnionFile) Close() error { 32 // first close base, so we have a newer timestamp in the overlay. If we'd close 33 // the overlay first, we'd get a cacheStale the next time we access this file 34 // -> cache would be useless ;-) 35 if f.Base != nil { 36 f.Base.Close() 37 } 38 if f.Layer != nil { 39 return f.Layer.Close() 40 } 41 return BADFD 42 } 43 44 func (f *UnionFile) Read(s []byte) (int, error) { 45 if f.Layer != nil { 46 n, err := f.Layer.Read(s) 47 if (err == nil || err == io.EOF) && f.Base != nil { 48 // advance the file position also in the base file, the next 49 // call may be a write at this position (or a seek with SEEK_CUR) 50 if _, seekErr := f.Base.Seek(int64(n), os.SEEK_CUR); seekErr != nil { 51 // only overwrite err in case the seek fails: we need to 52 // report an eventual io.EOF to the caller 53 err = seekErr 54 } 55 } 56 return n, err 57 } 58 if f.Base != nil { 59 return f.Base.Read(s) 60 } 61 return 0, BADFD 62 } 63 64 func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) { 65 if f.Layer != nil { 66 n, err := f.Layer.ReadAt(s, o) 67 if (err == nil || err == io.EOF) && f.Base != nil { 68 _, err = f.Base.Seek(o+int64(n), io.SeekStart) 69 } 70 return n, err 71 } 72 if f.Base != nil { 73 return f.Base.ReadAt(s, o) 74 } 75 return 0, BADFD 76 } 77 78 func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) { 79 if f.Layer != nil { 80 pos, err = f.Layer.Seek(o, w) 81 if (err == nil || err == io.EOF) && f.Base != nil { 82 _, err = f.Base.Seek(o, w) 83 } 84 return pos, err 85 } 86 if f.Base != nil { 87 return f.Base.Seek(o, w) 88 } 89 return 0, BADFD 90 } 91 92 func (f *UnionFile) Write(s []byte) (n int, err error) { 93 if f.Layer != nil { 94 n, err = f.Layer.Write(s) 95 if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark? 96 _, err = f.Base.Write(s) 97 } 98 return n, err 99 } 100 if f.Base != nil { 101 return f.Base.Write(s) 102 } 103 return 0, BADFD 104 } 105 106 func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) { 107 if f.Layer != nil { 108 n, err = f.Layer.WriteAt(s, o) 109 if err == nil && f.Base != nil { 110 _, err = f.Base.WriteAt(s, o) 111 } 112 return n, err 113 } 114 if f.Base != nil { 115 return f.Base.WriteAt(s, o) 116 } 117 return 0, BADFD 118 } 119 120 func (f *UnionFile) Name() string { 121 if f.Layer != nil { 122 return f.Layer.Name() 123 } 124 return f.Base.Name() 125 } 126 127 // DirsMerger is how UnionFile weaves two directories together. 128 // It takes the FileInfo slices from the layer and the base and returns a 129 // single view. 130 type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) 131 132 var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) { 133 var files = make(map[string]os.FileInfo) 134 135 for _, fi := range lofi { 136 files[fi.Name()] = fi 137 } 138 139 for _, fi := range bofi { 140 if _, exists := files[fi.Name()]; !exists { 141 files[fi.Name()] = fi 142 } 143 } 144 145 rfi := make([]os.FileInfo, len(files)) 146 147 i := 0 148 for _, fi := range files { 149 rfi[i] = fi 150 i++ 151 } 152 153 return rfi, nil 154 155 } 156 157 // Readdir will weave the two directories together and 158 // return a single view of the overlayed directories. 159 // At the end of the directory view, the error is io.EOF if c > 0. 160 func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) { 161 var merge DirsMerger = f.Merger 162 if merge == nil { 163 merge = defaultUnionMergeDirsFn 164 } 165 166 if f.off == 0 { 167 var lfi []os.FileInfo 168 if f.Layer != nil { 169 lfi, err = f.Layer.Readdir(-1) 170 if err != nil { 171 return nil, err 172 } 173 } 174 175 var bfi []os.FileInfo 176 if f.Base != nil { 177 bfi, err = f.Base.Readdir(-1) 178 if err != nil { 179 return nil, err 180 } 181 182 } 183 merged, err := merge(lfi, bfi) 184 if err != nil { 185 return nil, err 186 } 187 f.files = append(f.files, merged...) 188 } 189 files := f.files[f.off:] 190 191 if c <= 0 { 192 return files, nil 193 } 194 195 if len(files) == 0 { 196 return nil, io.EOF 197 } 198 199 if c > len(files) { 200 c = len(files) 201 } 202 203 defer func() { f.off += c }() 204 return files[:c], nil 205 } 206 207 func (f *UnionFile) Readdirnames(c int) ([]string, error) { 208 rfi, err := f.Readdir(c) 209 if err != nil { 210 return nil, err 211 } 212 var names []string 213 for _, fi := range rfi { 214 names = append(names, fi.Name()) 215 } 216 return names, nil 217 } 218 219 func (f *UnionFile) Stat() (os.FileInfo, error) { 220 if f.Layer != nil { 221 return f.Layer.Stat() 222 } 223 if f.Base != nil { 224 return f.Base.Stat() 225 } 226 return nil, BADFD 227 } 228 229 func (f *UnionFile) Sync() (err error) { 230 if f.Layer != nil { 231 err = f.Layer.Sync() 232 if err == nil && f.Base != nil { 233 err = f.Base.Sync() 234 } 235 return err 236 } 237 if f.Base != nil { 238 return f.Base.Sync() 239 } 240 return BADFD 241 } 242 243 func (f *UnionFile) Truncate(s int64) (err error) { 244 if f.Layer != nil { 245 err = f.Layer.Truncate(s) 246 if err == nil && f.Base != nil { 247 err = f.Base.Truncate(s) 248 } 249 return err 250 } 251 if f.Base != nil { 252 return f.Base.Truncate(s) 253 } 254 return BADFD 255 } 256 257 func (f *UnionFile) WriteString(s string) (n int, err error) { 258 if f.Layer != nil { 259 n, err = f.Layer.WriteString(s) 260 if err == nil && f.Base != nil { 261 _, err = f.Base.WriteString(s) 262 } 263 return n, err 264 } 265 if f.Base != nil { 266 return f.Base.WriteString(s) 267 } 268 return 0, BADFD 269 } 270 271 func copyFile(base Fs, layer Fs, name string, bfh File) error { 272 // First make sure the directory exists 273 exists, err := Exists(layer, filepath.Dir(name)) 274 if err != nil { 275 return err 276 } 277 if !exists { 278 err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME? 279 if err != nil { 280 return err 281 } 282 } 283 284 // Create the file on the overlay 285 lfh, err := layer.Create(name) 286 if err != nil { 287 return err 288 } 289 n, err := io.Copy(lfh, bfh) 290 if err != nil { 291 // If anything fails, clean up the file 292 layer.Remove(name) 293 lfh.Close() 294 return err 295 } 296 297 bfi, err := bfh.Stat() 298 if err != nil || bfi.Size() != n { 299 layer.Remove(name) 300 lfh.Close() 301 return syscall.EIO 302 } 303 304 err = lfh.Close() 305 if err != nil { 306 layer.Remove(name) 307 lfh.Close() 308 return err 309 } 310 return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime()) 311 } 312 313 func copyToLayer(base Fs, layer Fs, name string) error { 314 bfh, err := base.Open(name) 315 if err != nil { 316 return err 317 } 318 defer bfh.Close() 319 320 return copyFile(base, layer, name, bfh) 321 } 322 323 func copyFileToLayer(base Fs, layer Fs, name string, flag int, perm os.FileMode) error { 324 bfh, err := base.OpenFile(name, flag, perm) 325 if err != nil { 326 return err 327 } 328 defer bfh.Close() 329 330 return copyFile(base, layer, name, bfh) 331 }