2013-11-05 13:28:45 +08:00
|
|
|
package sftp
|
|
|
|
|
2013-11-06 16:12:25 +08:00
|
|
|
// ssh_FXP_ATTRS support
|
2013-11-05 13:28:45 +08:00
|
|
|
// see http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-5
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2019-08-30 23:04:37 +08:00
|
|
|
sshFileXferAttrSize = 0x00000001
|
|
|
|
sshFileXferAttrUIDGID = 0x00000002
|
|
|
|
sshFileXferAttrPermissions = 0x00000004
|
|
|
|
sshFileXferAttrACmodTime = 0x00000008
|
2021-03-20 18:46:58 +08:00
|
|
|
sshFileXferAttrExtended = 0x80000000
|
2019-12-27 17:56:42 +08:00
|
|
|
|
2019-12-31 07:36:04 +08:00
|
|
|
sshFileXferAttrAll = sshFileXferAttrSize | sshFileXferAttrUIDGID | sshFileXferAttrPermissions |
|
2021-03-20 18:46:58 +08:00
|
|
|
sshFileXferAttrACmodTime | sshFileXferAttrExtended
|
2013-11-05 13:28:45 +08:00
|
|
|
)
|
|
|
|
|
2014-06-23 11:11:28 +08:00
|
|
|
// fileInfo is an artificial type designed to satisfy os.FileInfo.
|
|
|
|
type fileInfo struct {
|
2014-09-27 05:56:26 +08:00
|
|
|
name string
|
|
|
|
size int64
|
|
|
|
mode os.FileMode
|
2013-11-05 13:28:45 +08:00
|
|
|
mtime time.Time
|
2014-09-27 05:56:26 +08:00
|
|
|
sys interface{}
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Name returns the base name of the file.
|
2014-06-23 18:55:00 +08:00
|
|
|
func (fi *fileInfo) Name() string { return fi.name }
|
2013-11-05 13:28:45 +08:00
|
|
|
|
|
|
|
// Size returns the length in bytes for regular files; system-dependent for others.
|
2014-06-23 18:55:00 +08:00
|
|
|
func (fi *fileInfo) Size() int64 { return fi.size }
|
2013-11-05 13:28:45 +08:00
|
|
|
|
|
|
|
// Mode returns file mode bits.
|
2014-06-23 18:55:00 +08:00
|
|
|
func (fi *fileInfo) Mode() os.FileMode { return fi.mode }
|
2013-11-05 13:28:45 +08:00
|
|
|
|
|
|
|
// ModTime returns the last modification time of the file.
|
2014-06-23 18:55:00 +08:00
|
|
|
func (fi *fileInfo) ModTime() time.Time { return fi.mtime }
|
2013-11-05 13:28:45 +08:00
|
|
|
|
|
|
|
// IsDir returns true if the file is a directory.
|
2014-06-23 18:55:00 +08:00
|
|
|
func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() }
|
2013-11-05 13:28:45 +08:00
|
|
|
|
2014-06-23 18:55:00 +08:00
|
|
|
func (fi *fileInfo) Sys() interface{} { return fi.sys }
|
2013-11-05 13:28:45 +08:00
|
|
|
|
2018-05-12 05:15:09 +08:00
|
|
|
// FileStat holds the original unmarshalled values from a call to READDIR or
|
|
|
|
// *STAT. It is exported for the purposes of accessing the raw values via
|
|
|
|
// os.FileInfo.Sys(). It is also used server side to store the unmarshalled
|
|
|
|
// values for SetStat.
|
2014-06-23 11:11:28 +08:00
|
|
|
type FileStat struct {
|
2014-09-27 05:56:26 +08:00
|
|
|
Size uint64
|
|
|
|
Mode uint32
|
|
|
|
Mtime uint32
|
|
|
|
Atime uint32
|
2016-01-05 05:15:21 +08:00
|
|
|
UID uint32
|
|
|
|
GID uint32
|
2014-06-23 11:11:28 +08:00
|
|
|
Extended []StatExtended
|
|
|
|
}
|
|
|
|
|
2016-01-08 04:11:16 +08:00
|
|
|
// StatExtended contains additional, extended information for a FileStat.
|
2014-06-23 11:11:28 +08:00
|
|
|
type StatExtended struct {
|
|
|
|
ExtType string
|
|
|
|
ExtData string
|
|
|
|
}
|
|
|
|
|
|
|
|
func fileInfoFromStat(st *FileStat, name string) os.FileInfo {
|
|
|
|
fs := &fileInfo{
|
2014-09-27 05:56:26 +08:00
|
|
|
name: name,
|
|
|
|
size: int64(st.Size),
|
|
|
|
mode: toFileMode(st.Mode),
|
2014-06-23 11:11:28 +08:00
|
|
|
mtime: time.Unix(int64(st.Mtime), 0),
|
2014-09-27 05:56:26 +08:00
|
|
|
sys: st,
|
2014-06-23 11:11:28 +08:00
|
|
|
}
|
|
|
|
return fs
|
|
|
|
}
|
|
|
|
|
2015-09-20 10:09:03 +08:00
|
|
|
func fileStatFromInfo(fi os.FileInfo) (uint32, FileStat) {
|
|
|
|
mtime := fi.ModTime().Unix()
|
|
|
|
atime := mtime
|
2019-08-30 23:04:37 +08:00
|
|
|
var flags uint32 = sshFileXferAttrSize |
|
|
|
|
sshFileXferAttrPermissions |
|
|
|
|
sshFileXferAttrACmodTime
|
2015-09-20 10:09:03 +08:00
|
|
|
|
|
|
|
fileStat := FileStat{
|
|
|
|
Size: uint64(fi.Size()),
|
|
|
|
Mode: fromFileMode(fi.Mode()),
|
|
|
|
Mtime: uint32(mtime),
|
|
|
|
Atime: uint32(atime),
|
|
|
|
}
|
|
|
|
|
|
|
|
// os specific file stat decoding
|
|
|
|
fileStatFromInfoOs(fi, &flags, &fileStat)
|
|
|
|
|
|
|
|
return flags, fileStat
|
|
|
|
}
|
|
|
|
|
2014-06-23 11:11:28 +08:00
|
|
|
func unmarshalAttrs(b []byte) (*FileStat, []byte) {
|
2013-11-05 13:28:45 +08:00
|
|
|
flags, b := unmarshalUint32(b)
|
2018-01-10 07:29:55 +08:00
|
|
|
return getFileStat(flags, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getFileStat(flags uint32, b []byte) (*FileStat, []byte) {
|
2014-06-23 18:55:00 +08:00
|
|
|
var fs FileStat
|
2019-08-30 23:04:37 +08:00
|
|
|
if flags&sshFileXferAttrSize == sshFileXferAttrSize {
|
2019-12-24 16:37:32 +08:00
|
|
|
fs.Size, b, _ = unmarshalUint64Safe(b)
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
2019-08-30 23:04:37 +08:00
|
|
|
if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID {
|
2019-12-24 16:37:32 +08:00
|
|
|
fs.UID, b, _ = unmarshalUint32Safe(b)
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
2019-08-30 23:04:37 +08:00
|
|
|
if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID {
|
2019-12-24 16:37:32 +08:00
|
|
|
fs.GID, b, _ = unmarshalUint32Safe(b)
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
2019-08-30 23:04:37 +08:00
|
|
|
if flags&sshFileXferAttrPermissions == sshFileXferAttrPermissions {
|
2019-12-24 16:37:32 +08:00
|
|
|
fs.Mode, b, _ = unmarshalUint32Safe(b)
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
2019-08-30 23:04:37 +08:00
|
|
|
if flags&sshFileXferAttrACmodTime == sshFileXferAttrACmodTime {
|
2019-12-24 16:37:32 +08:00
|
|
|
fs.Atime, b, _ = unmarshalUint32Safe(b)
|
|
|
|
fs.Mtime, b, _ = unmarshalUint32Safe(b)
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
2021-03-20 18:46:58 +08:00
|
|
|
if flags&sshFileXferAttrExtended == sshFileXferAttrExtended {
|
2013-11-05 13:28:45 +08:00
|
|
|
var count uint32
|
2019-12-24 16:37:32 +08:00
|
|
|
count, b, _ = unmarshalUint32Safe(b)
|
2018-02-16 03:17:19 +08:00
|
|
|
ext := make([]StatExtended, count)
|
2013-11-05 13:28:45 +08:00
|
|
|
for i := uint32(0); i < count; i++ {
|
2014-06-23 11:11:28 +08:00
|
|
|
var typ string
|
|
|
|
var data string
|
2019-12-24 16:37:32 +08:00
|
|
|
typ, b, _ = unmarshalStringSafe(b)
|
|
|
|
data, b, _ = unmarshalStringSafe(b)
|
2014-09-27 05:56:26 +08:00
|
|
|
ext[i] = StatExtended{typ, data}
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
2014-06-23 18:55:00 +08:00
|
|
|
fs.Extended = ext
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
2014-06-23 18:55:00 +08:00
|
|
|
return &fs, b
|
2013-11-05 13:28:45 +08:00
|
|
|
}
|
2013-11-05 14:21:30 +08:00
|
|
|
|
2015-07-25 16:19:29 +08:00
|
|
|
func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
|
|
|
|
// attributes variable struct, and also variable per protocol version
|
|
|
|
// spec version 3 attributes:
|
|
|
|
// uint32 flags
|
|
|
|
// uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
|
|
|
|
// uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
|
|
|
|
// uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
|
|
|
|
// uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
|
|
|
|
// uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
|
|
|
|
// uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
|
|
|
|
// uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
|
|
|
|
// string extended_type
|
|
|
|
// string extended_data
|
|
|
|
// ... more extended data (extended_type - extended_data pairs),
|
|
|
|
// so that number of pairs equals extended_count
|
|
|
|
|
2015-09-20 10:09:03 +08:00
|
|
|
flags, fileStat := fileStatFromInfo(fi)
|
2015-07-25 16:19:29 +08:00
|
|
|
|
2015-09-20 10:09:03 +08:00
|
|
|
b = marshalUint32(b, flags)
|
2019-08-30 23:04:37 +08:00
|
|
|
if flags&sshFileXferAttrSize != 0 {
|
2015-09-20 10:09:03 +08:00
|
|
|
b = marshalUint64(b, fileStat.Size)
|
2015-07-25 16:19:29 +08:00
|
|
|
}
|
2019-08-30 23:04:37 +08:00
|
|
|
if flags&sshFileXferAttrUIDGID != 0 {
|
2016-01-05 05:15:21 +08:00
|
|
|
b = marshalUint32(b, fileStat.UID)
|
|
|
|
b = marshalUint32(b, fileStat.GID)
|
2015-07-25 16:19:29 +08:00
|
|
|
}
|
2019-08-30 23:04:37 +08:00
|
|
|
if flags&sshFileXferAttrPermissions != 0 {
|
2015-09-20 10:09:03 +08:00
|
|
|
b = marshalUint32(b, fileStat.Mode)
|
2015-07-25 16:19:29 +08:00
|
|
|
}
|
2019-08-30 23:04:37 +08:00
|
|
|
if flags&sshFileXferAttrACmodTime != 0 {
|
2015-09-20 10:09:03 +08:00
|
|
|
b = marshalUint32(b, fileStat.Atime)
|
|
|
|
b = marshalUint32(b, fileStat.Mtime)
|
2015-07-25 16:19:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return b
|
|
|
|
}
|