| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2013-11-05 14:21:30 +08:00
										 |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2013-11-05 13:28:45 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2015-09-07 13:02:47 +08:00
										 |  |  | 	ssh_FILEXFER_ATTR_SIZE        = 0x00000001 | 
					
						
							|  |  |  | 	ssh_FILEXFER_ATTR_UIDGID      = 0x00000002 | 
					
						
							|  |  |  | 	ssh_FILEXFER_ATTR_PERMISSIONS = 0x00000004 | 
					
						
							|  |  |  | 	ssh_FILEXFER_ATTR_ACMODTIME   = 0x00000008 | 
					
						
							|  |  |  | 	ssh_FILEXFER_ATTR_EXTENDED    = 0x80000000 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-24 14:12:45 +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()
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	var flags uint32 = ssh_FILEXFER_ATTR_SIZE | | 
					
						
							|  |  |  | 		ssh_FILEXFER_ATTR_PERMISSIONS | | 
					
						
							|  |  |  | 		ssh_FILEXFER_ATTR_ACMODTIME | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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) | 
					
						
							| 
									
										
										
										
											2014-06-23 18:55:00 +08:00
										 |  |  | 	var fs FileStat | 
					
						
							| 
									
										
										
										
											2013-11-06 16:12:25 +08:00
										 |  |  | 	if flags&ssh_FILEXFER_ATTR_SIZE == ssh_FILEXFER_ATTR_SIZE { | 
					
						
							| 
									
										
										
										
											2014-06-23 18:55:00 +08:00
										 |  |  | 		fs.Size, b = unmarshalUint64(b) | 
					
						
							| 
									
										
										
										
											2013-11-05 13:28:45 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-06 16:12:25 +08:00
										 |  |  | 	if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID { | 
					
						
							| 
									
										
										
										
											2016-01-05 05:15:21 +08:00
										 |  |  | 		fs.UID, b = unmarshalUint32(b) | 
					
						
							| 
									
										
										
										
											2013-11-05 13:28:45 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-06 16:12:25 +08:00
										 |  |  | 	if flags&ssh_FILEXFER_ATTR_UIDGID == ssh_FILEXFER_ATTR_UIDGID { | 
					
						
							| 
									
										
										
										
											2016-01-05 05:15:21 +08:00
										 |  |  | 		fs.GID, b = unmarshalUint32(b) | 
					
						
							| 
									
										
										
										
											2013-11-05 13:28:45 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-06 16:12:25 +08:00
										 |  |  | 	if flags&ssh_FILEXFER_ATTR_PERMISSIONS == ssh_FILEXFER_ATTR_PERMISSIONS { | 
					
						
							| 
									
										
										
										
											2014-06-23 18:55:00 +08:00
										 |  |  | 		fs.Mode, b = unmarshalUint32(b) | 
					
						
							| 
									
										
										
										
											2013-11-05 13:28:45 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-06 16:12:25 +08:00
										 |  |  | 	if flags&ssh_FILEXFER_ATTR_ACMODTIME == ssh_FILEXFER_ATTR_ACMODTIME { | 
					
						
							| 
									
										
										
										
											2014-06-23 18:55:00 +08:00
										 |  |  | 		fs.Atime, b = unmarshalUint32(b) | 
					
						
							|  |  |  | 		fs.Mtime, b = unmarshalUint32(b) | 
					
						
							| 
									
										
										
										
											2013-11-05 13:28:45 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-06 16:12:25 +08:00
										 |  |  | 	if flags&ssh_FILEXFER_ATTR_EXTENDED == ssh_FILEXFER_ATTR_EXTENDED { | 
					
						
							| 
									
										
										
										
											2013-11-05 13:28:45 +08:00
										 |  |  | 		var count uint32 | 
					
						
							|  |  |  | 		count, b = unmarshalUint32(b) | 
					
						
							| 
									
										
										
										
											2014-09-27 05:56:26 +08:00
										 |  |  | 		ext := make([]StatExtended, count, 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 | 
					
						
							|  |  |  | 			typ, b = unmarshalString(b) | 
					
						
							|  |  |  | 			data, b = unmarshalString(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) | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	if flags&ssh_FILEXFER_ATTR_SIZE != 0 { | 
					
						
							| 
									
										
										
										
											2015-09-20 10:09:03 +08:00
										 |  |  | 		b = marshalUint64(b, fileStat.Size) | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if flags&ssh_FILEXFER_ATTR_UIDGID != 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
										 |  |  | 	} | 
					
						
							|  |  |  | 	if flags&ssh_FILEXFER_ATTR_PERMISSIONS != 0 { | 
					
						
							| 
									
										
										
										
											2015-09-20 10:09:03 +08:00
										 |  |  | 		b = marshalUint32(b, fileStat.Mode) | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if flags&ssh_FILEXFER_ATTR_ACMODTIME != 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-05 14:21:30 +08:00
										 |  |  | // toFileMode converts sftp filemode bits to the os.FileMode specification
 | 
					
						
							|  |  |  | func toFileMode(mode uint32) os.FileMode { | 
					
						
							|  |  |  | 	var fm = os.FileMode(mode & 0777) | 
					
						
							|  |  |  | 	switch mode & syscall.S_IFMT { | 
					
						
							|  |  |  | 	case syscall.S_IFBLK: | 
					
						
							|  |  |  | 		fm |= os.ModeDevice | 
					
						
							|  |  |  | 	case syscall.S_IFCHR: | 
					
						
							|  |  |  | 		fm |= os.ModeDevice | os.ModeCharDevice | 
					
						
							|  |  |  | 	case syscall.S_IFDIR: | 
					
						
							|  |  |  | 		fm |= os.ModeDir | 
					
						
							|  |  |  | 	case syscall.S_IFIFO: | 
					
						
							|  |  |  | 		fm |= os.ModeNamedPipe | 
					
						
							|  |  |  | 	case syscall.S_IFLNK: | 
					
						
							|  |  |  | 		fm |= os.ModeSymlink | 
					
						
							|  |  |  | 	case syscall.S_IFREG: | 
					
						
							|  |  |  | 		// nothing to do
 | 
					
						
							|  |  |  | 	case syscall.S_IFSOCK: | 
					
						
							|  |  |  | 		fm |= os.ModeSocket | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&syscall.S_ISGID != 0 { | 
					
						
							|  |  |  | 		fm |= os.ModeSetgid | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&syscall.S_ISUID != 0 { | 
					
						
							|  |  |  | 		fm |= os.ModeSetuid | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&syscall.S_ISVTX != 0 { | 
					
						
							|  |  |  | 		fm |= os.ModeSticky | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fm | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // fromFileMode converts from the os.FileMode specification to sftp filemode bits
 | 
					
						
							|  |  |  | func fromFileMode(mode os.FileMode) uint32 { | 
					
						
							|  |  |  | 	ret := uint32(0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if mode&os.ModeDevice != 0 { | 
					
						
							|  |  |  | 		if mode&os.ModeCharDevice != 0 { | 
					
						
							|  |  |  | 			ret |= syscall.S_IFCHR | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ret |= syscall.S_IFBLK | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&os.ModeDir != 0 { | 
					
						
							|  |  |  | 		ret |= syscall.S_IFDIR | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&os.ModeSymlink != 0 { | 
					
						
							|  |  |  | 		ret |= syscall.S_IFLNK | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&os.ModeNamedPipe != 0 { | 
					
						
							|  |  |  | 		ret |= syscall.S_IFIFO | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&os.ModeSetgid != 0 { | 
					
						
							|  |  |  | 		ret |= syscall.S_ISGID | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&os.ModeSetuid != 0 { | 
					
						
							|  |  |  | 		ret |= syscall.S_ISUID | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&os.ModeSticky != 0 { | 
					
						
							|  |  |  | 		ret |= syscall.S_ISVTX | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if mode&os.ModeSocket != 0 { | 
					
						
							|  |  |  | 		ret |= syscall.S_IFSOCK | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-09 08:03:18 +08:00
										 |  |  | 	if mode&os.ModeType == 0 { | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 		ret |= syscall.S_IFREG | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ret |= uint32(mode & os.ModePerm) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret | 
					
						
							|  |  |  | } |