diff options
| -rw-r--r-- | cryptfs/address_translation.go | 79 | ||||
| -rw-r--r-- | cryptfs/config_file.go | 2 | ||||
| -rw-r--r-- | cryptfs/content_test.go | 22 | ||||
| -rw-r--r-- | cryptfs/cryptfs.go | 7 | ||||
| -rw-r--r-- | cryptfs/cryptfs_content.go | 115 | ||||
| -rw-r--r-- | cryptfs/file_header.go | 10 | ||||
| -rw-r--r-- | cryptfs/intrablock.go | 16 | ||||
| -rw-r--r-- | main_test.go | 26 | ||||
| -rw-r--r-- | pathfs_frontend/file.go | 50 | ||||
| -rw-r--r-- | pathfs_frontend/file_holes.go | 4 | ||||
| -rw-r--r-- | pathfs_frontend/fs.go | 2 | ||||
| -rwxr-xr-x | test.bash | 11 | 
12 files changed, 158 insertions, 186 deletions
diff --git a/cryptfs/address_translation.go b/cryptfs/address_translation.go new file mode 100644 index 0000000..dfc6ef9 --- /dev/null +++ b/cryptfs/address_translation.go @@ -0,0 +1,79 @@ +package cryptfs + +// CryptFS methods that translate offsets between ciphertext and plaintext + +// get the block number at plain-text offset +func (be *CryptFS) PlainOffToBlockNo(plainOffset uint64) uint64 { +	return plainOffset / be.plainBS +} + +// get the block number at ciphter-text offset +func (be *CryptFS) CipherOffToBlockNo(cipherOffset uint64) uint64 { +	return (cipherOffset - HEADER_LEN) / be.cipherBS +} + +// get ciphertext offset of block "blockNo" +func (be *CryptFS) BlockNoToCipherOff(blockNo uint64) uint64 { +	return HEADER_LEN + blockNo*be.cipherBS +} + +// get plaintext offset of block "blockNo" +func (be *CryptFS) BlockNoToPlainOff(blockNo uint64) uint64 { +	return blockNo * be.plainBS +} + +// PlainSize - calculate plaintext size from ciphertext size +func (be *CryptFS) CipherSizeToPlainSize(cipherSize uint64) uint64 { + +	// Zero sized files stay zero-sized +	if cipherSize == 0 { +		return 0 +	} + +	// Block number at last byte +	blockNo := be.CipherOffToBlockNo(cipherSize - 1) +	blockCount := blockNo + 1 + +	overhead := BLOCK_OVERHEAD*blockCount + HEADER_LEN + +	return cipherSize - overhead +} + +// CipherSize - calculate ciphertext size from plaintext size +func (be *CryptFS) PlainSizeToCipherSize(plainSize uint64) uint64 { + +	// Block number at last byte +	blockNo := be.PlainOffToBlockNo(plainSize - 1) +	blockCount := blockNo + 1 + +	overhead := BLOCK_OVERHEAD*blockCount + HEADER_LEN + +	return plainSize + overhead +} + +// Split a plaintext byte range into (possibly partial) blocks +func (be *CryptFS) ExplodePlainRange(offset uint64, length uint64) []intraBlock { +	var blocks []intraBlock +	var nextBlock intraBlock +	nextBlock.fs = be + +	for length > 0 { +		nextBlock.BlockNo = be.PlainOffToBlockNo(offset) +		nextBlock.Skip = offset - be.BlockNoToPlainOff(nextBlock.BlockNo) + +		// Minimum of remaining data and remaining space in the block +		nextBlock.Length = MinUint64(length, be.plainBS-nextBlock.Skip) + +		blocks = append(blocks, nextBlock) +		offset += nextBlock.Length +		length -= nextBlock.Length +	} +	return blocks +} + +func MinUint64(x uint64, y uint64) uint64 { +	if x < y { +		return x +	} +	return y +} diff --git a/cryptfs/config_file.go b/cryptfs/config_file.go index 7e762ad..16a3eec 100644 --- a/cryptfs/config_file.go +++ b/cryptfs/config_file.go @@ -1,8 +1,8 @@  package cryptfs  import ( -	"fmt"  	"encoding/json" +	"fmt"  	"io/ioutil"  )  import "os" diff --git a/cryptfs/content_test.go b/cryptfs/content_test.go index 4e1b447..37635f0 100644 --- a/cryptfs/content_test.go +++ b/cryptfs/content_test.go @@ -16,7 +16,7 @@ func TestSplitRange(t *testing.T) {  		testRange{0, 10},  		testRange{234, 6511},  		testRange{65444, 54}, -		testRange{0, 1024*1024}, +		testRange{0, 1024 * 1024},  		testRange{0, 65536},  		testRange{6654, 8945}) @@ -24,8 +24,8 @@ func TestSplitRange(t *testing.T) {  	f := NewCryptFS(key, true)  	for _, r := range ranges { -		parts := f.SplitRange(r.offset, r.length) -		var lastBlockNo uint64 = 1<<63 +		parts := f.ExplodePlainRange(r.offset, r.length) +		var lastBlockNo uint64 = 1 << 63  		for _, p := range parts {  			if p.BlockNo == lastBlockNo {  				t.Errorf("Duplicate block number %d", p.BlockNo) @@ -51,11 +51,15 @@ func TestCiphertextRange(t *testing.T) {  	f := NewCryptFS(key, true)  	for _, r := range ranges { -		alignedOffset, alignedLength, skipBytes := f.CiphertextRange(r.offset, r.length) + +		blocks := f.ExplodePlainRange(r.offset, r.length) +		alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks) +		skipBytes := blocks[0].Skip +  		if alignedLength < r.length {  			t.Errorf("alignedLength=%s is smaller than length=%d", alignedLength, r.length)  		} -		if (alignedOffset - HEADER_LEN)%f.cipherBS != 0 { +		if (alignedOffset-HEADER_LEN)%f.cipherBS != 0 {  			t.Errorf("alignedOffset=%d is not aligned", alignedOffset)  		}  		if r.offset%f.plainBS != 0 && skipBytes == 0 { @@ -68,19 +72,19 @@ func TestBlockNo(t *testing.T) {  	key := make([]byte, KEY_LEN)  	f := NewCryptFS(key, true) -	b := f.BlockNoCipherOff(788) +	b := f.CipherOffToBlockNo(788)  	if b != 0 {  		t.Errorf("actual: %d", b)  	} -	b = f.BlockNoCipherOff(HEADER_LEN + f.CipherBS()) +	b = f.CipherOffToBlockNo(HEADER_LEN + f.cipherBS)  	if b != 1 {  		t.Errorf("actual: %d", b)  	} -	b = f.BlockNoPlainOff(788) +	b = f.PlainOffToBlockNo(788)  	if b != 0 {  		t.Errorf("actual: %d", b)  	} -	b = f.BlockNoPlainOff(f.PlainBS()) +	b = f.PlainOffToBlockNo(f.plainBS)  	if b != 1 {  		t.Errorf("actual: %d", b)  	} diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go index 0593214..9fe492d 100644 --- a/cryptfs/cryptfs.go +++ b/cryptfs/cryptfs.go @@ -13,7 +13,7 @@ const (  	KEY_LEN         = 32 // AES-256  	NONCE_LEN       = 12  	AUTH_TAG_LEN    = 16 -	BLOCK_OVERHEAD = NONCE_LEN + AUTH_TAG_LEN +	BLOCK_OVERHEAD  = NONCE_LEN + AUTH_TAG_LEN  )  type CryptFS struct { @@ -61,8 +61,3 @@ func NewCryptFS(key []byte, useOpenssl bool) *CryptFS {  func (be *CryptFS) PlainBS() uint64 {  	return be.plainBS  } - -// Get ciphertext block size -func (be *CryptFS) CipherBS() uint64 { -	return be.cipherBS -} diff --git a/cryptfs/cryptfs_content.go b/cryptfs/cryptfs_content.go index 03253d3..d74570f 100644 --- a/cryptfs/cryptfs_content.go +++ b/cryptfs/cryptfs_content.go @@ -12,11 +12,6 @@ import (  	"os"  ) -const ( -	// A block of 4124 zero bytes has this md5 -	ZeroBlockMd5 = "64331af89bd15a987b39855338336237" -) -  // md5sum - debug helper, return md5 hex string  func md5sum(buf []byte) string {  	rawHash := md5.Sum(buf) @@ -110,106 +105,6 @@ func (be *CryptFS) EncryptBlock(plaintext []byte, blockNo uint64, fileId []byte)  	return ciphertext  } -// Split a plaintext byte range into (possibly partial) blocks -func (be *CryptFS) SplitRange(offset uint64, length uint64) []intraBlock { -	var b intraBlock -	var parts []intraBlock - -	b.fs = be - -	for length > 0 { -		b.BlockNo = offset / be.plainBS -		b.Skip = offset % be.plainBS -		// Minimum of remaining data and remaining space in the block -		b.Length = be.minu64(length, be.plainBS-b.Skip) -		parts = append(parts, b) -		offset += b.Length -		length -= b.Length -	} -	return parts -} - -// PlainSize - calculate plaintext size from ciphertext size -func (be *CryptFS) PlainSize(size uint64) uint64 { - -	// Zero sized files stay zero-sized -	if size == 0 { -		return 0 -	} - -	// Account for header -	size -= HEADER_LEN - -	overhead := be.cipherBS - be.plainBS -	nBlocks := (size + be.cipherBS - 1) / be.cipherBS -	if nBlocks*overhead > size { -		Warn.Printf("PlainSize: Negative size, returning 0 instead\n") -		return 0 -	} -	size -= nBlocks * overhead - -	return size -} - -// CipherSize - calculate ciphertext size from plaintext size -func (be *CryptFS) CipherSize(size uint64) uint64 { -	overhead := be.cipherBS - be.plainBS -	nBlocks := (size + be.plainBS - 1) / be.plainBS -	size += nBlocks * overhead - -	return size -} - -func (be *CryptFS) minu64(x uint64, y uint64) uint64 { -	if x < y { -		return x -	} -	return y -} - -// CiphertextRange - Get byte range in backing ciphertext corresponding -// to plaintext range. Returns a range aligned to ciphertext blocks. -func (be *CryptFS) CiphertextRange(offset uint64, length uint64) (alignedOffset uint64, alignedLength uint64, skipBytes int) { -	// Decrypting the ciphertext will yield too many plaintext bytes. Skip this number -	// of bytes from the front. -	skip := offset % be.plainBS - -	firstBlockNo := offset / be.plainBS -	lastBlockNo := (offset + length - 1) / be.plainBS - -	alignedOffset = HEADER_LEN + firstBlockNo * be.cipherBS -	alignedLength = (lastBlockNo - firstBlockNo + 1) * be.cipherBS - -	skipBytes = int(skip) -	return alignedOffset, alignedLength, skipBytes -} - -// Get the byte range in the ciphertext corresponding to blocks -// (full blocks!) -func (be *CryptFS) JoinCiphertextRange(blocks []intraBlock) (uint64, uint64) { - -	offset, _ := blocks[0].CiphertextRange() -	last := blocks[len(blocks)-1] -	length := (last.BlockNo - blocks[0].BlockNo + 1) * be.cipherBS - -	return offset, length -} - -// Crop plaintext that correspons to complete cipher blocks down to what is -// requested according to "iblocks" -func (be *CryptFS) CropPlaintext(plaintext []byte, blocks []intraBlock) []byte { -	offset := blocks[0].Skip -	last := blocks[len(blocks)-1] -	length := (last.BlockNo - blocks[0].BlockNo + 1) * be.plainBS -	var cropped []byte -	if offset+length > uint64(len(plaintext)) { -		cropped = plaintext[offset:] -	} else { -		cropped = plaintext[offset : offset+length] -	} -	return cropped -} -  // MergeBlocks - Merge newData into oldData at offset  // New block may be bigger than both newData and oldData  func (be *CryptFS) MergeBlocks(oldData []byte, newData []byte, offset int) []byte { @@ -230,13 +125,3 @@ func (be *CryptFS) MergeBlocks(oldData []byte, newData []byte, offset int) []byt  	}  	return out[0:outLen]  } - -// Get the block number at plain-text offset -func (be *CryptFS) BlockNoPlainOff(plainOffset uint64) uint64 { -	return plainOffset / be.plainBS -} - -// Get the block number at ciphter-text offset -func (be *CryptFS) BlockNoCipherOff(cipherOffset uint64) uint64 { -	return (cipherOffset - HEADER_LEN) / be.cipherBS -} diff --git a/cryptfs/file_header.go b/cryptfs/file_header.go index 3fd7266..e16cbab 100644 --- a/cryptfs/file_header.go +++ b/cryptfs/file_header.go @@ -10,15 +10,15 @@ import (  )  const ( -	HEADER_CURRENT_VERSION  = 1 // Current on-disk-format version -	HEADER_VERSION_LEN = 2 // uint16 -	HEADER_ID_LEN = 16 // 128 bit random file id -	HEADER_LEN = HEADER_VERSION_LEN + HEADER_ID_LEN // Total header length +	HEADER_CURRENT_VERSION = 1                                  // Current on-disk-format version +	HEADER_VERSION_LEN     = 2                                  // uint16 +	HEADER_ID_LEN          = 16                                 // 128 bit random file id +	HEADER_LEN             = HEADER_VERSION_LEN + HEADER_ID_LEN // Total header length  )  type FileHeader struct {  	Version uint16 -	Id []byte +	Id      []byte  }  // Pack - serialize fileHeader object diff --git a/cryptfs/intrablock.go b/cryptfs/intrablock.go index c83976c..faff471 100644 --- a/cryptfs/intrablock.go +++ b/cryptfs/intrablock.go @@ -19,13 +19,13 @@ func (ib *intraBlock) IsPartial() bool {  // CiphertextRange - get byte range in ciphertext file corresponding to BlockNo  // (complete block)  func (ib *intraBlock) CiphertextRange() (offset uint64, length uint64) { -	return HEADER_LEN + ib.BlockNo * ib.fs.cipherBS, ib.fs.cipherBS +	return ib.fs.BlockNoToCipherOff(ib.BlockNo), ib.fs.cipherBS  }  // PlaintextRange - get byte range in plaintext corresponding to BlockNo  // (complete block)  func (ib *intraBlock) PlaintextRange() (offset uint64, length uint64) { -	return ib.BlockNo * ib.fs.plainBS, ib.fs.plainBS +	return ib.fs.BlockNoToPlainOff(ib.BlockNo), ib.fs.plainBS  }  // CropBlock - crop a potentially larger plaintext block down to the relevant part @@ -37,3 +37,15 @@ func (ib *intraBlock) CropBlock(d []byte) []byte {  	}  	return d[ib.Skip:lenWant]  } + +// Ciphertext range corresponding to the sum of all "blocks" (complete blocks) +func (ib *intraBlock) JointCiphertextRange(blocks []intraBlock) (offset uint64, length uint64) { +	firstBlock := blocks[0] +	lastBlock := blocks[len(blocks)-1] + +	offset = ib.fs.BlockNoToCipherOff(firstBlock.BlockNo) +	offsetLast := ib.fs.BlockNoToCipherOff(lastBlock.BlockNo) +	length = offsetLast + ib.fs.cipherBS - offset + +	return offset, length +} diff --git a/main_test.go b/main_test.go index 287c792..9262e6f 100644 --- a/main_test.go +++ b/main_test.go @@ -1,8 +1,6 @@  package main  import ( -	"runtime" -	"sync"  	"bytes"  	"crypto/md5"  	"encoding/hex" @@ -11,6 +9,8 @@ import (  	"io/ioutil"  	"os"  	"os/exec" +	"runtime" +	"sync"  	"testing"  ) @@ -121,7 +121,7 @@ func testWriteN(t *testing.T, fn string, n int) string {  	}  	file.Close() -	verifySize(t, plainDir + fn, n) +	verifySize(t, plainDir+fn, n)  	bin := md5.Sum(d)  	hashWant := hex.EncodeToString(bin[:]) @@ -244,12 +244,12 @@ func TestFileHoles(t *testing.T) {  }  func sContains(haystack []string, needle string) bool { -    for _, element := range haystack { -        if element == needle { -            return true -        } -    } -    return false +	for _, element := range haystack { +		if element == needle { +			return true +		} +	} +	return false  }  func TestRmwRace(t *testing.T) { @@ -313,10 +313,10 @@ func TestRmwRace(t *testing.T) {  		goodMd5[m] = goodMd5[m] + 1  		/* -		if m == "6c1660fdabccd448d1359f27b3db3c99" { -			fmt.Println(hex.Dump(buf)) -			t.FailNow() -		} +			if m == "6c1660fdabccd448d1359f27b3db3c99" { +				fmt.Println(hex.Dump(buf)) +				t.FailNow() +			}  		*/  	}  	fmt.Println(goodMd5) diff --git a/pathfs_frontend/file.go b/pathfs_frontend/file.go index 438fe77..e8d7003 100644 --- a/pathfs_frontend/file.go +++ b/pathfs_frontend/file.go @@ -128,8 +128,10 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {  	}  	// Read the backing ciphertext in one go -	alignedOffset, alignedLength, skip := f.cfs.CiphertextRange(off, length) -	cryptfs.Debug.Printf("CiphertextRange(%d, %d) -> %d, %d, %d\n", off, length, alignedOffset, alignedLength, skip) +	blocks := f.cfs.ExplodePlainRange(off, length) +	alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks) +	skip := blocks[0].Skip +	cryptfs.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d\n", off, length, alignedOffset, alignedLength, skip)  	ciphertext := make([]byte, int(alignedLength))  	f.fdLock.Lock()  	n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset)) @@ -141,27 +143,27 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {  	// Truncate ciphertext buffer down to actually read bytes  	ciphertext = ciphertext[0:n] -	blockNo := (alignedOffset - cryptfs.HEADER_LEN) / f.cfs.CipherBS() -	cryptfs.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d\n", alignedOffset, blockNo, alignedLength, n) +	firstBlockNo := blocks[0].BlockNo +	cryptfs.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d\n", alignedOffset, firstBlockNo, alignedLength, n)  	// Decrypt it -	plaintext, err := f.cfs.DecryptBlocks(ciphertext, blockNo, f.header.Id) +	plaintext, err := f.cfs.DecryptBlocks(ciphertext, firstBlockNo, f.header.Id)  	if err != nil { -		blockNo := (alignedOffset + uint64(len(plaintext))) / f.cfs.PlainBS() -		cipherOff := cryptfs.HEADER_LEN + blockNo * f.cfs.CipherBS() -		plainOff := blockNo * f.cfs.PlainBS() -		cryptfs.Warn.Printf("ino%d: doRead: corrupt block #%d (plainOff=%d/%d, cipherOff=%d/%d)\n", -			f.ino, blockNo, plainOff, f.cfs.PlainBS(), cipherOff, f.cfs.CipherBS()) +		curruptBlockNo := firstBlockNo + f.cfs.PlainOffToBlockNo(uint64(len(plaintext))) +		cipherOff := f.cfs.BlockNoToCipherOff(curruptBlockNo) +		plainOff := f.cfs.BlockNoToPlainOff(curruptBlockNo) +		cryptfs.Warn.Printf("ino%d: doRead: corrupt block #%d (plainOff=%d, cipherOff=%d)\n", +			f.ino, curruptBlockNo, plainOff, cipherOff)  		return nil, fuse.EIO  	}  	// Crop down to the relevant part  	var out []byte  	lenHave := len(plaintext) -	lenWant := skip + int(length) +	lenWant := int(skip + length)  	if lenHave > lenWant { -		out = plaintext[skip : skip+int(length)] -	} else if lenHave > skip { +		out = plaintext[skip:lenWant] +	} else if lenHave > int(skip) {  		out = plaintext[skip:lenHave]  	}  	// else: out stays empty, file was smaller than the requested offset @@ -216,7 +218,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {  	var written uint32  	status := fuse.OK  	dataBuf := bytes.NewBuffer(data) -	blocks := f.cfs.SplitRange(uint64(off), uint64(len(data))) +	blocks := f.cfs.ExplodePlainRange(uint64(off), uint64(len(data)))  	for _, b := range blocks {  		blockData := dataBuf.Next(int(b.Length)) @@ -239,11 +241,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {  		blockOffset, _ := b.CiphertextRange()  		blockData = f.cfs.EncryptBlock(blockData, b.BlockNo, f.header.Id)  		cryptfs.Debug.Printf("ino%d: Writing %d bytes to block #%d, md5=%s\n", -			f.ino, len(blockData) - cryptfs.BLOCK_OVERHEAD, b.BlockNo, cryptfs.Debug.Md5sum(blockData)) -		if len(blockData) != int(f.cfs.CipherBS()) { -			cryptfs.Debug.Printf("ino%d: Writing partial block #%d (%d bytes)\n", -				f.ino, b.BlockNo, len(blockData) - cryptfs.BLOCK_OVERHEAD) -		} +			f.ino, len(blockData)-cryptfs.BLOCK_OVERHEAD, b.BlockNo, cryptfs.Debug.Md5sum(blockData))  		f.fdLock.Lock()  		_, err := f.fd.WriteAt(blockData, int64(blockOffset))  		f.fdLock.Unlock() @@ -267,7 +265,7 @@ func (f *file) Write(data []byte, off int64) (uint32, fuse.Status) {  		cryptfs.Warn.Printf("Write: Fstat failed: %v\n", err)  		return 0, fuse.ToStatus(err)  	} -	plainSize := f.cfs.PlainSize(uint64(fi.Size())) +	plainSize := f.cfs.CipherSizeToPlainSize(uint64(fi.Size()))  	if f.createsHole(plainSize, off) {  		status := f.zeroPad(plainSize)  		if status != fuse.OK { @@ -332,7 +330,7 @@ func (f *file) Truncate(newSize uint64) fuse.Status {  		cryptfs.Warn.Printf("Truncate: Fstat failed: %v\n", err)  		return fuse.ToStatus(err)  	} -	oldSize := f.cfs.PlainSize(uint64(fi.Size())) +	oldSize := f.cfs.CipherSizeToPlainSize(uint64(fi.Size()))  	{  		oldB := float32(oldSize) / float32(f.cfs.PlainBS())  		newB := float32(newSize) / float32(f.cfs.PlainBS()) @@ -350,7 +348,7 @@ func (f *file) Truncate(newSize uint64) fuse.Status {  			}  		} -		blocks := f.cfs.SplitRange(oldSize, newSize-oldSize) +		blocks := f.cfs.ExplodePlainRange(oldSize, newSize-oldSize)  		for _, b := range blocks {  			// First and last block may be partial  			if b.IsPartial() { @@ -374,9 +372,9 @@ func (f *file) Truncate(newSize uint64) fuse.Status {  		return fuse.OK  	} else {  		// File shrinks -		blockNo := f.cfs.BlockNoPlainOff(newSize) -		cipherOff := cryptfs.HEADER_LEN + blockNo * f.cfs.CipherBS() -		plainOff := blockNo * f.cfs.PlainBS() +		blockNo := f.cfs.PlainOffToBlockNo(newSize) +		cipherOff := f.cfs.BlockNoToCipherOff(blockNo) +		plainOff := f.cfs.BlockNoToPlainOff(blockNo)  		lastBlockLen := newSize - plainOff  		var data []byte  		if lastBlockLen > 0 { @@ -430,7 +428,7 @@ func (f *file) GetAttr(a *fuse.Attr) fuse.Status {  		return fuse.ToStatus(err)  	}  	a.FromStat(&st) -	a.Size = f.cfs.PlainSize(a.Size) +	a.Size = f.cfs.CipherSizeToPlainSize(a.Size)  	return fuse.OK  } diff --git a/pathfs_frontend/file_holes.go b/pathfs_frontend/file_holes.go index f906aa6..3db4828 100644 --- a/pathfs_frontend/file_holes.go +++ b/pathfs_frontend/file_holes.go @@ -7,8 +7,8 @@ import (  // Will a write to offset "off" create a file hole?  func (f *file) createsHole(plainSize uint64, off int64) bool { -	nextBlock := f.cfs.BlockNoPlainOff(plainSize) -	targetBlock := f.cfs.BlockNoPlainOff(uint64(off)) +	nextBlock := f.cfs.PlainOffToBlockNo(plainSize) +	targetBlock := f.cfs.PlainOffToBlockNo(uint64(off))  	if targetBlock > nextBlock {  		return true  	} diff --git a/pathfs_frontend/fs.go b/pathfs_frontend/fs.go index eebc87b..3ec503f 100644 --- a/pathfs_frontend/fs.go +++ b/pathfs_frontend/fs.go @@ -41,7 +41,7 @@ func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Stat  		return a, status  	}  	if a.IsRegular() { -		a.Size = fs.PlainSize(a.Size) +		a.Size = fs.CipherSizeToPlainSize(a.Size)  	} else if a.IsSymlink() {  		target, _ := fs.Readlink(name, context)  		a.Size = uint64(len(target)) @@ -2,11 +2,10 @@  set -eux -cd cryptfs -go build -go test -cd .. +for i in ./cryptfs . +do -go build -go test +	go build $i +	go test $i +done  | 
