sftp/dirfs.go

86 lines
2.1 KiB
Go

package sftp
import (
"io/fs"
"path"
)
type fsys struct {
dir string
cl *Client
}
// DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir.
//
// Note that cl.DirFS("/prefix") only guarantees that the Open calls it makes to the remote system will begin with "/prefix":
// cl.DirFS("/prefix").Open("file") is the same as cl.Open("/prefix/file").
// So, if /prefix/file is a symbolic link pointing outside the /prefix tree,
// then using DirFS does not stop the access any more than using cl.Open does.
// DirFS is therefore not a general substitute for a chroot-style security mechanism when the directory tree contains arbitrary content.
//
// The directory dir must not be "".
//
// The result implements io/fs.StatFS, io/fs.ReadFileFS, and io/fs.ReadDirFS.
func (cl *Client) DirFS(dir string) fs.FS {
return &fsys{
dir: path.Clean(dir),
cl: cl,
}
}
func (cl *fsys) join(name string) string {
return path.Join(cl.dir, name)
}
// Open implements fs.FS.
func (cl *fsys) Open(name string) (fs.File, error) {
if !fs.ValidPath(name) {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
}
return cl.cl.Open(cl.join(name))
}
// ReadDir implements fs.ReadDirFS.
func (cl *fsys) ReadDir(name string) ([]fs.DirEntry, error) {
if !fs.ValidPath(name) {
return nil, &fs.PathError{Op: "readdir", Path: name, Err: fs.ErrInvalid}
}
return cl.cl.ReadDir(cl.join(name))
}
// ReadFile implements fs.ReadFileFS.
func (cl *fsys) ReadFile(name string) ([]byte, error) {
if !fs.ValidPath(name) {
return nil, &fs.PathError{Op: "readfile", Path: name, Err: fs.ErrInvalid}
}
return cl.cl.ReadFile(cl.join(name))
}
// Stat implements fs.StatFS.
func (cl *fsys) Stat(name string) (fs.FileInfo, error) {
if !fs.ValidPath(name) {
return nil, &fs.PathError{Op: "stat", Path: name, Err: fs.ErrInvalid}
}
return cl.cl.Stat(cl.join(name))
}
// Sub implements fs.SubFS.
func (cl *fsys) Sub(dir string) (fs.FS, error) {
if !fs.ValidPath(dir) {
return nil, &fs.PathError{Op: "sub", Path: dir, Err: fs.ErrInvalid}
}
if dir == "." {
return cl, nil
}
return &fsys{
dir: cl.join(dir),
cl: cl.cl,
}, nil
}