aboutsummaryrefslogtreecommitdiff
path: root/storage/filter
diff options
context:
space:
mode:
Diffstat (limited to 'storage/filter')
-rw-r--r--storage/filter/filter.go77
-rw-r--r--storage/filter/filter_test.go76
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"}))
+}