forked from mirror/afero
port unionFs from filters to filesystems
This commit is contained in:
parent
84018f2b47
commit
c6185187f1
102
union.go
102
union.go
|
@ -7,63 +7,6 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UnionFs func(Fs) FilterFs
|
|
||||||
|
|
||||||
// Create a new UnionFs:
|
|
||||||
//
|
|
||||||
// ufs := NewUnionFs(baseFs, layerFs, NewCoWUnionFs())
|
|
||||||
// cfs := NewUnionFs(baseFs, layerFs, NewCacheUnionFs(cacheTime))
|
|
||||||
func NewUnionFs(base Fs, overlay Fs, impl UnionFs) Fs {
|
|
||||||
ufs := impl(overlay)
|
|
||||||
ufs.SetSource(base)
|
|
||||||
return ufs
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyToLayer(base Fs, layer Fs, name string) error {
|
|
||||||
bfh, err := base.Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer bfh.Close()
|
|
||||||
|
|
||||||
exists, err := Exists(layer, filepath.Dir(name))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lfh, err := layer.Create(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
n, err := io.Copy(lfh, bfh)
|
|
||||||
if err != nil {
|
|
||||||
layer.Remove(name)
|
|
||||||
lfh.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
bfi, err := bfh.Stat()
|
|
||||||
if err != nil || bfi.Size() != n {
|
|
||||||
layer.Remove(name)
|
|
||||||
lfh.Close()
|
|
||||||
return syscall.EIO
|
|
||||||
}
|
|
||||||
|
|
||||||
err = lfh.Close()
|
|
||||||
if err != nil {
|
|
||||||
layer.Remove(name)
|
|
||||||
lfh.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
|
|
||||||
}
|
|
||||||
|
|
||||||
// The UnionFile implements the afero.File interface and will be returned
|
// The UnionFile implements the afero.File interface and will be returned
|
||||||
// when reading a directory present at least in the overlay or opening a file
|
// when reading a directory present at least in the overlay or opening a file
|
||||||
// for writing.
|
// for writing.
|
||||||
|
@ -278,3 +221,48 @@ func (f *UnionFile) WriteString(s string) (n int, err error) {
|
||||||
}
|
}
|
||||||
return 0, BADFD
|
return 0, BADFD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyToLayer(base Fs, layer Fs, name string) error {
|
||||||
|
bfh, err := base.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer bfh.Close()
|
||||||
|
|
||||||
|
exists, err := Exists(layer, filepath.Dir(name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lfh, err := layer.Create(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n, err := io.Copy(lfh, bfh)
|
||||||
|
if err != nil {
|
||||||
|
layer.Remove(name)
|
||||||
|
lfh.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bfi, err := bfh.Stat()
|
||||||
|
if err != nil || bfi.Size() != n {
|
||||||
|
layer.Remove(name)
|
||||||
|
lfh.Close()
|
||||||
|
return syscall.EIO
|
||||||
|
}
|
||||||
|
|
||||||
|
err = lfh.Close()
|
||||||
|
if err != nil {
|
||||||
|
layer.Remove(name)
|
||||||
|
lfh.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
|
||||||
|
}
|
||||||
|
|
|
@ -25,21 +25,6 @@ type CacheUnionFs struct {
|
||||||
cacheTime time.Duration
|
cacheTime time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCacheUnionFs(t time.Duration) UnionFs {
|
|
||||||
return func(layer Fs) FilterFs {
|
|
||||||
return &CacheUnionFs{cacheTime: t, layer: layer}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheUnionFs) AddFilter(fs FilterFs) {
|
|
||||||
fs.SetSource(u.base)
|
|
||||||
u.base = fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CacheUnionFs) SetSource(fs Fs) {
|
|
||||||
u.base = fs
|
|
||||||
}
|
|
||||||
|
|
||||||
type cacheState int
|
type cacheState int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
50
union_cow.go
50
union_cow.go
|
@ -6,7 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The CoWUnionFs is a union filesystem: a read only base file system with
|
// The CopyOnWriteUnionFs is a union filesystem: a read only base file system with
|
||||||
// a possibly writeable layer on top. Changes to the file system will only
|
// a possibly writeable layer on top. Changes to the file system will only
|
||||||
// be made in the overlay: Changing an existing file in the base layer which
|
// be made in the overlay: Changing an existing file in the base layer which
|
||||||
// is not present in the overlay will copy the file to the overlay ("changing"
|
// is not present in the overlay will copy the file to the overlay ("changing"
|
||||||
|
@ -17,28 +17,12 @@ import (
|
||||||
// can handle this).
|
// can handle this).
|
||||||
//
|
//
|
||||||
// Reading directories is currently only supported via Open(), not OpenFile().
|
// Reading directories is currently only supported via Open(), not OpenFile().
|
||||||
type CoWUnionFs struct {
|
type CopyOnWriteUnionFs struct {
|
||||||
base Fs
|
base Fs
|
||||||
layer Fs
|
layer Fs
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCoWUnionFs() UnionFs {
|
func (u *CopyOnWriteUnionFs) isBaseFile(name string) (bool, error) {
|
||||||
// returns a function to have it the same as other implemtations
|
|
||||||
return func(layer Fs) FilterFs {
|
|
||||||
return &CoWUnionFs{layer: layer}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CoWUnionFs) AddFilter(fs FilterFs) {
|
|
||||||
fs.SetSource(u.base)
|
|
||||||
u.base = fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CoWUnionFs) SetSource(fs Fs) {
|
|
||||||
u.base = fs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *CoWUnionFs) isBaseFile(name string) (bool, error) {
|
|
||||||
if _, err := u.layer.Stat(name); err == nil {
|
if _, err := u.layer.Stat(name); err == nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -46,11 +30,11 @@ func (u *CoWUnionFs) isBaseFile(name string) (bool, error) {
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) copyToLayer(name string) error {
|
func (u *CopyOnWriteUnionFs) copyToLayer(name string) error {
|
||||||
return copyToLayer(u.base, u.layer, name)
|
return copyToLayer(u.base, u.layer, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) Chtimes(name string, atime, mtime time.Time) error {
|
func (u *CopyOnWriteUnionFs) Chtimes(name string, atime, mtime time.Time) error {
|
||||||
b, err := u.isBaseFile(name)
|
b, err := u.isBaseFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -63,7 +47,7 @@ func (u *CoWUnionFs) Chtimes(name string, atime, mtime time.Time) error {
|
||||||
return u.layer.Chtimes(name, atime, mtime)
|
return u.layer.Chtimes(name, atime, mtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) Chmod(name string, mode os.FileMode) error {
|
func (u *CopyOnWriteUnionFs) Chmod(name string, mode os.FileMode) error {
|
||||||
b, err := u.isBaseFile(name)
|
b, err := u.isBaseFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -76,7 +60,7 @@ func (u *CoWUnionFs) Chmod(name string, mode os.FileMode) error {
|
||||||
return u.layer.Chmod(name, mode)
|
return u.layer.Chmod(name, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) Stat(name string) (os.FileInfo, error) {
|
func (u *CopyOnWriteUnionFs) Stat(name string) (os.FileInfo, error) {
|
||||||
fi, err := u.layer.Stat(name)
|
fi, err := u.layer.Stat(name)
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
|
@ -89,7 +73,7 @@ func (u *CoWUnionFs) Stat(name string) (os.FileInfo, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renaming files present only in the base layer is not permitted
|
// Renaming files present only in the base layer is not permitted
|
||||||
func (u *CoWUnionFs) Rename(oldname, newname string) error {
|
func (u *CopyOnWriteUnionFs) Rename(oldname, newname string) error {
|
||||||
b, err := u.isBaseFile(oldname)
|
b, err := u.isBaseFile(oldname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -103,7 +87,7 @@ func (u *CoWUnionFs) Rename(oldname, newname string) error {
|
||||||
// Removing files present only in the base layer is not permitted. If
|
// Removing files present only in the base layer is not permitted. If
|
||||||
// a file is present in the base layer and the overlay, only the overlay
|
// a file is present in the base layer and the overlay, only the overlay
|
||||||
// will be removed.
|
// will be removed.
|
||||||
func (u *CoWUnionFs) Remove(name string) error {
|
func (u *CopyOnWriteUnionFs) Remove(name string) error {
|
||||||
err := u.layer.Remove(name)
|
err := u.layer.Remove(name)
|
||||||
switch err {
|
switch err {
|
||||||
case syscall.ENOENT:
|
case syscall.ENOENT:
|
||||||
|
@ -117,7 +101,7 @@ func (u *CoWUnionFs) Remove(name string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) RemoveAll(name string) error {
|
func (u *CopyOnWriteUnionFs) RemoveAll(name string) error {
|
||||||
err := u.layer.RemoveAll(name)
|
err := u.layer.RemoveAll(name)
|
||||||
switch err {
|
switch err {
|
||||||
case syscall.ENOENT:
|
case syscall.ENOENT:
|
||||||
|
@ -131,7 +115,7 @@ func (u *CoWUnionFs) RemoveAll(name string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
func (u *CopyOnWriteUnionFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||||
b, err := u.isBaseFile(name)
|
b, err := u.isBaseFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -151,7 +135,7 @@ func (u *CoWUnionFs) OpenFile(name string, flag int, perm os.FileMode) (File, er
|
||||||
return u.layer.OpenFile(name, flag, perm)
|
return u.layer.OpenFile(name, flag, perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) Open(name string) (File, error) {
|
func (u *CopyOnWriteUnionFs) Open(name string) (File, error) {
|
||||||
b, err := u.isBaseFile(name)
|
b, err := u.isBaseFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -176,7 +160,7 @@ func (u *CoWUnionFs) Open(name string) (File, error) {
|
||||||
return &UnionFile{base: bfile, layer: lfile}, nil
|
return &UnionFile{base: bfile, layer: lfile}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) Mkdir(name string, perm os.FileMode) error {
|
func (u *CopyOnWriteUnionFs) Mkdir(name string, perm os.FileMode) error {
|
||||||
dir, err := IsDir(u.base, name)
|
dir, err := IsDir(u.base, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return u.layer.MkdirAll(name, perm)
|
return u.layer.MkdirAll(name, perm)
|
||||||
|
@ -187,11 +171,11 @@ func (u *CoWUnionFs) Mkdir(name string, perm os.FileMode) error {
|
||||||
return u.layer.MkdirAll(name, perm)
|
return u.layer.MkdirAll(name, perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) Name() string {
|
func (u *CopyOnWriteUnionFs) Name() string {
|
||||||
return "CoWUnionFs"
|
return "CopyOnWriteUnionFs"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) MkdirAll(name string, perm os.FileMode) error {
|
func (u *CopyOnWriteUnionFs) MkdirAll(name string, perm os.FileMode) error {
|
||||||
dir, err := IsDir(u.base, name)
|
dir, err := IsDir(u.base, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return u.layer.MkdirAll(name, perm)
|
return u.layer.MkdirAll(name, perm)
|
||||||
|
@ -202,7 +186,7 @@ func (u *CoWUnionFs) MkdirAll(name string, perm os.FileMode) error {
|
||||||
return u.layer.MkdirAll(name, perm)
|
return u.layer.MkdirAll(name, perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CoWUnionFs) Create(name string) (File, error) {
|
func (u *CopyOnWriteUnionFs) Create(name string) (File, error) {
|
||||||
b, err := u.isBaseFile(name)
|
b, err := u.isBaseFile(name)
|
||||||
if err == nil && b {
|
if err == nil && b {
|
||||||
if err = u.copyToLayer(name); err != nil {
|
if err = u.copyToLayer(name); err != nil {
|
||||||
|
|
|
@ -11,7 +11,7 @@ func TestUnionCreateExisting(t *testing.T) {
|
||||||
base := &MemMapFs{}
|
base := &MemMapFs{}
|
||||||
roBase := &ReadOnlyFs{source: base}
|
roBase := &ReadOnlyFs{source: base}
|
||||||
|
|
||||||
ufs := NewUnionFs(roBase, &MemMapFs{}, NewCoWUnionFs())
|
ufs := &CopyOnWriteUnionFs{base: roBase, layer: &MemMapFs{}}
|
||||||
|
|
||||||
base.MkdirAll("/home/test", 0777)
|
base.MkdirAll("/home/test", 0777)
|
||||||
fh, _ := base.Create("/home/test/file.txt")
|
fh, _ := base.Create("/home/test/file.txt")
|
||||||
|
@ -61,7 +61,7 @@ func TestUnionMergeReaddir(t *testing.T) {
|
||||||
base := &MemMapFs{}
|
base := &MemMapFs{}
|
||||||
roBase := &ReadOnlyFs{source: base}
|
roBase := &ReadOnlyFs{source: base}
|
||||||
|
|
||||||
ufs := NewUnionFs(roBase, &MemMapFs{}, NewCoWUnionFs())
|
ufs := &CopyOnWriteUnionFs{base: roBase, layer: &MemMapFs{}}
|
||||||
|
|
||||||
base.MkdirAll("/home/test", 0777)
|
base.MkdirAll("/home/test", 0777)
|
||||||
fh, _ := base.Create("/home/test/file.txt")
|
fh, _ := base.Create("/home/test/file.txt")
|
||||||
|
@ -85,7 +85,8 @@ func TestUnionMergeReaddir(t *testing.T) {
|
||||||
func TestUnionCacheWrite(t *testing.T) {
|
func TestUnionCacheWrite(t *testing.T) {
|
||||||
base := &MemMapFs{}
|
base := &MemMapFs{}
|
||||||
layer := &MemMapFs{}
|
layer := &MemMapFs{}
|
||||||
ufs := NewUnionFs(base, layer, NewCacheUnionFs(0))
|
|
||||||
|
ufs := &CacheUnionFs{base: base, layer: layer, cacheTime: 0}
|
||||||
|
|
||||||
base.Mkdir("/data", 0777)
|
base.Mkdir("/data", 0777)
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ func TestUnionCacheWrite(t *testing.T) {
|
||||||
func TestUnionCacheExpire(t *testing.T) {
|
func TestUnionCacheExpire(t *testing.T) {
|
||||||
base := &MemMapFs{}
|
base := &MemMapFs{}
|
||||||
layer := &MemMapFs{}
|
layer := &MemMapFs{}
|
||||||
ufs := NewUnionFs(base, layer, NewCacheUnionFs(1*time.Second))
|
ufs := &CacheUnionFs{base: base, layer: layer, cacheTime: 1 * time.Second}
|
||||||
|
|
||||||
base.Mkdir("/data", 0777)
|
base.Mkdir("/data", 0777)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue