aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/fusefrontend/file_allocate_truncate.go68
-rw-r--r--internal/fusefrontend/file_holes.go9
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))