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  }