diff --git a/fs/chdir.go b/fs/chdir.go new file mode 100644 index 00000000..9f6da23f --- /dev/null +++ b/fs/chdir.go @@ -0,0 +1,62 @@ +package fs + +import ( + "net/http" + "os" + "path/filepath" +) + +type chdirFileSystem struct { + prefix string + system http.FileSystem +} + +func (c chdirFileSystem) Open(name string) (http.File, error) { + name = filepath.Clean(name) + return c.system.Open(filepath.Join(c.prefix, name)) +} + +func Chdir(httpFS http.FileSystem, dir string) (http.FileSystem, error) { + switch fs := httpFS.(type) { + case *chdirFileSystem: + return chdirOnChdirFS(fs, dir) + default: + return chdirOnOtherFS(fs, dir) + } +} + +func chdirOnChdirFS(fs *chdirFileSystem, dir string) (*chdirFileSystem, error) { + if !isExistingDir(fs, dir) { + return nil, os.ErrNotExist + } + prefix := filepath.Join(fs.prefix, dir) + newFS := &chdirFileSystem{ + prefix: filepath.Clean(prefix), + system: fs.system, + } + return newFS, nil +} + +func chdirOnOtherFS(fs http.FileSystem, dir string) (http.FileSystem, error) { + if !isExistingDir(fs, dir) { + return nil, os.ErrNotExist + } + return &chdirFileSystem{ + prefix: dir, + system: fs, + }, nil +} + +func isExistingDir(fs http.FileSystem, dir string) bool { + f, err := fs.Open(dir) + if err != nil { + return false + } + defer f.Close() + + info, err := f.Stat() + if err != nil { + return false + } + return info.IsDir() +} diff --git a/fs/fs_test.go b/fs/fs_test.go index 2cf799ef..63a5b53c 100644 --- a/fs/fs_test.go +++ b/fs/fs_test.go @@ -240,6 +240,24 @@ func TestWalk(t *testing.T) { } } +func TestChdir(t *testing.T) { + Register(mustZipTree("../testdata")) + fs, err := New() + if err != nil { + t.Errorf("New() = %v", err) + return + } + fs, err = Chdir(fs, "/file") + if err != nil { + t.Errorf("Chdir(fs, /file) = %v", err) + return + } + if _, err := fs.Open("/file.txt"); err != nil { + t.Errorf("fs.Open(/file.txt) = %v", err) + return + } +} + func TestHTTPFile_Readdir(t *testing.T) { Register(mustZipTree("../testdata/readdir")) fs, err := New()