2015-06-21 11:02:56 +00:00
package main
import (
2017-03-08 19:12:16 +00:00
"context"
2015-06-21 11:02:56 +00:00
"fmt"
"io"
2016-09-12 12:08:51 +00:00
"io/ioutil"
2015-06-21 11:02:56 +00:00
"os"
2017-11-20 21:08:53 +00:00
"path/filepath"
2016-08-21 20:38:22 +00:00
"runtime"
2015-11-04 21:05:36 +00:00
"strings"
2016-02-13 17:29:26 +00:00
"syscall"
2017-10-14 12:51:00 +00:00
"time"
2015-06-21 11:02:56 +00:00
2017-09-24 18:04:23 +00:00
"github.com/restic/restic/internal/backend"
2017-07-08 13:38:48 +00:00
"github.com/restic/restic/internal/backend/azure"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/backend/b2"
2017-07-08 13:34:23 +00:00
"github.com/restic/restic/internal/backend/gs"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/backend/local"
"github.com/restic/restic/internal/backend/location"
"github.com/restic/restic/internal/backend/rest"
"github.com/restic/restic/internal/backend/s3"
"github.com/restic/restic/internal/backend/sftp"
"github.com/restic/restic/internal/backend/swift"
2017-06-10 11:10:08 +00:00
"github.com/restic/restic/internal/cache"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/debug"
2017-11-20 21:08:53 +00:00
"github.com/restic/restic/internal/fs"
2017-10-08 18:28:03 +00:00
"github.com/restic/restic/internal/limiter"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/options"
"github.com/restic/restic/internal/repository"
2017-07-24 15:42:25 +00:00
"github.com/restic/restic/internal/restic"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/errors"
2016-09-01 20:17:37 +00:00
2016-02-21 14:24:46 +00:00
"golang.org/x/crypto/ssh/terminal"
2015-06-21 11:02:56 +00:00
)
var version = "compiled manually"
2016-09-17 10:36:05 +00:00
// GlobalOptions hold all global options for restic.
2015-06-21 11:02:56 +00:00
type GlobalOptions struct {
2016-09-17 10:36:05 +00:00
Repo string
PasswordFile string
Quiet bool
NoLock bool
2017-02-12 20:43:39 +00:00
JSON bool
2017-06-10 11:10:08 +00:00
CacheDir string
NoCache bool
2017-09-24 18:04:23 +00:00
CACerts [ ] string
2017-11-20 21:08:53 +00:00
CleanupCache bool
2015-06-21 11:02:56 +00:00
2017-10-08 18:28:03 +00:00
LimitUploadKb int
LimitDownloadKb int
2017-03-08 19:12:16 +00:00
ctx context . Context
2015-06-21 11:02:56 +00:00
password string
stdout io . Writer
2015-06-21 13:20:54 +00:00
stderr io . Writer
2017-03-25 14:33:52 +00:00
Options [ ] string
extended options . Options
2015-06-21 11:02:56 +00:00
}
2016-09-17 10:36:05 +00:00
var globalOptions = GlobalOptions {
stdout : os . Stdout ,
stderr : os . Stderr ,
}
2016-02-13 17:29:26 +00:00
func init ( ) {
2017-03-08 19:12:16 +00:00
var cancel context . CancelFunc
globalOptions . ctx , cancel = context . WithCancel ( context . Background ( ) )
AddCleanupHandler ( func ( ) error {
cancel ( )
return nil
} )
2016-09-17 10:36:05 +00:00
f := cmdRoot . PersistentFlags ( )
2017-01-02 19:21:30 +00:00
f . StringVarP ( & globalOptions . Repo , "repo" , "r" , os . Getenv ( "RESTIC_REPOSITORY" ) , "repository to backup to or restore from (default: $RESTIC_REPOSITORY)" )
2017-07-19 15:34:45 +00:00
f . StringVarP ( & globalOptions . PasswordFile , "password-file" , "p" , os . Getenv ( "RESTIC_PASSWORD_FILE" ) , "read the repository password from a file (default: $RESTIC_PASSWORD_FILE)" )
2017-01-18 09:46:04 +00:00
f . BoolVarP ( & globalOptions . Quiet , "quiet" , "q" , false , "do not output comprehensive progress report" )
2016-09-17 10:36:05 +00:00
f . BoolVar ( & globalOptions . NoLock , "no-lock" , false , "do not lock the repo, this allows some operations on read-only repos" )
2017-02-12 20:43:39 +00:00
f . BoolVarP ( & globalOptions . JSON , "json" , "" , false , "set output mode to JSON for commands that support it" )
2017-06-10 11:10:08 +00:00
f . StringVar ( & globalOptions . CacheDir , "cache-dir" , "" , "set the cache directory" )
f . BoolVar ( & globalOptions . NoCache , "no-cache" , false , "do not use a local cache" )
2017-09-24 18:04:23 +00:00
f . StringSliceVar ( & globalOptions . CACerts , "cacert" , nil , "path to load root certificates from (default: use system certificates)" )
2017-11-20 21:08:53 +00:00
f . BoolVar ( & globalOptions . CleanupCache , "cleanup-cache" , false , "auto remove old cache directories" )
2017-10-08 18:28:03 +00:00
f . IntVar ( & globalOptions . LimitUploadKb , "limit-upload" , 0 , "limits uploads to a maximum rate in KiB/s. (default: unlimited)" )
f . IntVar ( & globalOptions . LimitDownloadKb , "limit-download" , 0 , "limits downloads to a maximum rate in KiB/s. (default: unlimited)" )
2017-03-25 14:33:52 +00:00
f . StringSliceVarP ( & globalOptions . Options , "option" , "o" , [ ] string { } , "set extended option (`key=value`, can be specified multiple times)" )
2016-02-13 17:29:26 +00:00
restoreTerminal ( )
}
// checkErrno returns nil when err is set to syscall.Errno(0), since this is no
// error condition.
func checkErrno ( err error ) error {
e , ok := err . ( syscall . Errno )
if ! ok {
return err
}
if e == 0 {
return nil
}
return err
}
2016-08-27 16:31:46 +00:00
func stdinIsTerminal ( ) bool {
return terminal . IsTerminal ( int ( os . Stdin . Fd ( ) ) )
}
func stdoutIsTerminal ( ) bool {
return terminal . IsTerminal ( int ( os . Stdout . Fd ( ) ) )
}
2017-03-06 10:23:00 +00:00
func stdoutTerminalWidth ( ) int {
w , _ , err := terminal . GetSize ( int ( os . Stdout . Fd ( ) ) )
if err != nil {
return 0
}
return w
}
2016-02-13 17:29:26 +00:00
// restoreTerminal installs a cleanup handler that restores the previous
// terminal state on exit.
func restoreTerminal ( ) {
2016-08-27 16:31:46 +00:00
if ! stdoutIsTerminal ( ) {
2016-02-13 17:29:26 +00:00
return
}
2016-08-25 20:13:47 +00:00
fd := int ( os . Stdout . Fd ( ) )
2016-02-13 17:29:26 +00:00
state , err := terminal . GetState ( fd )
if err != nil {
fmt . Fprintf ( os . Stderr , "unable to get terminal state: %v\n" , err )
return
}
AddCleanupHandler ( func ( ) error {
err := checkErrno ( terminal . Restore ( fd , state ) )
if err != nil {
fmt . Fprintf ( os . Stderr , "unable to get restore terminal state: %#+v\n" , err )
}
return err
} )
}
2016-08-21 21:10:28 +00:00
// ClearLine creates a platform dependent string to clear the current
// line, so it can be overwritten. ANSI sequences are not supported on
// current windows cmd shell.
2016-08-21 20:38:22 +00:00
func ClearLine ( ) string {
if runtime . GOOS == "windows" {
2017-03-06 10:23:00 +00:00
if w := stdoutTerminalWidth ( ) ; w > 0 {
2016-08-22 15:27:58 +00:00
return strings . Repeat ( " " , w - 1 ) + "\r"
}
return ""
2016-08-21 20:38:22 +00:00
}
2016-08-21 21:10:28 +00:00
return "\x1b[2K"
2016-08-21 20:38:22 +00:00
}
2016-01-17 15:59:03 +00:00
// Printf writes the message to the configured stdout stream.
2016-09-17 10:36:05 +00:00
func Printf ( format string , args ... interface { } ) {
_ , err := fmt . Fprintf ( globalOptions . stdout , format , args ... )
2015-06-21 11:02:56 +00:00
if err != nil {
fmt . Fprintf ( os . Stderr , "unable to write to stdout: %v\n" , err )
2016-12-28 09:53:31 +00:00
Exit ( 100 )
2015-06-21 11:02:56 +00:00
}
}
2016-01-17 15:59:03 +00:00
// Verbosef calls Printf to write the message when the verbose flag is set.
2016-09-17 10:36:05 +00:00
func Verbosef ( format string , args ... interface { } ) {
if globalOptions . Quiet {
2015-06-21 11:25:26 +00:00
return
}
2016-09-17 10:36:05 +00:00
Printf ( format , args ... )
2016-08-25 20:13:47 +00:00
}
// PrintProgress wraps fmt.Printf to handle the difference in writing progress
// information to terminals and non-terminal stdout
func PrintProgress ( format string , args ... interface { } ) {
var (
message string
carriageControl string
)
message = fmt . Sprintf ( format , args ... )
if ! ( strings . HasSuffix ( message , "\r" ) || strings . HasSuffix ( message , "\n" ) ) {
2016-08-27 16:31:46 +00:00
if stdoutIsTerminal ( ) {
2016-08-25 20:13:47 +00:00
carriageControl = "\r"
} else {
carriageControl = "\n"
}
message = fmt . Sprintf ( "%s%s" , message , carriageControl )
2015-06-21 11:02:56 +00:00
}
2016-08-27 16:31:46 +00:00
if stdoutIsTerminal ( ) {
2016-08-25 20:13:47 +00:00
message = fmt . Sprintf ( "%s%s" , ClearLine ( ) , message )
}
fmt . Print ( message )
2015-06-21 11:02:56 +00:00
}
2016-01-17 15:59:03 +00:00
// Warnf writes the message to the configured stderr stream.
2016-09-17 10:36:05 +00:00
func Warnf ( format string , args ... interface { } ) {
_ , err := fmt . Fprintf ( globalOptions . stderr , format , args ... )
2015-06-21 11:02:56 +00:00
if err != nil {
fmt . Fprintf ( os . Stderr , "unable to write to stderr: %v\n" , err )
2016-12-28 09:53:31 +00:00
Exit ( 100 )
2015-06-21 11:02:56 +00:00
}
}
2016-12-28 09:53:31 +00:00
// Exitf uses Warnf to write the message and then terminates the process with
// the given exit code.
2016-09-17 10:36:05 +00:00
func Exitf ( exitcode int , format string , args ... interface { } ) {
2015-06-21 11:02:56 +00:00
if format [ len ( format ) - 1 ] != '\n' {
format += "\n"
}
2016-09-17 10:36:05 +00:00
Warnf ( format , args ... )
2016-12-28 09:53:31 +00:00
Exit ( exitcode )
2015-06-21 11:02:56 +00:00
}
2017-07-24 21:01:16 +00:00
// resolvePassword determines the password to be used for opening the repository.
func resolvePassword ( opts GlobalOptions , env string ) ( string , error ) {
if opts . PasswordFile != "" {
s , err := ioutil . ReadFile ( opts . PasswordFile )
2017-07-27 11:23:08 +00:00
if os . IsNotExist ( err ) {
return "" , errors . Fatalf ( "%s does not exist" , opts . PasswordFile )
}
2017-07-24 21:01:16 +00:00
return strings . TrimSpace ( string ( s ) ) , errors . Wrap ( err , "Readfile" )
}
if pwd := os . Getenv ( env ) ; pwd != "" {
return pwd , nil
}
return "" , nil
}
2015-11-04 21:05:36 +00:00
// readPassword reads the password from the given reader directly.
func readPassword ( in io . Reader ) ( password string , err error ) {
buf := make ( [ ] byte , 1000 )
n , err := io . ReadFull ( in , buf )
buf = buf [ : n ]
2016-08-29 17:18:57 +00:00
if err != nil && errors . Cause ( err ) != io . ErrUnexpectedEOF {
2016-09-12 12:08:51 +00:00
return "" , errors . Wrap ( err , "ReadFull" )
2015-11-04 21:05:36 +00:00
}
return strings . TrimRight ( string ( buf ) , "\r\n" ) , nil
}
// readPasswordTerminal reads the password from the given reader which must be a
// tty. Prompt is printed on the writer out before attempting to read the
// password.
func readPasswordTerminal ( in * os . File , out io . Writer , prompt string ) ( password string , err error ) {
fmt . Fprint ( out , prompt )
buf , err := terminal . ReadPassword ( int ( in . Fd ( ) ) )
fmt . Fprintln ( out )
if err != nil {
2016-09-12 12:08:51 +00:00
return "" , errors . Wrap ( err , "ReadPassword" )
2015-11-04 21:05:36 +00:00
}
password = string ( buf )
return password , nil
}
2016-09-12 12:08:51 +00:00
// ReadPassword reads the password from a password file, the environment
// variable RESTIC_PASSWORD or prompts the user.
2016-09-17 10:36:05 +00:00
func ReadPassword ( opts GlobalOptions , prompt string ) ( string , error ) {
2017-07-24 21:01:16 +00:00
if opts . password != "" {
return opts . password , nil
2016-09-12 12:08:51 +00:00
}
2015-11-04 21:05:36 +00:00
var (
password string
err error
)
2016-08-27 16:31:46 +00:00
if stdinIsTerminal ( ) {
2015-11-04 21:05:36 +00:00
password , err = readPasswordTerminal ( os . Stdin , os . Stderr , prompt )
} else {
password , err = readPassword ( os . Stdin )
}
2015-06-21 11:02:56 +00:00
if err != nil {
2016-09-12 12:08:51 +00:00
return "" , errors . Wrap ( err , "unable to read password" )
2015-06-21 11:02:56 +00:00
}
2015-11-04 21:05:36 +00:00
if len ( password ) == 0 {
2016-09-12 12:08:51 +00:00
return "" , errors . Fatal ( "an empty password is not a password" )
2015-06-21 13:01:52 +00:00
}
2016-09-12 12:08:51 +00:00
return password , nil
2015-06-21 11:02:56 +00:00
}
2016-01-17 15:59:03 +00:00
// ReadPasswordTwice calls ReadPassword two times and returns an error when the
// passwords don't match.
2016-09-17 10:36:05 +00:00
func ReadPasswordTwice ( gopts GlobalOptions , prompt1 , prompt2 string ) ( string , error ) {
pw1 , err := ReadPassword ( gopts , prompt1 )
2016-09-12 12:08:51 +00:00
if err != nil {
return "" , err
}
2016-09-17 10:36:05 +00:00
pw2 , err := ReadPassword ( gopts , prompt2 )
2016-09-12 12:08:51 +00:00
if err != nil {
return "" , err
}
2015-06-21 11:02:56 +00:00
if pw1 != pw2 {
2016-09-12 12:08:51 +00:00
return "" , errors . Fatal ( "passwords do not match" )
2015-06-21 11:02:56 +00:00
}
2016-09-12 12:08:51 +00:00
return pw1 , nil
2015-06-21 11:02:56 +00:00
}
2016-08-21 11:09:31 +00:00
const maxKeys = 20
2016-01-17 15:59:03 +00:00
// OpenRepository reads the password and opens the repository.
2016-09-17 10:36:05 +00:00
func OpenRepository ( opts GlobalOptions ) ( * repository . Repository , error ) {
if opts . Repo == "" {
2016-09-01 20:17:37 +00:00
return nil , errors . Fatal ( "Please specify repository location (-r)" )
2015-06-21 11:02:56 +00:00
}
2017-12-29 11:43:49 +00:00
be , err := open ( opts . Repo , opts , opts . extended )
2015-06-21 11:02:56 +00:00
if err != nil {
return nil , err
}
2017-10-14 12:51:00 +00:00
be = backend . NewRetryBackend ( be , 10 , func ( msg string , err error , d time . Duration ) {
Warnf ( "%v returned error, retrying after %v: %v\n" , msg , d , err )
} )
2015-06-21 11:02:56 +00:00
s := repository . New ( be )
2017-07-24 21:15:31 +00:00
opts . password , err = ReadPassword ( opts , "enter password for repository: " )
if err != nil {
return nil , err
2015-06-21 11:02:56 +00:00
}
use global context for check, debug, dump, find, forget, init, key,
list, mount, tag, unlock commands
gh-1434
2017-12-06 12:02:55 +00:00
err = s . SearchKey ( opts . ctx , opts . password , maxKeys )
2015-06-21 11:02:56 +00:00
if err != nil {
2017-09-10 08:55:01 +00:00
return nil , err
2015-06-21 11:02:56 +00:00
}
2017-10-04 12:55:04 +00:00
if stdoutIsTerminal ( ) {
2017-10-04 11:45:05 +00:00
Verbosef ( "password is correct\n" )
}
2017-06-10 11:10:08 +00:00
if opts . NoCache {
return s , nil
}
2017-11-20 20:32:25 +00:00
c , err := cache . New ( s . Config ( ) . ID , opts . CacheDir )
2017-06-10 11:10:08 +00:00
if err != nil {
Warnf ( "unable to open cache: %v\n" , err )
2017-11-20 20:32:25 +00:00
return s , nil
}
2017-12-03 14:52:57 +00:00
// start using the cache
s . UseCache ( c )
2017-11-20 20:32:25 +00:00
oldCacheDirs , err := cache . Old ( c . Base )
if err != nil {
Warnf ( "unable to find old cache directories: %v" , err )
2017-11-20 21:08:53 +00:00
}
// nothing more to do if no old cache dirs could be found
if len ( oldCacheDirs ) == 0 {
return s , nil
}
// cleanup old cache dirs if instructed to do so
if opts . CleanupCache {
Printf ( "removing %d old cache dirs from %v\n" , len ( oldCacheDirs ) , c . Base )
for _ , item := range oldCacheDirs {
dir := filepath . Join ( c . Base , item )
err = fs . RemoveAll ( dir )
if err != nil {
Warnf ( "unable to remove %v: %v\n" , dir , err )
}
}
2017-06-10 11:10:08 +00:00
} else {
2017-12-13 18:57:05 +00:00
if stdoutIsTerminal ( ) {
Verbosef ( "found %d old cache directories in %v, pass --cleanup-cache to remove them\n" ,
len ( oldCacheDirs ) , c . Base )
}
2017-06-10 11:10:08 +00:00
}
2015-06-21 11:02:56 +00:00
return s , nil
}
2017-03-25 16:31:59 +00:00
func parseConfig ( loc location . Location , opts options . Options ) ( interface { } , error ) {
// only apply options for a particular backend here
opts = opts . Extract ( loc . Scheme )
2016-09-18 11:24:29 +00:00
2015-12-28 17:23:20 +00:00
switch loc . Scheme {
case "local" :
2017-03-25 16:31:59 +00:00
cfg := loc . Config . ( local . Config )
if err := opts . Apply ( loc . Scheme , & cfg ) ; err != nil {
return nil , err
}
debug . Log ( "opening local repository at %#v" , cfg )
return cfg , nil
2015-12-28 17:23:20 +00:00
case "sftp" :
2017-03-25 16:31:59 +00:00
cfg := loc . Config . ( sftp . Config )
if err := opts . Apply ( loc . Scheme , & cfg ) ; err != nil {
return nil , err
}
debug . Log ( "opening sftp repository at %#v" , cfg )
return cfg , nil
2015-12-28 17:23:20 +00:00
case "s3" :
cfg := loc . Config . ( s3 . Config )
if cfg . KeyID == "" {
cfg . KeyID = os . Getenv ( "AWS_ACCESS_KEY_ID" )
}
2017-03-25 16:31:59 +00:00
2015-12-28 17:23:20 +00:00
if cfg . Secret == "" {
cfg . Secret = os . Getenv ( "AWS_SECRET_ACCESS_KEY" )
}
2015-08-14 13:39:16 +00:00
2017-03-25 16:31:59 +00:00
if err := opts . Apply ( loc . Scheme , & cfg ) ; err != nil {
return nil , err
}
2016-09-27 20:35:08 +00:00
debug . Log ( "opening s3 repository at %#v" , cfg )
2017-03-25 16:31:59 +00:00
return cfg , nil
2017-07-08 13:34:23 +00:00
case "gs" :
cfg := loc . Config . ( gs . Config )
if cfg . ProjectID == "" {
cfg . ProjectID = os . Getenv ( "GOOGLE_PROJECT_ID" )
}
if cfg . JSONKeyPath == "" {
if path := os . Getenv ( "GOOGLE_APPLICATION_CREDENTIALS" ) ; path != "" {
// Check read access
if _ , err := ioutil . ReadFile ( path ) ; err != nil {
return nil , errors . Fatalf ( "Failed to read google credential from file %v: %v" , path , err )
}
cfg . JSONKeyPath = path
} else {
return nil , errors . Fatal ( "No credential file path is set" )
}
}
if err := opts . Apply ( loc . Scheme , & cfg ) ; err != nil {
return nil , err
}
debug . Log ( "opening gs repository at %#v" , cfg )
return cfg , nil
2017-07-08 13:38:48 +00:00
case "azure" :
cfg := loc . Config . ( azure . Config )
if cfg . AccountName == "" {
cfg . AccountName = os . Getenv ( "AZURE_ACCOUNT_NAME" )
}
if cfg . AccountKey == "" {
cfg . AccountKey = os . Getenv ( "AZURE_ACCOUNT_KEY" )
}
if err := opts . Apply ( loc . Scheme , & cfg ) ; err != nil {
return nil , err
}
debug . Log ( "opening gs repository at %#v" , cfg )
return cfg , nil
2017-03-29 21:58:25 +00:00
case "swift" :
cfg := loc . Config . ( swift . Config )
2017-05-01 08:13:03 +00:00
if err := swift . ApplyEnvironment ( "" , & cfg ) ; err != nil {
return nil , err
2017-03-29 21:58:25 +00:00
}
if err := opts . Apply ( loc . Scheme , & cfg ) ; err != nil {
return nil , err
}
debug . Log ( "opening swift repository at %#v" , cfg )
return cfg , nil
2017-05-28 08:19:01 +00:00
case "b2" :
cfg := loc . Config . ( b2 . Config )
if cfg . AccountID == "" {
cfg . AccountID = os . Getenv ( "B2_ACCOUNT_ID" )
}
2017-12-19 20:12:38 +00:00
if cfg . AccountID == "" {
return nil , errors . Fatalf ( "unable to open B2 backend: Account ID ($B2_ACCOUNT_ID) is empty" )
}
2017-05-28 08:19:01 +00:00
if cfg . Key == "" {
cfg . Key = os . Getenv ( "B2_ACCOUNT_KEY" )
}
2017-12-19 20:12:38 +00:00
if cfg . Key == "" {
return nil , errors . Fatalf ( "unable to open B2 backend: Key ($B2_ACCOUNT_KEY) is empty" )
}
2017-05-28 08:19:01 +00:00
if err := opts . Apply ( loc . Scheme , & cfg ) ; err != nil {
return nil , err
}
debug . Log ( "opening b2 repository at %#v" , cfg )
return cfg , nil
2016-02-21 14:24:46 +00:00
case "rest" :
2017-03-25 16:31:59 +00:00
cfg := loc . Config . ( rest . Config )
if err := opts . Apply ( loc . Scheme , & cfg ) ; err != nil {
return nil , err
}
debug . Log ( "opening rest repository at %#v" , cfg )
return cfg , nil
}
return nil , errors . Fatalf ( "invalid backend: %q" , loc . Scheme )
}
// Open the backend specified by a location config.
2017-12-29 11:43:49 +00:00
func open ( s string , gopts GlobalOptions , opts options . Options ) ( restic . Backend , error ) {
2017-03-25 16:31:59 +00:00
debug . Log ( "parsing location %v" , s )
loc , err := location . Parse ( s )
if err != nil {
return nil , errors . Fatalf ( "parsing repository location failed: %v" , err )
}
var be restic . Backend
cfg , err := parseConfig ( loc , opts )
if err != nil {
return nil , err
}
2017-09-24 18:04:23 +00:00
rt , err := backend . Transport ( globalOptions . CACerts )
if err != nil {
return nil , err
}
2017-12-29 11:43:49 +00:00
// wrap the transport so that the throughput via HTTP is limited
rt = limiter . NewStaticLimiter ( gopts . LimitUploadKb , gopts . LimitDownloadKb ) . Transport ( rt )
2017-03-25 16:31:59 +00:00
switch loc . Scheme {
case "local" :
be , err = local . Open ( cfg . ( local . Config ) )
2017-12-29 11:43:49 +00:00
// wrap the backend in a LimitBackend so that the throughput is limited
be = limiter . LimitBackend ( be , limiter . NewStaticLimiter ( gopts . LimitUploadKb , gopts . LimitDownloadKb ) )
2017-03-25 16:31:59 +00:00
case "sftp" :
2017-09-23 09:21:27 +00:00
be , err = sftp . Open ( cfg . ( sftp . Config ) , SuspendSignalHandler , InstallSignalHandler )
2017-12-29 11:43:49 +00:00
// wrap the backend in a LimitBackend so that the throughput is limited
be = limiter . LimitBackend ( be , limiter . NewStaticLimiter ( gopts . LimitUploadKb , gopts . LimitDownloadKb ) )
2017-03-25 16:31:59 +00:00
case "s3" :
2017-09-24 18:04:23 +00:00
be , err = s3 . Open ( cfg . ( s3 . Config ) , rt )
2017-07-08 13:34:23 +00:00
case "gs" :
be , err = gs . Open ( cfg . ( gs . Config ) )
2017-07-08 13:38:48 +00:00
case "azure" :
2017-09-24 18:04:23 +00:00
be , err = azure . Open ( cfg . ( azure . Config ) , rt )
2017-03-29 21:58:25 +00:00
case "swift" :
2017-09-24 18:04:23 +00:00
be , err = swift . Open ( cfg . ( swift . Config ) , rt )
2017-05-28 08:19:01 +00:00
case "b2" :
2017-11-22 11:27:29 +00:00
be , err = b2 . Open ( globalOptions . ctx , cfg . ( b2 . Config ) , rt )
2017-03-25 16:31:59 +00:00
case "rest" :
2017-09-24 18:04:23 +00:00
be , err = rest . Open ( cfg . ( rest . Config ) , rt )
2017-03-25 16:31:59 +00:00
2016-09-18 11:24:29 +00:00
default :
return nil , errors . Fatalf ( "invalid backend: %q" , loc . Scheme )
2015-06-21 11:02:56 +00:00
}
2016-09-18 11:24:29 +00:00
if err != nil {
return nil , errors . Fatalf ( "unable to open repo at %v: %v" , s , err )
}
2017-04-19 16:56:01 +00:00
// check if config is there
2017-11-22 11:27:29 +00:00
fi , err := be . Stat ( globalOptions . ctx , restic . Handle { Type : restic . ConfigFile } )
2017-04-19 16:56:01 +00:00
if err != nil {
return nil , errors . Fatalf ( "unable to open config file: %v\nIs there a repository at the following location?\n%v" , err , s )
}
if fi . Size == 0 {
return nil , errors . New ( "config file has zero size, invalid repository?" )
}
2016-09-18 11:24:29 +00:00
return be , nil
2015-06-21 11:02:56 +00:00
}
// Create the backend specified by URI.
2017-03-25 14:33:52 +00:00
func create ( s string , opts options . Options ) ( restic . Backend , error ) {
2016-09-27 20:35:08 +00:00
debug . Log ( "parsing location %v" , s )
2015-12-28 17:23:20 +00:00
loc , err := location . Parse ( s )
2015-06-21 11:02:56 +00:00
if err != nil {
return nil , err
}
2017-03-25 16:31:59 +00:00
cfg , err := parseConfig ( loc , opts )
if err != nil {
return nil , err
}
2017-09-24 18:04:23 +00:00
rt , err := backend . Transport ( globalOptions . CACerts )
if err != nil {
return nil , err
}
2015-12-28 17:23:20 +00:00
switch loc . Scheme {
case "local" :
2017-03-25 16:31:59 +00:00
return local . Create ( cfg . ( local . Config ) )
2015-12-28 17:23:20 +00:00
case "sftp" :
2017-09-23 09:21:27 +00:00
return sftp . Create ( cfg . ( sftp . Config ) , SuspendSignalHandler , InstallSignalHandler )
2015-12-28 17:23:20 +00:00
case "s3" :
2017-09-24 18:04:23 +00:00
return s3 . Create ( cfg . ( s3 . Config ) , rt )
2017-07-08 13:34:23 +00:00
case "gs" :
return gs . Create ( cfg . ( gs . Config ) )
2017-07-08 13:38:48 +00:00
case "azure" :
2017-09-24 18:04:23 +00:00
return azure . Create ( cfg . ( azure . Config ) , rt )
2017-03-29 21:58:25 +00:00
case "swift" :
2017-09-24 18:04:23 +00:00
return swift . Open ( cfg . ( swift . Config ) , rt )
2017-05-28 08:19:01 +00:00
case "b2" :
2017-11-22 11:27:29 +00:00
return b2 . Create ( globalOptions . ctx , cfg . ( b2 . Config ) , rt )
2016-02-21 14:24:46 +00:00
case "rest" :
2017-09-24 18:04:23 +00:00
return rest . Create ( cfg . ( rest . Config ) , rt )
2015-06-21 11:02:56 +00:00
}
2016-09-27 20:35:08 +00:00
debug . Log ( "invalid repository scheme: %v" , s )
2016-09-01 20:17:37 +00:00
return nil , errors . Fatalf ( "invalid scheme %q" , loc . Scheme )
2015-06-21 11:02:56 +00:00
}