144 lines
3.2 KiB
Go
144 lines
3.2 KiB
Go
|
// +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)
|
||
|
// TODO FIXME: uid -> username, gid -> groupname lookup for ls -l format output
|
||
|
|
||
|
mtime := dirent.ModTime()
|
||
|
monthStr := mtime.Month().String()[0:3]
|
||
|
day := mtime.Day()
|
||
|
year := mtime.Year()
|
||
|
now := time.Now()
|
||
|
isOld := mtime.Before(now.Add(-time.Hour * 24 * 365 / 2))
|
||
|
|
||
|
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())
|
||
|
}
|