Bundle up blocks for bigger reads from the backing filesystem

This commit is contained in:
Jakob Unterwurzacher 2015-09-06 09:47:01 +02:00
parent baa837b788
commit 448e88490b
2 changed files with 72 additions and 21 deletions

View File

@ -3,6 +3,7 @@ package cryptfs
// File content encryption / decryption
import (
"bytes"
"os"
"errors"
"crypto/cipher"
@ -13,7 +14,23 @@ type CryptFile struct {
gcm cipher.AEAD
}
// decryptBlock - Verify and decrypt GCM block
// DecryptBlocks - Decrypt a number of blocks
func (be *CryptFS) DecryptBlocks(ciphertext []byte) ([]byte, error) {
cBuf := bytes.NewBuffer(ciphertext)
var err error
var pBuf bytes.Buffer
for cBuf.Len() > 0 {
cBlock := cBuf.Next(int(be.cipherBS))
pBlock, err := be.DecryptBlock(cBlock)
if err != nil {
break
}
pBuf.Write(pBlock)
}
return pBuf.Bytes(), err
}
// DecryptBlock - Verify and decrypt GCM block
func (be *CryptFS) DecryptBlock(ciphertext []byte) ([]byte, error) {
// Empty block?
@ -92,3 +109,29 @@ func (be *CryptFS) minu64(x uint64, y uint64) uint64 {
}
return y
}
// Get the byte range in the ciphertext corresponding to blocks
// (full blocks!)
func (be *CryptFS) JoinCiphertextRange(blocks []intraBlock) (uint64, uint64) {
offset, _ := blocks[0].CiphertextRange()
last := blocks[len(blocks)-1]
length := (last.BlockNo - blocks[0].BlockNo + 1) * be.cipherBS
return offset, length
}
// Crop plaintext that correspons to complete cipher blocks down to what is
// requested according to "iblocks"
func (be *CryptFS) CropPlaintext(plaintext []byte, blocks []intraBlock) []byte {
offset := blocks[0].Offset
last := blocks[len(blocks)-1]
length := (last.BlockNo - blocks[0].BlockNo + 1) * be.plainBS
var cropped []byte
if offset + length > uint64(len(plaintext)) {
cropped = plaintext[offset:len(plaintext)]
} else {
cropped = plaintext[offset:offset+length]
}
return cropped
}

View File

@ -67,26 +67,32 @@ func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenR
}
func (f *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
fmt.Printf("Read: o=%d l=%d\n", req.Offset, req.Size)
// Read the backing ciphertext in one go
iblocks := f.crfs.SplitRange(uint64(req.Offset), uint64(req.Size))
for _, ib := range iblocks {
var partReq fuse.ReadRequest
var partResp fuse.ReadResponse
o, l := ib.CiphertextRange()
partReq.Offset = int64(o)
partReq.Size = int(l)
partResp.Data = make([]byte, int(l))
err := f.File.Read(ctx, &partReq, &partResp)
if err != nil {
return err
}
plaintext, err := f.crfs.DecryptBlock(partResp.Data)
if err != nil {
fmt.Printf("Read: Error reading block %d: %s\n", ib.BlockNo, err.Error())
return err
}
plaintext = ib.CropBlock(plaintext)
resp.Data = append(resp.Data, plaintext...)
var cipherReq fuse.ReadRequest
var cipherResp fuse.ReadResponse
o, l := f.crfs.JoinCiphertextRange(iblocks)
cipherResp.Data = make([]byte, int(l))
cipherReq.Offset = int64(o)
cipherReq.Size = int(l)
cryptfs.Debug.Printf("Read: cipherReq o=%d l=%d\n", o, l)
err := f.File.Read(ctx, &cipherReq, &cipherResp)
if err != nil {
return err
}
// Decrypt it
plaintext, err := f.crfs.DecryptBlocks(cipherResp.Data)
if err != nil {
resp.Data = plaintext
return err
}
// Crop down to relevant part
resp.Data = f.crfs.CropPlaintext(plaintext, iblocks)
return nil
}
@ -98,6 +104,7 @@ func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.Wri
for _, ib := range iblocks {
if ib.IsPartial() {
// RMW
cryptfs.Debug.Printf("RMW\n")
blockData = make([]byte, f.crfs.PlainBS())
var readReq fuse.ReadRequest
var readResp fuse.ReadResponse
@ -127,8 +134,9 @@ func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.Wri
return err
}
// Remove written data from the front of the request
req.Data = req.Data[len(blockData):len(req.Data)]
resp.Size += len(blockData)
cryptfs.Debug.Printf("req.Data[%d:%d]\n", int(ib.Length), len(req.Data))
req.Data = req.Data[int(ib.Length):len(req.Data)]
resp.Size += int(ib.Length)
}
return nil
}