github.com/listspa/afero@v1.2.3-0.20200303091702-462b969194ee/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) isNotExist(err error) bool { 121 if e, ok := err.(*os.PathError); ok { 122 err = e.Err 123 } 124 if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR { 125 return true 126 } 127 return false 128 } 129 130 // Renaming files present only in the base layer is not permitted 131 func (u *CopyOnWriteFs) Rename(oldname, newname string) error { 132 b, err := u.isBaseFile(oldname) 133 if err != nil { 134 return err 135 } 136 if b { 137 return syscall.EPERM 138 } 139 return u.layer.Rename(oldname, newname) 140 } 141 142 // Removing files present only in the base layer is not permitted. If 143 // a file is present in the base layer and the overlay, only the overlay 144 // will be removed. 145 func (u *CopyOnWriteFs) Remove(name string) error { 146 err := u.layer.Remove(name) 147 switch err { 148 case syscall.ENOENT: 149 _, err = u.base.Stat(name) 150 if err == nil { 151 return syscall.EPERM 152 } 153 return syscall.ENOENT 154 default: 155 return err 156 } 157 } 158 159 func (u *CopyOnWriteFs) RemoveAll(name string) error { 160 err := u.layer.RemoveAll(name) 161 switch err { 162 case syscall.ENOENT: 163 _, err = u.base.Stat(name) 164 if err == nil { 165 return syscall.EPERM 166 } 167 return syscall.ENOENT 168 default: 169 return err 170 } 171 } 172 173 func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { 174 b, err := u.isBaseFile(name) 175 if err != nil { 176 return nil, err 177 } 178 179 if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 { 180 if b { 181 if err = u.copyToLayer(name); err != nil { 182 return nil, err 183 } 184 return u.layer.OpenFile(name, flag, perm) 185 } 186 187 dir := filepath.Dir(name) 188 isaDir, err := IsDir(u.base, dir) 189 if err != nil && !os.IsNotExist(err) { 190 return nil, err 191 } 192 if isaDir { 193 if err = u.layer.MkdirAll(dir, 0777); err != nil { 194 return nil, err 195 } 196 return u.layer.OpenFile(name, flag, perm) 197 } 198 199 isaDir, err = IsDir(u.layer, dir) 200 if err != nil { 201 return nil, err 202 } 203 if isaDir { 204 return u.layer.OpenFile(name, flag, perm) 205 } 206 207 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist? 208 } 209 if b { 210 return u.base.OpenFile(name, flag, perm) 211 } 212 return u.layer.OpenFile(name, flag, perm) 213 } 214 215 // This function handles the 9 different possibilities caused 216 // by the union which are the intersection of the following... 217 // layer: doesn't exist, exists as a file, and exists as a directory 218 // base: doesn't exist, exists as a file, and exists as a directory 219 func (u *CopyOnWriteFs) Open(name string) (File, error) { 220 // Since the overlay overrides the base we check that first 221 b, err := u.isBaseFile(name) 222 if err != nil { 223 return nil, err 224 } 225 226 // If overlay doesn't exist, return the base (base state irrelevant) 227 if b { 228 return u.base.Open(name) 229 } 230 231 // If overlay is a file, return it (base state irrelevant) 232 dir, err := IsDir(u.layer, name) 233 if err != nil { 234 return nil, err 235 } 236 if !dir { 237 return u.layer.Open(name) 238 } 239 240 // Overlay is a directory, base state now matters. 241 // Base state has 3 states to check but 2 outcomes: 242 // A. It's a file or non-readable in the base (return just the overlay) 243 // B. It's an accessible directory in the base (return a UnionFile) 244 245 // If base is file or nonreadable, return overlay 246 dir, err = IsDir(u.base, name) 247 if !dir || err != nil { 248 return u.layer.Open(name) 249 } 250 251 // Both base & layer are directories 252 // Return union file (if opens are without error) 253 bfile, bErr := u.base.Open(name) 254 lfile, lErr := u.layer.Open(name) 255 256 // If either have errors at this point something is very wrong. Return nil and the errors 257 if bErr != nil || lErr != nil { 258 return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr) 259 } 260 261 return &UnionFile{Base: bfile, Layer: lfile}, nil 262 } 263 264 func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error { 265 dir, err := IsDir(u.base, name) 266 if err != nil { 267 return u.layer.MkdirAll(name, perm) 268 } 269 if dir { 270 return ErrFileExists 271 } 272 return u.layer.MkdirAll(name, perm) 273 } 274 275 func (u *CopyOnWriteFs) Name() string { 276 return "CopyOnWriteFs" 277 } 278 279 func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error { 280 dir, err := IsDir(u.base, name) 281 if err != nil { 282 return u.layer.MkdirAll(name, perm) 283 } 284 if dir { 285 // This is in line with how os.MkdirAll behaves. 286 return nil 287 } 288 return u.layer.MkdirAll(name, perm) 289 } 290 291 func (u *CopyOnWriteFs) Create(name string) (File, error) { 292 return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666) 293 }