diff options
| -rw-r--r-- | internal/fusefrontend/file_allocate_truncate.go | 68 | ||||
| -rw-r--r-- | internal/fusefrontend/file_holes.go | 9 | 
2 files changed, 45 insertions, 32 deletions
| diff --git a/internal/fusefrontend/file_allocate_truncate.go b/internal/fusefrontend/file_allocate_truncate.go index 6f27ac7..0e12981 100644 --- a/internal/fusefrontend/file_allocate_truncate.go +++ b/internal/fusefrontend/file_allocate_truncate.go @@ -183,35 +183,47 @@ func (f *file) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) fuse.Statu  	if newPlainSz <= oldPlainSz {  		log.Panicf("BUG: newSize=%d <= oldSize=%d", newPlainSz, oldPlainSz)  	} -	var err error -	// File was empty, create new header -	if oldPlainSz == 0 { -		f.fileTableEntry.IDLock.Lock() -		_, err = f.createHeader() -		f.fileTableEntry.IDLock.Unlock() -		if err != nil { -			return fuse.ToStatus(err) -		} -	} -	// New blocks to add -	addBlocks := f.contentEnc.ExplodePlainRange(oldPlainSz, newPlainSz-oldPlainSz) -	if oldPlainSz > 0 && len(addBlocks) >= 2 { -		// Zero-pad the first block (unless the first block is also the last block) -		f.zeroPad(oldPlainSz) -	} -	lastBlock := addBlocks[len(addBlocks)-1] -	if lastBlock.IsPartial() { -		// Write at the new end of the file. The seek implicitly grows the file -		// (creates a file hole) and doWrite() takes care of RMW. -		off := lastBlock.BlockPlainOff() -		_, status := f.doWrite(make([]byte, lastBlock.Length), int64(off+lastBlock.Skip)) +	var n1 uint64 +	if oldPlainSz > 0 { +		n1 = f.contentEnc.PlainOffToBlockNo(oldPlainSz - 1) +	} +	newEofOffset := newPlainSz - 1 +	n2 := f.contentEnc.PlainOffToBlockNo(newEofOffset) +	// The file is grown within one block, no need to pad anything. +	// Write a single zero to the last byte and let doWrite figure out the RMW. +	if n1 == n2 { +		buf := make([]byte, 1) +		_, status := f.doWrite(buf, int64(newEofOffset))  		return status  	} - -	off := lastBlock.BlockCipherOff() -	err = syscall.Ftruncate(f.intFd(), int64(off+f.contentEnc.CipherBS())) -	if err != nil { -		tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err) +	// The truncate creates at least one new block. +	// +	// Make sure the old last block is padded to the block boundary. This call +	// is a no-op if it is already block-aligned. +	f.zeroPad(oldPlainSz) +	// The new size is block-aligned. In this case we can do everything ourselves +	// and avoid the call to doWrite. +	if newPlainSz%f.contentEnc.PlainBS() == 0 { +		// The file was empty, so it did not have a header. Create one. +		if oldPlainSz == 0 { +			f.fileTableEntry.IDLock.Lock() +			defer f.fileTableEntry.IDLock.Unlock() +			id, err := f.createHeader() +			if err != nil { +				return fuse.ToStatus(err) +			} +			f.fileTableEntry.ID = id +		} +		cSz := int64(f.contentEnc.PlainSizeToCipherSize(newPlainSz)) +		err := syscall.Ftruncate(f.intFd(), cSz) +		if err != nil { +			tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err) +		} +		return fuse.ToStatus(err)  	} -	return fuse.ToStatus(err) +	// The new size is NOT aligned, so we need to write a partial block. +	// Write a single zero to the last byte and let doWrite figure it out. +	buf := make([]byte, 1) +	_, status := f.doWrite(buf, int64(newEofOffset)) +	return status  } diff --git a/internal/fusefrontend/file_holes.go b/internal/fusefrontend/file_holes.go index 5b7a244..d779191 100644 --- a/internal/fusefrontend/file_holes.go +++ b/internal/fusefrontend/file_holes.go @@ -30,7 +30,7 @@ func (f *file) writePadHole(targetOff int64) fuse.Status {  	}  	// The write goes past the next block. nextBlock has  	// to be zero-padded to the block boundary and (at least) nextBlock+1 -	// will become a file hole in the ciphertext. +	// will contain a file hole in the ciphertext.  	status := f.zeroPad(plainSize)  	if status != fuse.OK {  		tlog.Warn.Printf("zeroPad returned error %v", status) @@ -39,14 +39,15 @@ func (f *file) writePadHole(targetOff int64) fuse.Status {  	return fuse.OK  } -// Zero-pad the file of size plainSize to the next block boundary +// Zero-pad the file of size plainSize to the next block boundary. This is a no-op +// if the file is already block-aligned.  func (f *file) zeroPad(plainSize uint64) fuse.Status {  	lastBlockLen := plainSize % f.contentEnc.PlainBS() -	missing := f.contentEnc.PlainBS() - lastBlockLen -	if missing == 0 { +	if lastBlockLen == 0 {  		// Already block-aligned  		return fuse.OK  	} +	missing := f.contentEnc.PlainBS() - lastBlockLen  	pad := make([]byte, missing)  	tlog.Debug.Printf("zeroPad: Writing %d bytes\n", missing)  	_, status := f.doWrite(pad, int64(plainSize)) | 
