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:
Donavan Pantke 2014-06-22 23:11:28 -04:00
parent 1fdb26e0b9
commit 5ea2cc512e
2 changed files with 61 additions and 33 deletions

View File

@ -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
}

View File

@ -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