diff options
| author | Jakob Unterwurzacher | 2017-06-01 21:39:47 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2017-06-01 22:19:27 +0200 | 
| commit | a24faa3ba52c66dfc1707da5c8d001f2adff9ccc (patch) | |
| tree | a07a03444a1689a3c1c0fb97a18f245cacf289d7 | |
| parent | 53b7c17261dbf0aeb46ebb448f7c97d5c9fad986 (diff) | |
fusefrontend: write: consolidate and move encryption to contentenc
Collect all the plaintext and pass everything to contentenc in
one call.
This will allow easier parallization of the encryption.
https://github.com/rfjakob/gocryptfs/issues/116
| -rw-r--r-- | internal/contentenc/content.go | 18 | ||||
| -rw-r--r-- | internal/fusefrontend/file.go | 30 | 
2 files changed, 27 insertions, 21 deletions
| diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index 524296f..80bcf54 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -149,6 +149,24 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b  	return plaintext, nil  } +// EncryptBlocks is like EncryptBlock but takes multiple plaintext blocks. +func (be *ContentEnc) EncryptBlocks(plaintextBlocks [][]byte, firstBlockNo uint64, fileID []byte) []byte { +	// Encrypt piecewise. +	ciphertextBlocks := make([][]byte, len(plaintextBlocks)) +	for i, v := range plaintextBlocks { +		ciphertextBlocks[i] = be.EncryptBlock(v, firstBlockNo+uint64(i), fileID) +	} +	// Concatenate ciphertext into a single byte array. +	// Size the output buffer for the maximum possible size (all blocks complete) +	// to allocations in out.Write() +	tmp := make([]byte, len(plaintextBlocks)*int(be.CipherBS())) +	out := bytes.NewBuffer(tmp[:0]) +	for _, v := range ciphertextBlocks { +		out.Write(v) +	} +	return out.Bytes() +} +  // EncryptBlock - Encrypt plaintext using a random nonce.  // blockNo and fileID are used as associated data.  // The output is nonce + ciphertext + tag. diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go index 57ec9ba..ca1798b 100644 --- a/internal/fusefrontend/file.go +++ b/internal/fusefrontend/file.go @@ -272,22 +272,17 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {  		// re-read it after the RLock().  		f.fileTableEntry.HeaderLock.RLock()  	} -	fileID := f.fileTableEntry.ID  	defer f.fileTableEntry.HeaderLock.RUnlock()  	// Handle payload data -	status := fuse.OK  	dataBuf := bytes.NewBuffer(data)  	blocks := f.contentEnc.ExplodePlainRange(uint64(off), uint64(len(data))) -	writeChain := make([][]byte, len(blocks)) -	var numOutBytes int +	toEncrypt := make([][]byte, len(blocks))  	for i, b := range blocks {  		blockData := dataBuf.Next(int(b.Length))  		// Incomplete block -> Read-Modify-Write  		if b.IsPartial() {  			// Read -			o := b.BlockPlainOff() -			var oldData []byte -			oldData, status = f.doRead(o, f.contentEnc.PlainBS()) +			oldData, status := f.doRead(b.BlockPlainOff(), f.contentEnc.PlainBS())  			if status != fuse.OK {  				tlog.Warn.Printf("ino%d fh%d: RMW read failed: %s", f.qIno.Ino, f.intFd(), status.String())  				return 0, status @@ -296,33 +291,26 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {  			blockData = f.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip))  			tlog.Debug.Printf("len(oldData)=%d len(blockData)=%d", len(oldData), len(blockData))  		} -		// Encrypt -		blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, fileID)  		tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d",  			f.qIno.Ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo) -		// Store output data in the writeChain -		writeChain[i] = blockData -		numOutBytes += len(blockData) -	} -	// Concatenenate all elements in the writeChain into one contiguous buffer -	tmp := make([]byte, numOutBytes) -	writeBuf := bytes.NewBuffer(tmp[:0]) -	for _, w := range writeChain { -		writeBuf.Write(w) +		// Write into the to-encrypt list +		toEncrypt[i] = blockData  	} +	// Encrypt all blocks +	ciphertext := f.contentEnc.EncryptBlocks(toEncrypt, blocks[0].BlockNo, f.fileTableEntry.ID)  	// Preallocate so we cannot run out of space in the middle of the write.  	// This prevents partially written (=corrupt) blocks.  	var err error -	cOff := blocks[0].BlockCipherOff() +	cOff := int64(blocks[0].BlockCipherOff())  	if !f.fs.args.NoPrealloc { -		err = syscallcompat.EnospcPrealloc(int(f.fd.Fd()), int64(cOff), int64(writeBuf.Len())) +		err = syscallcompat.EnospcPrealloc(int(f.fd.Fd()), cOff, int64(len(ciphertext)))  		if err != nil {  			tlog.Warn.Printf("ino%d fh%d: doWrite: prealloc failed: %s", f.qIno.Ino, f.intFd(), err.Error())  			return 0, fuse.ToStatus(err)  		}  	}  	// Write -	_, err = f.fd.WriteAt(writeBuf.Bytes(), int64(cOff)) +	_, err = f.fd.WriteAt(ciphertext, cOff)  	if err != nil {  		tlog.Warn.Printf("doWrite: Write failed: %s", err.Error())  		return 0, fuse.ToStatus(err) | 
