restic/internal/backend/location/location.go

133 lines
3.3 KiB
Go
Raw Normal View History

2015-12-28 14:57:20 +00:00
// Package location implements parsing the restic repository location from a string.
package location
2015-12-28 14:51:24 +00:00
import (
"strings"
"github.com/restic/restic/internal/backend/azure"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/backend/b2"
"github.com/restic/restic/internal/backend/gs"
2017-07-23 12:21:03 +00:00
"github.com/restic/restic/internal/backend/local"
2018-03-13 21:30:51 +00:00
"github.com/restic/restic/internal/backend/rclone"
2017-07-23 12:21:03 +00:00
"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"
"github.com/restic/restic/internal/errors"
2015-12-28 14:51:24 +00:00
)
2015-12-28 14:57:20 +00:00
// Location specifies the location of a repository, including the method of
// access and (possibly) credentials needed for access.
type Location struct {
2015-12-28 14:51:24 +00:00
Scheme string
Config interface{}
}
type parser struct {
2020-03-20 22:52:27 +00:00
scheme string
parse func(string) (interface{}, error)
stripPassword func(string) string
2015-12-28 14:51:24 +00:00
}
// parsers is a list of valid config parsers for the backends. The first parser
// is the fallback and should always be set to the local backend.
var parsers = []parser{
2020-03-20 22:52:27 +00:00
{"b2", b2.ParseConfig, noPassword},
{"local", local.ParseConfig, noPassword},
{"sftp", sftp.ParseConfig, noPassword},
{"s3", s3.ParseConfig, noPassword},
{"gs", gs.ParseConfig, noPassword},
{"azure", azure.ParseConfig, noPassword},
{"swift", swift.ParseConfig, noPassword},
{"rest", rest.ParseConfig, rest.StripPassword},
{"rclone", rclone.ParseConfig, noPassword},
}
// noPassword returns the repository location unchanged (there's no sensitive information there)
func noPassword(s string) string {
return s
2015-12-28 14:51:24 +00:00
}
func isPath(s string) bool {
if strings.HasPrefix(s, "../") || strings.HasPrefix(s, `..\`) {
return true
}
if strings.HasPrefix(s, "/") || strings.HasPrefix(s, `\`) {
return true
}
if len(s) < 3 {
return false
}
// check for drive paths
drive := s[0]
if !(drive >= 'a' && drive <= 'z') && !(drive >= 'A' && drive <= 'Z') {
return false
}
if s[1] != ':' {
return false
}
if s[2] != '\\' && s[2] != '/' {
return false
}
return true
}
2015-12-28 15:42:44 +00:00
// Parse extracts repository location information from the string s. If s
// starts with a backend name followed by a colon, that backend's Parse()
// function is called. Otherwise, the local backend is used which interprets s
// as the name of a directory.
func Parse(s string) (u Location, err error) {
2015-12-28 14:51:24 +00:00
scheme := extractScheme(s)
u.Scheme = scheme
for _, parser := range parsers {
if parser.scheme != scheme {
continue
}
u.Config, err = parser.parse(s)
if err != nil {
2015-12-28 14:57:20 +00:00
return Location{}, err
2015-12-28 14:51:24 +00:00
}
return u, nil
}
// if s is not a path or contains ":", it's ambiguous
if !isPath(s) && strings.ContainsRune(s, ':') {
return Location{}, errors.New("invalid backend\nIf the repo is in a local directory, you need to add a `local:` prefix")
}
2015-12-28 14:51:24 +00:00
u.Scheme = "local"
u.Config, err = local.ParseConfig("local:" + s)
if err != nil {
2015-12-28 14:57:20 +00:00
return Location{}, err
2015-12-28 14:51:24 +00:00
}
return u, nil
}
2020-03-20 22:52:27 +00:00
// StripPassword returns a displayable version of a repository location (with any sensitive information removed)
func StripPassword(s string) string {
scheme := extractScheme(s)
for _, parser := range parsers {
if parser.scheme != scheme {
continue
}
return parser.stripPassword(s)
}
return s
}
2015-12-28 14:51:24 +00:00
func extractScheme(s string) string {
data := strings.SplitN(s, ":", 2)
return data[0]
}