mirror of
https://github.com/containers/podman.git
synced 2025-09-24 07:15:12 +08:00
Created scp.go image_scp_test.go and podman-image-scp.1.md
added functionality for image secure copying from local to remote. Also moved system connection add code around a bit so functions within that file can be used by scp. Signed-off-by: cdoern <cdoern@redhat.com>
This commit is contained in:
25
vendor/github.com/dtylman/scp/.gitignore
generated
vendored
Normal file
25
vendor/github.com/dtylman/scp/.gitignore
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
example/example
|
21
vendor/github.com/dtylman/scp/LICENSE
generated
vendored
Normal file
21
vendor/github.com/dtylman/scp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Danny
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
42
vendor/github.com/dtylman/scp/README.md
generated
vendored
Normal file
42
vendor/github.com/dtylman/scp/README.md
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# scp
|
||||
|
||||
[](https://goreportcard.com/report/github.com/dtylman/scp)
|
||||
|
||||
A Simple `go` SCP client library.
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/dtylman/scp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
```
|
||||
|
||||
## Sending Files
|
||||
|
||||
Copies `/var/log/messages` to remote `/tmp/lala`:
|
||||
|
||||
```go
|
||||
var sc* ssh.Client
|
||||
// establish ssh connection into sc here...
|
||||
n,err:=scp.CopyTo(sc, "/var/log/messages", "/tmp/lala")
|
||||
if err==nil{
|
||||
fmt.Printf("Sent %v bytes",n)
|
||||
}
|
||||
```
|
||||
|
||||
## Receiving Files
|
||||
|
||||
Copies remote `/var/log/message` to local `/tmp/lala`:
|
||||
|
||||
```go
|
||||
var sc* ssh.Client
|
||||
// establish ssh connection into sc here...
|
||||
n,err:=scp.CopyFrom(sc, "/var/log/message", "/tmp/lala")
|
||||
if err==nil{
|
||||
fmt.Printf("Sent %v bytes",n)
|
||||
}
|
||||
```
|
||||
|
||||
|
121
vendor/github.com/dtylman/scp/msg.go
generated
vendored
Normal file
121
vendor/github.com/dtylman/scp/msg.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
package scp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
//CopyMessage Copy Message Opcode
|
||||
CopyMessage = 'C'
|
||||
//ErrorMessage Error OpCode
|
||||
ErrorMessage = 0x1
|
||||
//WarnMessage Warning Opcode
|
||||
WarnMessage = 0x2
|
||||
)
|
||||
|
||||
//Message is scp control message
|
||||
type Message struct {
|
||||
Type byte
|
||||
Error error
|
||||
Mode string
|
||||
Size int64
|
||||
FileName string
|
||||
}
|
||||
|
||||
func (m *Message) readByte(reader io.Reader) (byte, error) {
|
||||
buff := make([]byte, 1)
|
||||
_, err := io.ReadFull(reader, buff)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return buff[0], nil
|
||||
|
||||
}
|
||||
|
||||
func (m *Message) readOpCode(reader io.Reader) error {
|
||||
var err error
|
||||
m.Type, err = m.readByte(reader)
|
||||
return err
|
||||
}
|
||||
|
||||
//ReadError reads an error message
|
||||
func (m *Message) ReadError(reader io.Reader) error {
|
||||
msg, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Error = errors.New(strings.TrimSpace(string(msg)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) readLine(reader io.Reader) (string, error) {
|
||||
line := ""
|
||||
b, err := m.readByte(reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for b != 10 {
|
||||
line += string(b)
|
||||
b, err = m.readByte(reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return line, nil
|
||||
}
|
||||
|
||||
func (m *Message) readCopy(reader io.Reader) error {
|
||||
line, err := m.readLine(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parts := strings.Split(line, " ")
|
||||
if len(parts) < 2 {
|
||||
return errors.New("Invalid copy line: " + line)
|
||||
}
|
||||
m.Mode = parts[0]
|
||||
m.Size, err = strconv.ParseInt(parts[1], 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.FileName = parts[2]
|
||||
return nil
|
||||
}
|
||||
|
||||
//ReadFrom reads message from reader
|
||||
func (m *Message) ReadFrom(reader io.Reader) (int64, error) {
|
||||
err := m.readOpCode(reader)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch m.Type {
|
||||
case CopyMessage:
|
||||
err = m.readCopy(reader)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
case ErrorMessage, WarnMessage:
|
||||
err = m.ReadError(reader)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
default:
|
||||
return 0, fmt.Errorf("Unsupported opcode: %v", m.Type)
|
||||
}
|
||||
return m.Size, nil
|
||||
}
|
||||
|
||||
//NewMessageFromReader constructs a new message from a data in reader
|
||||
func NewMessageFromReader(reader io.Reader) (*Message, error) {
|
||||
m := new(Message)
|
||||
_, err := m.ReadFrom(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
153
vendor/github.com/dtylman/scp/scp.go
generated
vendored
Normal file
153
vendor/github.com/dtylman/scp/scp.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
package scp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
const (
|
||||
fileMode = "0644"
|
||||
buffSize = 1024 * 256
|
||||
)
|
||||
|
||||
//CopyTo copy from local to remote
|
||||
func CopyTo(sshClient *ssh.Client, local string, remote string) (int64, error) {
|
||||
session, err := sshClient.NewSession()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer session.Close()
|
||||
stderr := &bytes.Buffer{}
|
||||
session.Stderr = stderr
|
||||
stdout := &bytes.Buffer{}
|
||||
session.Stdout = stdout
|
||||
writer, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer writer.Close()
|
||||
err = session.Start("scp -t " + filepath.Dir(remote))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
localFile, err := os.Open(local)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fileInfo, err := localFile.Stat()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, err = fmt.Fprintf(writer, "C%s %d %s\n", fileMode, fileInfo.Size(), filepath.Base(remote))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err := copyN(writer, localFile, fileInfo.Size())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = ack(writer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
err = session.Wait()
|
||||
log.Debugf("Copied %v bytes out of %v. err: %v stdout:%v. stderr:%v", n, fileInfo.Size(), err, stdout, stderr)
|
||||
//NOTE: Process exited with status 1 is not an error, it just how scp work. (waiting for the next control message and we send EOF)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
//CopyFrom copy from remote to local
|
||||
func CopyFrom(sshClient *ssh.Client, remote string, local string) (int64, error) {
|
||||
session, err := sshClient.NewSession()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer session.Close()
|
||||
stderr := &bytes.Buffer{}
|
||||
session.Stderr = stderr
|
||||
writer, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer writer.Close()
|
||||
reader, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = session.Start("scp -f " + remote)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = ack(writer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
msg, err := NewMessageFromReader(reader)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if msg.Type == ErrorMessage || msg.Type == WarnMessage {
|
||||
return 0, msg.Error
|
||||
}
|
||||
log.Debugf("Receiving %v", msg)
|
||||
|
||||
err = ack(writer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
outFile, err := os.Create(local)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer outFile.Close()
|
||||
n, err := copyN(outFile, reader, msg.Size)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = outFile.Sync()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = outFile.Close()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = session.Wait()
|
||||
log.Debugf("Copied %v bytes out of %v. err: %v stderr:%v", n, msg.Size, err, stderr)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func ack(writer io.Writer) error {
|
||||
var msg = []byte{0, 0, 10, 13}
|
||||
n, err := writer.Write(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n < len(msg) {
|
||||
return errors.New("Failed to write ack buffer")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyN(writer io.Writer, src io.Reader, size int64) (int64, error) {
|
||||
reader := io.LimitReader(src, size)
|
||||
var total int64
|
||||
for total < size {
|
||||
n, err := io.CopyBuffer(writer, reader, make([]byte, buffSize))
|
||||
log.Debugf("Copied chunk %v total: %v out of %v err: %v ", n, total, size, err)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
total += n
|
||||
}
|
||||
return total, nil
|
||||
}
|
Reference in New Issue
Block a user