Skip to content

Commit

Permalink
support transfer directories
Browse files Browse the repository at this point in the history
  • Loading branch information
lonnywong committed Jun 18, 2022
1 parent 0142507 commit cc9ce5c
Show file tree
Hide file tree
Showing 9 changed files with 503 additions and 167 deletions.
117 changes: 98 additions & 19 deletions trzsz/comm.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type ProgressCallback interface {
onName(name string)
onSize(size int64)
onStep(step int64)
onDone(name string)
onDone()
}

type BufferSize struct {
Expand All @@ -83,8 +83,9 @@ type Args struct {
Overwrite bool `arg:"-y" help:"yes, overwrite existing file(s)"`
Binary bool `arg:"-b" help:"binary transfer mode, faster for binary files"`
Escape bool `arg:"-e" help:"escape all known control characters"`
Directory bool `arg:"-d" help:"transfer directories and files"`
Bufsize BufferSize `arg:"-B" placeholder:"N" default:"10M" help:"max buffer chunk size (1K<=N<=1G). (default: 10M)"`
Timeout int `arg:"-t" placeholder:"N" default:"100" help:"timeout ( N seconds ) for each buffer chunk.\nN <= 0 means never timeout. (default: 100)"`
Timeout int `arg:"-t" placeholder:"N" default:"10" help:"timeout ( N seconds ) for each buffer chunk.\nN <= 0 means never timeout. (default: 10)"`
}

var sizeRegexp = regexp.MustCompile("(?i)^(\\d+)(b|k|m|g|kb|mb|gb)?$")
Expand Down Expand Up @@ -196,38 +197,107 @@ func (e *TrzszError) isRemoteFail() bool {
}

func checkPathWritable(path string) error {
fileInfo, err := os.Stat(path)
info, err := os.Stat(path)
if errors.Is(err, os.ErrNotExist) {
return newTrzszError(fmt.Sprintf("No such directory: %s", path))
} else if err != nil {
return err
}
if !fileInfo.IsDir() {
if !info.IsDir() {
return newTrzszError(fmt.Sprintf("Not a directory: %s", path))
}
if !IsWindows() {
if fileInfo.Mode().Perm()&(1<<7) == 0 {
return newTrzszError(fmt.Sprintf("No permission to write: %s", path))
}
if syscallAccessWok(path) != nil {
return newTrzszError(fmt.Sprintf("No permission to write: %s", path))
}
return nil
}

func checkFilesReadable(files []string) error {
type TrzszFile struct {
PathID int `json:"path_id"`
AbsPath string `json:"-"`
RelPath []string `json:"path_name"`
IsDir bool `json:"is_dir"`
}

func resolveLink(path string) string {
for {
p, err := os.Readlink(path)
if err != nil {
return path
}
path = p
}
}

func checkPathReadable(pathID int, path string, info os.FileInfo, list *[]*TrzszFile, relPath []string, visitedDir map[string]bool) error {
if !info.IsDir() {
if !info.Mode().IsRegular() {
return newTrzszError(fmt.Sprintf("Not a regular file: %s", path))
}
if syscallAccessRok(path) != nil {
return newTrzszError(fmt.Sprintf("No permission to read: %s", path))
}
*list = append(*list, &TrzszFile{pathID, path, relPath, false})
return nil
}
realPath := resolveLink(path)
if _, ok := visitedDir[realPath]; ok {
return newTrzszError(fmt.Sprintf("Loop link: %s", path))
}
visitedDir[realPath] = true
*list = append(*list, &TrzszFile{pathID, path, relPath, true})
f, err := os.Open(path)
if err != nil {
return newTrzszError(fmt.Sprintf("Open [%s] error: %v", path, err))
}
files, err := f.Readdir(-1)
if err != nil {
return newTrzszError(fmt.Sprintf("Readdir [%s] error: %v", path, err))
}
for _, file := range files {
fileInfo, err := os.Stat(file)
p := filepath.Join(path, file.Name())
r := make([]string, len(relPath))
copy(r, relPath)
r = append(r, file.Name())
if err := checkPathReadable(pathID, p, file, list, r, visitedDir); err != nil {
return err
}
}
return nil
}

func checkPathsReadable(paths []string, directory bool) ([]*TrzszFile, error) {
var list []*TrzszFile
visitedDir := make(map[string]bool)
for i, p := range paths {
path, err := filepath.Abs(p)
if err != nil {
return nil, err
}
info, err := os.Stat(path)
if errors.Is(err, os.ErrNotExist) {
return newTrzszError(fmt.Sprintf("No such file: %s", file))
return nil, newTrzszError(fmt.Sprintf("No such file: %s", path))
} else if err != nil {
return nil, err
}
if fileInfo.IsDir() {
return newTrzszError(fmt.Sprintf("Is a directory: %s", file))
if !directory && info.IsDir() {
return nil, newTrzszError(fmt.Sprintf("Is a directory: %s", path))
}
if !fileInfo.Mode().IsRegular() {
return newTrzszError(fmt.Sprintf("Not a regular file: %s", file))
if err := checkPathReadable(i, path, info, &list, []string{info.Name()}, visitedDir); err != nil {
return nil, err
}
if !IsWindows() {
if fileInfo.Mode().Perm()&(1<<8) == 0 {
return newTrzszError(fmt.Sprintf("No permission to read: %s", file))
}
}
return list, nil
}

func checkDuplicateNames(list []*TrzszFile) error {
m := make(map[string]bool)
for _, f := range list {
p := filepath.Join(f.RelPath...)
if _, ok := m[p]; ok {
return newTrzszError(fmt.Sprintf("Duplicate name: %s", p))
}
m[p] = true
}
return nil
}
Expand Down Expand Up @@ -367,3 +437,12 @@ func trimVT100(buf []byte) []byte {
}
return b.Bytes()
}

func containsString(elems []string, v string) bool {
for _, s := range elems {
if v == s {
return true
}
}
return false
}
2 changes: 1 addition & 1 deletion trzsz/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func (p *TextProgressBar) onStep(step int64) {
p.showProgress()
}

func (p *TextProgressBar) onDone(name string) {
func (p *TextProgressBar) onDone() {
}

func (p *TextProgressBar) showProgress() {
Expand Down
9 changes: 9 additions & 0 deletions trzsz/pty_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"syscall"

"github.com/creack/pty"
"golang.org/x/sys/unix"
)

type TrzszPty struct {
Expand Down Expand Up @@ -109,3 +110,11 @@ func (t *TrzszPty) Terminate() {
func (t *TrzszPty) ExitCode() int {
return t.cmd.ProcessState.ExitCode()
}

func syscallAccessWok(path string) error {
return syscall.Access(path, unix.W_OK)
}

func syscallAccessRok(path string) error {
return syscall.Access(path, unix.R_OK)
}
41 changes: 26 additions & 15 deletions trzsz/pty_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,16 @@ import (
)

type TrzszPty struct {
Stdin PtyIO
Stdout PtyIO
cpty *conpty.ConPty
inMode uint32
outMode uint32
width int
height int
closed bool
exitCode *uint32
Stdin PtyIO
Stdout PtyIO
cpty *conpty.ConPty
inMode uint32
outMode uint32
width int
height int
closed bool
exitCode *uint32
startTime time.Time
}

func getConsoleSize() (int, int, error) {
Expand Down Expand Up @@ -134,7 +135,7 @@ func Spawn(name string, args ...string) (*TrzszPty, error) {
return nil, err
}

return &TrzszPty{cpty, cpty, cpty, inMode, outMode, width, height, false, nil}, nil
return &TrzszPty{cpty, cpty, cpty, inMode, outMode, width, height, false, nil, time.Now()}, nil
}

func (t *TrzszPty) OnResize(cb func(int)) {
Expand All @@ -148,13 +149,15 @@ func (t *TrzszPty) Close() {
if t.closed {
return
}
t.closed = true
t.cpty.Close()
resetVirtualTerminal(t.inMode, t.outMode)
time.Sleep(100 * time.Millisecond)
cmd := exec.Command("cmd", "/c", "cls")
cmd.Stdout = os.Stdout
cmd.Run()
t.closed = true
if time.Now().Sub(t.startTime) > 10*time.Second {
time.Sleep(100 * time.Millisecond)
cmd := exec.Command("cmd", "/c", "cls")
cmd.Stdout = os.Stdout
cmd.Run()
}
}

func (t *TrzszPty) Wait() {
Expand All @@ -172,3 +175,11 @@ func (t *TrzszPty) ExitCode() int {
}
return int(*t.exitCode)
}

func syscallAccessWok(path string) error {
return nil
}

func syscallAccessRok(path string) error {
return nil
}
Loading

0 comments on commit cc9ce5c

Please sign in to comment.