fusefrontend: truncateGrowFile: avoid createHeader() call

...if doWrite() can do it for us. This avoids the situation
that the file only consists of a file header when calling
doWrite.

A later patch will check for this condition and warn about it,
as with this change it should no longer occour in normal operation.
This commit is contained in:
Jakob Unterwurzacher 2017-03-12 21:06:59 +01:00
parent 9a0808b1ee
commit d36d53c9bb
2 changed files with 44 additions and 31 deletions

View File

@ -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
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
}
// 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()
_, err = f.createHeader()
f.fileTableEntry.IDLock.Unlock()
defer f.fileTableEntry.IDLock.Unlock()
id, err := f.createHeader()
if err != nil {
return fuse.ToStatus(err)
}
f.fileTableEntry.ID = id
}
// 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))
return status
}
off := lastBlock.BlockCipherOff()
err = syscall.Ftruncate(f.intFd(), int64(off+f.contentEnc.CipherBS()))
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)
}
// 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
}

View File

@ -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))