contentenc: use sync.Pool memory pools for encryption
We use two levels of buffers: 1) 4kiB+overhead for each ciphertext block 2) 128kiB+overhead for each FUSE write (32 ciphertext blocks) This commit adds a sync.Pool for both levels. The memory-efficiency for small writes could be improved, as we now always use a 128kiB buffer.
This commit is contained in:
parent
609343accf
commit
3c6fe98eb1
@ -10,6 +10,8 @@ import (
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/hanwen/go-fuse/fuse"
|
||||
|
||||
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||
@ -51,20 +53,43 @@ type ContentEnc struct {
|
||||
allZeroNonce []byte
|
||||
// Force decode even if integrity check fails (openSSL only)
|
||||
forceDecode bool
|
||||
// Ciphertext block pool. Always returns cipherBS-sized byte slices.
|
||||
cBlockPool sync.Pool
|
||||
// Ciphertext write pool. Always returns byte slices of size
|
||||
// fuse.MAX_KERNEL_WRITE + overhead.
|
||||
cWritePool sync.Pool
|
||||
cWriteSize int
|
||||
}
|
||||
|
||||
// New returns an initialized ContentEnc instance.
|
||||
func New(cc *cryptocore.CryptoCore, plainBS uint64, forceDecode bool) *ContentEnc {
|
||||
cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen
|
||||
|
||||
return &ContentEnc{
|
||||
// Take IV and GHASH overhead into account.
|
||||
cWriteSize := int(fuse.MAX_KERNEL_WRITE / plainBS * cipherBS)
|
||||
if fuse.MAX_KERNEL_WRITE%plainBS != 0 {
|
||||
log.Panicf("unaligned MAX_KERNEL_WRITE=%d", fuse.MAX_KERNEL_WRITE)
|
||||
}
|
||||
c := &ContentEnc{
|
||||
cryptoCore: cc,
|
||||
plainBS: plainBS,
|
||||
cipherBS: cipherBS,
|
||||
allZeroBlock: make([]byte, cipherBS),
|
||||
allZeroNonce: make([]byte, cc.IVLen),
|
||||
forceDecode: forceDecode,
|
||||
cWriteSize: cWriteSize,
|
||||
}
|
||||
c.cBlockPool.New = func() interface{} { return make([]byte, cipherBS) }
|
||||
c.cWritePool.New = func() interface{} { return make([]byte, cWriteSize) }
|
||||
return c
|
||||
}
|
||||
|
||||
// CWritePut puts "buf" back into the cWritePool.
|
||||
func (be *ContentEnc) CWritePut(buf []byte) {
|
||||
buf = buf[:cap(buf)]
|
||||
if len(buf) != be.cWriteSize {
|
||||
log.Panicf("wrong len=%d, want=%d", len(buf), be.cWriteSize)
|
||||
}
|
||||
be.cWritePool.Put(buf)
|
||||
}
|
||||
|
||||
// PlainBS returns the plaintext block size
|
||||
@ -185,12 +210,16 @@ func (be *ContentEnc) EncryptBlocks(plaintextBlocks [][]byte, firstBlockNo uint6
|
||||
be.doEncryptBlocks(plaintextBlocks, ciphertextBlocks, firstBlockNo, fileID)
|
||||
}
|
||||
// Concatenate ciphertext into a single byte array.
|
||||
// Size the output buffer for the maximum possible size (all blocks complete)
|
||||
// to prevent further allocations in out.Write()
|
||||
tmp := make([]byte, len(plaintextBlocks)*int(be.CipherBS()))
|
||||
tmp := be.cWritePool.Get().([]byte)
|
||||
out := bytes.NewBuffer(tmp[:0])
|
||||
for _, v := range ciphertextBlocks {
|
||||
out.Write(v)
|
||||
// Return the memory to cBlockPool
|
||||
cBlock := v[:cap(v)]
|
||||
if len(cBlock) != int(be.cipherBS) {
|
||||
log.Panicf("unexpected cBlock length: len=%d cipherBS=%d", len(cBlock), be.cipherBS)
|
||||
}
|
||||
be.cBlockPool.Put(cBlock)
|
||||
}
|
||||
return out.Bytes()
|
||||
}
|
||||
@ -233,15 +262,22 @@ func (be *ContentEnc) doEncryptBlock(plaintext []byte, blockNo uint64, fileID []
|
||||
if len(nonce) != be.cryptoCore.IVLen {
|
||||
log.Panic("wrong nonce length")
|
||||
}
|
||||
|
||||
// Authenticate block with block number and file ID
|
||||
// Block is authenticated with block number and file ID
|
||||
aData := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(aData, blockNo)
|
||||
aData = append(aData, fileID...)
|
||||
|
||||
// Get a cipherBS-sized block of memory, copy the nonce into it and truncate to
|
||||
// nonce length
|
||||
cBlock := be.cBlockPool.Get().([]byte)
|
||||
copy(cBlock, nonce)
|
||||
cBlock = cBlock[0:len(nonce)]
|
||||
// Encrypt plaintext and append to nonce
|
||||
ciphertext := be.cryptoCore.AEADCipher.Seal(nonce, nonce, plaintext, aData)
|
||||
|
||||
ciphertext := be.cryptoCore.AEADCipher.Seal(cBlock, nonce, plaintext, aData)
|
||||
overhead := int(be.cipherBS - be.plainBS)
|
||||
if len(plaintext)+overhead != len(ciphertext) {
|
||||
log.Panicf("unexpected ciphertext length: plaintext=%d, overhead=%d, ciphertext=%d",
|
||||
len(plaintext), overhead, len(ciphertext))
|
||||
}
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
|
@ -311,6 +311,8 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
|
||||
}
|
||||
// Write
|
||||
_, err = f.fd.WriteAt(ciphertext, cOff)
|
||||
// Return memory to cWritePool
|
||||
f.fs.contentEnc.CWritePut(ciphertext)
|
||||
if err != nil {
|
||||
tlog.Warn.Printf("doWrite: Write failed: %s", err.Error())
|
||||
return 0, fuse.ToStatus(err)
|
||||
|
Loading…
x
Reference in New Issue
Block a user