mirror of
https://github.com/octoleo/restic.git
synced 2024-11-30 00:33:57 +00:00
sftp: Add Layout
This commit is contained in:
parent
2e53af1b75
commit
ab602c9d14
@ -11,6 +11,7 @@ import (
|
|||||||
// Config collects all information required to connect to an sftp server.
|
// Config collects all information required to connect to an sftp server.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
User, Host, Dir string
|
User, Host, Dir string
|
||||||
|
Layout string `option:"layout"`
|
||||||
Command string `option:"command"`
|
Command string `option:"command"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@ package sftp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@ -32,10 +30,14 @@ type SFTP struct {
|
|||||||
|
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
result <-chan error
|
result <-chan error
|
||||||
|
|
||||||
|
backend.Layout
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ restic.Backend = &SFTP{}
|
var _ restic.Backend = &SFTP{}
|
||||||
|
|
||||||
|
const defaultLayout = "default"
|
||||||
|
|
||||||
func startClient(program string, args ...string) (*SFTP, error) {
|
func startClient(program string, args ...string) (*SFTP, error) {
|
||||||
debug.Log("start client %v %v", program, args)
|
debug.Log("start client %v %v", program, args)
|
||||||
// Connect to a remote host and request the sftp subsystem via the 'ssh'
|
// Connect to a remote host and request the sftp subsystem via the 'ssh'
|
||||||
@ -127,6 +129,11 @@ func open(dir string, program string, args ...string) (*SFTP, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
l, err := backend.ParseLayout(sftp, cfg.Layout, defaultLayout, cfg.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// test if all necessary dirs and files are there
|
// test if all necessary dirs and files are there
|
||||||
for _, d := range paths(dir) {
|
for _, d := range paths(dir) {
|
||||||
if _, err := sftp.c.Lstat(d); err != nil {
|
if _, err := sftp.c.Lstat(d); err != nil {
|
||||||
@ -225,28 +232,6 @@ func (r *SFTP) Location() string {
|
|||||||
return r.p
|
return r.p
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return temp directory in correct directory for this backend.
|
|
||||||
func (r *SFTP) tempFile() (string, *sftp.File, error) {
|
|
||||||
// choose random suffix
|
|
||||||
buf := make([]byte, tempfileRandomSuffixLength)
|
|
||||||
_, err := io.ReadFull(rand.Reader, buf)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, errors.Errorf("unable to read %d random bytes for tempfile name: %v",
|
|
||||||
tempfileRandomSuffixLength, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// construct tempfile name
|
|
||||||
name := Join(r.p, backend.Paths.Temp, "temp-"+hex.EncodeToString(buf))
|
|
||||||
|
|
||||||
// create file in temp dir
|
|
||||||
f, err := r.c.Create(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, errors.Errorf("creating tempfile %q failed: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return name, f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SFTP) mkdirAll(dir string, mode os.FileMode) error {
|
func (r *SFTP) mkdirAll(dir string, mode os.FileMode) error {
|
||||||
// check if directory already exists
|
// check if directory already exists
|
||||||
fi, err := r.c.Lstat(dir)
|
fi, err := r.c.Lstat(dir)
|
||||||
@ -349,7 +334,7 @@ func (r *SFTP) dirname(h restic.Handle) string {
|
|||||||
|
|
||||||
// Save stores data in the backend at the handle.
|
// Save stores data in the backend at the handle.
|
||||||
func (r *SFTP) Save(h restic.Handle, rd io.Reader) (err error) {
|
func (r *SFTP) Save(h restic.Handle, rd io.Reader) (err error) {
|
||||||
debug.Log("save to %v", h)
|
debug.Log("Save %v", h)
|
||||||
if err := r.clientError(); err != nil {
|
if err := r.clientError(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -358,6 +343,35 @@ func (r *SFTP) Save(h restic.Handle, rd io.Reader) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filename := r.Filename(h)
|
||||||
|
|
||||||
|
// create directories if necessary
|
||||||
|
if h.Type == restic.DataFile {
|
||||||
|
err := r.mkdirAll(path.Dir(filename), backend.Modes.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if new file exists
|
||||||
|
if _, err := r.c.Lstat(filename); err == nil {
|
||||||
|
return errors.Errorf("Close(): file %v already exists", filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.c.Rename(oldname, filename)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Rename")
|
||||||
|
}
|
||||||
|
|
||||||
|
// set mode to read-only
|
||||||
|
fi, err := r.c.Lstat(filename)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Lstat")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.c.Chmod(filename, fi.Mode()&os.FileMode(^uint32(0222)))
|
||||||
|
return errors.Wrap(err, "Chmod")
|
||||||
|
|
||||||
filename, tmpfile, err := r.tempFile()
|
filename, tmpfile, err := r.tempFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
Loading…
Reference in New Issue
Block a user