func (vs *ValidatingSink) Store(loc BlockLocation, data []byte) error { vs.log("validating %+v (%d bytes)", loc, len(data)) if vs.hashGroups == nil { vs.log("making hash groups") err := vs.makeHashGroups() if err != nil { return errors.Wrap(err, 1) } vs.blockBuf = make([]byte, pwr.BlockSize) vs.split = splitfunc.New(int(pwr.BlockSize)) vs.sctx = wsync.NewContext(int(pwr.BlockSize)) } hashGroup := vs.hashGroups[loc] // see also wsync.CreateSignature s := bufio.NewScanner(bytes.NewReader(data)) s.Buffer(vs.blockBuf, 0) s.Split(vs.split) hashIndex := 0 for ; s.Scan(); hashIndex++ { smallBlock := s.Bytes() vs.log("validating sub #%d of %+v (%d bytes)", hashIndex, loc, len(smallBlock)) weakHash, strongHash := vs.sctx.HashBlock(smallBlock) bh := hashGroup[hashIndex] if bh.WeakHash != weakHash { err := fmt.Errorf("at %+v, expected weak hash %x, got %x", loc, bh.WeakHash, weakHash) return errors.Wrap(err, 1) } if !bytes.Equal(bh.StrongHash, strongHash) { err := fmt.Errorf("at %+v, expected strong hash %x, got %x", loc, bh.StrongHash, strongHash) return errors.Wrap(err, 1) } } return vs.Sink.Store(loc, data) }
// GetWriter returns a writer that checks hashes before writing to the underlying // pool's writer. It tries really hard to be transparent, but does buffer some data, // which means some writing is only done when the returned writer is closed. func (vp *ValidatingPool) GetWriter(fileIndex int64) (io.WriteCloser, error) { if vp.hashGroups == nil { err := vp.makeHashGroups() if err != nil { return nil, errors.Wrap(err, 1) } vp.sctx = wsync.NewContext(int(BlockSize)) } w, err := vp.Pool.GetWriter(fileIndex) if err != nil { return nil, errors.Wrap(err, 1) } hashGroup := vp.hashGroups[fileIndex] blockIndex := int64(0) fileSize := vp.Container.Files[fileIndex].Size validate := func(data []byte) error { bh := hashGroup[blockIndex] weakHash, strongHash := vp.sctx.HashBlock(data) start := blockIndex * BlockSize size := ComputeBlockSize(fileSize, blockIndex) if bh.WeakHash != weakHash { if vp.Wounds == nil { err := fmt.Errorf("at %d/%d, expected weak hash %x, got %x", fileIndex, blockIndex, bh.WeakHash, weakHash) return errors.Wrap(err, 1) } vp.Wounds <- &Wound{ Kind: WoundKind_FILE, Index: fileIndex, Start: start, End: start + size, } } else if !bytes.Equal(bh.StrongHash, strongHash) { if vp.Wounds == nil { err := fmt.Errorf("at %d/%d, expected strong hash %x, got %x", fileIndex, blockIndex, bh.StrongHash, strongHash) return errors.Wrap(err, 1) } vp.Wounds <- &Wound{ Kind: WoundKind_FILE, Index: fileIndex, Start: start, End: start + size, } } if vp.Wounds != nil { vp.Wounds <- &Wound{ Kind: WoundKind_CLOSED_FILE, Index: fileIndex, Start: start, End: start + size, } } blockIndex++ return nil } dw := &drip.Writer{ Writer: w, Buffer: make([]byte, BlockSize), Validate: validate, } return dw, nil }
func mksync() *wsync.Context { return wsync.NewContext(int(BlockSize)) }