github.com/LandonTClipp/afero@v1.3.6-0.20200907052150-97f9d166c7a3/copyOnWriteFs.go (about) 1 package afero 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "syscall" 8 "time" 9 ) 10 11 var _ Lstater = (*CopyOnWriteFs)(nil) 12 13 // The CopyOnWriteFs is a union filesystem: a read only base file system with 14 // a possibly writeable layer on top. Changes to the file system will only 15 // be made in the overlay: Changing an existing file in the base layer which 16 // is not present in the overlay will copy the file to the overlay ("changing" 17 // includes also calls to e.g. Chtimes() and Chmod()). 18 // 19 // Reading directories is currently only supported via Open(), not OpenFile(). 20 type CopyOnWriteFs struct { 21 base Fs 22 layer Fs 23 } 24 25 func NewCopyOnWriteFs(base Fs, layer Fs) Fs { 26 return &CopyOnWriteFs{base: base, layer: layer} 27 } 28 29 // Returns true if the file is not in the overlay 30 func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) { 31 if _, err := u.layer.Stat(name); err == nil { 32 return false, nil 33 } 34 _, err := u.base.Stat(name) 35 if err != nil { 36 if oerr, ok := err.(*os.PathError); ok { 37 if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR { 38 return false, nil 39 } 40 } 41 if err == syscall.ENOENT { 42 return false, nil 43 } 44 } 45 return true, err 46 } 47 48 func (u *CopyOnWriteFs) copyToLayer(name string) error { 49 return copyToLayer(u.base, u.layer, name) 50 } 51 52 func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error { 53 b, err := u.isBaseFile(name) 54 if err != nil { 55 return err 56 } 57 if b { 58 if err := u.copyToLayer(name); err != nil { 59 return err 60 } 61 } 62 return u.layer.Chtimes(name, atime, mtime) 63 } 64 65 func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error { 66 b, err := u.isBaseFile(name) 67 if err != nil { 68 return err 69 } 70 if b { 71 if err := u.copyToLayer(name); err != nil { 72 return err 73 } 74 } 75 return u.layer.Chmod(name, mode) 76 } 77 78 func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) { 79 fi, err := u.layer.Stat(name) 80 if err != nil { 81 isNotExist := u.isNotExist(err) 82 if isNotExist { 83 return u.base.Stat(name) 84 } 85 return nil, err 86 } 87 return fi, nil 88 } 89 90 func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { 91 llayer, ok1 := u.layer.(Lstater) 92 lbase, ok2 := u.base.(Lstater) 93 94 if ok1 { 95 fi, b, err := llayer.LstatIfPossible(name) 96 if err == nil { 97 return fi, b, nil 98 } 99 100 if !u.isNotExist(err) { 101 return nil, b, err 102 } 103 } 104 105 if ok2 { 106 fi, b, err := lbase.LstatIfPossible(name) 107 if err == nil { 108 return fi, b, nil 109 } 110 if !u.isNotExist(err) { 111 return nil, b, err 112 } 113 } 114 115 fi, err := u.Stat(name) 116 117 return fi, false, err 118 } 119 120 func (u *CopyOnWriteFs) SymlinkIfPossible(oldname, newname string) error { 121 if slayer, ok := u.layer.(Linker); ok { 122 return slayer.SymlinkIfPossible(oldname, newname) 123 } 124 125 return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink} 126 } 127 128 func (u *CopyOnWriteFs) ReadlinkIfPossible(name string) (string, error) { 129 if rlayer, ok := u.layer.(LinkReader); ok { 130 return rlayer.ReadlinkIfPossible(name) 131 } 132 133 if rbase, ok := u.base.(LinkReader); ok { 134 return rbase.ReadlinkIfPossible(name) 135 } 136 137 return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink} 138 } 139 140 func (u *CopyOnWriteFs) isNotExist(err error) bool { 141 if e, ok := err.(*os.PathError); ok { 142 err = e.Err 143 } 144 if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR { 145 return true 146 } 147 return false 148 } 149 150 // Renaming files present only in the base layer is not permitted 151 func (u *CopyOnWriteFs) Rename(oldname, newname string) error { 152 b, err := u.isBaseFile(oldname) 153 if err != nil { 154 return err 155 } 156 if b { 157 return syscall.EPERM 158 } 159 return u.layer.Rename(oldname, newname) 160 } 161 162 // Removing files present only in the base layer is not permitted. If 163 // a file is present in the base layer and the overlay, only the overlay 164 // will be removed. 165 func (u *CopyOnWriteFs) Remove(name string) error { 166 err := u.layer.Remove(name) 167 switch err { 168 case syscall.ENOENT: 169 _, err = u.base.Stat(name) 170 if err == nil { 171 return syscall.EPERM 172 } 173 return syscall.ENOENT 174 default: 175 return err 176 } 177 } 178 179 func (u *CopyOnWriteFs) RemoveAll(name string) error { 180 err := u.layer.RemoveAll(name) 181 switch err { 182 case syscall.ENOENT: 183 _, err = u.base.Stat(name) 184 if err == nil { 185 return syscall.EPERM 186 } 187 return syscall.ENOENT 188 default: 189 return err 190 } 191 } 192 193 func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { 194 b, err := u.isBaseFile(name) 195 if err != nil { 196 return nil, err 197 } 198 199 if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 { 200 if b { 201 if err = u.copyToLayer(name); err != nil { 202 return nil, err 203 } 204 return u.layer.OpenFile(name, flag, perm) 205 } 206 207 dir := filepath.Dir(name) 208 isaDir, err := IsDir(u.base, dir) 209 if err != nil && !os.IsNotExist(err) { 210 return nil, err 211 } 212 if isaDir { 213 if err = u.layer.MkdirAll(dir, 0777); err != nil { 214 return nil, err 215 } 216 return u.layer.OpenFile(name, flag, perm) 217 } 218 219 isaDir, err = IsDir(u.layer, dir) 220 if err != nil { 221 return nil, err 222 } 223 if isaDir { 224 return u.layer.OpenFile(name, flag, perm) 225 } 226 227 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist? 228 } 229 if b { 230 return u.base.OpenFile(name, flag, perm) 231 } 232 return u.layer.OpenFile(name, flag, perm) 233 } 234 235 // This function handles the 9 different possibilities caused 236 // by the union which are the intersection of the following... 237 // layer: doesn't exist, exists as a file, and exists as a directory 238 // base: doesn't exist, exists as a file, and exists as a directory 239 func (u *CopyOnWriteFs) Open(name string) (File, error) { 240 // Since the overlay overrides the base we check that first 241 b, err := u.isBaseFile(name) 242 if err != nil { 243 return nil, err 244 } 245 246 // If overlay doesn't exist, return the base (base state irrelevant) 247 if b { 248 return u.base.Open(name) 249 } 250 251 // If overlay is a file, return it (base state irrelevant) 252 dir, err := IsDir(u.layer, name) 253 if err != nil { 254 return nil, err 255 } 256 if !dir { 257 return u.layer.Open(name) 258 } 259 260 // Overlay is a directory, base state now matters. 261 // Base state has 3 states to check but 2 outcomes: 262 // A. It's a file or non-readable in the base (return just the overlay) 263 // B. It's an accessible directory in the base (return a UnionFile) 264 265 // If base is file or nonreadable, return overlay 266 dir, err = IsDir(u.base, name) 267 if !dir || err != nil { 268 return u.layer.Open(name) 269 } 270 271 // Both base & layer are directories 272 // Return union file (if opens are without error) 273 bfile, bErr := u.base.Open(name) 274 lfile, lErr := u.layer.Open(name) 275 276 // If either have errors at this point something is very wrong. Return nil and the errors 277 if bErr != nil || lErr != nil { 278 return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr) 279 } 280 281 return &UnionFile{Base: bfile, Layer: lfile}, nil 282 } 283 284 func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error { 285 dir, err := IsDir(u.base, name) 286 if err != nil { 287 return u.layer.MkdirAll(name, perm) 288 } 289 if dir { 290 return ErrFileExists 291 } 292 return u.layer.MkdirAll(name, perm) 293 } 294 295 func (u *CopyOnWriteFs) Name() string { 296 return "CopyOnWriteFs" 297 } 298 299 func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error { 300 dir, err := IsDir(u.base, name) 301 if err != nil { 302 return u.layer.MkdirAll(name, perm) 303 } 304 if dir { 305 // This is in line with how os.MkdirAll behaves. 306 return nil 307 } 308 return u.layer.MkdirAll(name, perm) 309 } 310 311 func (u *CopyOnWriteFs) Create(name string) (File, error) { 312 return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666) 313 }