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