11 var _ Lstater = (*CopyOnWriteFs)(nil)
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()).
19 // Reading directories is currently only supported via Open(), not OpenFile().
20 type CopyOnWriteFs struct {
25 func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
26 return &CopyOnWriteFs{base: base, layer: layer}
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 {
34 _, err := u.base.Stat(name)
36 if oerr, ok := err.(*os.PathError); ok {
37 if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
41 if err == syscall.ENOENT {
48 func (u *CopyOnWriteFs) copyToLayer(name string) error {
49 return copyToLayer(u.base, u.layer, name)
52 func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
53 b, err := u.isBaseFile(name)
58 if err := u.copyToLayer(name); err != nil {
62 return u.layer.Chtimes(name, atime, mtime)
65 func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
66 b, err := u.isBaseFile(name)
71 if err := u.copyToLayer(name); err != nil {
75 return u.layer.Chmod(name, mode)
78 func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
79 fi, err := u.layer.Stat(name)
81 isNotExist := u.isNotExist(err)
83 return u.base.Stat(name)
90 func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
91 llayer, ok1 := u.layer.(Lstater)
92 lbase, ok2 := u.base.(Lstater)
95 fi, b, err := llayer.LstatIfPossible(name)
100 if !u.isNotExist(err) {
106 fi, b, err := lbase.LstatIfPossible(name)
110 if !u.isNotExist(err) {
115 fi, err := u.Stat(name)
117 return fi, false, err
120 func (u *CopyOnWriteFs) isNotExist(err error) bool {
121 if e, ok := err.(*os.PathError); ok {
124 if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
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)
139 return u.layer.Rename(oldname, newname)
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
145 func (u *CopyOnWriteFs) Remove(name string) error {
146 err := u.layer.Remove(name)
149 _, err = u.base.Stat(name)
153 return syscall.ENOENT
159 func (u *CopyOnWriteFs) RemoveAll(name string) error {
160 err := u.layer.RemoveAll(name)
163 _, err = u.base.Stat(name)
167 return syscall.ENOENT
173 func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
174 b, err := u.isBaseFile(name)
179 if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
181 if err = u.copyToLayer(name); err != nil {
184 return u.layer.OpenFile(name, flag, perm)
187 dir := filepath.Dir(name)
188 isaDir, err := IsDir(u.base, dir)
189 if err != nil && !os.IsNotExist(err) {
193 if err = u.layer.MkdirAll(dir, 0777); err != nil {
196 return u.layer.OpenFile(name, flag, perm)
199 isaDir, err = IsDir(u.layer, dir)
204 return u.layer.OpenFile(name, flag, perm)
207 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
210 return u.base.OpenFile(name, flag, perm)
212 return u.layer.OpenFile(name, flag, perm)
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)
226 // If overlay doesn't exist, return the base (base state irrelevant)
228 return u.base.Open(name)
231 // If overlay is a file, return it (base state irrelevant)
232 dir, err := IsDir(u.layer, name)
237 return u.layer.Open(name)
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)
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)
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)
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)
261 return &UnionFile{Base: bfile, Layer: lfile}, nil
264 func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
265 dir, err := IsDir(u.base, name)
267 return u.layer.MkdirAll(name, perm)
272 return u.layer.MkdirAll(name, perm)
275 func (u *CopyOnWriteFs) Name() string {
276 return "CopyOnWriteFs"
279 func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
280 dir, err := IsDir(u.base, name)
282 return u.layer.MkdirAll(name, perm)
285 // This is in line with how os.MkdirAll behaves.
288 return u.layer.MkdirAll(name, perm)
291 func (u *CopyOnWriteFs) Create(name string) (File, error) {
292 return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)