diff options
Diffstat (limited to 'pathfs_frontend')
| -rw-r--r-- | pathfs_frontend/file.go | 101 | 
1 files changed, 94 insertions, 7 deletions
| diff --git a/pathfs_frontend/file.go b/pathfs_frontend/file.go index e3be7ff..fe346b6 100644 --- a/pathfs_frontend/file.go +++ b/pathfs_frontend/file.go @@ -51,7 +51,11 @@ func (f *file) String() string {  	return fmt.Sprintf("cryptFile(%s)", f.fd.Name())  } -// Called by Read() and for RMW in Write() +// doRead - returns "length" plaintext bytes from plaintext offset "offset". +// Reads the corresponding ciphertext from disk, decryptfs it, returns the relevant +// part. +// +// Called by Read(), for RMW in Write() and Truncate()  func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {  	// Read the backing ciphertext in one go @@ -117,7 +121,7 @@ func (f *file) Write(data []byte, off int64) (uint32, fuse.Status) {  	cryptfs.Debug.Printf("Write %s: offset=%d length=%d\n", f.fd.Name(), off, len(data))  	var written uint32 -	var status fuse.Status +	status := fuse.OK  	dataBuf := bytes.NewBuffer(data)  	blocks := f.cfs.SplitRange(uint64(off), uint64(len(data)))  	for _, b := range(blocks) { @@ -189,12 +193,95 @@ func (f *file) Fsync(flags int) (code fuse.Status) {  	return r  } -func (f *file) Truncate(size uint64) fuse.Status { -	f.lock.Lock() -	r := fuse.ToStatus(syscall.Ftruncate(int(f.fd.Fd()), int64(size))) -	f.lock.Unlock() +func (f *file) Truncate(newSize uint64) fuse.Status { -	return r +	// Common case: Truncate to zero +	if newSize == 0 { +		f.lock.Lock() +		err := syscall.Ftruncate(int(f.fd.Fd()), 0) +		f.lock.Unlock() +		return fuse.ToStatus(err) +	} + +	// We need the old file size to determine if we are growing or shrinking +	// the file +	fi, err := f.fd.Stat() +	if err != nil { +		cryptfs.Warn.Printf("Truncate: fstat failed: %v\n", err) +		return fuse.ToStatus(err) +	} +	oldSize := uint64(fi.Size()) + +	// Grow file by appending zeros +	if newSize > oldSize { +		remaining := newSize - oldSize +		offset := oldSize +		var zeros []byte +		// Append a maximum of 1MB in each iteration +		if remaining > 1048576 { +			zeros = make([]byte, 1048576) +		} else { +			zeros = make([]byte, remaining) +		} +		for remaining >= uint64(len(zeros)) { +			written, status := f.Write(zeros, int64(offset)) +			if status != fuse.OK { +				return status +			} +			remaining -= uint64(written) +			offset += uint64(written) +			cryptfs.Debug.Printf("Truncate: written=%d remaining=%d offset=%d\n", +				written, remaining, offset) +		} +		if remaining > 0 { +			_, status := f.Write(zeros[0:remaining], int64(offset)) +			return status +		} +		return fuse.OK +	} + +	// Shrink file by truncating +	newBlockLen := int(newSize % f.cfs.PlainBS()) +	// New file size is aligned to block size - just truncate +	if newBlockLen == 0 { +		cSize := int64(f.cfs.CipherSize(newSize)) +		f.lock.Lock() +		err := syscall.Ftruncate(int(f.fd.Fd()), cSize) +		f.lock.Unlock() +		return fuse.ToStatus(err) +	} +	// New file size is not aligned - need to do RMW on the last block +	var blockOffset, blockLen uint64 +	{ +		// Get the block the last byte belongs to. +		// This is, by definition, the last block. +		blockList := f.cfs.SplitRange(newSize - 1, 1) +		lastBlock := blockList[0] +		blockOffset, blockLen = lastBlock.PlaintextRange() +	} +	blockData, status := f.doRead(blockOffset, blockLen) +	if status != fuse.OK { +		cryptfs.Warn.Printf("Truncate: doRead failed: %v\n", err) +		return status +	} +	if len(blockData) < newBlockLen { +		cryptfs.Warn.Printf("Truncate: file has shrunk under our feet\n") +		return fuse.OK +	} +	// Truncate the file down to the next block +	{ +		nextBlockSz := int64(f.cfs.CipherSize(newSize - uint64(newBlockLen))) +		f.lock.Lock() +		err = syscall.Ftruncate(int(f.fd.Fd()), nextBlockSz) +		f.lock.Unlock() +		if err != nil { +			cryptfs.Warn.Printf("Truncate: Intermediate Ftruncate failed: %v\n", err) +			return fuse.ToStatus(err) +		} +	} +	// Append truncated last block +	_, status = f.Write(blockData[0:newBlockLen], int64(blockOffset)) +	return status  }  func (f *file) Chmod(mode uint32) fuse.Status { | 
