| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | package b2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-12-19 12:39:48 +01:00
										 |  |  | 	"hash" | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-09-24 20:04:23 +02:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	"path" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-23 14:21:03 +02:00
										 |  |  | 	"github.com/restic/restic/internal/backend" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/debug" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/errors" | 
					
						
							| 
									
										
										
										
											2017-07-24 17:42:25 +02:00
										 |  |  | 	"github.com/restic/restic/internal/restic" | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:47:53 +01:00
										 |  |  | 	"github.com/cenkalti/backoff/v4" | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	"github.com/kurin/blazer/b2" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // b2Backend is a backend which stores its data on Backblaze B2. | 
					
						
							|  |  |  | type b2Backend struct { | 
					
						
							| 
									
										
										
										
											2017-09-18 12:13:35 +02:00
										 |  |  | 	client       *b2.Client | 
					
						
							|  |  |  | 	bucket       *b2.Bucket | 
					
						
							|  |  |  | 	cfg          Config | 
					
						
							|  |  |  | 	listMaxItems int | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	backend.Layout | 
					
						
							|  |  |  | 	sem *backend.Semaphore | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-18 12:13:35 +02:00
										 |  |  | const defaultListMaxItems = 1000 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | // ensure statically that *b2Backend implements restic.Backend. | 
					
						
							|  |  |  | var _ restic.Backend = &b2Backend{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-24 20:04:23 +02:00
										 |  |  | func newClient(ctx context.Context, cfg Config, rt http.RoundTripper) (*b2.Client, error) { | 
					
						
							|  |  |  | 	opts := []b2.ClientOption{b2.Transport(rt)} | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	c, err := b2.NewClient(ctx, cfg.AccountID, cfg.Key, opts...) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(err, "b2.NewClient") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return c, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Open opens a connection to the B2 service. | 
					
						
							| 
									
										
										
										
											2017-11-22 06:27:29 -05:00
										 |  |  | func Open(ctx context.Context, cfg Config, rt http.RoundTripper) (restic.Backend, error) { | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	debug.Log("cfg %#v", cfg) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 06:27:29 -05:00
										 |  |  | 	ctx, cancel := context.WithCancel(ctx) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-24 20:04:23 +02:00
										 |  |  | 	client, err := newClient(ctx, cfg, rt) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bucket, err := client.Bucket(ctx, cfg.Bucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(err, "Bucket") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-06 00:17:21 +02:00
										 |  |  | 	sem, err := backend.NewSemaphore(cfg.Connections) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	be := &b2Backend{ | 
					
						
							|  |  |  | 		client: client, | 
					
						
							|  |  |  | 		bucket: bucket, | 
					
						
							|  |  |  | 		cfg:    cfg, | 
					
						
							|  |  |  | 		Layout: &backend.DefaultLayout{ | 
					
						
							|  |  |  | 			Join: path.Join, | 
					
						
							|  |  |  | 			Path: cfg.Prefix, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2017-10-26 14:22:16 -04:00
										 |  |  | 		listMaxItems: defaultListMaxItems, | 
					
						
							| 
									
										
										
										
											2017-10-26 16:37:11 -04:00
										 |  |  | 		sem:          sem, | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return be, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Create opens a connection to the B2 service. If the bucket does not exist yet, | 
					
						
							|  |  |  | // it is created. | 
					
						
							| 
									
										
										
										
											2017-11-22 06:27:29 -05:00
										 |  |  | func Create(ctx context.Context, cfg Config, rt http.RoundTripper) (restic.Backend, error) { | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	debug.Log("cfg %#v", cfg) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 06:27:29 -05:00
										 |  |  | 	ctx, cancel := context.WithCancel(ctx) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-24 20:04:23 +02:00
										 |  |  | 	client, err := newClient(ctx, cfg, rt) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	attr := b2.BucketAttrs{ | 
					
						
							|  |  |  | 		Type: b2.Private, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	bucket, err := client.NewBucket(ctx, cfg.Bucket, &attr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(err, "NewBucket") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-06 00:17:21 +02:00
										 |  |  | 	sem, err := backend.NewSemaphore(cfg.Connections) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	be := &b2Backend{ | 
					
						
							|  |  |  | 		client: client, | 
					
						
							|  |  |  | 		bucket: bucket, | 
					
						
							|  |  |  | 		cfg:    cfg, | 
					
						
							|  |  |  | 		Layout: &backend.DefaultLayout{ | 
					
						
							|  |  |  | 			Join: path.Join, | 
					
						
							|  |  |  | 			Path: cfg.Prefix, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2017-10-26 14:22:16 -04:00
										 |  |  | 		listMaxItems: defaultListMaxItems, | 
					
						
							| 
									
										
										
										
											2017-10-26 16:37:11 -04:00
										 |  |  | 		sem:          sem, | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												use global context for check, debug, dump, find, forget, init, key,
list, mount, tag, unlock commands
gh-1434
											
										 
											2017-12-06 07:02:55 -05:00
										 |  |  | 	present, err := be.Test(ctx, restic.Handle{Type: restic.ConfigFile}) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if present { | 
					
						
							|  |  |  | 		return nil, errors.New("config already exists") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return be, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-18 12:13:35 +02:00
										 |  |  | // SetListMaxItems sets the number of list items to load per request. | 
					
						
							|  |  |  | func (be *b2Backend) SetListMaxItems(i int) { | 
					
						
							|  |  |  | 	be.listMaxItems = i | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-07 22:20:49 +02:00
										 |  |  | func (be *b2Backend) Connections() uint { | 
					
						
							|  |  |  | 	return be.cfg.Connections | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | // Location returns the location for the backend. | 
					
						
							|  |  |  | func (be *b2Backend) Location() string { | 
					
						
							|  |  |  | 	return be.cfg.Bucket | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-19 12:39:48 +01:00
										 |  |  | // Hasher may return a hash function for calculating a content hash for the backend | 
					
						
							|  |  |  | func (be *b2Backend) Hasher() hash.Hash { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-01 20:07:29 +02:00
										 |  |  | // HasAtomicReplace returns whether Save() can atomically replace files | 
					
						
							|  |  |  | func (be *b2Backend) HasAtomicReplace() bool { | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-15 13:40:27 +02:00
										 |  |  | // IsNotExist returns true if the error is caused by a non-existing file. | 
					
						
							|  |  |  | func (be *b2Backend) IsNotExist(err error) bool { | 
					
						
							|  |  |  | 	return b2.IsNotExist(errors.Cause(err)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-16 23:59:16 -05:00
										 |  |  | // Load runs fn with a reader that yields the contents of the file at h at the | 
					
						
							|  |  |  | // given offset. | 
					
						
							|  |  |  | func (be *b2Backend) Load(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error { | 
					
						
							|  |  |  | 	return backend.DefaultLoad(ctx, h, length, offset, be.openReader, fn) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (be *b2Backend) openReader(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) { | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	debug.Log("Load %v, length %v, offset %v from %v", h, length, offset, be.Filename(h)) | 
					
						
							|  |  |  | 	if err := h.Valid(); err != nil { | 
					
						
							| 
									
										
										
										
											2020-12-17 12:47:53 +01:00
										 |  |  | 		return nil, backoff.Permanent(err) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if offset < 0 { | 
					
						
							|  |  |  | 		return nil, errors.New("offset is negative") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if length < 0 { | 
					
						
							|  |  |  | 		return nil, errors.Errorf("invalid length %d", length) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | 	ctx, cancel := context.WithCancel(ctx) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	be.sem.GetToken() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	name := be.Layout.Filename(h) | 
					
						
							|  |  |  | 	obj := be.bucket.Object(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if offset == 0 && length == 0 { | 
					
						
							|  |  |  | 		rd := obj.NewReader(ctx) | 
					
						
							| 
									
										
										
										
											2017-11-02 12:38:17 -04:00
										 |  |  | 		return be.sem.ReleaseTokenOnClose(rd, cancel), nil | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// pass a negative length to NewRangeReader so that the remainder of the | 
					
						
							|  |  |  | 	// file is read. | 
					
						
							|  |  |  | 	if length == 0 { | 
					
						
							|  |  |  | 		length = -1 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rd := obj.NewRangeReader(ctx, offset, int64(length)) | 
					
						
							| 
									
										
										
										
											2017-11-02 12:38:17 -04:00
										 |  |  | 	return be.sem.ReleaseTokenOnClose(rd, cancel), nil | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Save stores data in the backend at the handle. | 
					
						
							| 
									
										
										
										
											2018-03-03 14:20:54 +01:00
										 |  |  | func (be *b2Backend) Save(ctx context.Context, h restic.Handle, rd restic.RewindReader) error { | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | 	ctx, cancel := context.WithCancel(ctx) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := h.Valid(); err != nil { | 
					
						
							| 
									
										
										
										
											2020-12-17 12:47:53 +01:00
										 |  |  | 		return backoff.Permanent(err) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.sem.GetToken() | 
					
						
							|  |  |  | 	defer be.sem.ReleaseToken() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	name := be.Filename(h) | 
					
						
							|  |  |  | 	debug.Log("Save %v, name %v", h, name) | 
					
						
							|  |  |  | 	obj := be.bucket.Object(name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-19 12:29:02 +01:00
										 |  |  | 	// b2 always requires sha1 checksums for uploaded file parts | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	w := obj.NewWriter(ctx) | 
					
						
							|  |  |  | 	n, err := io.Copy(w, rd) | 
					
						
							|  |  |  | 	debug.Log("  saved %d bytes, err %v", n, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		_ = w.Close() | 
					
						
							|  |  |  | 		return errors.Wrap(err, "Copy") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-18 23:41:29 +01:00
										 |  |  | 	// sanity check | 
					
						
							|  |  |  | 	if n != rd.Length() { | 
					
						
							|  |  |  | 		return errors.Errorf("wrote %d bytes instead of the expected %d bytes", n, rd.Length()) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	return errors.Wrap(w.Close(), "Close") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stat returns information about a blob. | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | func (be *b2Backend) Stat(ctx context.Context, h restic.Handle) (bi restic.FileInfo, err error) { | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	debug.Log("Stat %v", h) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.sem.GetToken() | 
					
						
							|  |  |  | 	defer be.sem.ReleaseToken() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	name := be.Filename(h) | 
					
						
							|  |  |  | 	obj := be.bucket.Object(name) | 
					
						
							|  |  |  | 	info, err := obj.Attrs(ctx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		debug.Log("Attrs() err %v", err) | 
					
						
							|  |  |  | 		return restic.FileInfo{}, errors.Wrap(err, "Stat") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-20 19:34:38 +01:00
										 |  |  | 	return restic.FileInfo{Size: info.Size, Name: h.Name}, nil | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test returns true if a blob of the given type and name exists in the backend. | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | func (be *b2Backend) Test(ctx context.Context, h restic.Handle) (bool, error) { | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	debug.Log("Test %v", h) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.sem.GetToken() | 
					
						
							|  |  |  | 	defer be.sem.ReleaseToken() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	found := false | 
					
						
							|  |  |  | 	name := be.Filename(h) | 
					
						
							|  |  |  | 	obj := be.bucket.Object(name) | 
					
						
							|  |  |  | 	info, err := obj.Attrs(ctx) | 
					
						
							|  |  |  | 	if err == nil && info != nil && info.Status == b2.Uploaded { | 
					
						
							|  |  |  | 		found = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return found, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Remove removes the blob with the given name and type. | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | func (be *b2Backend) Remove(ctx context.Context, h restic.Handle) error { | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	debug.Log("Remove %v", h) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	be.sem.GetToken() | 
					
						
							|  |  |  | 	defer be.sem.ReleaseToken() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	obj := be.bucket.Object(be.Filename(h)) | 
					
						
							| 
									
										
										
										
											2021-10-09 22:57:34 +02:00
										 |  |  | 	err := obj.Delete(ctx) | 
					
						
							|  |  |  | 	// consider a file as removed if b2 informs us that it does not exist | 
					
						
							|  |  |  | 	if b2.IsNotExist(err) { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return errors.Wrap(err, "Delete") | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-25 14:53:49 -07:00
										 |  |  | type semLocker struct { | 
					
						
							|  |  |  | 	*backend.Semaphore | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (sm semLocker) Lock()   { sm.GetToken() } | 
					
						
							|  |  |  | func (sm semLocker) Unlock() { sm.ReleaseToken() } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // List returns a channel that yields all names of blobs of type t. | 
					
						
							| 
									
										
										
										
											2018-01-20 19:34:38 +01:00
										 |  |  | func (be *b2Backend) List(ctx context.Context, t restic.FileType, fn func(restic.FileInfo) error) error { | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	debug.Log("List %v", t) | 
					
						
							| 
									
										
										
										
											2018-01-20 19:34:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | 	ctx, cancel := context.WithCancel(ctx) | 
					
						
							| 
									
										
										
										
											2018-01-20 19:34:38 +01:00
										 |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-25 14:53:49 -07:00
										 |  |  | 	prefix, _ := be.Basedir(t) | 
					
						
							|  |  |  | 	iter := be.bucket.List(ctx, b2.ListPrefix(prefix), b2.ListPageSize(be.listMaxItems), b2.ListLocker(semLocker{be.sem})) | 
					
						
							| 
									
										
										
										
											2018-01-20 19:34:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-25 14:53:49 -07:00
										 |  |  | 	for iter.Next() { | 
					
						
							|  |  |  | 		obj := iter.Object() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		attrs, err := obj.Attrs(ctx) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-01-20 19:34:38 +01:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-25 14:53:49 -07:00
										 |  |  | 		fi := restic.FileInfo{ | 
					
						
							|  |  |  | 			Name: path.Base(obj.Name()), | 
					
						
							|  |  |  | 			Size: attrs.Size, | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-25 14:53:49 -07:00
										 |  |  | 		if err := fn(fi); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2018-01-20 19:34:38 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-25 14:53:49 -07:00
										 |  |  | 	if err := iter.Err(); err != nil { | 
					
						
							|  |  |  | 		debug.Log("List: %v", err) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Remove keys for a specified backend type. | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | func (be *b2Backend) removeKeys(ctx context.Context, t restic.FileType) error { | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	debug.Log("removeKeys %v", t) | 
					
						
							| 
									
										
										
										
											2018-01-20 19:34:38 +01:00
										 |  |  | 	return be.List(ctx, t, func(fi restic.FileInfo) error { | 
					
						
							|  |  |  | 		return be.Remove(ctx, restic.Handle{Type: t, Name: fi.Name}) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Delete removes all restic keys in the bucket. It will not remove the bucket itself. | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | func (be *b2Backend) Delete(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	alltypes := []restic.FileType{ | 
					
						
							| 
									
										
										
										
											2020-08-16 11:16:38 +02:00
										 |  |  | 		restic.PackFile, | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 		restic.KeyFile, | 
					
						
							|  |  |  | 		restic.LockFile, | 
					
						
							|  |  |  | 		restic.SnapshotFile, | 
					
						
							|  |  |  | 		restic.IndexFile} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, t := range alltypes { | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | 		err := be.removeKeys(ctx, t) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-03 17:39:57 +02:00
										 |  |  | 	err := be.Remove(ctx, restic.Handle{Type: restic.ConfigFile}) | 
					
						
							| 
									
										
										
										
											2017-05-28 10:19:01 +02:00
										 |  |  | 	if err != nil && b2.IsNotExist(errors.Cause(err)) { | 
					
						
							|  |  |  | 		err = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Close does nothing | 
					
						
							|  |  |  | func (be *b2Backend) Close() error { return nil } |