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(), Chmod() and Chown()).
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) Chown(name string, uid, gid int) error {
79
b, err := u.isBaseFile(name)
84
if err := u.copyToLayer(name); err != nil {
88
return u.layer.Chown(name, uid, gid)
91
func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
92
fi, err := u.layer.Stat(name)
94
isNotExist := u.isNotExist(err)
96
return u.base.Stat(name)
103
func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
104
llayer, ok1 := u.layer.(Lstater)
105
lbase, ok2 := u.base.(Lstater)
108
fi, b, err := llayer.LstatIfPossible(name)
113
if !u.isNotExist(err) {
119
fi, b, err := lbase.LstatIfPossible(name)
123
if !u.isNotExist(err) {
128
fi, err := u.Stat(name)
130
return fi, false, err
133
func (u *CopyOnWriteFs) SymlinkIfPossible(oldname, newname string) error {
134
if slayer, ok := u.layer.(Linker); ok {
135
return slayer.SymlinkIfPossible(oldname, newname)
138
return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink}
141
func (u *CopyOnWriteFs) ReadlinkIfPossible(name string) (string, error) {
142
if rlayer, ok := u.layer.(LinkReader); ok {
143
return rlayer.ReadlinkIfPossible(name)
146
if rbase, ok := u.base.(LinkReader); ok {
147
return rbase.ReadlinkIfPossible(name)
150
return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink}
153
func (u *CopyOnWriteFs) isNotExist(err error) bool {
154
if e, ok := err.(*os.PathError); ok {
157
if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
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)
172
return u.layer.Rename(oldname, newname)
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
178
func (u *CopyOnWriteFs) Remove(name string) error {
179
err := u.layer.Remove(name)
182
_, err = u.base.Stat(name)
186
return syscall.ENOENT
192
func (u *CopyOnWriteFs) RemoveAll(name string) error {
193
err := u.layer.RemoveAll(name)
196
_, err = u.base.Stat(name)
200
return syscall.ENOENT
206
func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
207
b, err := u.isBaseFile(name)
212
if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
214
if err = u.copyToLayer(name); err != nil {
217
return u.layer.OpenFile(name, flag, perm)
220
dir := filepath.Dir(name)
221
isaDir, err := IsDir(u.base, dir)
222
if err != nil && !os.IsNotExist(err) {
226
if err = u.layer.MkdirAll(dir, 0o777); err != nil {
229
return u.layer.OpenFile(name, flag, perm)
232
isaDir, err = IsDir(u.layer, dir)
237
return u.layer.OpenFile(name, flag, perm)
240
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
243
return u.base.OpenFile(name, flag, perm)
245
return u.layer.OpenFile(name, flag, perm)
248
// This function handles the 9 different possibilities caused
249
// by the union which are the intersection of the following...
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)
260
// If overlay doesn't exist, return the base (base state irrelevant)
262
return u.base.Open(name)
265
// If overlay is a file, return it (base state irrelevant)
266
dir, err := IsDir(u.layer, name)
271
return u.layer.Open(name)
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)
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)
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)
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)
295
return &UnionFile{Base: bfile, Layer: lfile}, nil
298
func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
299
dir, err := IsDir(u.base, name)
301
return u.layer.MkdirAll(name, perm)
306
return u.layer.MkdirAll(name, perm)
309
func (u *CopyOnWriteFs) Name() string {
310
return "CopyOnWriteFs"
313
func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
314
dir, err := IsDir(u.base, name)
316
return u.layer.MkdirAll(name, perm)
319
// This is in line with how os.MkdirAll behaves.
322
return u.layer.MkdirAll(name, perm)
325
func (u *CopyOnWriteFs) Create(name string) (File, error) {
326
return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o666)