diff options
Diffstat (limited to 'storage/filter')
-rw-r--r-- | storage/filter/filter.go | 77 | ||||
-rw-r--r-- | storage/filter/filter_test.go | 76 |
2 files changed, 153 insertions, 0 deletions
diff --git a/storage/filter/filter.go b/storage/filter/filter.go new file mode 100644 index 0000000..52ecbfd --- /dev/null +++ b/storage/filter/filter.go @@ -0,0 +1,77 @@ +package filter + +import ( + "bytes" + "code.laria.me/petrific/objects" + "code.laria.me/petrific/storage" + "errors" + "os/exec" +) + +type Filter interface { + Transform([]byte) ([]byte, error) +} + +type PipeFilter []string + +var emptyCommand = errors.New("Need at least one argument for pipeFilter") + +func (pf PipeFilter) Transform(b []byte) ([]byte, error) { + if len(pf) == 0 { + return []byte{}, emptyCommand + } + + cmd := exec.Command(pf[0], pf[1:]...) + + buf := new(bytes.Buffer) + cmd.Stdout = buf + cmd.Stdin = bytes.NewReader(b) + + if err := cmd.Run(); err != nil { + return []byte{}, err + } + + return buf.Bytes(), nil +} + +type FilterStorage struct { + Base storage.Storage + Decode, Encode Filter +} + +func (filt FilterStorage) Get(id objects.ObjectId) ([]byte, error) { + data, err := filt.Base.Get(id) + if err != nil { + return data, err + } + + if filt.Decode == nil { + return data, nil + } + + return filt.Decode.Transform(data) +} + +func (filt FilterStorage) Has(id objects.ObjectId) (bool, error) { + return filt.Base.Has(id) +} + +func (filt FilterStorage) Set(id objects.ObjectId, typ objects.ObjectType, raw []byte) error { + if filt.Encode != nil { + var err error + raw, err = filt.Encode.Transform(raw) + if err != nil { + return err + } + } + + return filt.Base.Set(id, typ, raw) +} + +func (filt FilterStorage) List(typ objects.ObjectType) ([]objects.ObjectId, error) { + return filt.Base.List(typ) +} + +func (filt FilterStorage) Close() error { + return filt.Base.Close() +} diff --git a/storage/filter/filter_test.go b/storage/filter/filter_test.go new file mode 100644 index 0000000..78d4e7f --- /dev/null +++ b/storage/filter/filter_test.go @@ -0,0 +1,76 @@ +package filter + +import ( + "bytes" + "code.laria.me/petrific/backup" + "code.laria.me/petrific/storage/memory" + "testing" +) + +type XorFilter byte + +func (xf XorFilter) Transform(b []byte) ([]byte, error) { + for i := range b { + b[i] ^= byte(xf) + } + return b, nil +} + +func genTestData(size int) []byte { + data := make([]byte, size) + for i := range data { + data[i] = byte(i & 0xff) + } + return data +} + +func TestXorReverse(t *testing.T) { + orig := genTestData(1 << 20) + + filter := XorFilter(42) + + filtered := make([]byte, len(orig)) + copy(filtered, orig) + + filtered, _ = filter.Transform(filtered) + filtered, _ = filter.Transform(filtered) + + if !bytes.Equal(filtered, orig) { + t.Errorf("orig != filtered:\n{%x}\n{%x}", orig, filtered) + } +} + +func testFilter(t *testing.T, encode, decode Filter) { + base := memory.NewMemoryStorage() + storage := FilterStorage{ + Base: base, + Encode: encode, + Decode: decode, + } + + size := backup.BlobChunkSize*2 + 10 + data := genTestData(size) + + id, err := backup.WriteFile(storage, bytes.NewReader(data)) + if err != nil { + t.Fatalf("Unexpeced error from WriteFile: %s", err) + } + + buf := new(bytes.Buffer) + if err := backup.RestoreFile(storage, id, buf); err != nil { + t.Fatalf("Unexpeced error from RestoreFile: %s", err) + } + + if !bytes.Equal(data, buf.Bytes()) { + t.Errorf("data != buf.Bytes()") + } + +} + +func TestFilterStorage(t *testing.T) { + testFilter(t, XorFilter(42), XorFilter(42)) +} + +func TestPipeFilter(t *testing.T) { + testFilter(t, PipeFilter([]string{"cat"}), PipeFilter([]string{"cat"})) +} |