| 
									
										
										
										
											2015-09-07 12:37:33 +08:00
										 |  |  | // +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris
 | 
					
						
							|  |  |  | // +build cgo
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package sftp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"path" | 
					
						
							|  |  |  | 	"syscall" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func runLsTypeWord(dirent os.FileInfo) string { | 
					
						
							|  |  |  | 	// find first character, the type char
 | 
					
						
							|  |  |  | 	// b     Block special file.
 | 
					
						
							|  |  |  | 	// c     Character special file.
 | 
					
						
							|  |  |  | 	// d     Directory.
 | 
					
						
							|  |  |  | 	// l     Symbolic link.
 | 
					
						
							|  |  |  | 	// s     Socket link.
 | 
					
						
							|  |  |  | 	// p     FIFO.
 | 
					
						
							|  |  |  | 	// -     Regular file.
 | 
					
						
							|  |  |  | 	tc := '-' | 
					
						
							|  |  |  | 	mode := dirent.Mode() | 
					
						
							|  |  |  | 	if (mode & os.ModeDir) != 0 { | 
					
						
							|  |  |  | 		tc = 'd' | 
					
						
							|  |  |  | 	} else if (mode & os.ModeDevice) != 0 { | 
					
						
							|  |  |  | 		tc = 'b' | 
					
						
							|  |  |  | 		if (mode & os.ModeCharDevice) != 0 { | 
					
						
							|  |  |  | 			tc = 'c' | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (mode & os.ModeSymlink) != 0 { | 
					
						
							|  |  |  | 		tc = 'l' | 
					
						
							|  |  |  | 	} else if (mode & os.ModeSocket) != 0 { | 
					
						
							|  |  |  | 		tc = 's' | 
					
						
							|  |  |  | 	} else if (mode & os.ModeNamedPipe) != 0 { | 
					
						
							|  |  |  | 		tc = 'p' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// owner
 | 
					
						
							|  |  |  | 	orc := '-' | 
					
						
							|  |  |  | 	if (mode & 0400) != 0 { | 
					
						
							|  |  |  | 		orc = 'r' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	owc := '-' | 
					
						
							|  |  |  | 	if (mode & 0200) != 0 { | 
					
						
							|  |  |  | 		owc = 'w' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	oxc := '-' | 
					
						
							|  |  |  | 	ox := (mode & 0100) != 0 | 
					
						
							|  |  |  | 	setuid := (mode & os.ModeSetuid) != 0 | 
					
						
							|  |  |  | 	if ox && setuid { | 
					
						
							|  |  |  | 		oxc = 's' | 
					
						
							|  |  |  | 	} else if setuid { | 
					
						
							|  |  |  | 		oxc = 'S' | 
					
						
							|  |  |  | 	} else if ox { | 
					
						
							|  |  |  | 		oxc = 'x' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// group
 | 
					
						
							|  |  |  | 	grc := '-' | 
					
						
							|  |  |  | 	if (mode & 040) != 0 { | 
					
						
							|  |  |  | 		grc = 'r' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gwc := '-' | 
					
						
							|  |  |  | 	if (mode & 020) != 0 { | 
					
						
							|  |  |  | 		gwc = 'w' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gxc := '-' | 
					
						
							|  |  |  | 	gx := (mode & 010) != 0 | 
					
						
							|  |  |  | 	setgid := (mode & os.ModeSetgid) != 0 | 
					
						
							|  |  |  | 	if gx && setgid { | 
					
						
							|  |  |  | 		gxc = 's' | 
					
						
							|  |  |  | 	} else if setgid { | 
					
						
							|  |  |  | 		gxc = 'S' | 
					
						
							|  |  |  | 	} else if gx { | 
					
						
							|  |  |  | 		gxc = 'x' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// all / others
 | 
					
						
							|  |  |  | 	arc := '-' | 
					
						
							|  |  |  | 	if (mode & 04) != 0 { | 
					
						
							|  |  |  | 		arc = 'r' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	awc := '-' | 
					
						
							|  |  |  | 	if (mode & 02) != 0 { | 
					
						
							|  |  |  | 		awc = 'w' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	axc := '-' | 
					
						
							|  |  |  | 	ax := (mode & 01) != 0 | 
					
						
							|  |  |  | 	sticky := (mode & os.ModeSticky) != 0 | 
					
						
							|  |  |  | 	if ax && sticky { | 
					
						
							|  |  |  | 		axc = 't' | 
					
						
							|  |  |  | 	} else if sticky { | 
					
						
							|  |  |  | 		axc = 'T' | 
					
						
							|  |  |  | 	} else if ax { | 
					
						
							|  |  |  | 		axc = 'x' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fmt.Sprintf("%c%c%c%c%c%c%c%c%c%c", tc, orc, owc, oxc, grc, gwc, gxc, arc, awc, axc) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func runLsStatt(dirname string, dirent os.FileInfo, statt *syscall.Stat_t) string { | 
					
						
							|  |  |  | 	// example from openssh sftp server:
 | 
					
						
							|  |  |  | 	// crw-rw-rw-    1 root     wheel           0 Jul 31 20:52 ttyvd
 | 
					
						
							|  |  |  | 	// format:
 | 
					
						
							|  |  |  | 	// {directory / char device / etc}{rwxrwxrwx}  {number of links} owner group size month day [time (this year) | year (otherwise)] name
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	typeword := runLsTypeWord(dirent) | 
					
						
							|  |  |  | 	numLinks := statt.Nlink | 
					
						
							|  |  |  | 	uid := statt.Uid | 
					
						
							|  |  |  | 	gid := statt.Gid | 
					
						
							|  |  |  | 	username := fmt.Sprintf("%d", uid) | 
					
						
							|  |  |  | 	groupname := fmt.Sprintf("%d", gid) | 
					
						
							| 
									
										
										
										
											2015-09-08 13:10:18 +08:00
										 |  |  | 	// TODO FIXME: uid -> username, gid -> groupname lookup for ls -l format output
 | 
					
						
							| 
									
										
										
										
											2015-09-07 12:37:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mtime := dirent.ModTime() | 
					
						
							|  |  |  | 	monthStr := mtime.Month().String()[0:3] | 
					
						
							|  |  |  | 	day := mtime.Day() | 
					
						
							|  |  |  | 	year := mtime.Year() | 
					
						
							|  |  |  | 	now := time.Now() | 
					
						
							| 
									
										
										
										
											2015-09-09 08:03:18 +08:00
										 |  |  | 	isOld := mtime.Before(now.Add(-time.Hour * 24 * 365 / 2)) | 
					
						
							| 
									
										
										
										
											2015-09-08 13:50:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-07 12:37:33 +08:00
										 |  |  | 	yearOrTime := fmt.Sprintf("%02d:%02d", mtime.Hour(), mtime.Minute()) | 
					
						
							|  |  |  | 	if isOld { | 
					
						
							|  |  |  | 		yearOrTime = fmt.Sprintf("%d", year) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fmt.Sprintf("%s %4d %-8s %-8s %8d %s %2d %5s %s", typeword, numLinks, username, groupname, dirent.Size(), monthStr, day, yearOrTime, dirent.Name()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ls -l style output for a file, which is in the 'long output' section of a readdir response packet
 | 
					
						
							|  |  |  | // this is a very simple (lazy) implementation, just enough to look almost like openssh in a few basic cases
 | 
					
						
							|  |  |  | func runLs(dirname string, dirent os.FileInfo) string { | 
					
						
							|  |  |  | 	dsys := dirent.Sys() | 
					
						
							|  |  |  | 	if dsys == nil { | 
					
						
							|  |  |  | 	} else if statt, ok := dsys.(*syscall.Stat_t); !ok { | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return runLsStatt(dirname, dirent, statt) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return path.Join(dirname, dirent.Name()) | 
					
						
							|  |  |  | } |