2024-01-06 20:04:19 +08:00
|
|
|
//go:build go1.18
|
|
|
|
|
2022-10-18 23:15:21 +08:00
|
|
|
package sftp
|
|
|
|
|
|
|
|
import (
|
2024-01-06 20:04:19 +08:00
|
|
|
"fmt"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
2022-10-18 23:15:21 +08:00
|
|
|
"path"
|
|
|
|
"path/filepath"
|
2024-01-06 20:04:19 +08:00
|
|
|
"syscall"
|
|
|
|
"time"
|
2022-10-18 23:15:21 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
func (s *Server) toLocalPath(p string) string {
|
|
|
|
if s.workDir != "" && !path.IsAbs(p) {
|
|
|
|
p = path.Join(s.workDir, p)
|
|
|
|
}
|
|
|
|
|
|
|
|
lp := filepath.FromSlash(p)
|
|
|
|
|
|
|
|
if path.IsAbs(p) {
|
|
|
|
tmp := lp
|
|
|
|
for len(tmp) > 0 && tmp[0] == '\\' {
|
|
|
|
tmp = tmp[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
if filepath.IsAbs(tmp) {
|
|
|
|
// If the FromSlash without any starting slashes is absolute,
|
|
|
|
// then we have a filepath encoded with a prefix '/'.
|
|
|
|
// e.g. "/C:/Windows" to "C:\\Windows"
|
|
|
|
return tmp
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp += "\\"
|
|
|
|
|
|
|
|
if filepath.IsAbs(tmp) {
|
|
|
|
// If the FromSlash without any starting slashes but with extra end slash is absolute,
|
|
|
|
// then we have a filepath encoded with a prefix '/' and a dropped '/' at the end.
|
|
|
|
// e.g. "/C:" to "C:\\"
|
|
|
|
return tmp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return lp
|
|
|
|
}
|
2024-01-06 20:04:19 +08:00
|
|
|
|
|
|
|
var kernel32, _ = syscall.LoadLibrary("kernel32.dll")
|
|
|
|
var getLogicalDrivesHandle, _ = syscall.GetProcAddress(kernel32, "GetLogicalDrives")
|
|
|
|
|
|
|
|
func bitsToDrives(bitmap uint32) []string {
|
|
|
|
var drive rune = 'A'
|
|
|
|
var drives []string
|
|
|
|
|
|
|
|
for bitmap != 0 {
|
|
|
|
if bitmap&1 == 1 {
|
|
|
|
drives = append(drives, string(drive))
|
|
|
|
}
|
|
|
|
drive++
|
|
|
|
bitmap >>= 1
|
|
|
|
}
|
|
|
|
|
|
|
|
return drives
|
|
|
|
}
|
|
|
|
|
|
|
|
func getDrives() ([]string, error) {
|
|
|
|
if ret, _, callErr := syscall.Syscall(uintptr(getLogicalDrivesHandle), 0, 0, 0, 0); callErr != 0 {
|
|
|
|
return nil, fmt.Errorf("GetLogicalDrives: %w", callErr)
|
|
|
|
} else {
|
|
|
|
drives := bitsToDrives(uint32(ret))
|
|
|
|
return drives, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type dummyDriveStat struct {
|
|
|
|
name string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *dummyDriveStat) Name() string {
|
|
|
|
return s.name
|
|
|
|
}
|
|
|
|
func (s *dummyDriveStat) Size() int64 {
|
|
|
|
return 1024
|
|
|
|
}
|
|
|
|
func (s *dummyDriveStat) Mode() os.FileMode {
|
|
|
|
return os.FileMode(0755)
|
|
|
|
}
|
|
|
|
func (s *dummyDriveStat) ModTime() time.Time {
|
|
|
|
return time.Now()
|
|
|
|
}
|
|
|
|
func (s *dummyDriveStat) IsDir() bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
func (s *dummyDriveStat) Sys() any {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type WinRoot struct {
|
|
|
|
dummyFile
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *WinRoot) Readdir(int) ([]os.FileInfo, error) {
|
|
|
|
drives, err := getDrives()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
infos := []os.FileInfo{}
|
|
|
|
for _, drive := range drives {
|
|
|
|
infos = append(infos, &dummyDriveStat{drive})
|
|
|
|
}
|
|
|
|
return infos, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func openFileLike(path string, flag int, mode fs.FileMode) (FileLike, error) {
|
|
|
|
if path == "/" {
|
|
|
|
return &WinRoot{}, nil
|
|
|
|
}
|
|
|
|
return os.OpenFile(path, flag, mode)
|
|
|
|
}
|