diff options
| -rw-r--r-- | internal/contentenc/intrablock.go | 2 | ||||
| -rw-r--r-- | internal/contentenc/offsets.go | 2 | ||||
| -rw-r--r-- | internal/fusefrontend/file.go | 110 | ||||
| -rw-r--r-- | internal/fusefrontend/file_allocate_truncate.go | 123 | 
4 files changed, 125 insertions, 112 deletions
| diff --git a/internal/contentenc/intrablock.go b/internal/contentenc/intrablock.go index ec9562d..9a22ea2 100644 --- a/internal/contentenc/intrablock.go +++ b/internal/contentenc/intrablock.go @@ -4,7 +4,7 @@ package contentenc  type intraBlock struct {  	BlockNo uint64 // Block number in file  	Skip    uint64 // Offset into block plaintext -	Length  uint64 // Length of data from this block +	Length  uint64 // Length of plaintext data in this block  	fs      *ContentEnc  } diff --git a/internal/contentenc/offsets.go b/internal/contentenc/offsets.go index 256ea26..ebafe9b 100644 --- a/internal/contentenc/offsets.go +++ b/internal/contentenc/offsets.go @@ -76,7 +76,7 @@ func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []intraBlo  		nextBlock.BlockNo = be.PlainOffToBlockNo(offset)  		nextBlock.Skip = offset - be.BlockNoToPlainOff(nextBlock.BlockNo) -		// Minimum of remaining data and remaining space in the block +		// Minimum of remaining plaintext data and remaining space in the block  		nextBlock.Length = MinUint64(length, be.plainBS-nextBlock.Skip)  		blocks = append(blocks, nextBlock) diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go index b5775c2..1835b53 100644 --- a/internal/fusefrontend/file.go +++ b/internal/fusefrontend/file.go @@ -354,103 +354,6 @@ func (f *file) Fsync(flags int) (code fuse.Status) {  	return fuse.ToStatus(syscall.Fsync(int(f.fd.Fd())))  } -// Truncate - FUSE call -func (f *file) Truncate(newSize uint64) fuse.Status { -	f.fdLock.RLock() -	defer f.fdLock.RUnlock() -	if f.released { -		// The file descriptor has been closed concurrently. -		tlog.Warn.Printf("ino%d fh%d: Truncate on released file", f.ino, f.intFd()) -		return fuse.EBADF -	} -	wlock.lock(f.ino) -	defer wlock.unlock(f.ino) -	var err error -	// Common case first: Truncate to zero -	if newSize == 0 { -		err = syscall.Ftruncate(int(f.fd.Fd()), 0) -		if err != nil { -			tlog.Warn.Printf("ino%d fh%d: Ftruncate(fd, 0) returned error: %v", f.ino, f.intFd(), err) -			return fuse.ToStatus(err) -		} -		// Truncate to zero kills the file header -		f.header = nil -		return fuse.OK -	} -	// We need the old file size to determine if we are growing or shrinking -	// the file -	fi, err := f.fd.Stat() -	if err != nil { -		tlog.Warn.Printf("ino%d fh%d: Truncate: Fstat failed: %v", f.ino, f.intFd(), err) -		return fuse.ToStatus(err) -	} -	oldSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size())) -	{ -		oldB := float32(oldSize) / float32(f.contentEnc.PlainBS()) -		newB := float32(newSize) / float32(f.contentEnc.PlainBS()) -		tlog.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.ino, oldB, newB, oldSize, newSize) -	} -	// File size stays the same - nothing to do -	if newSize == oldSize { -		return fuse.OK -	} -	// File grows -	if newSize > oldSize { -		// File was empty, create new header -		if oldSize == 0 { -			err = f.createHeader() -			if err != nil { -				return fuse.ToStatus(err) -			} -		} -		// New blocks to add -		addBlocks := f.contentEnc.ExplodePlainRange(oldSize, newSize-oldSize) -		if len(addBlocks) >= 2 { -			f.zeroPad(oldSize) -		} -		lastBlock := addBlocks[len(addBlocks)-1] -		if lastBlock.IsPartial() { -			off := lastBlock.BlockPlainOff() -			_, status := f.doWrite(make([]byte, lastBlock.Length), int64(off+lastBlock.Skip)) -			return status -		} else { -			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) -			} -			return fuse.ToStatus(err) -		} -	} else { -		// File shrinks -		blockNo := f.contentEnc.PlainOffToBlockNo(newSize) -		cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo) -		plainOff := f.contentEnc.BlockNoToPlainOff(blockNo) -		lastBlockLen := newSize - plainOff -		var data []byte -		if lastBlockLen > 0 { -			var status fuse.Status -			data, status = f.doRead(plainOff, lastBlockLen) -			if status != fuse.OK { -				tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err) -				return status -			} -		} -		// Truncate down to the last complete block -		err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff)) -		if err != nil { -			tlog.Warn.Printf("Truncate: shrink Ftruncate returned error: %v", err) -			return fuse.ToStatus(err) -		} -		// Append partial block -		if lastBlockLen > 0 { -			_, status := f.doWrite(data, int64(plainOff)) -			return status -		} -		return fuse.OK -	} -} -  func (f *file) Chmod(mode uint32) fuse.Status {  	f.fdLock.RLock()  	defer f.fdLock.RUnlock() @@ -484,19 +387,6 @@ func (f *file) GetAttr(a *fuse.Attr) fuse.Status {  	return fuse.OK  } -// Only warn once -var allocateWarnOnce sync.Once - -// Allocate - FUSE call, fallocate(2) -// This is not implemented yet in gocryptfs, but it is neither in EncFS. This -// suggests that the user demand is low. -func (f *file) Allocate(off uint64, sz uint64, mode uint32) fuse.Status { -	allocateWarnOnce.Do(func() { -		tlog.Warn.Printf("fallocate(2) is not supported, returning ENOSYS - see https://github.com/rfjakob/gocryptfs/issues/1") -	}) -	return fuse.ENOSYS -} -  const _UTIME_OMIT = ((1 << 30) - 2)  func (f *file) Utimens(a *time.Time, m *time.Time) fuse.Status { diff --git a/internal/fusefrontend/file_allocate_truncate.go b/internal/fusefrontend/file_allocate_truncate.go new file mode 100644 index 0000000..5be7df4 --- /dev/null +++ b/internal/fusefrontend/file_allocate_truncate.go @@ -0,0 +1,123 @@ +package fusefrontend + +// FUSE operations Truncate and Allocate on file handles +// i.e. ftruncate and fallocate + +import ( +	"sync" +	"syscall" + +	"github.com/hanwen/go-fuse/fuse" + +	"github.com/rfjakob/gocryptfs/internal/tlog" +) + +// Only warn once +var allocateWarnOnce sync.Once + +// Allocate - FUSE call, fallocate(2) +// This is not implemented yet in gocryptfs, but it is neither in EncFS. This +// suggests that the user demand is low. +func (f *file) Allocate(off uint64, sz uint64, mode uint32) fuse.Status { +	allocateWarnOnce.Do(func() { +		tlog.Warn.Printf("fallocate(2) is not supported, returning ENOSYS - see https://github.com/rfjakob/gocryptfs/issues/1") +	}) +	return fuse.ENOSYS +} + +// Truncate - FUSE call +func (f *file) Truncate(newSize uint64) fuse.Status { +	f.fdLock.RLock() +	defer f.fdLock.RUnlock() +	if f.released { +		// The file descriptor has been closed concurrently. +		tlog.Warn.Printf("ino%d fh%d: Truncate on released file", f.ino, f.intFd()) +		return fuse.EBADF +	} +	wlock.lock(f.ino) +	defer wlock.unlock(f.ino) +	var err error +	// Common case first: Truncate to zero +	if newSize == 0 { +		err = syscall.Ftruncate(int(f.fd.Fd()), 0) +		if err != nil { +			tlog.Warn.Printf("ino%d fh%d: Ftruncate(fd, 0) returned error: %v", f.ino, f.intFd(), err) +			return fuse.ToStatus(err) +		} +		// Truncate to zero kills the file header +		f.header = nil +		return fuse.OK +	} +	// We need the old file size to determine if we are growing or shrinking +	// the file +	fi, err := f.fd.Stat() +	if err != nil { +		tlog.Warn.Printf("ino%d fh%d: Truncate: Fstat failed: %v", f.ino, f.intFd(), err) +		return fuse.ToStatus(err) +	} +	oldSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size())) +	{ +		oldB := float32(oldSize) / float32(f.contentEnc.PlainBS()) +		newB := float32(newSize) / float32(f.contentEnc.PlainBS()) +		tlog.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.ino, oldB, newB, oldSize, newSize) +	} +	// File size stays the same - nothing to do +	if newSize == oldSize { +		return fuse.OK +	} +	// File grows +	if newSize > oldSize { +		// File was empty, create new header +		if oldSize == 0 { +			err = f.createHeader() +			if err != nil { +				return fuse.ToStatus(err) +			} +		} +		// New blocks to add +		addBlocks := f.contentEnc.ExplodePlainRange(oldSize, newSize-oldSize) +		if len(addBlocks) >= 2 { +			f.zeroPad(oldSize) +		} +		lastBlock := addBlocks[len(addBlocks)-1] +		if lastBlock.IsPartial() { +			off := lastBlock.BlockPlainOff() +			_, status := f.doWrite(make([]byte, lastBlock.Length), int64(off+lastBlock.Skip)) +			return status +		} else { +			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) +			} +			return fuse.ToStatus(err) +		} +	} else { +		// File shrinks +		blockNo := f.contentEnc.PlainOffToBlockNo(newSize) +		cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo) +		plainOff := f.contentEnc.BlockNoToPlainOff(blockNo) +		lastBlockLen := newSize - plainOff +		var data []byte +		if lastBlockLen > 0 { +			var status fuse.Status +			data, status = f.doRead(plainOff, lastBlockLen) +			if status != fuse.OK { +				tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err) +				return status +			} +		} +		// Truncate down to the last complete block +		err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff)) +		if err != nil { +			tlog.Warn.Printf("Truncate: shrink Ftruncate returned error: %v", err) +			return fuse.ToStatus(err) +		} +		// Append partial block +		if lastBlockLen > 0 { +			_, status := f.doWrite(data, int64(plainOff)) +			return status +		} +		return fuse.OK +	} +} | 
