github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/core/scp/scp.go (about) 1 // Copyright 2012-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Scp copies files between hosts on a network. 6 // 7 // Synopsis: 8 // 9 // scp [-t|-f] [FILE] 10 // 11 // Description: 12 // 13 // If -t is given, decode SCP protocol from stdin and write to FILE. 14 // If -f is given, stream FILE over SCP protocol to stdout. 15 // 16 // Options: 17 // 18 // -t: Act as the target 19 // -f: Act as the source 20 // -v: Passed if SCP is verbose, ignored 21 package main 22 23 import ( 24 "flag" 25 "fmt" 26 "io" 27 "log" 28 "os" 29 "path" 30 ) 31 32 const ( 33 SUCCESS = 0 34 ) 35 36 var ( 37 isTarget = flag.Bool("t", false, "Act as the target") 38 isSource = flag.Bool("f", false, "Act as the source") 39 _ = flag.Bool("v", false, "Ignored") 40 ) 41 42 func scpSingleSource(w io.Writer, r io.Reader, pth string) error { 43 f, err := os.Open(pth) 44 if err != nil { 45 return err 46 } 47 defer f.Close() 48 s, err := f.Stat() 49 if err != nil { 50 return err 51 } 52 filename := path.Base(pth) 53 w.Write([]byte(fmt.Sprintf("C0%o %d %s\n", s.Mode(), s.Size(), filename))) 54 if response(r) != SUCCESS { 55 return fmt.Errorf("response was not success") 56 } 57 _, err = io.Copy(w, f) 58 if err != nil { 59 return fmt.Errorf("copy error: %v", err) 60 } 61 reply(w, SUCCESS) 62 63 if response(r) != SUCCESS { 64 return fmt.Errorf("response was not success") 65 } 66 return nil 67 } 68 69 func scpSingleSink(w io.Writer, r io.Reader, path string) error { 70 var mode os.FileMode 71 var size int64 72 filename := "" 73 74 // Ignore the filename, assume it has been provided on the command line. 75 // This will not work with directories and recursive copy, but that's not 76 // supported right now. 77 if _, err := fmt.Fscanf(r, "C0%o %d %s\n", &mode, &size, &filename); err != nil { 78 if err == io.ErrUnexpectedEOF { 79 return io.EOF 80 } 81 return fmt.Errorf("fscanf: %v", err) 82 } 83 f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, mode) 84 if err != nil { 85 return fmt.Errorf("open error: %v", err) 86 } 87 reply(w, SUCCESS) 88 defer f.Close() 89 90 _, err = io.CopyN(f, r, size) 91 if err != nil { 92 return fmt.Errorf("copy error: %v", err) 93 } 94 if response(r) != SUCCESS { 95 return fmt.Errorf("response was not success") 96 } 97 reply(w, SUCCESS) 98 return nil 99 } 100 101 func scpSource(w io.Writer, r io.Reader, path string) error { 102 // Sink->Source is started with a response 103 if response(r) != SUCCESS { 104 return fmt.Errorf("response was not success") 105 } 106 return scpSingleSource(w, r, path) 107 } 108 109 func scpSink(w io.Writer, r io.Reader, path string) error { 110 reply(w, SUCCESS) 111 for { 112 if err := scpSingleSink(w, r, path); err != nil { 113 if err == io.EOF { 114 break 115 } 116 return err 117 } 118 } 119 return nil 120 } 121 122 func reply(out io.Writer, r byte) { 123 out.Write([]byte{r}) 124 } 125 126 func response(in io.Reader) byte { 127 b := make([]byte, 1) 128 in.Read(b) 129 return b[0] 130 } 131 132 func main() { 133 flag.Parse() 134 135 if flag.NArg() == 0 { 136 log.Fatalf("no file provided") 137 } 138 139 if *isSource == *isTarget { 140 log.Fatalf("-t or -f needs to be supplied, and not both") 141 } 142 143 if *isSource { 144 if err := scpSource(os.Stdout, os.Stdin, flag.Args()[0]); err != nil { 145 log.Fatalf("scp: %v", err) 146 } 147 } else if *isTarget { 148 if err := scpSink(os.Stdout, os.Stdin, flag.Args()[0]); err != nil { 149 log.Fatalf("scp: %v", err) 150 } 151 } 152 }