Skip to content

Commit 3c6fe98

Browse files
committed
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.
1 parent 609343a commit 3c6fe98

File tree

2 files changed

+48
-10
lines changed

2 files changed

+48
-10
lines changed

internal/contentenc/content.go

+46-10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"runtime"
1111
"sync"
1212

13+
"github.com/hanwen/go-fuse/fuse"
14+
1315
"github.com/rfjakob/gocryptfs/internal/cryptocore"
1416
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
1517
"github.com/rfjakob/gocryptfs/internal/tlog"
@@ -51,20 +53,43 @@ type ContentEnc struct {
5153
allZeroNonce []byte
5254
// Force decode even if integrity check fails (openSSL only)
5355
forceDecode bool
56+
// Ciphertext block pool. Always returns cipherBS-sized byte slices.
57+
cBlockPool sync.Pool
58+
// Ciphertext write pool. Always returns byte slices of size
59+
// fuse.MAX_KERNEL_WRITE + overhead.
60+
cWritePool sync.Pool
61+
cWriteSize int
5462
}
5563

5664
// New returns an initialized ContentEnc instance.
5765
func New(cc *cryptocore.CryptoCore, plainBS uint64, forceDecode bool) *ContentEnc {
5866
cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen
59-
60-
return &ContentEnc{
67+
// Take IV and GHASH overhead into account.
68+
cWriteSize := int(fuse.MAX_KERNEL_WRITE / plainBS * cipherBS)
69+
if fuse.MAX_KERNEL_WRITE%plainBS != 0 {
70+
log.Panicf("unaligned MAX_KERNEL_WRITE=%d", fuse.MAX_KERNEL_WRITE)
71+
}
72+
c := &ContentEnc{
6173
cryptoCore: cc,
6274
plainBS: plainBS,
6375
cipherBS: cipherBS,
6476
allZeroBlock: make([]byte, cipherBS),
6577
allZeroNonce: make([]byte, cc.IVLen),
6678
forceDecode: forceDecode,
79+
cWriteSize: cWriteSize,
80+
}
81+
c.cBlockPool.New = func() interface{} { return make([]byte, cipherBS) }
82+
c.cWritePool.New = func() interface{} { return make([]byte, cWriteSize) }
83+
return c
84+
}
85+
86+
// CWritePut puts "buf" back into the cWritePool.
87+
func (be *ContentEnc) CWritePut(buf []byte) {
88+
buf = buf[:cap(buf)]
89+
if len(buf) != be.cWriteSize {
90+
log.Panicf("wrong len=%d, want=%d", len(buf), be.cWriteSize)
6791
}
92+
be.cWritePool.Put(buf)
6893
}
6994

7095
// PlainBS returns the plaintext block size
@@ -185,12 +210,16 @@ func (be *ContentEnc) EncryptBlocks(plaintextBlocks [][]byte, firstBlockNo uint6
185210
be.doEncryptBlocks(plaintextBlocks, ciphertextBlocks, firstBlockNo, fileID)
186211
}
187212
// Concatenate ciphertext into a single byte array.
188-
// Size the output buffer for the maximum possible size (all blocks complete)
189-
// to prevent further allocations in out.Write()
190-
tmp := make([]byte, len(plaintextBlocks)*int(be.CipherBS()))
213+
tmp := be.cWritePool.Get().([]byte)
191214
out := bytes.NewBuffer(tmp[:0])
192215
for _, v := range ciphertextBlocks {
193216
out.Write(v)
217+
// Return the memory to cBlockPool
218+
cBlock := v[:cap(v)]
219+
if len(cBlock) != int(be.cipherBS) {
220+
log.Panicf("unexpected cBlock length: len=%d cipherBS=%d", len(cBlock), be.cipherBS)
221+
}
222+
be.cBlockPool.Put(cBlock)
194223
}
195224
return out.Bytes()
196225
}
@@ -233,15 +262,22 @@ func (be *ContentEnc) doEncryptBlock(plaintext []byte, blockNo uint64, fileID []
233262
if len(nonce) != be.cryptoCore.IVLen {
234263
log.Panic("wrong nonce length")
235264
}
236-
237-
// Authenticate block with block number and file ID
265+
// Block is authenticated with block number and file ID
238266
aData := make([]byte, 8)
239267
binary.BigEndian.PutUint64(aData, blockNo)
240268
aData = append(aData, fileID...)
241-
269+
// Get a cipherBS-sized block of memory, copy the nonce into it and truncate to
270+
// nonce length
271+
cBlock := be.cBlockPool.Get().([]byte)
272+
copy(cBlock, nonce)
273+
cBlock = cBlock[0:len(nonce)]
242274
// Encrypt plaintext and append to nonce
243-
ciphertext := be.cryptoCore.AEADCipher.Seal(nonce, nonce, plaintext, aData)
244-
275+
ciphertext := be.cryptoCore.AEADCipher.Seal(cBlock, nonce, plaintext, aData)
276+
overhead := int(be.cipherBS - be.plainBS)
277+
if len(plaintext)+overhead != len(ciphertext) {
278+
log.Panicf("unexpected ciphertext length: plaintext=%d, overhead=%d, ciphertext=%d",
279+
len(plaintext), overhead, len(ciphertext))
280+
}
245281
return ciphertext
246282
}
247283

internal/fusefrontend/file.go

+2
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
311311
}
312312
// Write
313313
_, err = f.fd.WriteAt(ciphertext, cOff)
314+
// Return memory to cWritePool
315+
f.fs.contentEnc.CWritePut(ciphertext)
314316
if err != nil {
315317
tlog.Warn.Printf("doWrite: Write failed: %s", err.Error())
316318
return 0, fuse.ToStatus(err)

0 commit comments

Comments
 (0)