Exemple #1
0
// Stat returns information about a blob.
func (b *restBackend) Stat(h restic.Handle) (restic.FileInfo, error) {
	if err := h.Valid(); err != nil {
		return restic.FileInfo{}, err
	}

	<-b.connChan
	resp, err := b.client.Head(restPath(b.url, h))
	b.connChan <- struct{}{}
	if err != nil {
		return restic.FileInfo{}, errors.Wrap(err, "client.Head")
	}

	io.Copy(ioutil.Discard, resp.Body)
	if err = resp.Body.Close(); err != nil {
		return restic.FileInfo{}, errors.Wrap(err, "Close")
	}

	if resp.StatusCode != 200 {
		return restic.FileInfo{}, errors.Errorf("unexpected HTTP response code %v", resp.StatusCode)
	}

	if resp.ContentLength < 0 {
		return restic.FileInfo{}, errors.New("negative content length")
	}

	bi := restic.FileInfo{
		Size: resp.ContentLength,
	}

	return bi, nil
}
Exemple #2
0
// Remove removes the blob with the given name and type.
func (b *restBackend) Remove(t restic.FileType, name string) error {
	h := restic.Handle{Type: t, Name: name}
	if err := h.Valid(); err != nil {
		return err
	}

	req, err := http.NewRequest("DELETE", restPath(b.url, h), nil)
	if err != nil {
		return errors.Wrap(err, "http.NewRequest")
	}
	<-b.connChan
	resp, err := b.client.Do(req)
	b.connChan <- struct{}{}

	if err != nil {
		return errors.Wrap(err, "client.Do")
	}

	if resp.StatusCode != 200 {
		return errors.New("blob not removed")
	}

	io.Copy(ioutil.Discard, resp.Body)
	return resp.Body.Close()
}
Exemple #3
0
// Load returns the data stored in the backend for h at the given offset
// and saves it in p. Load has the same semantics as io.ReaderAt.
func (r *SFTP) Load(h restic.Handle, p []byte, off int64) (n int, err error) {
	debug.Log("load %v, %d bytes, offset %v", h, len(p), off)
	if err := r.clientError(); err != nil {
		return 0, err
	}

	if err := h.Valid(); err != nil {
		return 0, err
	}

	f, err := r.c.Open(r.filename(h.Type, h.Name))
	if err != nil {
		return 0, errors.Wrap(err, "Open")
	}

	defer func() {
		e := f.Close()
		if err == nil && e != nil {
			err = errors.Wrap(e, "Close")
		}
	}()

	switch {
	case off > 0:
		_, err = f.Seek(off, 0)
	case off < 0:
		_, err = f.Seek(off, 2)
	}

	if err != nil {
		return 0, errors.Wrap(err, "Seek")
	}

	return io.ReadFull(f, p)
}
Exemple #4
0
// Save stores data in the backend at the handle.
func (b *restBackend) Save(h restic.Handle, p []byte) (err error) {
	if err := h.Valid(); err != nil {
		return err
	}

	<-b.connChan
	resp, err := b.client.Post(restPath(b.url, h), "binary/octet-stream", bytes.NewReader(p))
	b.connChan <- struct{}{}

	if resp != nil {
		defer func() {
			io.Copy(ioutil.Discard, resp.Body)
			e := resp.Body.Close()

			if err == nil {
				err = errors.Wrap(e, "Close")
			}
		}()
	}

	if err != nil {
		return errors.Wrap(err, "client.Post")
	}

	if resp.StatusCode != 200 {
		return errors.Errorf("unexpected HTTP response code %v", resp.StatusCode)
	}

	return nil
}
Exemple #5
0
// Load returns the data stored in the backend for h at the given offset and
// saves it in p. Load has the same semantics as io.ReaderAt, with one
// exception: when off is lower than zero, it is treated as an offset relative
// to the end of the file.
func (b *Local) Load(h restic.Handle, p []byte, off int64) (n int, err error) {
	debug.Log("Load %v, length %v at %v", h, len(p), off)
	if err := h.Valid(); err != nil {
		return 0, err
	}

	f, err := fs.Open(filename(b.p, h.Type, h.Name))
	if err != nil {
		return 0, errors.Wrap(err, "Open")
	}

	defer func() {
		e := f.Close()
		if err == nil {
			err = errors.Wrap(e, "Close")
		}
	}()

	switch {
	case off > 0:
		_, err = f.Seek(off, 0)
	case off < 0:
		_, err = f.Seek(off, 2)
	}

	if err != nil {
		return 0, errors.Wrap(err, "Seek")
	}

	return io.ReadFull(f, p)
}
Exemple #6
0
// Save stores data in the backend at the handle.
func (be s3) Save(h restic.Handle, p []byte) (err error) {
	if err := h.Valid(); err != nil {
		return err
	}

	debug.Log("%v with %d bytes", h, len(p))

	path := be.s3path(h.Type, h.Name)

	// Check key does not already exist
	_, err = be.client.StatObject(be.bucketname, path)
	if err == nil {
		debug.Log("%v already exists", h)
		return errors.New("key already exists")
	}

	<-be.connChan
	defer func() {
		be.connChan <- struct{}{}
	}()

	debug.Log("PutObject(%v, %v, %v, %v)",
		be.bucketname, path, int64(len(p)), "binary/octet-stream")
	n, err := be.client.PutObject(be.bucketname, path, bytes.NewReader(p), "binary/octet-stream")
	debug.Log("%v -> %v bytes, err %#v", path, n, err)

	return errors.Wrap(err, "client.PutObject")
}
Exemple #7
0
// Stat returns information about a blob.
func (b *Local) Stat(h restic.Handle) (restic.FileInfo, error) {
	debug.Log("Stat %v", h)
	if err := h.Valid(); err != nil {
		return restic.FileInfo{}, err
	}

	fi, err := fs.Stat(filename(b.p, h.Type, h.Name))
	if err != nil {
		return restic.FileInfo{}, errors.Wrap(err, "Stat")
	}

	return restic.FileInfo{Size: fi.Size()}, nil
}
Exemple #8
0
// Load returns the data stored in the backend for h at the given offset
// and saves it in p. Load has the same semantics as io.ReaderAt.
func (b *restBackend) Load(h restic.Handle, p []byte, off int64) (n int, err error) {
	if err := h.Valid(); err != nil {
		return 0, err
	}

	// invert offset
	if off < 0 {
		info, err := b.Stat(h)
		if err != nil {
			return 0, errors.Wrap(err, "Stat")
		}

		if -off > info.Size {
			off = 0
		} else {
			off = info.Size + off
		}
	}

	req, err := http.NewRequest("GET", restPath(b.url, h), nil)
	if err != nil {
		return 0, errors.Wrap(err, "http.NewRequest")
	}
	req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", off, off+int64(len(p))))
	<-b.connChan
	resp, err := b.client.Do(req)
	b.connChan <- struct{}{}

	if resp != nil {
		defer func() {
			io.Copy(ioutil.Discard, resp.Body)
			e := resp.Body.Close()

			if err == nil {
				err = errors.Wrap(e, "Close")
			}
		}()
	}

	if err != nil {
		return 0, errors.Wrap(err, "client.Do")
	}
	if resp.StatusCode != 200 && resp.StatusCode != 206 {
		return 0, errors.Errorf("unexpected HTTP response code %v", resp.StatusCode)
	}

	return io.ReadFull(resp.Body, p)
}
Exemple #9
0
// restPath returns the path to the given resource.
func restPath(url *url.URL, h restic.Handle) string {
	u := *url

	var dir string

	switch h.Type {
	case restic.ConfigFile:
		dir = ""
		h.Name = "config"
	case restic.DataFile:
		dir = backend.Paths.Data
	case restic.SnapshotFile:
		dir = backend.Paths.Snapshots
	case restic.IndexFile:
		dir = backend.Paths.Index
	case restic.LockFile:
		dir = backend.Paths.Locks
	case restic.KeyFile:
		dir = backend.Paths.Keys
	default:
		dir = string(h.Type)
	}

	u.Path = path.Join(url.Path, dir, h.Name)

	return u.String()
}
Exemple #10
0
// Stat returns information about a blob.
func (r *SFTP) Stat(h restic.Handle) (restic.FileInfo, error) {
	debug.Log("stat %v", h)
	if err := r.clientError(); err != nil {
		return restic.FileInfo{}, err
	}

	if err := h.Valid(); err != nil {
		return restic.FileInfo{}, err
	}

	fi, err := r.c.Lstat(r.filename(h.Type, h.Name))
	if err != nil {
		return restic.FileInfo{}, errors.Wrap(err, "Lstat")
	}

	return restic.FileInfo{Size: fi.Size()}, nil
}
Exemple #11
0
// Save stores data in the backend at the handle.
func (b *Local) Save(h restic.Handle, p []byte) (err error) {
	debug.Log("Save %v, length %v", h, len(p))
	if err := h.Valid(); err != nil {
		return err
	}

	tmpfile, err := writeToTempfile(filepath.Join(b.p, backend.Paths.Temp), p)
	debug.Log("saved %v (%d bytes) to %v", h, len(p), tmpfile)
	if err != nil {
		return err
	}

	filename := filename(b.p, h.Type, h.Name)

	// test if new path already exists
	if _, err := fs.Stat(filename); err == nil {
		return errors.Errorf("Rename(): file %v already exists", filename)
	}

	// create directories if necessary, ignore errors
	if h.Type == restic.DataFile {
		err = fs.MkdirAll(filepath.Dir(filename), backend.Modes.Dir)
		if err != nil {
			return errors.Wrap(err, "MkdirAll")
		}
	}

	err = fs.Rename(tmpfile, filename)
	debug.Log("save %v: rename %v -> %v: %v",
		h, filepath.Base(tmpfile), filepath.Base(filename), err)

	if err != nil {
		return errors.Wrap(err, "Rename")
	}

	// set mode to read-only
	fi, err := fs.Stat(filename)
	if err != nil {
		return errors.Wrap(err, "Stat")
	}

	return setNewFileMode(filename, fi)
}
Exemple #12
0
// Stat returns information about a file in the backend.
func (be *MemoryBackend) Stat(h restic.Handle) (restic.FileInfo, error) {
	be.m.Lock()
	defer be.m.Unlock()

	if err := h.Valid(); err != nil {
		return restic.FileInfo{}, err
	}

	if h.Type == restic.ConfigFile {
		h.Name = ""
	}

	debug.Log("stat %v", h)

	e, ok := be.data[entry{h.Type, h.Name}]
	if !ok {
		return restic.FileInfo{}, errors.New("no such data")
	}

	return restic.FileInfo{Size: int64(len(e))}, nil
}
Exemple #13
0
// Load reads data from the backend.
func (be *MemoryBackend) Load(h restic.Handle, p []byte, off int64) (int, error) {
	if err := h.Valid(); err != nil {
		return 0, err
	}

	be.m.Lock()
	defer be.m.Unlock()

	if h.Type == restic.ConfigFile {
		h.Name = ""
	}

	debug.Log("get %v offset %v len %v", h, off, len(p))

	if _, ok := be.data[entry{h.Type, h.Name}]; !ok {
		return 0, errors.New("no such data")
	}

	buf := be.data[entry{h.Type, h.Name}]
	switch {
	case off > int64(len(buf)):
		return 0, errors.New("offset beyond end of file")
	case off < -int64(len(buf)):
		off = 0
	case off < 0:
		off = int64(len(buf)) + off
	}

	buf = buf[off:]

	n := copy(p, buf)

	if len(p) > len(buf) {
		return n, io.ErrUnexpectedEOF
	}

	return n, nil
}
Exemple #14
0
// Save adds new Data to the backend.
func (be *MemoryBackend) Save(h restic.Handle, p []byte) error {
	if err := h.Valid(); err != nil {
		return err
	}

	be.m.Lock()
	defer be.m.Unlock()

	if h.Type == restic.ConfigFile {
		h.Name = ""
	}

	if _, ok := be.data[entry{h.Type, h.Name}]; ok {
		return errors.New("file already exists")
	}

	debug.Log("save %v bytes at %v", len(p), h)
	buf := make([]byte, len(p))
	copy(buf, p)
	be.data[entry{h.Type, h.Name}] = buf

	return nil
}
Exemple #15
0
// Save stores data in the backend at the handle.
func (r *SFTP) Save(h restic.Handle, p []byte) (err error) {
	debug.Log("save %v bytes to %v", h, len(p))
	if err := r.clientError(); err != nil {
		return err
	}

	if err := h.Valid(); err != nil {
		return err
	}

	filename, tmpfile, err := r.tempFile()
	if err != nil {
		return err
	}

	debug.Log("save %v (%d bytes) to %v", h, len(p), filename)

	n, err := tmpfile.Write(p)
	if err != nil {
		return errors.Wrap(err, "Write")
	}

	if n != len(p) {
		return errors.New("not all bytes writen")
	}

	err = tmpfile.Close()
	if err != nil {
		return errors.Wrap(err, "Close")
	}

	err = r.renameFile(filename, h.Type, h.Name)
	debug.Log("save %v: rename %v: %v",
		h, path.Base(filename), err)
	return err
}