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