unionFile.go (7018B)
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), io.SeekCurrent); 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 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 // Readdir will weave the two directories together and 157 // return a single view of the overlayed directories. 158 // At the end of the directory view, the error is io.EOF if c > 0. 159 func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) { 160 var merge DirsMerger = f.Merger 161 if merge == nil { 162 merge = defaultUnionMergeDirsFn 163 } 164 165 if f.off == 0 { 166 var lfi []os.FileInfo 167 if f.Layer != nil { 168 lfi, err = f.Layer.Readdir(-1) 169 if err != nil { 170 return nil, err 171 } 172 } 173 174 var bfi []os.FileInfo 175 if f.Base != nil { 176 bfi, err = f.Base.Readdir(-1) 177 if err != nil { 178 return nil, err 179 } 180 181 } 182 merged, err := merge(lfi, bfi) 183 if err != nil { 184 return nil, err 185 } 186 f.files = append(f.files, merged...) 187 } 188 files := f.files[f.off:] 189 190 if c <= 0 { 191 return files, nil 192 } 193 194 if len(files) == 0 { 195 return nil, io.EOF 196 } 197 198 if c > len(files) { 199 c = len(files) 200 } 201 202 defer func() { f.off += c }() 203 return files[:c], nil 204 } 205 206 func (f *UnionFile) Readdirnames(c int) ([]string, error) { 207 rfi, err := f.Readdir(c) 208 if err != nil { 209 return nil, err 210 } 211 var names []string 212 for _, fi := range rfi { 213 names = append(names, fi.Name()) 214 } 215 return names, nil 216 } 217 218 func (f *UnionFile) Stat() (os.FileInfo, error) { 219 if f.Layer != nil { 220 return f.Layer.Stat() 221 } 222 if f.Base != nil { 223 return f.Base.Stat() 224 } 225 return nil, BADFD 226 } 227 228 func (f *UnionFile) Sync() (err error) { 229 if f.Layer != nil { 230 err = f.Layer.Sync() 231 if err == nil && f.Base != nil { 232 err = f.Base.Sync() 233 } 234 return err 235 } 236 if f.Base != nil { 237 return f.Base.Sync() 238 } 239 return BADFD 240 } 241 242 func (f *UnionFile) Truncate(s int64) (err error) { 243 if f.Layer != nil { 244 err = f.Layer.Truncate(s) 245 if err == nil && f.Base != nil { 246 err = f.Base.Truncate(s) 247 } 248 return err 249 } 250 if f.Base != nil { 251 return f.Base.Truncate(s) 252 } 253 return BADFD 254 } 255 256 func (f *UnionFile) WriteString(s string) (n int, err error) { 257 if f.Layer != nil { 258 n, err = f.Layer.WriteString(s) 259 if err == nil && f.Base != nil { 260 _, err = f.Base.WriteString(s) 261 } 262 return n, err 263 } 264 if f.Base != nil { 265 return f.Base.WriteString(s) 266 } 267 return 0, BADFD 268 } 269 270 func copyFile(base Fs, layer Fs, name string, bfh File) error { 271 // First make sure the directory exists 272 exists, err := Exists(layer, filepath.Dir(name)) 273 if err != nil { 274 return err 275 } 276 if !exists { 277 err = layer.MkdirAll(filepath.Dir(name), 0o777) // FIXME? 278 if err != nil { 279 return err 280 } 281 } 282 283 // Create the file on the overlay 284 lfh, err := layer.Create(name) 285 if err != nil { 286 return err 287 } 288 n, err := io.Copy(lfh, bfh) 289 if err != nil { 290 // If anything fails, clean up the file 291 layer.Remove(name) 292 lfh.Close() 293 return err 294 } 295 296 bfi, err := bfh.Stat() 297 if err != nil || bfi.Size() != n { 298 layer.Remove(name) 299 lfh.Close() 300 return syscall.EIO 301 } 302 303 err = lfh.Close() 304 if err != nil { 305 layer.Remove(name) 306 lfh.Close() 307 return err 308 } 309 return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime()) 310 } 311 312 func copyToLayer(base Fs, layer Fs, name string) error { 313 bfh, err := base.Open(name) 314 if err != nil { 315 return err 316 } 317 defer bfh.Close() 318 319 return copyFile(base, layer, name, bfh) 320 } 321 322 func copyFileToLayer(base Fs, layer Fs, name string, flag int, perm os.FileMode) error { 323 bfh, err := base.OpenFile(name, flag, perm) 324 if err != nil { 325 return err 326 } 327 defer bfh.Close() 328 329 return copyFile(base, layer, name, bfh) 330 }