From a62d059561837e5db66ed2e00b13f29fed93fdf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Tue, 8 Sep 2020 20:19:27 +0200 Subject: [PATCH 01/51] Initial commit for tarfs --- tarfs/file.go | 70 +++++++++++++++++++++++++++++++ tarfs/fs.go | 95 +++++++++++++++++++++++++++++++++++++++++++ tarfs/tarfs_test.go | 47 +++++++++++++++++++++ tarfs/testdata/t.tar | Bin 0 -> 30720 bytes 4 files changed, 212 insertions(+) create mode 100644 tarfs/file.go create mode 100644 tarfs/fs.go create mode 100644 tarfs/tarfs_test.go create mode 100644 tarfs/testdata/t.tar diff --git a/tarfs/file.go b/tarfs/file.go new file mode 100644 index 00000000..dc12d3ea --- /dev/null +++ b/tarfs/file.go @@ -0,0 +1,70 @@ +package tarfs + +import ( + "archive/tar" + "os" + "syscall" +) + +type File struct { + h *tar.Header + data []byte + at int64 + closed bool +} + +func (f *File) Close() error { + panic("not implemented") // TODO: Implement +} + +func (f *File) Read(p []byte) (n int, err error) { + if f.h.Typeflag == tar.TypeDir { + return 0, syscall.EISDIR + } + + return 0, nil +} + +func (f *File) ReadAt(p []byte, off int64) (n int, err error) { + panic("not implemented") // TODO: Implement +} + +func (f *File) Seek(offset int64, whence int) (int64, error) { + panic("not implemented") // TODO: Implement +} + +func (f *File) Write(p []byte) (n int, err error) { + panic("not implemented") // TODO: Implement +} + +func (f *File) WriteAt(p []byte, off int64) (n int, err error) { + panic("not implemented") // TODO: Implement +} + +func (f *File) Name() string { + panic("not implemented") // TODO: Implement +} + +func (f *File) Readdir(count int) ([]os.FileInfo, error) { + panic("not implemented") // TODO: Implement +} + +func (f *File) Readdirnames(n int) ([]string, error) { + panic("not implemented") // TODO: Implement +} + +func (f *File) Stat() (os.FileInfo, error) { + panic("not implemented") // TODO: Implement +} + +func (f *File) Sync() error { + panic("not implemented") // TODO: Implement +} + +func (f *File) Truncate(size int64) error { + panic("not implemented") // TODO: Implement +} + +func (f *File) WriteString(s string) (ret int, err error) { + panic("not implemented") // TODO: Implement +} diff --git a/tarfs/fs.go b/tarfs/fs.go new file mode 100644 index 00000000..cbae87c9 --- /dev/null +++ b/tarfs/fs.go @@ -0,0 +1,95 @@ +// package tarfs implements a read-only in-memory representation of a tar archive +package tarfs + +import ( + "archive/tar" + "bytes" + "errors" + "io" + "os" + "path/filepath" + "time" + + "github.com/spf13/afero" +) + +var ( + ErrNotImplemented = errors.New("Not implemented") +) + +type Fs struct { + files map[string]*File +} + +func New(t *tar.Reader) *Fs { + fs := &Fs{files: make(map[string]*File)} + for { + hdr, err := t.Next() + if err == io.EOF { + break + } + if err != nil { + return nil + } + + f := &File{ + h: hdr, + at: 0, + closed: true, + } + + var buf bytes.Buffer + io.Copy(&buf, t) + + fs.files[filepath.Clean(hdr.Name)] = f + + } + + return fs +} + +func (fs *Fs) Open(name string) (afero.File, error) { + panic("not implemented") +} + +func (fs *Fs) Name() string { return "tarfs" } + +func (fs *Fs) Create(name string) (afero.File, error) { + panic("not implemented") +} + +func (fs *Fs) Mkdir(name string, perm os.FileMode) error { + panic("not implemented") +} + +func (fs *Fs) MkdirAll(path string, perm os.FileMode) error { + panic("not implemented") +} + +func (fs *Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { + panic("not implemented") +} + +func (fs *Fs) Remove(name string) error { + panic("not implemented") +} + +func (fs *Fs) RemoveAll(path string) error { + panic("not implemented") +} + +func (fs *Fs) Rename(oldname string, newname string) error { + panic("not implemented") +} + +func (fs *Fs) Stat(name string) (os.FileInfo, error) { + panic("not implemented") +} + +func (fs *Fs) Chmod(name string, mode os.FileMode) error { + panic("not implemented") +} + +func (fs *Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { + panic("not implemented") +} diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go new file mode 100644 index 00000000..5b543f8f --- /dev/null +++ b/tarfs/tarfs_test.go @@ -0,0 +1,47 @@ +package tarfs + +import ( + "archive/tar" + "os" + "testing" +) + +func TestFsNew(t *testing.T) { + tf, err := os.Open("testdata/t.tar") + if err != nil { + t.Fatal(err) + } + + tr := tar.NewReader(tf) + tfs := New(tr) + + files := []struct { + name string + exists bool + isdir bool + size int64 + }{ + {"sub", true, true, 0}, + {"sub/testDir2", true, true, 0}, + {"testFile", true, false, 8192}, + {"testDir1/testFile", true, false, 8192}, + + {"nonExisting", false, false, 0}, + } + + t.Logf("+%v", tfs) + + for _, f := range files { + e, found := tfs.files[f.name] + if found != f.exists { + t.Fatalf("%v exists == %v, should be %v", f.name, found, f.exists) + } + + if f.exists { + if e.h.Typeflag == tar.TypeDir && !f.isdir { + t.Errorf("%v is a directory, and should not be", e.Name()) + } + } + } + +} diff --git a/tarfs/testdata/t.tar b/tarfs/testdata/t.tar new file mode 100644 index 0000000000000000000000000000000000000000..d5b9aa0fb5ebdcc5f85b3921202f8b4d5d513af2 GIT binary patch literal 30720 zcmeI)OHPA800v-=(i;dfJT73oM_Y+$qAvQ_czS1y5>04i0j9xkHZ)8+!1oWkKb$VR zyJF!{rqMM`kq%YYl;4keDvzRyp^Rl6n>Ni?p$xIuGz;I?$MTocd3)S!itW?krGM?; zu3huD`D_2X@$;vY|G7V%?+?eY)JDhwPt3m#U7hleO=#u+7hC5?{&^!j9G?4`FP!sl z>y+#0`ycZ3@cy?kru>^wrToj8?_+eifBydO4FUuR5FkK+009C72oNAZVD$oz1_1&D z2oNAZfB*pk1PBlyuzG>d?oYK;_a}EQa=~MA|ECSP|JB7x|7WzXGV{qH|Ao3gU-?h& z|CC+Izpg{0|1)#cqs#3K0t5&UAV7cs0RjXF5FkKc^#ZFeIC($<1PBlyK!5-N0t5&U zAV6ThVC`e*8|41a#QsMaWA1;1E`R^Y_g@CW#eQrJ0t5&UAV7cs0RjXF5FkKc^#ZFe qxOhMU1PBlyK!5-N0t5&UAV6TZSh2vu1PBlyK!5-N0t5)$oWMKSbAGu1 literal 0 HcmV?d00001 From b5d563f368a9ae4a1540da3084272ebaeaf9f997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Tue, 8 Sep 2020 21:27:47 +0200 Subject: [PATCH 02/51] tarfs: reword "open" status field --- tarfs/file.go | 8 ++++---- tarfs/fs.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tarfs/file.go b/tarfs/file.go index dc12d3ea..dfda5477 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -7,10 +7,10 @@ import ( ) type File struct { - h *tar.Header - data []byte - at int64 - closed bool + h *tar.Header + data []byte + at int64 + open bool } func (f *File) Close() error { diff --git a/tarfs/fs.go b/tarfs/fs.go index cbae87c9..e402ed59 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -33,9 +33,9 @@ func New(t *tar.Reader) *Fs { } f := &File{ - h: hdr, - at: 0, - closed: true, + h: hdr, + at: 0, + open: false, } var buf bytes.Buffer From 65021f4c9cdc0b6054d93f226be1a8149db8c51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Tue, 8 Sep 2020 21:46:07 +0200 Subject: [PATCH 03/51] tarfs: use TestMain for FS setup We want to have the FS variable available through all the tests, so we we use a common "setup" function to initialise it. --- tarfs/tarfs_test.go | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 5b543f8f..dbd8ec23 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -2,32 +2,39 @@ package tarfs import ( "archive/tar" + "fmt" "os" "testing" ) -func TestFsNew(t *testing.T) { +var files = []struct { + name string + exists bool + isdir bool + size int64 +}{ + {"sub", true, true, 0}, + {"sub/testDir2", true, true, 0}, + {"testFile", true, false, 8192}, + {"testDir1/testFile", true, false, 8192}, + + {"nonExisting", false, false, 0}, +} + +var tfs *Fs + +func TestMain(m *testing.M) { tf, err := os.Open("testdata/t.tar") if err != nil { - t.Fatal(err) + fmt.Print(err) + os.Exit(1) } - tr := tar.NewReader(tf) - tfs := New(tr) - - files := []struct { - name string - exists bool - isdir bool - size int64 - }{ - {"sub", true, true, 0}, - {"sub/testDir2", true, true, 0}, - {"testFile", true, false, 8192}, - {"testDir1/testFile", true, false, 8192}, - - {"nonExisting", false, false, 0}, - } + tfs = New(tar.NewReader(tf)) + os.Exit(m.Run()) +} + +func TestFsNew(t *testing.T) { t.Logf("+%v", tfs) From b9649ab945b60967b341880711bb890e92d3933d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Tue, 8 Sep 2020 21:48:29 +0200 Subject: [PATCH 04/51] tarfs: test: early exit for nonexisting files --- tarfs/tarfs_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index dbd8ec23..35fe7a85 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -44,10 +44,12 @@ func TestFsNew(t *testing.T) { t.Fatalf("%v exists == %v, should be %v", f.name, found, f.exists) } - if f.exists { - if e.h.Typeflag == tar.TypeDir && !f.isdir { - t.Errorf("%v is a directory, and should not be", e.Name()) - } + if !f.exists { + continue + } + + if e.h.Typeflag == tar.TypeDir && !f.isdir { + t.Errorf("%v is a directory, and should not be", e.Name()) } } From dca46441a89c2e1eaeb83d30c81ce201112e15f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Tue, 8 Sep 2020 21:50:29 +0200 Subject: [PATCH 05/51] tarfs: create test for filesystem Open --- tarfs/tarfs_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 35fe7a85..3070fc48 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -53,4 +53,29 @@ func TestFsNew(t *testing.T) { } } +func TestFsOpen(t *testing.T) { + for _, f := range files { + file, err := tfs.Open(f.name) + if (err == nil) != f.exists { + t.Errorf("%v exists = %v, but got err = %v", file.Name(), f.exists, err) + } + + if !f.exists { + continue + } + + s, err := file.Stat() + if err != nil { + t.Errorf("stat %v: got error '%v'", file.Name(), err) + continue + } + + if isdir := s.IsDir(); isdir != f.isdir { + t.Errorf("%v directory, got: %v, expected: %v", file.Name(), isdir, f.isdir) + } + + if size := s.Size(); size != f.size { + t.Errorf("%v size, got: %v, expected: %v", file.Name(), size, f.size) + } + } } From 3878f910da95ba8e21b25c9961f4837f342c3c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Tue, 8 Sep 2020 21:51:46 +0200 Subject: [PATCH 06/51] tarfs: implement File.Stat --- tarfs/file.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tarfs/file.go b/tarfs/file.go index dfda5477..7cfc41dd 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -54,7 +54,8 @@ func (f *File) Readdirnames(n int) ([]string, error) { } func (f *File) Stat() (os.FileInfo, error) { - panic("not implemented") // TODO: Implement + return f.h.FileInfo(), nil + } func (f *File) Sync() error { From 240527da07c5c9687082898aa85480acd27d773b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Tue, 8 Sep 2020 21:52:34 +0200 Subject: [PATCH 07/51] tarfs: implement Fs.Open --- tarfs/fs.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index e402ed59..57173481 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -8,6 +8,7 @@ import ( "io" "os" "path/filepath" + "syscall" "time" "github.com/spf13/afero" @@ -49,7 +50,15 @@ func New(t *tar.Reader) *Fs { } func (fs *Fs) Open(name string) (afero.File, error) { - panic("not implemented") + f, ok := fs.files[name] + if !ok { + return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} + } + + f.open = true + f.at = 0 + + return f, nil } func (fs *Fs) Name() string { return "tarfs" } From 70d0b417da3534a024fece251e7c063884cbcfbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Tue, 8 Sep 2020 22:04:07 +0200 Subject: [PATCH 08/51] tarfs: return error on non-supported methods As tarfs is a read-only filesystem backend, we return EROFS (Read-only file system) from any method that makes modifications. --- tarfs/file.go | 10 +++++----- tarfs/fs.go | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tarfs/file.go b/tarfs/file.go index 7cfc41dd..34a90814 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -34,11 +34,11 @@ func (f *File) Seek(offset int64, whence int) (int64, error) { } func (f *File) Write(p []byte) (n int, err error) { - panic("not implemented") // TODO: Implement + return 0, syscall.EROFS } func (f *File) WriteAt(p []byte, off int64) (n int, err error) { - panic("not implemented") // TODO: Implement + return 0, syscall.EROFS } func (f *File) Name() string { @@ -59,13 +59,13 @@ func (f *File) Stat() (os.FileInfo, error) { } func (f *File) Sync() error { - panic("not implemented") // TODO: Implement + return nil } func (f *File) Truncate(size int64) error { - panic("not implemented") // TODO: Implement + return syscall.EROFS } func (f *File) WriteString(s string) (ret int, err error) { - panic("not implemented") // TODO: Implement + return 0, syscall.EROFS } diff --git a/tarfs/fs.go b/tarfs/fs.go index 57173481..87853ec3 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -64,15 +64,15 @@ func (fs *Fs) Open(name string) (afero.File, error) { func (fs *Fs) Name() string { return "tarfs" } func (fs *Fs) Create(name string) (afero.File, error) { - panic("not implemented") + return nil, syscall.EROFS } func (fs *Fs) Mkdir(name string, perm os.FileMode) error { - panic("not implemented") + return syscall.EROFS } func (fs *Fs) MkdirAll(path string, perm os.FileMode) error { - panic("not implemented") + return syscall.EROFS } func (fs *Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { @@ -80,15 +80,15 @@ func (fs *Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, err } func (fs *Fs) Remove(name string) error { - panic("not implemented") + return syscall.EROFS } func (fs *Fs) RemoveAll(path string) error { - panic("not implemented") + return syscall.EROFS } func (fs *Fs) Rename(oldname string, newname string) error { - panic("not implemented") + return syscall.EROFS } func (fs *Fs) Stat(name string) (os.FileInfo, error) { @@ -96,9 +96,9 @@ func (fs *Fs) Stat(name string) (os.FileInfo, error) { } func (fs *Fs) Chmod(name string, mode os.FileMode) error { - panic("not implemented") + return syscall.EROFS } func (fs *Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { - panic("not implemented") + return syscall.EROFS } From 1313d42fbfc7b2793347f91a77ae0e7233dea2cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Wed, 9 Sep 2020 20:10:10 +0200 Subject: [PATCH 09/51] tarfs: implement File.data as bytes.Reader Most of the operations that we want to implement for tarfs.File are already defined in bytes.Reader. We could use a plain slice and implement all the seeking manually, but I think using this is more convenient. --- tarfs/file.go | 4 ++-- tarfs/fs.go | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tarfs/file.go b/tarfs/file.go index 34a90814..4737fd0b 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -2,14 +2,14 @@ package tarfs import ( "archive/tar" + "bytes" "os" "syscall" ) type File struct { h *tar.Header - data []byte - at int64 + data *bytes.Reader open bool } diff --git a/tarfs/fs.go b/tarfs/fs.go index 87853ec3..e892bf3b 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -35,12 +35,20 @@ func New(t *tar.Reader) *Fs { f := &File{ h: hdr, - at: 0, open: false, } var buf bytes.Buffer - io.Copy(&buf, t) + size, err := buf.ReadFrom(t) + if err != nil { + panic("tarfs: reading from tar:" + err.Error()) + } + + if size != f.h.Size { + panic("tarfs: size mismatch") + } + + f.data = bytes.NewReader(buf.Bytes()) fs.files[filepath.Clean(hdr.Name)] = f @@ -56,7 +64,7 @@ func (fs *Fs) Open(name string) (afero.File, error) { } f.open = true - f.at = 0 + f.data.Seek(0, io.SeekStart) return f, nil } From c3757a78ad43d8368caa6eecec8fc86a2cd3a5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Wed, 9 Sep 2020 20:16:12 +0200 Subject: [PATCH 10/51] tarfs: short format for simple methods --- tarfs/file.go | 25 ++++++------------------- tarfs/fs.go | 32 ++++++++------------------------ 2 files changed, 14 insertions(+), 43 deletions(-) diff --git a/tarfs/file.go b/tarfs/file.go index 4737fd0b..eb099db2 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -33,13 +33,9 @@ func (f *File) Seek(offset int64, whence int) (int64, error) { panic("not implemented") // TODO: Implement } -func (f *File) Write(p []byte) (n int, err error) { - return 0, syscall.EROFS -} +func (f *File) Write(p []byte) (n int, err error) { return 0, syscall.EROFS } -func (f *File) WriteAt(p []byte, off int64) (n int, err error) { - return 0, syscall.EROFS -} +func (f *File) WriteAt(p []byte, off int64) (n int, err error) { return 0, syscall.EROFS } func (f *File) Name() string { panic("not implemented") // TODO: Implement @@ -53,19 +49,10 @@ func (f *File) Readdirnames(n int) ([]string, error) { panic("not implemented") // TODO: Implement } -func (f *File) Stat() (os.FileInfo, error) { - return f.h.FileInfo(), nil +func (f *File) Stat() (os.FileInfo, error) { return f.h.FileInfo(), nil } -} +func (f *File) Sync() error { return nil } -func (f *File) Sync() error { - return nil -} +func (f *File) Truncate(size int64) error { return syscall.EROFS } -func (f *File) Truncate(size int64) error { - return syscall.EROFS -} - -func (f *File) WriteString(s string) (ret int, err error) { - return 0, syscall.EROFS -} +func (f *File) WriteString(s string) (ret int, err error) { return 0, syscall.EROFS } diff --git a/tarfs/fs.go b/tarfs/fs.go index e892bf3b..5160b4c0 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -71,42 +71,26 @@ func (fs *Fs) Open(name string) (afero.File, error) { func (fs *Fs) Name() string { return "tarfs" } -func (fs *Fs) Create(name string) (afero.File, error) { - return nil, syscall.EROFS -} +func (fs *Fs) Create(name string) (afero.File, error) { return nil, syscall.EROFS } -func (fs *Fs) Mkdir(name string, perm os.FileMode) error { - return syscall.EROFS -} +func (fs *Fs) Mkdir(name string, perm os.FileMode) error { return syscall.EROFS } -func (fs *Fs) MkdirAll(path string, perm os.FileMode) error { - return syscall.EROFS -} +func (fs *Fs) MkdirAll(path string, perm os.FileMode) error { return syscall.EROFS } func (fs *Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { panic("not implemented") } -func (fs *Fs) Remove(name string) error { - return syscall.EROFS -} +func (fs *Fs) Remove(name string) error { return syscall.EROFS } -func (fs *Fs) RemoveAll(path string) error { - return syscall.EROFS -} +func (fs *Fs) RemoveAll(path string) error { return syscall.EROFS } -func (fs *Fs) Rename(oldname string, newname string) error { - return syscall.EROFS -} +func (fs *Fs) Rename(oldname string, newname string) error { return syscall.EROFS } func (fs *Fs) Stat(name string) (os.FileInfo, error) { panic("not implemented") } -func (fs *Fs) Chmod(name string, mode os.FileMode) error { - return syscall.EROFS -} +func (fs *Fs) Chmod(name string, mode os.FileMode) error { return syscall.EROFS } -func (fs *Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { - return syscall.EROFS -} +func (fs *Fs) Chtimes(name string, atime time.Time, mtime time.Time) error { return syscall.EROFS } From 4fa2df30d10e9eb48e6bf761fe96925a02c13324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Wed, 9 Sep 2020 20:55:41 +0200 Subject: [PATCH 11/51] tarfs: add missing closing brace in tests --- tarfs/tarfs_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 3070fc48..887e01c0 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -1,3 +1,4 @@ +// Most of the tests are stolen from the zipfs implementation package tarfs import ( @@ -53,6 +54,8 @@ func TestFsNew(t *testing.T) { } } +} + func TestFsOpen(t *testing.T) { for _, f := range files { file, err := tfs.Open(f.name) From fd69b075575437d11785c48719f305b1dfd85754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Wed, 9 Sep 2020 20:56:34 +0200 Subject: [PATCH 12/51] tarfs: add test for File.ReadAt --- tarfs/tarfs_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 887e01c0..df636135 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -82,3 +82,26 @@ func TestFsOpen(t *testing.T) { } } } + +func TestFileOps(t *testing.T) { + for _, f := range files { + if !f.exists { + continue + } + + file, err := tfs.Open(f.name) + if err != nil { + t.Fatalf("opening %v: %v", f.name, err) + } + + buf := make([]byte, 8) + if n, err := file.ReadAt(buf, 4092); err != nil { + t.Error(err) + } else if n != 8 { + t.Errorf("expected to read 8 bytes, got %d", n) + } else if string(buf) != "aaaabbbb" { + t.Errorf("expected to get , got <%s>", string(buf)) + } + + } +} From c00fca0ee59da4cce49959cedf8d26e46add0b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Wed, 9 Sep 2020 22:06:51 +0200 Subject: [PATCH 13/51] tarfs: test File.ReadAt --- tarfs/file.go | 5 ++++- tarfs/tarfs_test.go | 37 ++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/tarfs/file.go b/tarfs/file.go index eb099db2..0525b4a9 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -26,7 +26,10 @@ func (f *File) Read(p []byte) (n int, err error) { } func (f *File) ReadAt(p []byte, off int64) (n int, err error) { - panic("not implemented") // TODO: Implement + if f.h.Typeflag == tar.TypeDir { + return 0, syscall.EISDIR + } + return f.data.ReadAt(p, off) } func (f *File) Seek(offset int64, whence int) (int64, error) { diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index df636135..90b53534 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -5,21 +5,23 @@ import ( "archive/tar" "fmt" "os" + "syscall" "testing" ) var files = []struct { - name string - exists bool - isdir bool - size int64 + name string + exists bool + isdir bool + size int64 + contentAt4k string }{ - {"sub", true, true, 0}, - {"sub/testDir2", true, true, 0}, - {"testFile", true, false, 8192}, - {"testDir1/testFile", true, false, 8192}, + {"sub", true, true, 0, ""}, + {"sub/testDir2", true, true, 0, ""}, + {"testFile", true, false, 8192, "aaaabbbb"}, + {"testDir1/testFile", true, false, 8192, "bbbbcccc"}, - {"nonExisting", false, false, 0}, + {"nonExisting", false, false, 0, ""}, } var tfs *Fs @@ -83,7 +85,7 @@ func TestFsOpen(t *testing.T) { } } -func TestFileOps(t *testing.T) { +func TestReadAt(t *testing.T) { for _, f := range files { if !f.exists { continue @@ -95,12 +97,17 @@ func TestFileOps(t *testing.T) { } buf := make([]byte, 8) - if n, err := file.ReadAt(buf, 4092); err != nil { - t.Error(err) + n, err := file.ReadAt(buf, 4092) + if err != nil { + if f.isdir && (err != syscall.EISDIR) { + t.Errorf("%v got error %v, expected EISDIR", f.name, err) + } else if !f.isdir { + t.Errorf("%v: %v", f.name, err) + } } else if n != 8 { - t.Errorf("expected to read 8 bytes, got %d", n) - } else if string(buf) != "aaaabbbb" { - t.Errorf("expected to get , got <%s>", string(buf)) + t.Errorf("%v: got %d read bytes, expected 8", f.name, n) + } else if string(buf) != f.contentAt4k { + t.Errorf("%v: got <%s>, expected <%s>", f.name, f.contentAt4k, string(buf)) } } From cf078ed52ffc2e8ec22db87bdb1eeef2f404243b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Thu, 10 Sep 2020 19:49:39 +0200 Subject: [PATCH 14/51] tarfs: add tests for File.Read --- tarfs/tarfs_test.go | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 90b53534..7d7f72a7 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -14,14 +14,15 @@ var files = []struct { exists bool isdir bool size int64 + content string contentAt4k string }{ - {"sub", true, true, 0, ""}, - {"sub/testDir2", true, true, 0, ""}, - {"testFile", true, false, 8192, "aaaabbbb"}, - {"testDir1/testFile", true, false, 8192, "bbbbcccc"}, + {"sub", true, true, 0, "", ""}, + {"sub/testDir2", true, true, 0, "", ""}, + {"testFile", true, false, 8192, "aaaaaaaa", "aaaabbbb"}, + {"testDir1/testFile", true, false, 8192, "bbbbbbbb", "bbbbcccc"}, - {"nonExisting", false, false, 0, ""}, + {"nonExisting", false, false, 0, "", ""}, } var tfs *Fs @@ -85,6 +86,34 @@ func TestFsOpen(t *testing.T) { } } +func TestRead(t *testing.T) { + for _, f := range files { + if !f.exists { + continue + } + + file, err := tfs.Open(f.name) + if err != nil { + t.Fatalf("opening %v: %v", f.name, err) + } + + buf := make([]byte, 8) + n, err := file.Read(buf) + if err != nil { + if f.isdir && (err != syscall.EISDIR) { + t.Errorf("%v got error %v, expected EISDIR", f.name, err) + } else if !f.isdir { + t.Errorf("%v: %v", f.name, err) + } + } else if n != 8 { + t.Errorf("%v: got %d read bytes, expected 8", f.name, n) + } else if string(buf) != f.content { + t.Errorf("%v: got <%s>, expected <%s>", f.name, f.content, string(buf)) + } + + } +} + func TestReadAt(t *testing.T) { for _, f := range files { if !f.exists { From a6fc4ef1876a7798777e544d7616e2788969e45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Thu, 10 Sep 2020 19:52:09 +0200 Subject: [PATCH 15/51] tarfs: implement File.Read --- tarfs/file.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tarfs/file.go b/tarfs/file.go index 0525b4a9..1f97c68c 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -18,11 +18,7 @@ func (f *File) Close() error { } func (f *File) Read(p []byte) (n int, err error) { - if f.h.Typeflag == tar.TypeDir { - return 0, syscall.EISDIR - } - - return 0, nil + return f.ReadAt(p, 0) } func (f *File) ReadAt(p []byte, off int64) (n int, err error) { From d2a31b6f25610be44ebf8adb8968783b85210271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Thu, 10 Sep 2020 20:07:27 +0200 Subject: [PATCH 16/51] tarfs: add tests for File.Seek --- tarfs/tarfs_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 7d7f72a7..71696552 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -4,6 +4,7 @@ package tarfs import ( "archive/tar" "fmt" + "io" "os" "syscall" "testing" @@ -141,3 +142,45 @@ func TestReadAt(t *testing.T) { } } + +func TestSeek(t *testing.T) { + for _, f := range files { + if !f.exists { + continue + } + + file, err := tfs.Open(f.name) + if err != nil { + t.Fatalf("opening %v: %v", f.name, err) + } + + var tests = []struct { + offin int64 + whence int + offout int64 + }{ + {0, io.SeekStart, 0}, + {10, io.SeekStart, 10}, + {1, io.SeekCurrent, 11}, + {10, io.SeekCurrent, 21}, + {0, io.SeekEnd, f.size}, + {-1, io.SeekEnd, f.size - 1}, + } + + for _, s := range tests { + n, err := file.Seek(s.offin, s.whence) + if err != nil { + if f.isdir && err == syscall.EISDIR { + continue + } + + t.Errorf("%v: %v", f.name, err) + } + + if n != s.offout { + t.Errorf("%v: (off: %v, whence: %v): got %v, expected %v", f.name, s.offin, s.whence, n, s.offout) + } + } + + } +} From cb072ce9d353f4af5bf604190b63918985a15195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Thu, 10 Sep 2020 20:16:38 +0200 Subject: [PATCH 17/51] tarfs: implement File.Seek --- tarfs/file.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tarfs/file.go b/tarfs/file.go index 1f97c68c..5a92318e 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -29,7 +29,11 @@ func (f *File) ReadAt(p []byte, off int64) (n int, err error) { } func (f *File) Seek(offset int64, whence int) (int64, error) { - panic("not implemented") // TODO: Implement + if f.h.Typeflag == tar.TypeDir { + return 0, syscall.EISDIR + } + + return f.data.Seek(offset, whence) } func (f *File) Write(p []byte) (n int, err error) { return 0, syscall.EROFS } From 5efe21566cf8f3b5776220a6affea4a37d6182d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Thu, 10 Sep 2020 20:20:31 +0200 Subject: [PATCH 18/51] tarfs: add tests for File.Name --- tarfs/tarfs_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 71696552..59eb8877 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -184,3 +184,22 @@ func TestSeek(t *testing.T) { } } + +func TestName(t *testing.T) { + for _, f := range files { + if !f.exists { + continue + } + + file, err := tfs.Open(f.name) + if err != nil { + t.Fatalf("opening %v: %v", f.name, err) + } + + n := file.Name() + if n != f.name { + t.Errorf("got: %v, expected: %v", n, f.name) + } + + } +} From 1964cda00382ba3959f0e86cff1ec8388d0f01a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Thu, 10 Sep 2020 20:27:06 +0200 Subject: [PATCH 19/51] tarfs: implement File.Name --- tarfs/file.go | 2 +- tarfs/fs.go | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tarfs/file.go b/tarfs/file.go index 5a92318e..88bed112 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -41,7 +41,7 @@ func (f *File) Write(p []byte) (n int, err error) { return 0, syscall.EROFS } func (f *File) WriteAt(p []byte, off int64) (n int, err error) { return 0, syscall.EROFS } func (f *File) Name() string { - panic("not implemented") // TODO: Implement + return f.h.Name } func (f *File) Readdir(count int) ([]os.FileInfo, error) { diff --git a/tarfs/fs.go b/tarfs/fs.go index 5160b4c0..f8d0a15e 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -50,7 +50,13 @@ func New(t *tar.Reader) *Fs { f.data = bytes.NewReader(buf.Bytes()) - fs.files[filepath.Clean(hdr.Name)] = f + name := filepath.Clean(hdr.Name) + if name[len(name)-1] == '/' { + name = name[1:] + } + + fs.files[name] = f + f.h.Name = name } From 2460424de566a09dfb14e6b134f61d12d60c59f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Thu, 10 Sep 2020 20:34:15 +0200 Subject: [PATCH 20/51] tarfs: add tests for File.Close --- tarfs/tarfs_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 59eb8877..45459bb8 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -203,3 +203,42 @@ func TestName(t *testing.T) { } } + +func TestClose(t *testing.T) { + for _, f := range files { + if !f.exists { + continue + } + + file, err := tfs.Open(f.name) + if err != nil { + t.Fatalf("opening %v: %v", f.name, err) + } + + err = file.Close() + if err != nil { + t.Errorf("%v: %v", f.name, err) + } + + err = file.Close() + if err == nil { + t.Errorf("%v: closing twice should return an error", f.name) + } + + buf := make([]byte, 8) + n, err := file.Read(buf) + if n != 0 || err == nil { + t.Errorf("%v: could read from a closed file", f.name) + } + + n, err = file.ReadAt(buf, 256) + if n != 0 || err == nil { + t.Errorf("%v: could readAt from a closed file", f.name) + } + + off, err := file.Seek(0, io.SeekStart) + if off != 0 || err == nil { + t.Errorf("%v: could seek from a closed file", f.name) + } + } +} From de4ff61149759b78789997be8c51d49dfbbba95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Thu, 10 Sep 2020 21:01:51 +0200 Subject: [PATCH 21/51] tarfs: implement File.Close --- tarfs/file.go | 37 ++++++++++++++++++++++++++++++++----- tarfs/fs.go | 7 ++++--- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/tarfs/file.go b/tarfs/file.go index 88bed112..8d2f45a9 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -5,30 +5,57 @@ import ( "bytes" "os" "syscall" + + "github.com/spf13/afero" ) type File struct { - h *tar.Header - data *bytes.Reader - open bool + h *tar.Header + data *bytes.Reader + closed bool } func (f *File) Close() error { - panic("not implemented") // TODO: Implement + if f.closed { + return afero.ErrFileClosed + } + + f.closed = true + f.h = nil + f.data = nil + + return nil } func (f *File) Read(p []byte) (n int, err error) { - return f.ReadAt(p, 0) + if f.closed { + return 0, afero.ErrFileClosed + } + + if f.h.Typeflag == tar.TypeDir { + return 0, syscall.EISDIR + } + + return f.data.Read(p) } func (f *File) ReadAt(p []byte, off int64) (n int, err error) { + if f.closed { + return 0, afero.ErrFileClosed + } + if f.h.Typeflag == tar.TypeDir { return 0, syscall.EISDIR } + return f.data.ReadAt(p, off) } func (f *File) Seek(offset int64, whence int) (int64, error) { + if f.closed { + return 0, afero.ErrFileClosed + } + if f.h.Typeflag == tar.TypeDir { return 0, syscall.EISDIR } diff --git a/tarfs/fs.go b/tarfs/fs.go index f8d0a15e..62d85575 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -34,8 +34,7 @@ func New(t *tar.Reader) *Fs { } f := &File{ - h: hdr, - open: false, + h: hdr, } var buf bytes.Buffer @@ -69,7 +68,9 @@ func (fs *Fs) Open(name string) (afero.File, error) { return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} } - f.open = true + // TODO: return a copy of the File structure instead of a pointer to the original + // saved in the filesystem + f.closed = false f.data.Seek(0, io.SeekStart) return f, nil From 2272432f9eea5e640948f4c369fe9806dfa879fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 00:01:11 +0200 Subject: [PATCH 22/51] tarfs: add tests for OpenFile --- tarfs/tarfs_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 45459bb8..823d3e19 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -3,6 +3,7 @@ package tarfs import ( "archive/tar" + "errors" "fmt" "io" "os" @@ -242,3 +243,29 @@ func TestClose(t *testing.T) { } } } + +func TestOpenFile(t *testing.T) { + for _, f := range files { + file, err := tfs.OpenFile(f.name, os.O_RDONLY, 0400) + if !f.exists { + if !errors.Is(err, syscall.ENOENT) { + t.Errorf("%v: got %v, expected%v", f.name, err, syscall.ENOENT) + } + file.Close() + + continue + } + + if err != nil { + t.Errorf("%v: %v", f.name, err) + } + file.Close() + + file, err = tfs.OpenFile(f.name, os.O_CREATE, 0600) + if !errors.Is(err, syscall.EPERM) { + t.Errorf("%v: open for write: got %v, expected %v", f.name, err, syscall.EPERM) + } + file.Close() + + } +} From fa95f2a32102b4bedbf4ae37ef294cfc474fa05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 12:42:50 +0200 Subject: [PATCH 23/51] tarfs: fix test for Fs.OpenFile If the call fails, we don't have to close the file --- tarfs/tarfs_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 823d3e19..b098182c 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -251,7 +251,6 @@ func TestOpenFile(t *testing.T) { if !errors.Is(err, syscall.ENOENT) { t.Errorf("%v: got %v, expected%v", f.name, err, syscall.ENOENT) } - file.Close() continue } @@ -265,7 +264,6 @@ func TestOpenFile(t *testing.T) { if !errors.Is(err, syscall.EPERM) { t.Errorf("%v: open for write: got %v, expected %v", f.name, err, syscall.EPERM) } - file.Close() } } From 1263aae68c9170cbf96232a56d298a432b97da35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 12:44:34 +0200 Subject: [PATCH 24/51] tarfs: remove code not needed after using filepath.Clean --- tarfs/fs.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index 62d85575..656b5438 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -50,10 +50,6 @@ func New(t *tar.Reader) *Fs { f.data = bytes.NewReader(buf.Bytes()) name := filepath.Clean(hdr.Name) - if name[len(name)-1] == '/' { - name = name[1:] - } - fs.files[name] = f f.h.Name = name From 46a440e6ad85e861717fcb54fb09efdd0ab13913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 12:45:49 +0200 Subject: [PATCH 25/51] tarfs: Open: return a copy of the internal structure As we modify the struct fields when closing, we don't want to lose the internal representation of the file, in case we want to reopen it. Return a copy of the File, although we keep using the same pointers to tar.Header and buffer.Reader. Maybe we will need to change that in the future. --- tarfs/fs.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index 656b5438..cb169cfb 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -64,12 +64,10 @@ func (fs *Fs) Open(name string) (afero.File, error) { return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} } - // TODO: return a copy of the File structure instead of a pointer to the original - // saved in the filesystem - f.closed = false - f.data.Seek(0, io.SeekStart) + // preserve the original data, and return a copy instead + cp := &File{h: f.h, data: f.data} - return f, nil + return cp, nil } func (fs *Fs) Name() string { return "tarfs" } From 61466af3b20a31594c160ae549eba890f33f79bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 12:47:36 +0200 Subject: [PATCH 26/51] tarfs: implement Fs.OpenFile --- tarfs/fs.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index cb169cfb..a7bafe4a 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -79,7 +79,11 @@ func (fs *Fs) Mkdir(name string, perm os.FileMode) error { return syscall.EROFS func (fs *Fs) MkdirAll(path string, perm os.FileMode) error { return syscall.EROFS } func (fs *Fs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { - panic("not implemented") + if flag != os.O_RDONLY { + return nil, &os.PathError{Op: "open", Path: name, Err: syscall.EPERM} + } + + return fs.Open(name) } func (fs *Fs) Remove(name string) error { return syscall.EROFS } From 18b25525ff2171da29ae8802d7a5a893f4851988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 13:02:48 +0200 Subject: [PATCH 27/51] tarfs: use Fatalf for unexpected error in TestFsOpen --- tarfs/tarfs_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index b098182c..4ed730c9 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -74,8 +74,7 @@ func TestFsOpen(t *testing.T) { s, err := file.Stat() if err != nil { - t.Errorf("stat %v: got error '%v'", file.Name(), err) - continue + t.Fatalf("stat %v: got error '%v'", file.Name(), err) } if isdir := s.IsDir(); isdir != f.isdir { From 668fe50a029cbf4eb60dbc218ed7f1355bd9ce69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 13:03:45 +0200 Subject: [PATCH 28/51] tarfs: add tests for Fs.Stat --- tarfs/tarfs_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 4ed730c9..c567a1e9 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -266,3 +266,28 @@ func TestOpenFile(t *testing.T) { } } + +func TestFsStat(t *testing.T) { + for _, f := range files { + fi, err := tfs.Stat(f.name) + if !f.exists { + if !errors.Is(err, syscall.ENOENT) { + t.Errorf("%v: got %v, expected%v", f.name, err, syscall.ENOENT) + } + + continue + } + + if err != nil { + t.Errorf("stat %v: got error '%v'", f.name, err) + } + + if isdir := fi.IsDir(); isdir != f.isdir { + t.Errorf("%v directory, got: %v, expected: %v", f.name, isdir, f.isdir) + } + + if size := fi.Size(); size != f.size { + t.Errorf("%v size, got: %v, expected: %v", f.name, size, f.size) + } + } +} From ff11db1e3246cceb96103464a2def98477a1feaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 13:05:39 +0200 Subject: [PATCH 29/51] tarfs: implement Fs.Stat --- tarfs/fs.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index a7bafe4a..20fa15f0 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -93,7 +93,12 @@ func (fs *Fs) RemoveAll(path string) error { return syscall.EROFS } func (fs *Fs) Rename(oldname string, newname string) error { return syscall.EROFS } func (fs *Fs) Stat(name string) (os.FileInfo, error) { - panic("not implemented") + f, ok := fs.files[name] + if !ok { + return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT} + } + + return f.h.FileInfo(), nil } func (fs *Fs) Chmod(name string, mode os.FileMode) error { return syscall.EROFS } From aeb4371ea5246853701a0ceb34b381d558afaf44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 20:54:49 +0200 Subject: [PATCH 30/51] tarfs: remove TestNewFs That test depends too much on the internal imlementation, and it is easier to break if we change it. --- tarfs/tarfs_test.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index c567a1e9..a19ba28a 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -40,27 +40,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestFsNew(t *testing.T) { - - t.Logf("+%v", tfs) - - for _, f := range files { - e, found := tfs.files[f.name] - if found != f.exists { - t.Fatalf("%v exists == %v, should be %v", f.name, found, f.exists) - } - - if !f.exists { - continue - } - - if e.h.Typeflag == tar.TypeDir && !f.isdir { - t.Errorf("%v is a directory, and should not be", e.Name()) - } - } - -} - func TestFsOpen(t *testing.T) { for _, f := range files { file, err := tfs.Open(f.name) From e551b889eb0b593ed99508ab4c5c59080c775312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 20:56:30 +0200 Subject: [PATCH 31/51] tarfs: remove unused code --- tarfs/fs.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index 20fa15f0..883a0f72 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -4,7 +4,6 @@ package tarfs import ( "archive/tar" "bytes" - "errors" "io" "os" "path/filepath" @@ -14,10 +13,6 @@ import ( "github.com/spf13/afero" ) -var ( - ErrNotImplemented = errors.New("Not implemented") -) - type Fs struct { files map[string]*File } From 2244d647e84753a6aa9e1b4cd92a6027e6b6ca6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 20:57:32 +0200 Subject: [PATCH 32/51] tarfs: change internal implementation To be able to handle directories (File.Readdir, File.Readdirnames), the naive single-map implementation makes it a bit harder to implement. Inspired by the zipfs backend, switch to an internal implementation of a map of directories that contains a map of files, so the directory methods are easier to implement. Also, treat the "virtual" filesystem as absolute, just like zipfs does. --- tarfs/fs.go | 53 +++++++++++++++++++++++++++++++-------------- tarfs/tarfs_test.go | 10 ++++----- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index 883a0f72..a03c91f6 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -14,11 +14,22 @@ import ( ) type Fs struct { - files map[string]*File + files map[string]map[string]*File +} + +func splitpath(name string) (dir, file string) { + name = filepath.ToSlash(name) + if len(name) == 0 || name[0] != '/' { + name = "/" + name + } + name = filepath.Clean(name) + dir, file = filepath.Split(name) + dir = filepath.Clean(dir) + return } func New(t *tar.Reader) *Fs { - fs := &Fs{files: make(map[string]*File)} + fs := &Fs{files: make(map[string]map[string]*File)} for { hdr, err := t.Next() if err == io.EOF { @@ -28,8 +39,9 @@ func New(t *tar.Reader) *Fs { return nil } - f := &File{ - h: hdr, + d, f := splitpath(hdr.Name) + if _, ok := fs.files[d]; !ok { + fs.files[d] = make(map[string]*File) } var buf bytes.Buffer @@ -38,15 +50,17 @@ func New(t *tar.Reader) *Fs { panic("tarfs: reading from tar:" + err.Error()) } - if size != f.h.Size { + if size != hdr.Size { panic("tarfs: size mismatch") } - f.data = bytes.NewReader(buf.Bytes()) + file := &File{ + h: hdr, + data: bytes.NewReader(buf.Bytes()), + } + file.h.Name = filepath.Join(d, f) - name := filepath.Clean(hdr.Name) - fs.files[name] = f - f.h.Name = name + fs.files[d][f] = file } @@ -54,15 +68,17 @@ func New(t *tar.Reader) *Fs { } func (fs *Fs) Open(name string) (afero.File, error) { - f, ok := fs.files[name] - if !ok { + d, f := splitpath(name) + if _, ok := fs.files[d]; !ok { return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} } - // preserve the original data, and return a copy instead - cp := &File{h: f.h, data: f.data} + file, ok := fs.files[d][f] + if !ok { + return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} + } - return cp, nil + return &File{h: file.h, data: file.data}, nil } func (fs *Fs) Name() string { return "tarfs" } @@ -88,12 +104,17 @@ func (fs *Fs) RemoveAll(path string) error { return syscall.EROFS } func (fs *Fs) Rename(oldname string, newname string) error { return syscall.EROFS } func (fs *Fs) Stat(name string) (os.FileInfo, error) { - f, ok := fs.files[name] + d, f := splitpath(name) + if _, ok := fs.files[d]; !ok { + return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT} + } + + file, ok := fs.files[d][f] if !ok { return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT} } - return f.h.FileInfo(), nil + return file.h.FileInfo(), nil } func (fs *Fs) Chmod(name string, mode os.FileMode) error { return syscall.EROFS } diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index a19ba28a..4d842544 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -19,12 +19,12 @@ var files = []struct { content string contentAt4k string }{ - {"sub", true, true, 0, "", ""}, - {"sub/testDir2", true, true, 0, "", ""}, - {"testFile", true, false, 8192, "aaaaaaaa", "aaaabbbb"}, - {"testDir1/testFile", true, false, 8192, "bbbbbbbb", "bbbbcccc"}, + {"/sub", true, true, 0, "", ""}, + {"/sub/testDir2", true, true, 0, "", ""}, + {"/testFile", true, false, 8192, "aaaaaaaa", "aaaabbbb"}, + {"/testDir1/testFile", true, false, 8192, "bbbbbbbb", "bbbbcccc"}, - {"nonExisting", false, false, 0, "", ""}, + {"/nonExisting", false, false, 0, "", ""}, } var tfs *Fs From 649d57ce3ac55dcda76314bb4d89e1c9b090f44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 21:19:07 +0200 Subject: [PATCH 33/51] tarfs: use Fatal errors to avoid panics --- tarfs/tarfs_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 4d842544..42103300 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -44,12 +44,15 @@ func TestFsOpen(t *testing.T) { for _, f := range files { file, err := tfs.Open(f.name) if (err == nil) != f.exists { - t.Errorf("%v exists = %v, but got err = %v", file.Name(), f.exists, err) + t.Errorf("%v exists = %v, but got err = %v", f.name, f.exists, err) } if !f.exists { continue } + if err != nil { + t.Fatalf("%v: %v", f.name, err) + } s, err := file.Stat() if err != nil { @@ -234,7 +237,7 @@ func TestOpenFile(t *testing.T) { } if err != nil { - t.Errorf("%v: %v", f.name, err) + t.Fatalf("%v: %v", f.name, err) } file.Close() @@ -258,7 +261,7 @@ func TestFsStat(t *testing.T) { } if err != nil { - t.Errorf("stat %v: got error '%v'", f.name, err) + t.Fatalf("stat %v: got error '%v'", f.name, err) } if isdir := fi.IsDir(); isdir != f.isdir { From cade31062b4d9ec37bbf16cd78c0d53ea3db6387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 21:21:52 +0200 Subject: [PATCH 34/51] tarfs: add pseudoroot --- tarfs/fs.go | 10 ++++++++++ tarfs/tarfs_test.go | 1 + 2 files changed, 11 insertions(+) diff --git a/tarfs/fs.go b/tarfs/fs.go index a03c91f6..1da9f1ac 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -64,6 +64,16 @@ func New(t *tar.Reader) *Fs { } + // Add a pseudoroot + fs.files["/"][""] = &File{ + h: &tar.Header{ + Name: "/", + Typeflag: tar.TypeDir, + Size: 0, + }, + data: bytes.NewReader(nil), + } + return fs } diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 42103300..9b29f102 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -19,6 +19,7 @@ var files = []struct { content string contentAt4k string }{ + {"/", true, true, 0, "", ""}, {"/sub", true, true, 0, "", ""}, {"/sub/testDir2", true, true, 0, "", ""}, {"/testFile", true, false, 8192, "aaaaaaaa", "aaaabbbb"}, From b6929bbee4f22c1852df957c685fea1643b01270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 22:14:13 +0200 Subject: [PATCH 35/51] tarfs: add tests for File.Readdir --- tarfs/tarfs_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 9b29f102..f12fe03d 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "os" + "reflect" "syscall" "testing" ) @@ -22,12 +23,23 @@ var files = []struct { {"/", true, true, 0, "", ""}, {"/sub", true, true, 0, "", ""}, {"/sub/testDir2", true, true, 0, "", ""}, + {"/sub/testDir2/testFile", true, false, 8192, "cccccccc", "ccccdddd"}, {"/testFile", true, false, 8192, "aaaaaaaa", "aaaabbbb"}, {"/testDir1/testFile", true, false, 8192, "bbbbbbbb", "bbbbcccc"}, {"/nonExisting", false, false, 0, "", ""}, } +var dirs = []struct { + name string + children []string +}{ + {"/", []string{"sub", "testDir1", "testFile"}}, + {"/sub", []string{"testDir2"}}, + {"/sub/testDir2", []string{"testFile"}}, + {"/testDir1", []string{"testFile"}}, +} + var tfs *Fs func TestMain(m *testing.M) { @@ -274,3 +286,40 @@ func TestFsStat(t *testing.T) { } } } + +func TestReaddir(t *testing.T) { + for _, d := range dirs { + dir, err := tfs.Open(d.name) + if err != nil { + t.Fatal(err) + } + + fi, err := dir.Readdir(0) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(fi, d.children) { + t.Errorf("%v: children, got '%v', expected '%v'", d.name, fi, d.children) + } + + fi, err := dir.Readdir(1) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(fi, d.children[0:1]) { + t.Errorf("%v: children, got '%v', expected '%v'", d.name, fi, d.children[0:1]) + } + } + + dir, err := tfs.Open("/testFile") + if err != nil { + t.Fatal(err) + } + + _, err = dir.Readdir(-1) + if err != syscall.ENOTDIR { + t.Fatal("Expected error") + } +} From b78e068fc68c65156f7ea073a4225141a06b54d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 22:18:28 +0200 Subject: [PATCH 36/51] tarfs: add pointer Fs in the File structure For directory-related operations we will need to access the internal structure in the Fs. As Readdir and Readdirnames are File methods, we need to access such structure from the File. --- tarfs/file.go | 2 ++ tarfs/fs.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tarfs/file.go b/tarfs/file.go index 8d2f45a9..bd72c284 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -13,6 +13,7 @@ type File struct { h *tar.Header data *bytes.Reader closed bool + fs *Fs } func (f *File) Close() error { @@ -23,6 +24,7 @@ func (f *File) Close() error { f.closed = true f.h = nil f.data = nil + f.fs = nil return nil } diff --git a/tarfs/fs.go b/tarfs/fs.go index 1da9f1ac..8210a229 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -57,6 +57,7 @@ func New(t *tar.Reader) *Fs { file := &File{ h: hdr, data: bytes.NewReader(buf.Bytes()), + fs: fs, } file.h.Name = filepath.Join(d, f) @@ -72,6 +73,7 @@ func New(t *tar.Reader) *Fs { Size: 0, }, data: bytes.NewReader(nil), + fs: fs, } return fs From f32342c8a4826fa39c5b2381c88062cb6c13d385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Fri, 11 Sep 2020 22:22:00 +0200 Subject: [PATCH 37/51] tarfs: fix error --- tarfs/tarfs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index f12fe03d..402c8ee5 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -303,7 +303,7 @@ func TestReaddir(t *testing.T) { t.Errorf("%v: children, got '%v', expected '%v'", d.name, fi, d.children) } - fi, err := dir.Readdir(1) + fi, err = dir.Readdir(1) if err != nil { t.Fatal(err) } From 013b1283d20888c29d97738dd5e114b56a805b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sat, 12 Sep 2020 20:16:07 +0200 Subject: [PATCH 38/51] tarfs: use just the names for TestReaddir, easier than using fill os.FileInfo entries --- tarfs/tarfs_test.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 402c8ee5..68587390 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -298,9 +298,13 @@ func TestReaddir(t *testing.T) { if err != nil { t.Fatal(err) } + var names []string + for _, f := range fi { + names = append(names, f.Name()) + } - if !reflect.DeepEqual(fi, d.children) { - t.Errorf("%v: children, got '%v', expected '%v'", d.name, fi, d.children) + if !reflect.DeepEqual(names, d.children) { + t.Errorf("%v: children, got '%v', expected '%v'", d.name, names, d.children) } fi, err = dir.Readdir(1) @@ -308,8 +312,13 @@ func TestReaddir(t *testing.T) { t.Fatal(err) } - if !reflect.DeepEqual(fi, d.children[0:1]) { - t.Errorf("%v: children, got '%v', expected '%v'", d.name, fi, d.children[0:1]) + names = []string{} + for _, f := range fi { + names = append(names, f.Name()) + } + + if !reflect.DeepEqual(names, d.children[0:1]) { + t.Errorf("%v: children, got '%v', expected '%v'", d.name, names, d.children[0:1]) } } From 009c0136e03a73d8cbe62d6f4b7a25a568104813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sat, 12 Sep 2020 20:16:53 +0200 Subject: [PATCH 39/51] tarfs: create a copy of the original entry when opening a file We added the fs field in the File struct to reference the underlying Fs object, but in the Open cal we were not passing it, making all the opened files to have a nil pointer in that field. Change to make a copy of the original file, and returning that --- tarfs/fs.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index 8210a229..3cda013a 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -90,7 +90,9 @@ func (fs *Fs) Open(name string) (afero.File, error) { return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} } - return &File{h: file.h, data: file.data}, nil + nf := *file + + return &nf, nil } func (fs *Fs) Name() string { return "tarfs" } From fbc802a3efdf9edc3d8bb4544e6462cb08483da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sat, 12 Sep 2020 21:25:08 +0200 Subject: [PATCH 40/51] tarfs: implement File.Readdir --- tarfs/file.go | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/tarfs/file.go b/tarfs/file.go index bd72c284..22d703f5 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -4,6 +4,7 @@ import ( "archive/tar" "bytes" "os" + "sort" "syscall" "github.com/spf13/afero" @@ -73,8 +74,50 @@ func (f *File) Name() string { return f.h.Name } +func (f *File) getDirectoryNames() ([]string, error) { + d, ok := f.fs.files[f.Name()] + if !ok { + return nil, &os.PathError{Op: "readdir", Path: f.Name(), Err: syscall.ENOENT} + } + + var names []string + for n := range d { + names = append(names, n) + } + sort.Strings(names) + + return names, nil +} + func (f *File) Readdir(count int) ([]os.FileInfo, error) { - panic("not implemented") // TODO: Implement + if f.closed { + return nil, afero.ErrFileClosed + } + + if !f.h.FileInfo().IsDir() { + return nil, syscall.ENOTDIR + } + + names, err := f.getDirectoryNames() + if err != nil { + return nil, err + } + + d := f.fs.files[f.Name()] + var fi []os.FileInfo + for _, n := range names { + if n == "" { + continue + } + + f := d[n] + fi = append(fi, f.h.FileInfo()) + if count > 0 && len(fi) >= count { + break + } + } + + return fi, nil } func (f *File) Readdirnames(n int) ([]string, error) { From 94f7af9c7dfcb4afb219bc976cd827ac6e77e578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sat, 12 Sep 2020 21:28:31 +0200 Subject: [PATCH 41/51] tarfs: add tests for File.Readdirnames --- tarfs/tarfs_test.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 68587390..2ae6e243 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -332,3 +332,40 @@ func TestReaddir(t *testing.T) { t.Fatal("Expected error") } } + +func TestReaddirnames(t *testing.T) { + for _, d := range dirs { + dir, err := tfs.Open(d.name) + if err != nil { + t.Fatal(err) + } + + names, err := dir.Readdirnames(0) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(names, d.children) { + t.Errorf("%v: children, got '%v', expected '%v'", d.name, names, d.children) + } + + names, err = dir.Readdirnames(1) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(names, d.children[0:1]) { + t.Errorf("%v: children, got '%v', expected '%v'", d.name, names, d.children[0:1]) + } + } + + dir, err := tfs.Open("/testFile") + if err != nil { + t.Fatal(err) + } + + _, err = dir.Readdir(-1) + if err != syscall.ENOTDIR { + t.Fatal("Expected error") + } +} From e36f5a9d2b005ef6a6bbe79a44d46681e019fe47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sat, 12 Sep 2020 21:31:29 +0200 Subject: [PATCH 42/51] tarfs: implement Readdirnames --- tarfs/file.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tarfs/file.go b/tarfs/file.go index 22d703f5..677e5e6a 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -121,7 +121,17 @@ func (f *File) Readdir(count int) ([]os.FileInfo, error) { } func (f *File) Readdirnames(n int) ([]string, error) { - panic("not implemented") // TODO: Implement + fi, err := f.Readdir(n) + if err != nil { + return nil, err + } + + var names []string + for _, f := range fi { + names = append(names, f.Name()) + } + + return names, nil } func (f *File) Stat() (os.FileInfo, error) { return f.h.FileInfo(), nil } From c24a1b732f5a60e46e18a1962098fb0fe05f372e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sat, 12 Sep 2020 21:46:22 +0200 Subject: [PATCH 43/51] tarfs: add test for File.Name --- tarfs/tarfs_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 2ae6e243..b3d0bb94 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -67,6 +67,10 @@ func TestFsOpen(t *testing.T) { t.Fatalf("%v: %v", f.name, err) } + if file.Name() != f.name { + t.Errorf("Name(), got %v, expected %v", file.Name(), f.name) + } + s, err := file.Stat() if err != nil { t.Fatalf("stat %v: got error '%v'", file.Name(), err) From ac803ca16bc97e05841ba575dc77c09347e463be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sat, 12 Sep 2020 21:46:45 +0200 Subject: [PATCH 44/51] tarfs: change tests to use the Afero interface instead --- tarfs/tarfs_test.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index b3d0bb94..9b653eba 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -10,6 +10,8 @@ import ( "reflect" "syscall" "testing" + + "github.com/spf13/afero" ) var files = []struct { @@ -40,7 +42,7 @@ var dirs = []struct { {"/testDir1", []string{"testFile"}}, } -var tfs *Fs +var afs *afero.Afero func TestMain(m *testing.M) { tf, err := os.Open("testdata/t.tar") @@ -49,13 +51,14 @@ func TestMain(m *testing.M) { os.Exit(1) } - tfs = New(tar.NewReader(tf)) + tfs := New(tar.NewReader(tf)) + afs = &afero.Afero{Fs: tfs} os.Exit(m.Run()) } func TestFsOpen(t *testing.T) { for _, f := range files { - file, err := tfs.Open(f.name) + file, err := afs.Open(f.name) if (err == nil) != f.exists { t.Errorf("%v exists = %v, but got err = %v", f.name, f.exists, err) } @@ -92,7 +95,7 @@ func TestRead(t *testing.T) { continue } - file, err := tfs.Open(f.name) + file, err := afs.Open(f.name) if err != nil { t.Fatalf("opening %v: %v", f.name, err) } @@ -120,7 +123,7 @@ func TestReadAt(t *testing.T) { continue } - file, err := tfs.Open(f.name) + file, err := afs.Open(f.name) if err != nil { t.Fatalf("opening %v: %v", f.name, err) } @@ -148,7 +151,7 @@ func TestSeek(t *testing.T) { continue } - file, err := tfs.Open(f.name) + file, err := afs.Open(f.name) if err != nil { t.Fatalf("opening %v: %v", f.name, err) } @@ -190,7 +193,7 @@ func TestName(t *testing.T) { continue } - file, err := tfs.Open(f.name) + file, err := afs.Open(f.name) if err != nil { t.Fatalf("opening %v: %v", f.name, err) } @@ -209,7 +212,7 @@ func TestClose(t *testing.T) { continue } - file, err := tfs.Open(f.name) + file, err := afs.Open(f.name) if err != nil { t.Fatalf("opening %v: %v", f.name, err) } @@ -244,7 +247,7 @@ func TestClose(t *testing.T) { func TestOpenFile(t *testing.T) { for _, f := range files { - file, err := tfs.OpenFile(f.name, os.O_RDONLY, 0400) + file, err := afs.OpenFile(f.name, os.O_RDONLY, 0400) if !f.exists { if !errors.Is(err, syscall.ENOENT) { t.Errorf("%v: got %v, expected%v", f.name, err, syscall.ENOENT) @@ -258,7 +261,7 @@ func TestOpenFile(t *testing.T) { } file.Close() - file, err = tfs.OpenFile(f.name, os.O_CREATE, 0600) + file, err = afs.OpenFile(f.name, os.O_CREATE, 0600) if !errors.Is(err, syscall.EPERM) { t.Errorf("%v: open for write: got %v, expected %v", f.name, err, syscall.EPERM) } @@ -268,7 +271,7 @@ func TestOpenFile(t *testing.T) { func TestFsStat(t *testing.T) { for _, f := range files { - fi, err := tfs.Stat(f.name) + fi, err := afs.Stat(f.name) if !f.exists { if !errors.Is(err, syscall.ENOENT) { t.Errorf("%v: got %v, expected%v", f.name, err, syscall.ENOENT) @@ -293,7 +296,7 @@ func TestFsStat(t *testing.T) { func TestReaddir(t *testing.T) { for _, d := range dirs { - dir, err := tfs.Open(d.name) + dir, err := afs.Open(d.name) if err != nil { t.Fatal(err) } @@ -326,7 +329,7 @@ func TestReaddir(t *testing.T) { } } - dir, err := tfs.Open("/testFile") + dir, err := afs.Open("/testFile") if err != nil { t.Fatal(err) } @@ -339,7 +342,7 @@ func TestReaddir(t *testing.T) { func TestReaddirnames(t *testing.T) { for _, d := range dirs { - dir, err := tfs.Open(d.name) + dir, err := afs.Open(d.name) if err != nil { t.Fatal(err) } @@ -363,7 +366,7 @@ func TestReaddirnames(t *testing.T) { } } - dir, err := tfs.Open("/testFile") + dir, err := afs.Open("/testFile") if err != nil { t.Fatal(err) } From 059ba00912d8ea48d1a32ef434cfe24e64d6bf6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sat, 12 Sep 2020 21:49:49 +0200 Subject: [PATCH 45/51] tarfs: add tests for Glob from zipfs --- tarfs/tarfs_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 9b653eba..de7475db 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "os" + "path/filepath" "reflect" "syscall" "testing" @@ -376,3 +377,26 @@ func TestReaddirnames(t *testing.T) { t.Fatal("Expected error") } } + +func TestGlob(t *testing.T) { + for _, s := range []struct { + glob string + entries []string + }{ + {filepath.FromSlash("/*"), []string{filepath.FromSlash("/sub"), filepath.FromSlash("/testDir1"), filepath.FromSlash("/testFile")}}, + {filepath.FromSlash("*"), []string{filepath.FromSlash("sub"), filepath.FromSlash("testDir1"), filepath.FromSlash("testFile")}}, + {filepath.FromSlash("sub/*"), []string{filepath.FromSlash("sub/testDir2")}}, + {filepath.FromSlash("sub/testDir2/*"), []string{filepath.FromSlash("sub/testDir2/testFile")}}, + {filepath.FromSlash("testDir1/*"), []string{filepath.FromSlash("testDir1/testFile")}}, + } { + entries, err := afero.Glob(afs.Fs, s.glob) + if err != nil { + t.Error(err) + } + if reflect.DeepEqual(entries, s.entries) { + t.Logf("glob: %s: glob ok", s.glob) + } else { + t.Errorf("glob: %s: got %#v, expected %#v", s.glob, entries, s.entries) + } + } +} From 36bccae3e0e4b9e8202670d5177742dbbd9049a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sat, 12 Sep 2020 21:51:54 +0200 Subject: [PATCH 46/51] tarfs: update main repo references to tarfs --- .travis.yml | 2 +- README.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fdaa9998..14596449 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,4 +19,4 @@ script: - go build -v ./... - go test -count=1 -cover -race -v ./... - go vet ./... - - FILES=$(gofmt -s -l . zipfs sftpfs mem); if [[ -n "${FILES}" ]]; then echo "You have go format errors; gofmt your changes"; exit 1; fi + - FILES=$(gofmt -s -l . zipfs sftpfs mem tarfs); if [[ -n "${FILES}" ]]; then echo "You have go format errors; gofmt your changes"; exit 1; fi diff --git a/README.md b/README.md index acd49306..c3e807ae 100644 --- a/README.md +++ b/README.md @@ -380,7 +380,6 @@ The following is a short list of possible backends we hope someone will implement: * SSH -* TAR * S3 # About the project From 0522d194576e611cf3243a6aa433b3f156fc876d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sun, 13 Sep 2020 11:26:18 +0200 Subject: [PATCH 47/51] tarfs: use OS-specific file separator for pseudoroot --- tarfs/fs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index 3cda013a..add02b94 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -66,7 +66,7 @@ func New(t *tar.Reader) *Fs { } // Add a pseudoroot - fs.files["/"][""] = &File{ + fs.files[afero.FilePathSeparator][""] = &File{ h: &tar.Header{ Name: "/", Typeflag: tar.TypeDir, From f6a3394b724f6d289a4a5e88c3c9fcea6ce34161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sun, 13 Sep 2020 11:37:56 +0200 Subject: [PATCH 48/51] test name err for windows --- tarfs/file.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tarfs/file.go b/tarfs/file.go index 677e5e6a..567c77ee 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -3,6 +3,7 @@ package tarfs import ( "archive/tar" "bytes" + "fmt" "os" "sort" "syscall" @@ -71,6 +72,7 @@ func (f *File) Write(p []byte) (n int, err error) { return 0, syscall.EROFS } func (f *File) WriteAt(p []byte, off int64) (n int, err error) { return 0, syscall.EROFS } func (f *File) Name() string { + fmt.Printf("f: %+v\n", f) return f.h.Name } From 6fc148c13bbaa5dcb78a7173494d002e058ac727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sun, 13 Sep 2020 16:56:44 +0200 Subject: [PATCH 49/51] fixup! test name err for windows --- tarfs/tarfs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index de7475db..a5101bdb 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -200,8 +200,8 @@ func TestName(t *testing.T) { } n := file.Name() - if n != f.name { - t.Errorf("got: %v, expected: %v", n, f.name) + if n != filepath.FromSlash(f.name) { + t.Errorf("got: %v, expected: %v", n, filepath.FromSlash(f.name)) } } From 6238a5e7e5328e30ff57857e7e4692f3de4f805a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sun, 13 Sep 2020 17:00:05 +0200 Subject: [PATCH 50/51] fixup! fixup! test name err for windows --- tarfs/fs.go | 2 +- tarfs/tarfs_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tarfs/fs.go b/tarfs/fs.go index add02b94..6f5cdac4 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -68,7 +68,7 @@ func New(t *tar.Reader) *Fs { // Add a pseudoroot fs.files[afero.FilePathSeparator][""] = &File{ h: &tar.Header{ - Name: "/", + Name: afero.FilePathSeparator, Typeflag: tar.TypeDir, Size: 0, }, diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index a5101bdb..8d5eaab7 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -71,8 +71,8 @@ func TestFsOpen(t *testing.T) { t.Fatalf("%v: %v", f.name, err) } - if file.Name() != f.name { - t.Errorf("Name(), got %v, expected %v", file.Name(), f.name) + if file.Name() != filepath.FromSlash(f.name) { + t.Errorf("Name(), got %v, expected %v", file.Name(), filepath.FromSlash(f.name)) } s, err := file.Stat() From 2a62693406983a4b31a6fe49d369a0b5f2bdc295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20Alberto=20Gim=C3=A9nez?= Date: Sun, 13 Sep 2020 17:06:35 +0200 Subject: [PATCH 51/51] fixup! fixup! fixup! test name err for windows --- tarfs/file.go | 2 -- tarfs/tarfs_test.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tarfs/file.go b/tarfs/file.go index 567c77ee..677e5e6a 100644 --- a/tarfs/file.go +++ b/tarfs/file.go @@ -3,7 +3,6 @@ package tarfs import ( "archive/tar" "bytes" - "fmt" "os" "sort" "syscall" @@ -72,7 +71,6 @@ func (f *File) Write(p []byte) (n int, err error) { return 0, syscall.EROFS } func (f *File) WriteAt(p []byte, off int64) (n int, err error) { return 0, syscall.EROFS } func (f *File) Name() string { - fmt.Printf("f: %+v\n", f) return f.h.Name } diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 8d5eaab7..426b2d45 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -297,7 +297,7 @@ func TestFsStat(t *testing.T) { func TestReaddir(t *testing.T) { for _, d := range dirs { - dir, err := afs.Open(d.name) + dir, err := afs.Open(filepath.FromSlash(d.name)) if err != nil { t.Fatal(err) }