diff --git a/tarfs/fs.go b/tarfs/fs.go index 0d764e5f..c8e4e1e3 100644 --- a/tarfs/fs.go +++ b/tarfs/fs.go @@ -14,7 +14,12 @@ import ( ) type Fs struct { - files map[string]map[string]*File + files map[string]map[string]*fsEntry +} + +type fsEntry struct { + h *tar.Header + data []byte } func splitpath(name string) (dir, file string) { @@ -29,7 +34,7 @@ func splitpath(name string) (dir, file string) { } func New(t *tar.Reader) *Fs { - fs := &Fs{files: make(map[string]map[string]*File)} + fs := &Fs{files: make(map[string]map[string]*fsEntry)} for { hdr, err := t.Next() if err == io.EOF { @@ -41,7 +46,7 @@ func New(t *tar.Reader) *Fs { d, f := splitpath(hdr.Name) if _, ok := fs.files[d]; !ok { - fs.files[d] = make(map[string]*File) + fs.files[d] = make(map[string]*fsEntry) } var buf bytes.Buffer @@ -54,27 +59,25 @@ func New(t *tar.Reader) *Fs { panic("tarfs: size mismatch") } - file := &File{ + file := &fsEntry{ h: hdr, - data: bytes.NewReader(buf.Bytes()), - fs: fs, + data: buf.Bytes(), } fs.files[d][f] = file } if fs.files[afero.FilePathSeparator] == nil { - fs.files[afero.FilePathSeparator] = make(map[string]*File) + fs.files[afero.FilePathSeparator] = make(map[string]*fsEntry) } // Add a pseudoroot - fs.files[afero.FilePathSeparator][""] = &File{ + fs.files[afero.FilePathSeparator][""] = &fsEntry{ h: &tar.Header{ Name: afero.FilePathSeparator, Typeflag: tar.TypeDir, Size: 0, }, - data: bytes.NewReader(nil), - fs: fs, + data: nil, } return fs @@ -91,7 +94,11 @@ func (fs *Fs) Open(name string) (afero.File, error) { return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} } - nf := *file + nf := File{ + h: file.h, + data: bytes.NewReader(file.data), + fs: fs, + } return &nf, nil } diff --git a/tarfs/tarfs_test.go b/tarfs/tarfs_test.go index 228ed9b4..1258f02d 100644 --- a/tarfs/tarfs_test.go +++ b/tarfs/tarfs_test.go @@ -150,6 +150,54 @@ func TestReadAt(t *testing.T) { } } +func TestMultipleReads(t *testing.T) { + for _, f := range files { + if !f.exists { + continue + } + + readFile := func(file afero.File) { + buf := make([]byte, 8) + n, err := file.Read(buf) + if f.isdir && (err != syscall.EISDIR) { + t.Errorf("%v got error %v, expected EISDIR", f.name, err) + } else if !f.isdir && (err != nil) { + if err != nil { + 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)) + } + } + + _, err = io.ReadAll(file) + if f.isdir && (err != syscall.EISDIR) { + t.Errorf("%v got error %v, expected EISDIR", f.name, err) + } else if !f.isdir && (err != nil) { + t.Errorf("reading %v: %v", f.name, err) + } + + err = file.Close() + if err != nil { + t.Errorf("closing %v: %v", f.name, err) + } + } + + fileA, err := afs.Open(f.name) + if err != nil { + t.Fatalf("opening %v: %v", f.name, err) + } + fileB, err := afs.Open(f.name) + if err != nil { + t.Fatalf("opening %v: %v", f.name, err) + } + + readFile(fileA) + readFile(fileB) + } +} + func TestSeek(t *testing.T) { for _, f := range files { if !f.exists {