forked from TrueCloudLab/restic
commit
ba688aad20
10 changed files with 38 additions and 84 deletions
|
@ -22,7 +22,6 @@ import (
|
||||||
"github.com/restic/restic/internal/backend/rest"
|
"github.com/restic/restic/internal/backend/rest"
|
||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
"golang.org/x/net/context/ctxhttp"
|
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -216,7 +215,7 @@ func newBackend(cfg Config, lim limiter.Limiter) (*Backend, error) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// send an HTTP request to the base URL, see if the server is there
|
// send an HTTP request to the base URL, see if the server is there
|
||||||
client := &http.Client{
|
client := http.Client{
|
||||||
Transport: debug.RoundTripper(tr),
|
Transport: debug.RoundTripper(tr),
|
||||||
Timeout: cfg.Timeout,
|
Timeout: cfg.Timeout,
|
||||||
}
|
}
|
||||||
|
@ -231,7 +230,7 @@ func newBackend(cfg Config, lim limiter.Limiter) (*Backend, error) {
|
||||||
}
|
}
|
||||||
req.Header.Set("Accept", rest.ContentTypeV2)
|
req.Header.Set("Accept", rest.ContentTypeV2)
|
||||||
|
|
||||||
res, err := ctxhttp.Do(ctx, client, req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// ignore subsequent errors
|
// ignore subsequent errors
|
||||||
_ = bg()
|
_ = bg()
|
||||||
|
@ -240,7 +239,7 @@ func newBackend(cfg Config, lim limiter.Limiter) (*Backend, error) {
|
||||||
// wait for rclone to exit
|
// wait for rclone to exit
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
// try to return the program exit code if communication with rclone has failed
|
// try to return the program exit code if communication with rclone has failed
|
||||||
if be.waitResult != nil && (err == context.Canceled || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.EPIPE)) {
|
if be.waitResult != nil && (errors.Is(err, context.Canceled) || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, syscall.EPIPE)) {
|
||||||
err = be.waitResult
|
err = be.waitResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/net/context/ctxhttp"
|
|
||||||
|
|
||||||
"github.com/restic/restic/internal/backend"
|
"github.com/restic/restic/internal/backend"
|
||||||
"github.com/restic/restic/internal/backend/sema"
|
"github.com/restic/restic/internal/backend/sema"
|
||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
|
@ -33,7 +31,7 @@ type Backend struct {
|
||||||
url *url.URL
|
url *url.URL
|
||||||
connections uint
|
connections uint
|
||||||
sem sema.Semaphore
|
sem sema.Semaphore
|
||||||
client *http.Client
|
client http.Client
|
||||||
backend.Layout
|
backend.Layout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +43,6 @@ const (
|
||||||
|
|
||||||
// Open opens the REST backend with the given config.
|
// Open opens the REST backend with the given config.
|
||||||
func Open(cfg Config, rt http.RoundTripper) (*Backend, error) {
|
func Open(cfg Config, rt http.RoundTripper) (*Backend, error) {
|
||||||
client := &http.Client{Transport: rt}
|
|
||||||
|
|
||||||
sem, err := sema.New(cfg.Connections)
|
sem, err := sema.New(cfg.Connections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -60,7 +56,7 @@ func Open(cfg Config, rt http.RoundTripper) (*Backend, error) {
|
||||||
|
|
||||||
be := &Backend{
|
be := &Backend{
|
||||||
url: cfg.URL,
|
url: cfg.URL,
|
||||||
client: client,
|
client: http.Client{Transport: rt},
|
||||||
Layout: &backend.RESTLayout{URL: url, Join: path.Join},
|
Layout: &backend.RESTLayout{URL: url, Join: path.Join},
|
||||||
connections: cfg.Connections,
|
connections: cfg.Connections,
|
||||||
sem: sem,
|
sem: sem,
|
||||||
|
@ -138,9 +134,10 @@ func (b *Backend) Save(ctx context.Context, h restic.Handle, rd restic.RewindRea
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// make sure that client.Post() cannot close the reader by wrapping it
|
// make sure that client.Post() cannot close the reader by wrapping it
|
||||||
req, err := http.NewRequest(http.MethodPost, b.Filename(h), ioutil.NopCloser(rd))
|
req, err := http.NewRequestWithContext(ctx,
|
||||||
|
http.MethodPost, b.Filename(h), ioutil.NopCloser(rd))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "NewRequest")
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/octet-stream")
|
req.Header.Set("Content-Type", "application/octet-stream")
|
||||||
req.Header.Set("Accept", ContentTypeV2)
|
req.Header.Set("Accept", ContentTypeV2)
|
||||||
|
@ -150,7 +147,7 @@ func (b *Backend) Save(ctx context.Context, h restic.Handle, rd restic.RewindRea
|
||||||
req.ContentLength = rd.Length()
|
req.ContentLength = rd.Length()
|
||||||
|
|
||||||
b.sem.GetToken()
|
b.sem.GetToken()
|
||||||
resp, err := ctxhttp.Do(ctx, b.client, req)
|
resp, err := b.client.Do(req)
|
||||||
b.sem.ReleaseToken()
|
b.sem.ReleaseToken()
|
||||||
|
|
||||||
var cerr error
|
var cerr error
|
||||||
|
@ -160,7 +157,7 @@ func (b *Backend) Save(ctx context.Context, h restic.Handle, rd restic.RewindRea
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "client.Post")
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
|
@ -269,9 +266,9 @@ func (b *Backend) openReader(ctx context.Context, h restic.Handle, length int, o
|
||||||
return nil, errors.Errorf("invalid length %d", length)
|
return nil, errors.Errorf("invalid length %d", length)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", b.Filename(h), nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", b.Filename(h), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "http.NewRequest")
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
byteRange := fmt.Sprintf("bytes=%d-", offset)
|
byteRange := fmt.Sprintf("bytes=%d-", offset)
|
||||||
|
@ -283,7 +280,7 @@ func (b *Backend) openReader(ctx context.Context, h restic.Handle, length int, o
|
||||||
debug.Log("Load(%v) send range %v", h, byteRange)
|
debug.Log("Load(%v) send range %v", h, byteRange)
|
||||||
|
|
||||||
b.sem.GetToken()
|
b.sem.GetToken()
|
||||||
resp, err := ctxhttp.Do(ctx, b.client, req)
|
resp, err := b.client.Do(req)
|
||||||
b.sem.ReleaseToken()
|
b.sem.ReleaseToken()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -321,17 +318,17 @@ func (b *Backend) Stat(ctx context.Context, h restic.Handle) (restic.FileInfo, e
|
||||||
return restic.FileInfo{}, backoff.Permanent(err)
|
return restic.FileInfo{}, backoff.Permanent(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodHead, b.Filename(h), nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodHead, b.Filename(h), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return restic.FileInfo{}, errors.Wrap(err, "NewRequest")
|
return restic.FileInfo{}, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
req.Header.Set("Accept", ContentTypeV2)
|
req.Header.Set("Accept", ContentTypeV2)
|
||||||
|
|
||||||
b.sem.GetToken()
|
b.sem.GetToken()
|
||||||
resp, err := ctxhttp.Do(ctx, b.client, req)
|
resp, err := b.client.Do(req)
|
||||||
b.sem.ReleaseToken()
|
b.sem.ReleaseToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return restic.FileInfo{}, errors.Wrap(err, "client.Head")
|
return restic.FileInfo{}, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = io.Copy(ioutil.Discard, resp.Body)
|
_, _ = io.Copy(ioutil.Discard, resp.Body)
|
||||||
|
@ -376,14 +373,14 @@ func (b *Backend) Remove(ctx context.Context, h restic.Handle) error {
|
||||||
return backoff.Permanent(err)
|
return backoff.Permanent(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("DELETE", b.Filename(h), nil)
|
req, err := http.NewRequestWithContext(ctx, "DELETE", b.Filename(h), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "http.NewRequest")
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
req.Header.Set("Accept", ContentTypeV2)
|
req.Header.Set("Accept", ContentTypeV2)
|
||||||
|
|
||||||
b.sem.GetToken()
|
b.sem.GetToken()
|
||||||
resp, err := ctxhttp.Do(ctx, b.client, req)
|
resp, err := b.client.Do(req)
|
||||||
b.sem.ReleaseToken()
|
b.sem.ReleaseToken()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -415,14 +412,14 @@ func (b *Backend) List(ctx context.Context, t restic.FileType, fn func(restic.Fi
|
||||||
url += "/"
|
url += "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "NewRequest")
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
req.Header.Set("Accept", ContentTypeV2)
|
req.Header.Set("Accept", ContentTypeV2)
|
||||||
|
|
||||||
b.sem.GetToken()
|
b.sem.GetToken()
|
||||||
resp, err := ctxhttp.Do(ctx, b.client, req)
|
resp, err := b.client.Do(req)
|
||||||
b.sem.ReleaseToken()
|
b.sem.ReleaseToken()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -82,19 +82,6 @@ func (id ID) Equal(other ID) bool {
|
||||||
return id == other
|
return id == other
|
||||||
}
|
}
|
||||||
|
|
||||||
// EqualString compares this ID to another one, given as a string.
|
|
||||||
func (id ID) EqualString(other string) (bool, error) {
|
|
||||||
s, err := hex.DecodeString(other)
|
|
||||||
if err != nil {
|
|
||||||
return false, errors.Wrap(err, "hex.DecodeString")
|
|
||||||
}
|
|
||||||
|
|
||||||
id2 := ID{}
|
|
||||||
copy(id2[:], s)
|
|
||||||
|
|
||||||
return id == id2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON returns the JSON encoding of id.
|
// MarshalJSON returns the JSON encoding of id.
|
||||||
func (id ID) MarshalJSON() ([]byte, error) {
|
func (id ID) MarshalJSON() ([]byte, error) {
|
||||||
buf := make([]byte, 2+hex.EncodedLen(len(id)))
|
buf := make([]byte, 2+hex.EncodedLen(len(id)))
|
||||||
|
|
|
@ -30,14 +30,6 @@ func TestID(t *testing.T) {
|
||||||
t.Errorf("ID.Equal() does not work as expected")
|
t.Errorf("ID.Equal() does not work as expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := id.EqualString(test.id)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if !ret {
|
|
||||||
t.Error("ID.EqualString() returned wrong value")
|
|
||||||
}
|
|
||||||
|
|
||||||
// test json marshalling
|
// test json marshalling
|
||||||
buf, err := id.MarshalJSON()
|
buf, err := id.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -126,7 +126,7 @@ func (l *Lock) fillUserInfo() error {
|
||||||
}
|
}
|
||||||
l.Username = usr.Username
|
l.Username = usr.Username
|
||||||
|
|
||||||
l.UID, l.GID, err = uidGidInt(*usr)
|
l.UID, l.GID, err = uidGidInt(usr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,25 +9,21 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/errors"
|
|
||||||
|
|
||||||
"github.com/restic/restic/internal/debug"
|
"github.com/restic/restic/internal/debug"
|
||||||
|
"github.com/restic/restic/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// uidGidInt returns uid, gid of the user as a number.
|
// uidGidInt returns uid, gid of the user as a number.
|
||||||
func uidGidInt(u user.User) (uid, gid uint32, err error) {
|
func uidGidInt(u *user.User) (uid, gid uint32, err error) {
|
||||||
var ui, gi int64
|
ui, err := strconv.ParseUint(u.Uid, 10, 32)
|
||||||
ui, err = strconv.ParseInt(u.Uid, 10, 32)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return uid, gid, errors.Wrap(err, "ParseInt")
|
return 0, 0, errors.Errorf("invalid UID %q", u.Uid)
|
||||||
}
|
}
|
||||||
gi, err = strconv.ParseInt(u.Gid, 10, 32)
|
gi, err := strconv.ParseUint(u.Gid, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return uid, gid, errors.Wrap(err, "ParseInt")
|
return 0, 0, errors.Errorf("invalid GID %q", u.Gid)
|
||||||
}
|
}
|
||||||
uid = uint32(ui)
|
return uint32(ui), uint32(gi), nil
|
||||||
gid = uint32(gi)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkProcess will check if the process retaining the lock
|
// checkProcess will check if the process retaining the lock
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// uidGidInt always returns 0 on Windows, since uid isn't numbers
|
// uidGidInt always returns 0 on Windows, since uid isn't numbers
|
||||||
func uidGidInt(u user.User) (uid, gid uint32, err error) {
|
func uidGidInt(u *user.User) (uid, gid uint32, err error) {
|
||||||
return 0, 0, nil
|
return 0, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,7 @@ func (sn *Snapshot) fillUserInfo() error {
|
||||||
sn.Username = usr.Username
|
sn.Username = usr.Username
|
||||||
|
|
||||||
// set userid and groupid
|
// set userid and groupid
|
||||||
sn.UID, sn.GID, err = uidGidInt(*usr)
|
sn.UID, sn.GID, err = uidGidInt(usr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context/ctxhttp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Release collects data about a single release on GitHub.
|
// Release collects data about a single release on GitHub.
|
||||||
|
@ -53,7 +52,7 @@ func GitHubLatestRelease(ctx context.Context, owner, repo string) (Release, erro
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo)
|
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo)
|
||||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Release{}, err
|
return Release{}, err
|
||||||
}
|
}
|
||||||
|
@ -61,7 +60,7 @@ func GitHubLatestRelease(ctx context.Context, owner, repo string) (Release, erro
|
||||||
// pin API version 3
|
// pin API version 3
|
||||||
req.Header.Set("Accept", "application/vnd.github.v3+json")
|
req.Header.Set("Accept", "application/vnd.github.v3+json")
|
||||||
|
|
||||||
res, err := ctxhttp.Do(ctx, http.DefaultClient, req)
|
res, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Release{}, err
|
return Release{}, err
|
||||||
}
|
}
|
||||||
|
@ -112,7 +111,7 @@ func GitHubLatestRelease(ctx context.Context, owner, repo string) (Release, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGithubData(ctx context.Context, url string) ([]byte, error) {
|
func getGithubData(ctx context.Context, url string) ([]byte, error) {
|
||||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -120,7 +119,7 @@ func getGithubData(ctx context.Context, url string) ([]byte, error) {
|
||||||
// request binary data
|
// request binary data
|
||||||
req.Header.Set("Accept", "application/octet-stream")
|
req.Header.Set("Accept", "application/octet-stream")
|
||||||
|
|
||||||
res, err := ctxhttp.Do(ctx, http.DefaultClient, req)
|
res, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,18 +39,6 @@ type fileWorkerMessage struct {
|
||||||
done bool
|
done bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProgressReporter interface {
|
|
||||||
CompleteItem(item string, previous, current *restic.Node, s archiver.ItemStats, d time.Duration)
|
|
||||||
StartFile(filename string)
|
|
||||||
CompleteBlob(filename string, bytes uint64)
|
|
||||||
ScannerError(item string, err error) error
|
|
||||||
ReportTotal(item string, s archiver.ScanStats)
|
|
||||||
SetMinUpdatePause(d time.Duration)
|
|
||||||
Run(ctx context.Context) error
|
|
||||||
Error(item string, err error) error
|
|
||||||
Finish(snapshotID restic.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Summary struct {
|
type Summary struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
Files, Dirs struct {
|
Files, Dirs struct {
|
||||||
|
@ -69,8 +57,6 @@ type Progress struct {
|
||||||
start time.Time
|
start time.Time
|
||||||
dry bool
|
dry bool
|
||||||
|
|
||||||
totalBytes uint64
|
|
||||||
|
|
||||||
totalCh chan Counter
|
totalCh chan Counter
|
||||||
processedCh chan Counter
|
processedCh chan Counter
|
||||||
errCh chan struct{}
|
errCh chan struct{}
|
||||||
|
@ -130,7 +116,6 @@ func (p *Progress) Run(ctx context.Context) error {
|
||||||
} else {
|
} else {
|
||||||
// scan has finished
|
// scan has finished
|
||||||
p.totalCh = nil
|
p.totalCh = nil
|
||||||
p.totalBytes = total.Bytes
|
|
||||||
}
|
}
|
||||||
case s := <-p.processedCh:
|
case s := <-p.processedCh:
|
||||||
processed.Files += s.Files
|
processed.Files += s.Files
|
||||||
|
@ -312,8 +297,7 @@ func (p *Progress) Finish(snapshotID restic.ID) {
|
||||||
p.printer.Finish(snapshotID, p.start, p.summary, p.dry)
|
p.printer.Finish(snapshotID, p.start, p.summary, p.dry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMinUpdatePause sets b.MinUpdatePause. It satisfies the
|
// SetMinUpdatePause sets b.MinUpdatePause.
|
||||||
// ArchiveProgressReporter interface.
|
|
||||||
func (p *Progress) SetMinUpdatePause(d time.Duration) {
|
func (p *Progress) SetMinUpdatePause(d time.Duration) {
|
||||||
p.MinUpdatePause = d
|
p.MinUpdatePause = d
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue