// 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 }
// 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() }
// 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) }
// 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 }
// 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) }
// 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") }
// 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 }
// 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) }
// 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() }
// 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 }
// 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) }
// 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 }
// 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 }
// 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 }
// 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 }