diff --git a/backend/interface.go b/backend/interface.go index 24424dd96..9fff4bf1e 100644 --- a/backend/interface.go +++ b/backend/interface.go @@ -40,6 +40,9 @@ type Backend interface { // and saves it in p. Load has the same semantics as io.ReaderAt. Load(h Handle, p []byte, off int64) (int, error) + // Save stores the data in the backend under the given handle. + Save(h Handle, p []byte) error + // Stat returns information about the blob identified by h. Stat(h Handle) (BlobInfo, error) } diff --git a/backend/local/local.go b/backend/local/local.go index 0d12e4109..4183ff474 100644 --- a/backend/local/local.go +++ b/backend/local/local.go @@ -225,6 +225,33 @@ func (b *Local) Load(h backend.Handle, p []byte, off int64) (n int, err error) { return io.ReadFull(f, p) } +// Save stores data in the backend at the handle. +func (b *Local) Save(h backend.Handle, p []byte) (err error) { + if err := h.Valid(); err != nil { + return err + } + + f, err := os.Create(filename(b.p, h.Type, h.Name)) + if err != nil { + return err + } + + n, err := f.Write(p) + if err != nil { + return err + } + + if n != len(p) { + return errors.New("not all bytes writen") + } + + if err = f.Sync(); err != nil { + return err + } + + return f.Close() +} + // Stat returns information about a blob. func (b *Local) Stat(h backend.Handle) (backend.BlobInfo, error) { if err := h.Valid(); err != nil { diff --git a/backend/mem/mem_backend.go b/backend/mem/mem_backend.go index b999efd1d..0de0c0837 100644 --- a/backend/mem/mem_backend.go +++ b/backend/mem/mem_backend.go @@ -130,13 +130,13 @@ func memCreate(be *MemoryBackend) (backend.Blob, error) { } func memLoad(be *MemoryBackend, h backend.Handle, p []byte, off int64) (int, error) { - be.m.Lock() - defer be.m.Unlock() - if err := h.Valid(); err != nil { return 0, err } + be.m.Lock() + defer be.m.Unlock() + if h.Type == backend.Config { h.Name = "" } @@ -163,6 +163,26 @@ func memLoad(be *MemoryBackend, h backend.Handle, p []byte, off int64) (int, err return n, nil } +func memSave(be *MemoryBackend, h backend.Handle, p []byte) error { + if err := h.Valid(); err != nil { + return err + } + + be.m.Lock() + defer be.m.Unlock() + + if h.Type == backend.Config { + h.Name = "" + } + + debug.Log("MemoryBackend.Save", "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 +} + func memStat(be *MemoryBackend, h backend.Handle) (backend.BlobInfo, error) { be.m.Lock() defer be.m.Unlock() diff --git a/backend/mock_backend.go b/backend/mock_backend.go index 1075292d6..9e692883f 100644 --- a/backend/mock_backend.go +++ b/backend/mock_backend.go @@ -8,6 +8,7 @@ type MockBackend struct { CloseFn func() error CreateFn func() (Blob, error) LoadFn func(h Handle, p []byte, off int64) (int, error) + SaveFn func(h Handle, p []byte) error StatFn func(h Handle) (BlobInfo, error) ListFn func(Type, <-chan struct{}) <-chan string RemoveFn func(Type, string) error @@ -48,6 +49,14 @@ func (m *MockBackend) Load(h Handle, p []byte, off int64) (int, error) { return m.LoadFn(h, p, off) } +func (m *MockBackend) Save(h Handle, p []byte) error { + if m.SaveFn == nil { + return errors.New("not implemented") + } + + return m.SaveFn(h, p) +} + func (m *MockBackend) Stat(h Handle) (BlobInfo, error) { if m.StatFn == nil { return BlobInfo{}, errors.New("not implemented") diff --git a/backend/s3/s3.go b/backend/s3/s3.go index a46cdb22d..9fd5c6152 100644 --- a/backend/s3/s3.go +++ b/backend/s3/s3.go @@ -163,9 +163,36 @@ func (be S3Backend) Load(h backend.Handle, p []byte, off int64) (int, error) { } } + <-be.connChan + defer func() { + be.connChan <- struct{}{} + }() return io.ReadFull(obj, p) } +// Save stores data in the backend at the handle. +func (be S3Backend) Save(h backend.Handle, p []byte) (err error) { + if err := h.Valid(); err != nil { + return err + } + + debug.Log("s3.Save", "%v bytes at %d", len(p), h) + + path := s3path(h.Type, h.Name) + + <-be.connChan + defer func() { + be.connChan <- struct{}{} + }() + + debug.Log("s3.Save", "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("s3.Save", "%v -> %v bytes, err %#v", path, n, err) + + return err +} + // Stat returns information about a blob. func (be S3Backend) Stat(h backend.Handle) (backend.BlobInfo, error) { debug.Log("s3.Stat", "%v") diff --git a/backend/sftp/sftp.go b/backend/sftp/sftp.go index e40a3cd00..c5017bdfc 100644 --- a/backend/sftp/sftp.go +++ b/backend/sftp/sftp.go @@ -373,6 +373,29 @@ func (r *SFTP) Load(h backend.Handle, p []byte, off int64) (n int, err error) { return io.ReadFull(f, p) } +// Save stores data in the backend at the handle. +func (r *SFTP) Save(h backend.Handle, p []byte) (err error) { + if err := h.Valid(); err != nil { + return err + } + + f, err := r.c.Create(r.filename(h.Type, h.Name)) + if err != nil { + return err + } + + n, err := f.Write(p) + if err != nil { + return err + } + + if n != len(p) { + return errors.New("not all bytes writen") + } + + return f.Close() +} + // Stat returns information about a blob. func (r *SFTP) Stat(h backend.Handle) (backend.BlobInfo, error) { if err := h.Valid(); err != nil {