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