From dd37d562537184a19603626d0d51f5174cc9c314 Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Fri, 3 Aug 2018 08:49:46 +0200 Subject: [PATCH] Added afero proxy to ricefs. Added mechanism to copy afero dir (temporarily) to a local directory. --- ricefs/file.go | 74 ++++++++++++++++++++++++++++++++++++++++++++ ricefs/fs.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ vfs/copydir.go | 71 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 ricefs/file.go create mode 100644 ricefs/fs.go create mode 100644 vfs/copydir.go diff --git a/ricefs/file.go b/ricefs/file.go new file mode 100644 index 0000000..7f399a3 --- /dev/null +++ b/ricefs/file.go @@ -0,0 +1,74 @@ +package ricefs + +import ( + "io" + "os" + + "github.com/GeertJohan/go.rice" + + "github.com/spf13/afero" +) + +var _ afero.File = &file{} + +type file struct { + f *rice.File + info os.FileInfo +} + +func (f *file) Close() error { return f.f.Close() } + +func (f *file) Read(p []byte) (int, error) { return f.f.Read(p) } + +func (f *file) ReadAt(p []byte, off int64) (int, error) { + _, err := f.Seek(off, io.SeekStart) + if nil != err { + return 0, err + } + return f.Read(p) +} + +func (f *file) Seek(off int64, whence int) (int64, error) { return f.f.Seek(off, whence) } + +func (f *file) Write(p []byte) (int, error) { return 0, ErrReadOnly } + +func (f *file) WriteAt(p []byte, off int64) (int, error) { return 0, ErrReadOnly } + +func (f *file) Name() string { + i, err := f.Stat() + if nil != err { + return "" + } + return i.Name() +} + +func (f *file) Readdir(n int) ([]os.FileInfo, error) { return f.f.Readdir(n) } + +func (f *file) Readdirnames(n int) ([]string, error) { + var dirs, err = f.Readdir(n) + if nil != err { + return nil, err + } + var names = make([]string, len(dirs)) + for i, dir := range dirs { + names[i] = dir.Name() + } + return names, nil +} + +func (f *file) Stat() (os.FileInfo, error) { + if nil == f.info { + i, err := f.f.Stat() + if nil != err { + return nil, err + } + f.info = i + } + return f.info, nil +} + +func (f *file) Sync() error { return ErrReadOnly } + +func (f *file) Truncate(size int64) error { return ErrReadOnly } + +func (f *file) WriteString(s string) (int, error) { return 0, ErrReadOnly } diff --git a/ricefs/fs.go b/ricefs/fs.go new file mode 100644 index 0000000..a24acc6 --- /dev/null +++ b/ricefs/fs.go @@ -0,0 +1,84 @@ +package ricefs + +import ( + "errors" + "os" + "time" + + rice "github.com/GeertJohan/go.rice" + "github.com/spf13/afero" +) + +var _ afero.Fs = &RiceFs{} + +func NewFs(b *rice.Box) *RiceFs { + return &RiceFs{b} +} + +type RiceFs struct { + Box *rice.Box +} + +// ErrReadOnly is returned when a write method is accessed. +var ErrReadOnly = errors.New("file system is read-only") + +// ErrUseOpen is returned when trying to use OpenFile, use Open to open the (read-only) file. +var ErrUseOpen = errors.New("use Open method instead, non-read-only perms are not supported") + +// Create is not supported for a RiceFs. +func (f *RiceFs) Create(name string) (afero.File, error) { + return nil, ErrReadOnly +} + +// Mkdir is not supported for a RiceFs. +func (f *RiceFs) Mkdir(name string, perm os.FileMode) error { + return ErrReadOnly +} + +// MkdirAll is not supported for a RiceFs. +func (f *RiceFs) MkdirAll(path string, perm os.FileMode) error { + return ErrReadOnly +} + +// Open opens a file, returning it or an error, if any happens. +func (f *RiceFs) Open(name string) (afero.File, error) { + fBox, err := f.Box.Open(name) + if nil != err { + return nil, err + } + return &file{f: fBox}, nil +} + +// OpenFile is not supported for a RiceFs. +func (f *RiceFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { + return nil, ErrUseOpen +} + +// Remove is not supported for a RiceFs. +func (f *RiceFs) Remove(name string) error { return ErrReadOnly } + +// RemoveAll is not supported for a RiceFs. +func (f *RiceFs) RemoveAll(path string) error { return ErrReadOnly } + +// Rename is not supported for a RiceFs. +func (f *RiceFs) Rename(oldname, newname string) error { return ErrReadOnly } + +// Stat returns a FileInfo describing the named file, or an error, if any happens. +func (f *RiceFs) Stat(name string) (os.FileInfo, error) { + fBox, err := f.Box.Open(name) + if nil != err { + return nil, err + } + return fBox.Stat() +} + +// Name returns the name of the file system +func (f *RiceFs) Name() string { + return "Rice FS" +} + +// Chmod is not supported for a RiceFs. +func (f *RiceFs) Chmod(name string, mode os.FileMode) error { return ErrReadOnly } + +// Chtimes is not supported for a RiceFs. +func (f *RiceFs) Chtimes(name string, atime time.Time, mtime time.Time) error { return ErrReadOnly } diff --git a/vfs/copydir.go b/vfs/copydir.go new file mode 100644 index 0000000..02e5060 --- /dev/null +++ b/vfs/copydir.go @@ -0,0 +1,71 @@ +package vfs + +import ( + "io" + "io/ioutil" + "os" + "path/filepath" + + "github.com/spf13/afero" +) + +type CopyDir interface { + Retrieve(name string) (string, error) + Path(name string) string + Open(name string) (*os.File, error) + Destroy() error +} + +var _ CopyDir = ©Dir{} + +type copyDir struct { + fs afero.Fs + path string +} + +func NewCopyDir(fs afero.Fs) (CopyDir, error) { + dir, err := ioutil.TempDir("", "vfsCopy") + if nil != err { + return nil, err + } + return ©Dir{fs, dir}, nil +} + +func (d *copyDir) Retrieve(name string) (string, error) { + var path = d.Path(name) + if _, err := os.Stat(path); os.IsNotExist(err) { + var dir = filepath.Dir(path) + os.MkdirAll(dir, 0744) + dst, err := os.Create(path) + if nil != err { + return path, err + } + defer dst.Close() + src, err := d.fs.Open(name) + if nil != err { + return path, err + } + defer src.Close() + _, err = io.Copy(dst, src) + if nil != err { + return path, err + } + } + return path, nil +} + +func (d *copyDir) Path(name string) string { + return filepath.Join(d.path, name) +} + +func (d *copyDir) Open(name string) (*os.File, error) { + path, err := d.Retrieve(name) + if nil != err { + return nil, err + } + return os.Open(path) +} + +func (d *copyDir) Destroy() error { + return os.RemoveAll(d.path) +}