github.com/hbbio/afero@v1.2.3-0.20190412153849-bd67867be27b/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), os.SEEK_SET) 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 190 if c <= 0 && len(f.files) == 0 { 191 return f.files, nil 192 } 193 194 if f.off >= len(f.files) { 195 return nil, io.EOF 196 } 197 198 if c <= 0 { 199 return f.files[f.off:], nil 200 } 201 202 if c > len(f.files) { 203 c = len(f.files) 204 } 205 206 defer func() { f.off += c }() 207 return f.files[f.off:c], nil 208 } 209 210 func (f *UnionFile) Readdirnames(c int) ([]string, error) { 211 rfi, err := f.Readdir(c) 212 if err != nil { 213 return nil, err 214 } 215 var names []string 216 for _, fi := range rfi { 217 names = append(names, fi.Name()) 218 } 219 return names, nil 220 } 221 222 func (f *UnionFile) Stat() (os.FileInfo, error) { 223 if f.Layer != nil { 224 return f.Layer.Stat() 225 } 226 if f.Base != nil { 227 return f.Base.Stat() 228 } 229 return nil, BADFD 230 } 231 232 func (f *UnionFile) Sync() (err error) { 233 if f.Layer != nil { 234 err = f.Layer.Sync() 235 if err == nil && f.Base != nil { 236 err = f.Base.Sync() 237 } 238 return err 239 } 240 if f.Base != nil { 241 return f.Base.Sync() 242 } 243 return BADFD 244 } 245 246 func (f *UnionFile) Truncate(s int64) (err error) { 247 if f.Layer != nil { 248 err = f.Layer.Truncate(s) 249 if err == nil && f.Base != nil { 250 err = f.Base.Truncate(s) 251 } 252 return err 253 } 254 if f.Base != nil { 255 return f.Base.Truncate(s) 256 } 257 return BADFD 258 } 259 260 func (f *UnionFile) WriteString(s string) (n int, err error) { 261 if f.Layer != nil { 262 n, err = f.Layer.WriteString(s) 263 if err == nil && f.Base != nil { 264 _, err = f.Base.WriteString(s) 265 } 266 return n, err 267 } 268 if f.Base != nil { 269 return f.Base.WriteString(s) 270 } 271 return 0, BADFD 272 } 273 274 func copyToLayer(base Fs, layer Fs, name string) error { 275 bfh, err := base.Open(name) 276 if err != nil { 277 return err 278 } 279 defer bfh.Close() 280 281 // First make sure the directory exists 282 exists, err := Exists(layer, filepath.Dir(name)) 283 if err != nil { 284 return err 285 } 286 if !exists { 287 err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME? 288 if err != nil { 289 return err 290 } 291 } 292 293 // Create the file on the overlay 294 lfh, err := layer.Create(name) 295 if err != nil { 296 return err 297 } 298 n, err := io.Copy(lfh, bfh) 299 if err != nil { 300 // If anything fails, clean up the file 301 layer.Remove(name) 302 lfh.Close() 303 return err 304 } 305 306 bfi, err := bfh.Stat() 307 if err != nil || bfi.Size() != n { 308 layer.Remove(name) 309 lfh.Close() 310 return syscall.EIO 311 } 312 313 err = lfh.Close() 314 if err != nil { 315 layer.Remove(name) 316 lfh.Close() 317 return err 318 } 319 return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime()) 320 }