diff options
| -rw-r--r-- | cryptfs/cryptfile.go | 18 | ||||
| -rw-r--r-- | cryptfs/cryptfs.go | 4 | ||||
| -rw-r--r-- | frontend/dir.go | 3 | ||||
| -rw-r--r-- | frontend/file.go | 75 | 
4 files changed, 91 insertions, 9 deletions
| diff --git a/cryptfs/cryptfile.go b/cryptfs/cryptfile.go index 0cd11da..98b6d12 100644 --- a/cryptfs/cryptfile.go +++ b/cryptfs/cryptfile.go @@ -115,14 +115,26 @@ func (ib *intraBlock) IsPartial() bool {  	return false  } -// ciphertextRange - get byte range in ciphertext file corresponding to BlockNo +// CiphertextRange - get byte range in ciphertext file corresponding to BlockNo +// (complete block)  func (ib *intraBlock) CiphertextRange() (offset int64, length int64) {  	return ib.BlockNo * ib.fs.cipherBS, ib.fs.cipherBS  } -// CropBlock - crop a full plaintext block down to the relevant part +// PlaintextRange - get byte range in plaintext corresponding to BlockNo +// (complete block) +func (ib *intraBlock) PlaintextRange() (offset int64, length int64) { +	return ib.BlockNo * ib.fs.plainBS, ib.fs.plainBS +} + +// CropBlock - crop a potentially larger plaintext block down to the relevant part  func (ib *intraBlock) CropBlock(d []byte) []byte{ -	return d[ib.Offset:ib.Offset+ib.Length] +	lenHave := len(d) +	lenWant := int(ib.Offset+ib.Length) +	if lenHave < lenWant { +		return d[ib.Offset:lenHave] +	} +	return d[ib.Offset:lenWant]  }  // Split a plaintext byte range into (possible partial) blocks diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go index f5781e2..72eea61 100644 --- a/cryptfs/cryptfs.go +++ b/cryptfs/cryptfs.go @@ -55,6 +55,10 @@ func (fs *CryptFS) NewFile(f *os.File) *CryptFile {  	}  } +func (be *CryptFS) PlainBS() int64 { +	return be.plainBS +} +  // DecryptName - decrypt filename  func (be *CryptFS) decryptName(cipherName string) (string, error) { diff --git a/frontend/dir.go b/frontend/dir.go index ce64a5a..46be0db 100644 --- a/frontend/dir.go +++ b/frontend/dir.go @@ -103,6 +103,9 @@ func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {  func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fusefs.Node, fusefs.Handle, error) {  	fmt.Printf("Create\n") + +	req.Flags, _ = fixFlags(req.Flags) +  	req.Name = d.crfs.EncryptPath(req.Name)  	n, _, err := d.Dir.Create(ctx, req, resp)  	if err != nil { diff --git a/frontend/file.go b/frontend/file.go index c5a02be..8014753 100644 --- a/frontend/file.go +++ b/frontend/file.go @@ -10,13 +10,40 @@ import (  	"golang.org/x/net/context"  ) +func fixFlags(flags fuse.OpenFlags) (fuse.OpenFlags, bool) { +	fmt.Printf("fixFlags: Before: %s\n", flags.String()) +	var writeOnly bool +	// We always need read access to do read-modify-write cycles +	if flags & fuse.OpenWriteOnly > 0 { +		flags = flags &^ fuse.OpenWriteOnly +		flags = flags | fuse.OpenReadWrite +		writeOnly = true +	} +	// We also cannot open the file in append mode, we need to seek back for RMW +	flags = flags &^ fuse.OpenAppend +	fmt.Printf("fixFlags: After: %s\n", flags.String()) +	return flags, writeOnly +} + +func max(x int, y int) int { +	if x > y { +		return x +	} +	return y +} +  type File struct {  	*cluefs.File  	crfs *cryptfs.CryptFS +	// Remember if the file is supposed to be write-only +	writeOnly bool  }  func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fusefs.Handle, error) { -	fmt.Printf("File.Open: f.crfs=%p\n", f.crfs) +	fmt.Printf("File.Open\n") + +	req.Flags, f.writeOnly = fixFlags(req.Flags) +  	h, err := f.File.Open(ctx, req, resp)  	if err != nil {  		return nil, err @@ -29,32 +56,68 @@ func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenR  }  func (f *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { -	fmt.Printf("File.Read: f.crfs=%p\n", f.crfs)  	iblocks := f.crfs.SplitRange(req.Offset, int64(req.Size))  	for _, ib := range iblocks {  		var partReq fuse.ReadRequest  		var partResp fuse.ReadResponse  		o, l := ib.CiphertextRange() +		partReq.Offset = o  		partReq.Size = int(l)  		partResp.Data = make([]byte, int(l)) -		partReq.Offset = o  		err := f.File.Read(ctx, &partReq, &partResp)  		if err != nil {  			return err  		}  		plaintext, err := f.crfs.DecryptBlock(partResp.Data)  		if err != nil { -			fmt.Printf("Read: Block %d: %s\n", ib.BlockNo, err.Error()) +			fmt.Printf("Read: Error reading block %d: %s\n", ib.BlockNo, err.Error())  			return err  		} -		plaintext = ib.CropBlock(partResp.Data) +		plaintext = ib.CropBlock(plaintext)  		resp.Data = append(resp.Data, plaintext...)  	}  	return nil  }  func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { +	fmt.Printf("File.Write\n") +	resp.Size = 0  	iblocks := f.crfs.SplitRange(req.Offset, int64(len(req.Data))) -	iblocks = iblocks +	var blockData []byte +	for _, ib := range iblocks { +		if ib.IsPartial() { +			// RMW +			blockData = make([]byte, f.crfs.PlainBS()) +			var readReq fuse.ReadRequest +			var readResp fuse.ReadResponse +			o, l := ib.PlaintextRange() +			readReq.Offset = o +			readReq.Size = int(l) +			err := f.Read(ctx, &readReq, &readResp) +			if err != nil { +				return err +			} +			copy(blockData, readResp.Data) +			copy(blockData[ib.Offset:ib.Offset+ib.Length], req.Data) +			blockLen := max(len(readResp.Data), int(ib.Offset+ib.Length)) +			blockData = blockData[0:blockLen] +		} else { +			blockData = req.Data[0:f.crfs.PlainBS()] +		} +		ciphertext := f.crfs.EncryptBlock(blockData) +		var partReq fuse.WriteRequest +		var partResp fuse.WriteResponse +		o, _ := ib.CiphertextRange() +		partReq.Data = ciphertext +		partReq.Offset = o +		err := f.File.Write(ctx, &partReq, &partResp) +		if err != nil { +			fmt.Printf("Write failure: %s\n", err.Error()) +			return err +		} +		// Remove written data from the front of the request +		req.Data = req.Data[len(blockData):len(req.Data)] +		resp.Size += len(blockData) +	}  	return nil  } | 
