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