11 // The CopyOnWriteFs is a union filesystem: a read only base file system with
12 // a possibly writeable layer on top. Changes to the file system will only
13 // be made in the overlay: Changing an existing file in the base layer which
14 // is not present in the overlay will copy the file to the overlay ("changing"
15 // includes also calls to e.g. Chtimes() and Chmod()).
17 // Reading directories is currently only supported via Open(), not OpenFile().
18 type CopyOnWriteFs struct {
23 func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
24 return &CopyOnWriteFs{base: base, layer: layer}
27 // Returns true if the file is not in the overlay
28 func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
29 if _, err := u.layer.Stat(name); err == nil {
32 _, err := u.base.Stat(name)
34 if oerr, ok := err.(*os.PathError); ok {
35 if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
39 if err == syscall.ENOENT {
46 func (u *CopyOnWriteFs) copyToLayer(name string) error {
47 return copyToLayer(u.base, u.layer, name)
50 func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
51 b, err := u.isBaseFile(name)
56 if err := u.copyToLayer(name); err != nil {
60 return u.layer.Chtimes(name, atime, mtime)
63 func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
64 b, err := u.isBaseFile(name)
69 if err := u.copyToLayer(name); err != nil {
73 return u.layer.Chmod(name, mode)
76 func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
77 fi, err := u.layer.Stat(name)
80 if e, ok := err.(*os.PathError); ok {
83 if err == syscall.ENOENT || err == syscall.ENOTDIR {
84 return u.base.Stat(name)
91 // Renaming files present only in the base layer is not permitted
92 func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
93 b, err := u.isBaseFile(oldname)
100 return u.layer.Rename(oldname, newname)
103 // Removing files present only in the base layer is not permitted. If
104 // a file is present in the base layer and the overlay, only the overlay
106 func (u *CopyOnWriteFs) Remove(name string) error {
107 err := u.layer.Remove(name)
110 _, err = u.base.Stat(name)
114 return syscall.ENOENT
120 func (u *CopyOnWriteFs) RemoveAll(name string) error {
121 err := u.layer.RemoveAll(name)
124 _, err = u.base.Stat(name)
128 return syscall.ENOENT
134 func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
135 b, err := u.isBaseFile(name)
140 if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
142 if err = u.copyToLayer(name); err != nil {
145 return u.layer.OpenFile(name, flag, perm)
148 dir := filepath.Dir(name)
149 isaDir, err := IsDir(u.base, dir)
150 if err != nil && !os.IsNotExist(err) {
154 if err = u.layer.MkdirAll(dir, 0777); err != nil {
157 return u.layer.OpenFile(name, flag, perm)
160 isaDir, err = IsDir(u.layer, dir)
165 return u.layer.OpenFile(name, flag, perm)
168 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
171 return u.base.OpenFile(name, flag, perm)
173 return u.layer.OpenFile(name, flag, perm)
176 // This function handles the 9 different possibilities caused
177 // by the union which are the intersection of the following...
178 // layer: doesn't exist, exists as a file, and exists as a directory
179 // base: doesn't exist, exists as a file, and exists as a directory
180 func (u *CopyOnWriteFs) Open(name string) (File, error) {
181 // Since the overlay overrides the base we check that first
182 b, err := u.isBaseFile(name)
187 // If overlay doesn't exist, return the base (base state irrelevant)
189 return u.base.Open(name)
192 // If overlay is a file, return it (base state irrelevant)
193 dir, err := IsDir(u.layer, name)
198 return u.layer.Open(name)
201 // Overlay is a directory, base state now matters.
202 // Base state has 3 states to check but 2 outcomes:
203 // A. It's a file or non-readable in the base (return just the overlay)
204 // B. It's an accessible directory in the base (return a UnionFile)
206 // If base is file or nonreadable, return overlay
207 dir, err = IsDir(u.base, name)
208 if !dir || err != nil {
209 return u.layer.Open(name)
212 // Both base & layer are directories
213 // Return union file (if opens are without error)
214 bfile, bErr := u.base.Open(name)
215 lfile, lErr := u.layer.Open(name)
217 // If either have errors at this point something is very wrong. Return nil and the errors
218 if bErr != nil || lErr != nil {
219 return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
222 return &UnionFile{base: bfile, layer: lfile}, nil
225 func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
226 dir, err := IsDir(u.base, name)
228 return u.layer.MkdirAll(name, perm)
231 return syscall.EEXIST
233 return u.layer.MkdirAll(name, perm)
236 func (u *CopyOnWriteFs) Name() string {
237 return "CopyOnWriteFs"
240 func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
241 dir, err := IsDir(u.base, name)
243 return u.layer.MkdirAll(name, perm)
246 return syscall.EEXIST
248 return u.layer.MkdirAll(name, perm)
251 func (u *CopyOnWriteFs) Create(name string) (File, error) {
252 return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)