Skip to content

Commit

Permalink
🐛 Implement root file, Read and ReadFile return ErrDir
Browse files Browse the repository at this point in the history
  • Loading branch information
nlepage committed Feb 4, 2021
1 parent af4f69b commit cf99b5a
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ func main() {
if err != nil {
panic(err)
}
// defer f.Close() isn't necessary, it is a noop
// defer f.Close() isn't necessary, it is a noop

// use f...
// use f...
}
```

Expand Down
9 changes: 7 additions & 2 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ import (
"io/fs"
)

// Generic errors
var (
// ErrNotDir may be returned by fs.ReadDir()
ErrNotDir = errors.New("not a directory")
ErrDir = errors.New("is a directory")
)

func newErrNotDir(op, name string) error {
return &fs.PathError{Op: "readdir", Path: name, Err: ErrNotDir}
return &fs.PathError{Op: op, Path: name, Err: ErrNotDir}
}

func newErrDir(op, name string) error {
return &fs.PathError{Op: op, Path: name, Err: ErrDir}
}
53 changes: 52 additions & 1 deletion file.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"bytes"
"io"
"io/fs"
"time"
)

type file struct {
entry
io.Reader
r io.Reader
readDirPos int
}

Expand All @@ -22,6 +23,14 @@ func (f *file) Stat() (fs.FileInfo, error) {
return f.h.FileInfo(), nil
}

func (f *file) Read(b []byte) (int, error) {
if f.IsDir() {
return 0, newErrDir("read", f.Name())
}

return f.r.Read(b)
}

func (f *file) Close() error {
return nil
}
Expand Down Expand Up @@ -49,3 +58,45 @@ func (f *file) ReadDir(n int) ([]fs.DirEntry, error) {

return f.entries[start:end], nil
}

type rootFile struct{}

var _ fs.File = &rootFile{}

func (rf *rootFile) Stat() (fs.FileInfo, error) {
return rf, nil
}

func (*rootFile) Read([]byte) (int, error) {
return 0, newErrDir("read", ".")
}

func (*rootFile) Close() error {
return nil
}

var _ fs.FileInfo = &rootFile{}

func (rf *rootFile) Name() string {
return "."
}

func (rf *rootFile) Size() int64 {
return 0
}

func (rf *rootFile) Mode() fs.FileMode {
return fs.FileMode(fs.ModeDir | 0755)
}

func (rf *rootFile) ModTime() time.Time {
return time.Time{}
}

func (rf *rootFile) IsDir() bool {
return true
}

func (rf *rootFile) Sys() interface{} {
return nil
}
20 changes: 18 additions & 2 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
type tarfs struct {
files map[string]*entry
rootEntries []fs.DirEntry
rootEntry *entry
}

type entry struct {
Expand Down Expand Up @@ -41,7 +42,7 @@ func (e *entry) Info() (fs.FileInfo, error) {
// New creates a new tar fs.FS from r
func New(r io.Reader) (fs.FS, error) {
tr := tar.NewReader(r)
tfs := &tarfs{make(map[string]*entry), make([]fs.DirEntry, 0, 10)}
tfs := &tarfs{make(map[string]*entry), make([]fs.DirEntry, 0, 10), nil}

for {
h, err := tr.Next()
Expand Down Expand Up @@ -92,6 +93,13 @@ func (tfs *tarfs) get(name, op string) (*entry, error) {
}

func (tfs *tarfs) Open(name string) (fs.File, error) {
if name == "." {
if tfs.rootEntry == nil {
return (*rootFile)(nil), nil
}
return newFile(*tfs.rootEntry), nil
}

e, err := tfs.get(name, "open")
if err != nil {
return nil, err
Expand Down Expand Up @@ -122,11 +130,19 @@ func (tfs *tarfs) ReadDir(name string) ([]fs.DirEntry, error) {
var _ fs.ReadFileFS = &tarfs{}

func (tfs *tarfs) ReadFile(name string) ([]byte, error) {
if name == "." {
return nil, newErrDir("readfile", name)
}

e, err := tfs.get(name, "readfile")
if err != nil {
return nil, err
}

if e.IsDir() {
return nil, newErrDir("readfile", name)
}

return e.b, nil
}

Expand Down Expand Up @@ -172,7 +188,7 @@ func (tfs *tarfs) Sub(dir string) (fs.FS, error) {
return nil, newErrNotDir("sub", dir)
}

subfs := &tarfs{make(map[string]*entry), e.entries}
subfs := &tarfs{make(map[string]*entry), e.entries, e}
prefix := dir + "/"
for name, file := range tfs.files {
if strings.HasPrefix(name, prefix) {
Expand Down
33 changes: 32 additions & 1 deletion fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestOpen(t *testing.T) {
t.Fatal(err)
}

for _, name := range []string{"foo", "bar", "dir1", "dir1/file11"} {
for _, name := range []string{"foo", "bar", "dir1", "dir1/file11", "."} {
f, err := tfs.Open(name)
if err != nil {
t.Errorf("tarfs.Open(%#v) should succeed, got %v", name, err)
Expand Down Expand Up @@ -292,3 +292,34 @@ func TestSubThenReadFile(t *testing.T) {
t.Errorf("%s content should be %#v, got %#v", name, content, string(b))
}
}

func TestReadOnDir(t *testing.T) {
tf, err := os.Open("test.tar")
if err != nil {
t.Fatal(err)
}
defer tf.Close()

tfs, err := New(tf)
if err != nil {
t.Fatal(err)
}

var dirs = []string{"dir1", "dir2/dir21", "."}

for _, name := range dirs {
f, err := tfs.Open(name)
if err != nil {
t.Errorf("fs.ReadFile(subfs, %#v) should succeed, got %v", name, err)
continue
}

if _, err := f.Read(make([]byte, 1)); !errors.Is(err, ErrDir) {
t.Errorf("file{%#v}.Read() should return ErrDir, got %v", name, err)
}

if _, err := fs.ReadFile(tfs, name); !errors.Is(err, ErrDir) {
t.Errorf("fs.ReadFile(tfs, %#v) should return ErrDir, got %v", name, err)
}
}
}

0 comments on commit cf99b5a

Please sign in to comment.