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