mirror of https://github.com/pkg/sftp.git
Refactor stat functions to look more like Go's original. Also allows for access to fields we can access from SFTP but do not conform to os.FileInfo.
This commit is contained in:
parent
1fdb26e0b9
commit
5ea2cc512e
78
attrs.go
78
attrs.go
|
@ -17,60 +17,90 @@ const (
|
|||
ssh_FILEXFER_ATTR_EXTENDED = 0x80000000
|
||||
)
|
||||
|
||||
type attr struct {
|
||||
name string
|
||||
size uint64
|
||||
mode os.FileMode
|
||||
// fileInfo is an artificial type designed to satisfy os.FileInfo.
|
||||
type fileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
mtime time.Time
|
||||
sys interface{}
|
||||
}
|
||||
|
||||
// Name returns the base name of the file.
|
||||
func (a *attr) Name() string { return a.name }
|
||||
func (a *fileInfo) Name() string { return a.name }
|
||||
|
||||
// Size returns the length in bytes for regular files; system-dependent for others.
|
||||
func (a *attr) Size() int64 { return int64(a.size) }
|
||||
func (a *fileInfo) Size() int64 { return a.size }
|
||||
|
||||
// Mode returns file mode bits.
|
||||
func (a *attr) Mode() os.FileMode { return a.mode }
|
||||
func (a *fileInfo) Mode() os.FileMode { return a.mode }
|
||||
|
||||
// ModTime returns the last modification time of the file.
|
||||
func (a *attr) ModTime() time.Time { return a.mtime }
|
||||
func (a *fileInfo) ModTime() time.Time { return a.mtime }
|
||||
|
||||
// IsDir returns true if the file is a directory.
|
||||
func (a *attr) IsDir() bool { return a.Mode().IsDir() }
|
||||
func (a *fileInfo) IsDir() bool { return a.Mode().IsDir() }
|
||||
|
||||
func (a *attr) Sys() interface{} { return a }
|
||||
func (a *fileInfo) Sys() interface{} { return a.sys }
|
||||
|
||||
func unmarshalAttrs(b []byte) (*attr, []byte) {
|
||||
// FileStat holds the original unmarshalled values from a call LSTAT.
|
||||
type FileStat struct {
|
||||
Size uint64
|
||||
Mode uint32
|
||||
Mtime uint32
|
||||
Atime uint32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
Extended []StatExtended
|
||||
}
|
||||
|
||||
type StatExtended struct {
|
||||
ExtType string
|
||||
ExtData string
|
||||
}
|
||||
|
||||
func fileInfoFromStat(st *FileStat, name string) os.FileInfo {
|
||||
fs := &fileInfo{
|
||||
name: name,
|
||||
size: int64(st.Size),
|
||||
mode: toFileMode(st.Mode),
|
||||
mtime: time.Unix(int64(st.Mtime), 0),
|
||||
sys: st,
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
func unmarshalAttrs(b []byte) (*FileStat, []byte) {
|
||||
flags, b := unmarshalUint32(b)
|
||||
var a attr
|
||||
var a FileStat
|
||||
if flags&ssh_FILEXFER_ATTR_SIZE == ssh_FILEXFER_ATTR_SIZE {
|
||||
a.size, b = unmarshalUint64(b)
|
||||
a.Size, b = unmarshalUint64(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID {
|
||||
_, b = unmarshalUint32(b) // discarded
|
||||
a.Uid, b = unmarshalUint32(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID {
|
||||
_, b = unmarshalUint32(b) // discarded
|
||||
a.Gid, b = unmarshalUint32(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_PERMISSIONS == ssh_FILEXFER_ATTR_PERMISSIONS {
|
||||
var mode uint32
|
||||
mode, b = unmarshalUint32(b)
|
||||
a.mode = toFileMode(mode)
|
||||
a.Mode, b = unmarshalUint32(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_ACMODTIME == ssh_FILEXFER_ATTR_ACMODTIME {
|
||||
var mtime uint32
|
||||
_, b = unmarshalUint32(b) // discarded
|
||||
mtime, b = unmarshalUint32(b)
|
||||
a.mtime = time.Unix(int64(mtime), 0)
|
||||
a.Atime, b = unmarshalUint32(b)
|
||||
a.Mtime, b = unmarshalUint32(b)
|
||||
}
|
||||
if flags&ssh_FILEXFER_ATTR_EXTENDED == ssh_FILEXFER_ATTR_EXTENDED {
|
||||
var count uint32
|
||||
count, b = unmarshalUint32(b)
|
||||
ext := make([]StatExtended,count,count)
|
||||
for i := uint32(0); i < count; i++ {
|
||||
_, b = unmarshalString(b)
|
||||
_, b = unmarshalString(b)
|
||||
var typ string
|
||||
var data string
|
||||
typ, b = unmarshalString(b)
|
||||
data, b = unmarshalString(b)
|
||||
ext[i] = StatExtended{typ,data}
|
||||
}
|
||||
a.Extended = ext
|
||||
}
|
||||
return &a, b
|
||||
}
|
||||
|
|
16
client.go
16
client.go
|
@ -139,13 +139,12 @@ func (c *Client) ReadDir(p string) ([]os.FileInfo, error) {
|
|||
var filename string
|
||||
filename, data = unmarshalString(data)
|
||||
_, data = unmarshalString(data) // discard longname
|
||||
var attr *attr
|
||||
var attr *FileStat
|
||||
attr, data = unmarshalAttrs(data)
|
||||
if filename == "." || filename == ".." {
|
||||
continue
|
||||
}
|
||||
attr.name = path.Base(filename)
|
||||
attrs = append(attrs, attr)
|
||||
attrs = append(attrs, fileInfoFromStat(attr, path.Base(filename)) )
|
||||
}
|
||||
case ssh_FXP_STATUS:
|
||||
// TODO(dfc) scope warning!
|
||||
|
@ -216,8 +215,7 @@ func (c *Client) Lstat(p string) (os.FileInfo, error) {
|
|||
return nil, &unexpectedIdErr{id, sid}
|
||||
}
|
||||
attr, _ := unmarshalAttrs(data)
|
||||
attr.name = path.Base(p)
|
||||
return attr, nil
|
||||
return fileInfoFromStat(attr, path.Base(p)), nil
|
||||
case ssh_FXP_STATUS:
|
||||
return nil, unmarshalStatus(id, data)
|
||||
default:
|
||||
|
@ -405,7 +403,7 @@ func (c *Client) close(handle string) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Client) fstat(handle string) (*attr, error) {
|
||||
func (c *Client) fstat(handle string) (*FileStat, error) {
|
||||
type packet struct {
|
||||
Type byte
|
||||
Id uint32
|
||||
|
@ -638,10 +636,10 @@ func (f *File) Read(b []byte) (int, error) {
|
|||
// error.
|
||||
func (f *File) Stat() (os.FileInfo, error) {
|
||||
fi, err := f.c.fstat(f.handle)
|
||||
if err == nil {
|
||||
fi.name = path.Base(f.path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fi, err
|
||||
return fileInfoFromStat(fi, path.Base(f.path)), nil
|
||||
}
|
||||
|
||||
// clamp writes to less than 32k
|
||||
|
|
Loading…
Reference in New Issue