aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2015-09-05 20:30:20 +0200
committerJakob Unterwurzacher2015-09-05 20:30:20 +0200
commit11fb037e7e7d84405b3bed9de2aa500e49d9502d (patch)
treef94eea4b2cccc1620b51f163837d26082e6cae58
parent7e564f928fef80b0485210603a8aa58b14ca5109 (diff)
Cleanup and rename files
-rw-r--r--cryptfs/cryptfile.go226
-rw-r--r--cryptfs/cryptfs.go172
-rw-r--r--cryptfs/cryptfs_content.go94
-rw-r--r--cryptfs/cryptfs_names.go150
-rw-r--r--cryptfs/intrablock.go39
-rw-r--r--frontend/fe_checks.go (renamed from frontend/checks.go)0
-rw-r--r--frontend/fe_dir.go (renamed from frontend/dir.go)11
-rw-r--r--frontend/fe_file.go (renamed from frontend/file.go)11
-rw-r--r--frontend/fe_fs.go (renamed from frontend/fs.go)11
9 files changed, 318 insertions, 396 deletions
diff --git a/cryptfs/cryptfile.go b/cryptfs/cryptfile.go
deleted file mode 100644
index e5e9462..0000000
--- a/cryptfs/cryptfile.go
+++ /dev/null
@@ -1,226 +0,0 @@
-package cryptfs
-
-import (
- //"fmt"
- "os"
- //"io"
- "errors"
- "crypto/cipher"
-)
-
-type CryptFile struct {
- file *os.File
- gcm cipher.AEAD
-}
-
-// decryptBlock - Verify and decrypt GCM block
-func (be *CryptFS) DecryptBlock(ciphertext []byte) ([]byte, error) {
-
- // Empty block?
- if len(ciphertext) == 0 {
- return ciphertext, nil
- }
-
- if len(ciphertext) < NONCE_LEN {
- warn.Printf("decryptBlock: Block is too short: %d bytes\n", len(ciphertext))
- return nil, errors.New("Block is too short")
- }
-
- // Extract nonce
- nonce := ciphertext[:NONCE_LEN]
- ciphertext = ciphertext[NONCE_LEN:]
-
- // Decrypt
- var plaintext []byte
- plaintext, err := be.gcm.Open(plaintext, nonce, ciphertext, nil)
- if err != nil {
- return nil, err
- }
-
- return plaintext, nil
-}
-
-// encryptBlock - Encrypt and add MAC using GCM
-func (be *CryptFS) EncryptBlock(plaintext []byte) []byte {
-
- // Empty block?
- if len(plaintext) == 0 {
- return plaintext
- }
-
- // Get fresh nonce
- nonce := gcmNonce.Get()
-
- // Encrypt plaintext and append to nonce
- ciphertext := be.gcm.Seal(nonce, nonce, plaintext, nil)
-
- return ciphertext
-}
-
-/*
-// readCipherBlock - Read ciphertext block number "blockNo", decrypt,
-// return plaintext
-func (be *CryptFile) readCipherBlock(blockNo int64) ([]byte, error) {
- off := blockNo * int64(be.cipherBS)
- buf := make([]byte, be.cipherBS)
-
- readN, err := be.file.ReadAt(buf, off)
-
- if err != nil && err != io.EOF {
- return nil, err
- }
-
- // Truncate buffer to actually read bytes
- buf = buf[:readN]
-
- // Empty block?
- if len(buf) == 0 {
- return buf, nil
- }
-
- if len(buf) < NONCE_LEN {
- warn.Printf("readCipherBlock: Block is too short: %d bytes\n", len(buf))
- return nil, errors.New("Block is too short")
- }
-
- // Extract nonce
- nonce := buf[:NONCE_LEN]
- buf = buf[NONCE_LEN:]
-
- // Decrypt
- var plainBuf []byte
- plainBuf, err = be.gcm.Open(plainBuf, nonce, buf, nil)
- if err != nil {
- fmt.Printf("gcm.Open() failed: %d\n", err)
- return nil, err
- }
-
- return plainBuf, nil
-}
-*/
-
-// intraBlock identifies a part of a file block
-type intraBlock struct {
- BlockNo uint64 // Block number in file
- Offset uint64 // Offset into block plaintext
- Length uint64 // Length of data from this block
- fs *CryptFS
-}
-
-// isPartial - is the block partial? This means we have to do read-modify-write.
-func (ib *intraBlock) IsPartial() bool {
- if ib.Offset > 0 || ib.Length < ib.fs.plainBS {
- return true
- }
- return false
-}
-
-// CiphertextRange - get byte range in ciphertext file corresponding to BlockNo
-// (complete block)
-func (ib *intraBlock) CiphertextRange() (offset uint64, length uint64) {
- return ib.BlockNo * ib.fs.cipherBS, 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
-}
-
-// CropBlock - crop a potentially larger plaintext block down to the relevant part
-func (ib *intraBlock) CropBlock(d []byte) []byte{
- 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
-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.Offset = offset % be.plainBS
- b.Length = be.minu64(length, be.plainBS - b.Offset)
- parts = append(parts, b)
- offset += b.Length
- length -= b.Length
- }
- return parts
-}
-
-func (be *CryptFS) minu64(x uint64, y uint64) uint64 {
- if x < y {
- return x
- }
- return y
-}
-
-/*
-// writeCipherBlock - Encrypt plaintext and write it to file block "blockNo"
-func (be *CryptFile) writeCipherBlock(blockNo int64, plain []byte) error {
-
- if int64(len(plain)) > be.plainBS {
- panic("writeCipherBlock: Cannot write block that is larger than plainBS")
- }
-
- // Get fresh nonce
- nonce := gcmNonce.Get()
- // Encrypt data and append to nonce
- cipherBuf := be.gcm.Seal(nonce, nonce, plain, nil)
-
- // WriteAt retries short writes autmatically
- written, err := be.file.WriteAt(cipherBuf, blockNo * be.cipherBS)
-
- debug.Printf("writeCipherBlock: wrote %d ciphertext bytes to block %d\n",
- written, blockNo)
-
- return err
-}
-
-// Perform RMW cycle on block
-// Write "data" into file location specified in "b"
-func (be *CryptFile) rmwWrite(b intraBlock, data []byte, f *os.File) error {
- if b.Length != uint64(len(data)) {
- panic("Length mismatch")
- }
-
- oldBlock, err := be.readCipherBlock(b.BlockNo)
- if err != nil {
- return err
- }
- newBlockLen := b.Offset + b.Length
- debug.Printf("newBlockLen := %d + %d\n", b.Offset, b.Length)
- var newBlock []byte
-
- // Write goes beyond the old block and grows the file?
- // Must create a bigger newBlock
- if newBlockLen > uint64(len(oldBlock)) {
- newBlock = make([]byte, newBlockLen)
- } else {
- newBlock = make([]byte, len(oldBlock))
- }
-
- // Fill with old data
- copy(newBlock, oldBlock)
- // Then overwrite the relevant parts with new data
- copy(newBlock[b.Offset:b.Offset + b.Length], data)
-
- // Actual write
- err = be.writeCipherBlock(b.BlockNo, newBlock)
-
- if err != nil {
- // An incomplete write to a ciphertext block means that the whole block
- // is destroyed.
- fmt.Printf("rmwWrite: Write error: %s\n", err)
- }
-
- return err
-}
-*/
diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go
index d385a77..9ec511a 100644
--- a/cryptfs/cryptfs.go
+++ b/cryptfs/cryptfs.go
@@ -1,21 +1,16 @@
package cryptfs
+// CryptFS is the crypto backend of GoCryptFS
+
import (
"crypto/cipher"
"crypto/aes"
- "fmt"
- "strings"
- "encoding/base64"
- "errors"
)
const (
NONCE_LEN = 12
AUTH_TAG_LEN = 16
DEFAULT_PLAINBS = 4096
-
- ENCRYPT = true
- DECRYPT = false
)
type CryptFS struct {
@@ -45,169 +40,6 @@ func NewCryptFS(key [16]byte) *CryptFS {
}
}
-/*
-func (fs *CryptFS) NewFile(f *os.File) *CryptFile {
- return &CryptFile {
- file: f,
- gcm: fs.gcm,
- plainBS: fs.plainBS,
- cipherBS: fs.cipherBS,
- }
-}
-*/
-
func (be *CryptFS) PlainBS() uint64 {
return be.plainBS
}
-
-// DecryptName - decrypt filename
-func (be *CryptFS) decryptName(cipherName string) (string, error) {
-
- bin, err := base64.URLEncoding.DecodeString(cipherName)
- if err != nil {
- return "", err
- }
-
- if len(bin) % aes.BlockSize != 0 {
- return "", errors.New(fmt.Sprintf("Name len=%d is not a multiple of 16", len(bin)))
- }
-
- iv := make([]byte, aes.BlockSize) // TODO ?
- cbc := cipher.NewCBCDecrypter(be.blockCipher, iv)
- cbc.CryptBlocks(bin, bin)
-
- bin, err = be.unPad16(bin)
- if err != nil {
- return "", err
- }
-
- plain := string(bin)
- return plain, err
-}
-
-// EncryptName - encrypt filename
-func (be *CryptFS) encryptName(plainName string) string {
-
- bin := []byte(plainName)
- bin = be.pad16(bin)
-
- iv := make([]byte, 16) // TODO ?
- cbc := cipher.NewCBCEncrypter(be.blockCipher, iv)
- cbc.CryptBlocks(bin, bin)
-
- cipherName64 := base64.URLEncoding.EncodeToString(bin)
-
- return cipherName64
-}
-
-// TranslatePath - encrypt or decrypt path. Just splits the string on "/"
-// and hands the parts to EncryptName() / DecryptName()
-func (be *CryptFS) translatePath(path string, op bool) (string, error) {
- var err error
-
- // Empty string means root directory
- if path == "" {
- return path, err
- }
-
- // Run operation on each path component
- var translatedParts []string
- parts := strings.Split(path, "/")
- for _, part := range parts {
- var newPart string
- if op == ENCRYPT {
- newPart = be.encryptName(part)
- } else {
- newPart, err = be.decryptName(part)
- if err != nil {
- return "", err
- }
- }
- translatedParts = append(translatedParts, newPart)
- }
-
- return strings.Join(translatedParts, "/"), err
-}
-
-// EncryptPath - encrypt filename or path. Just hands it to TranslatePath().
-func (be *CryptFS) EncryptPath(path string) string {
- newPath, _ := be.translatePath(path, ENCRYPT)
- return newPath
-}
-
-// DecryptPath - decrypt filename or path. Just hands it to TranslatePath().
-func (be *CryptFS) DecryptPath(path string) (string, error) {
- return be.translatePath(path, DECRYPT)
-}
-
-// PlainSize - calculate plaintext size from ciphertext size
-func (be *CryptFS) PlainSize(size uint64) uint64 {
- // Zero sized files stay zero-sized
- if size > 0 {
- overhead := be.cipherBS - be.plainBS
- nBlocks := (size + be.cipherBS - 1) / be.cipherBS
- size -= nBlocks * overhead
- }
- return size
-}
-
-
-// plainSizeFromCipherSize - calculate plaintext size from ciphertext size
-func (be *CryptFS) plainSizeFromCipherSize(size int64) int64 {
- if size > 0 {
-
- }
- return size
-}
-// pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding
-// https://tools.ietf.org/html/rfc5652#section-6.3
-func (be *CryptFS) pad16(orig []byte) (padded []byte) {
- oldLen := len(orig)
- if oldLen == 0 {
- panic("Padding zero-length string makes no sense")
- }
- padLen := aes.BlockSize - oldLen % aes.BlockSize
- if padLen == 0 {
- padLen = aes.BlockSize
- }
- newLen := oldLen + padLen
- padded = make([]byte, newLen)
- copy(padded, orig)
- padByte := byte(padLen)
- for i := oldLen; i < newLen; i++ {
- padded[i] = padByte
- }
- return padded
-}
-
-// unPad16 - remove padding
-func (be *CryptFS) unPad16(orig []byte) ([]byte, error) {
- oldLen := len(orig)
- if oldLen % aes.BlockSize != 0 {
- return nil, errors.New("Unaligned size")
- }
- // The last byte is always a padding byte
- padByte := orig[oldLen -1]
- // The padding byte's value is the padding length
- padLen := int(padByte)
- // Padding must be at least 1 byte
- if padLen <= 0 {
- return nil, errors.New("Padding cannot be zero-length")
- }
- // Larger paddings make no sense
- if padLen > aes.BlockSize {
- return nil, errors.New("Padding cannot be larger than 16")
- }
- // All padding bytes must be identical
- for i := oldLen - padLen; i < oldLen; i++ {
- if orig[i] != padByte {
- return nil, errors.New(fmt.Sprintf("Padding byte at i=%d is invalid", i))
- }
- }
- newLen := oldLen - padLen
- // Padding an empty string makes no sense
- if newLen == 0 {
- return nil, errors.New("Unpadded length is zero")
- }
- return orig[0:newLen], nil
-}
diff --git a/cryptfs/cryptfs_content.go b/cryptfs/cryptfs_content.go
new file mode 100644
index 0000000..b3c7f95
--- /dev/null
+++ b/cryptfs/cryptfs_content.go
@@ -0,0 +1,94 @@
+package cryptfs
+
+// File content encryption / decryption
+
+import (
+ "os"
+ "errors"
+ "crypto/cipher"
+)
+
+type CryptFile struct {
+ file *os.File
+ gcm cipher.AEAD
+}
+
+// decryptBlock - Verify and decrypt GCM block
+func (be *CryptFS) DecryptBlock(ciphertext []byte) ([]byte, error) {
+
+ // Empty block?
+ if len(ciphertext) == 0 {
+ return ciphertext, nil
+ }
+
+ if len(ciphertext) < NONCE_LEN {
+ warn.Printf("decryptBlock: Block is too short: %d bytes\n", len(ciphertext))
+ return nil, errors.New("Block is too short")
+ }
+
+ // Extract nonce
+ nonce := ciphertext[:NONCE_LEN]
+ ciphertext = ciphertext[NONCE_LEN:]
+
+ // Decrypt
+ var plaintext []byte
+ plaintext, err := be.gcm.Open(plaintext, nonce, ciphertext, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return plaintext, nil
+}
+
+// encryptBlock - Encrypt and add MAC using GCM
+func (be *CryptFS) EncryptBlock(plaintext []byte) []byte {
+
+ // Empty block?
+ if len(plaintext) == 0 {
+ return plaintext
+ }
+
+ // Get fresh nonce
+ nonce := gcmNonce.Get()
+
+ // Encrypt plaintext and append to nonce
+ ciphertext := be.gcm.Seal(nonce, nonce, plaintext, nil)
+
+ return ciphertext
+}
+
+// Split a plaintext byte range into (possible 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.Offset = offset % be.plainBS
+ b.Length = be.minu64(length, be.plainBS - b.Offset)
+ 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 {
+ overhead := be.cipherBS - be.plainBS
+ nBlocks := (size + be.cipherBS - 1) / be.cipherBS
+ size -= nBlocks * overhead
+ }
+ return size
+}
+
+func (be *CryptFS) minu64(x uint64, y uint64) uint64 {
+ if x < y {
+ return x
+ }
+ return y
+}
diff --git a/cryptfs/cryptfs_names.go b/cryptfs/cryptfs_names.go
new file mode 100644
index 0000000..3dfcae9
--- /dev/null
+++ b/cryptfs/cryptfs_names.go
@@ -0,0 +1,150 @@
+package cryptfs
+
+// Filename encryption / decryption function
+
+import (
+ "crypto/cipher"
+ "crypto/aes"
+ "fmt"
+ "strings"
+ "encoding/base64"
+ "errors"
+)
+
+const (
+ ENCRYPT = true
+ DECRYPT = false
+)
+
+// DecryptName - decrypt filename
+func (be *CryptFS) decryptName(cipherName string) (string, error) {
+
+ bin, err := base64.URLEncoding.DecodeString(cipherName)
+ if err != nil {
+ return "", err
+ }
+
+ if len(bin) % aes.BlockSize != 0 {
+ return "", errors.New(fmt.Sprintf("Name len=%d is not a multiple of 16", len(bin)))
+ }
+
+ iv := make([]byte, aes.BlockSize) // TODO ?
+ cbc := cipher.NewCBCDecrypter(be.blockCipher, iv)
+ cbc.CryptBlocks(bin, bin)
+
+ bin, err = be.unPad16(bin)
+ if err != nil {
+ return "", err
+ }
+
+ plain := string(bin)
+ return plain, err
+}
+
+// EncryptName - encrypt filename
+func (be *CryptFS) encryptName(plainName string) string {
+
+ bin := []byte(plainName)
+ bin = be.pad16(bin)
+
+ iv := make([]byte, 16) // TODO ?
+ cbc := cipher.NewCBCEncrypter(be.blockCipher, iv)
+ cbc.CryptBlocks(bin, bin)
+
+ cipherName64 := base64.URLEncoding.EncodeToString(bin)
+
+ return cipherName64
+}
+
+// TranslatePath - encrypt or decrypt path. Just splits the string on "/"
+// and hands the parts to EncryptName() / DecryptName()
+func (be *CryptFS) translatePath(path string, op bool) (string, error) {
+ var err error
+
+ // Empty string means root directory
+ if path == "" {
+ return path, err
+ }
+
+ // Run operation on each path component
+ var translatedParts []string
+ parts := strings.Split(path, "/")
+ for _, part := range parts {
+ var newPart string
+ if op == ENCRYPT {
+ newPart = be.encryptName(part)
+ } else {
+ newPart, err = be.decryptName(part)
+ if err != nil {
+ return "", err
+ }
+ }
+ translatedParts = append(translatedParts, newPart)
+ }
+
+ return strings.Join(translatedParts, "/"), err
+}
+
+// EncryptPath - encrypt filename or path. Just hands it to TranslatePath().
+func (be *CryptFS) EncryptPath(path string) string {
+ newPath, _ := be.translatePath(path, ENCRYPT)
+ return newPath
+}
+
+// DecryptPath - decrypt filename or path. Just hands it to TranslatePath().
+func (be *CryptFS) DecryptPath(path string) (string, error) {
+ return be.translatePath(path, DECRYPT)
+}
+
+// pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding
+// https://tools.ietf.org/html/rfc5652#section-6.3
+func (be *CryptFS) pad16(orig []byte) (padded []byte) {
+ oldLen := len(orig)
+ if oldLen == 0 {
+ panic("Padding zero-length string makes no sense")
+ }
+ padLen := aes.BlockSize - oldLen % aes.BlockSize
+ if padLen == 0 {
+ padLen = aes.BlockSize
+ }
+ newLen := oldLen + padLen
+ padded = make([]byte, newLen)
+ copy(padded, orig)
+ padByte := byte(padLen)
+ for i := oldLen; i < newLen; i++ {
+ padded[i] = padByte
+ }
+ return padded
+}
+
+// unPad16 - remove padding
+func (be *CryptFS) unPad16(orig []byte) ([]byte, error) {
+ oldLen := len(orig)
+ if oldLen % aes.BlockSize != 0 {
+ return nil, errors.New("Unaligned size")
+ }
+ // The last byte is always a padding byte
+ padByte := orig[oldLen -1]
+ // The padding byte's value is the padding length
+ padLen := int(padByte)
+ // Padding must be at least 1 byte
+ if padLen <= 0 {
+ return nil, errors.New("Padding cannot be zero-length")
+ }
+ // Larger paddings make no sense
+ if padLen > aes.BlockSize {
+ return nil, errors.New("Padding cannot be larger than 16")
+ }
+ // All padding bytes must be identical
+ for i := oldLen - padLen; i < oldLen; i++ {
+ if orig[i] != padByte {
+ return nil, errors.New(fmt.Sprintf("Padding byte at i=%d is invalid", i))
+ }
+ }
+ newLen := oldLen - padLen
+ // Padding an empty string makes no sense
+ if newLen == 0 {
+ return nil, errors.New("Unpadded length is zero")
+ }
+ return orig[0:newLen], nil
+}
diff --git a/cryptfs/intrablock.go b/cryptfs/intrablock.go
new file mode 100644
index 0000000..78fd140
--- /dev/null
+++ b/cryptfs/intrablock.go
@@ -0,0 +1,39 @@
+package cryptfs
+
+// intraBlock identifies a part of a file block
+type intraBlock struct {
+ BlockNo uint64 // Block number in file
+ Offset uint64 // Offset into block plaintext
+ Length uint64 // Length of data from this block
+ fs *CryptFS
+}
+
+// isPartial - is the block partial? This means we have to do read-modify-write.
+func (ib *intraBlock) IsPartial() bool {
+ if ib.Offset > 0 || ib.Length < ib.fs.plainBS {
+ return true
+ }
+ return false
+}
+
+// CiphertextRange - get byte range in ciphertext file corresponding to BlockNo
+// (complete block)
+func (ib *intraBlock) CiphertextRange() (offset uint64, length uint64) {
+ return ib.BlockNo * ib.fs.cipherBS, 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
+}
+
+// CropBlock - crop a potentially larger plaintext block down to the relevant part
+func (ib *intraBlock) CropBlock(d []byte) []byte{
+ lenHave := len(d)
+ lenWant := int(ib.Offset+ib.Length)
+ if lenHave < lenWant {
+ return d[ib.Offset:lenHave]
+ }
+ return d[ib.Offset:lenWant]
+}
diff --git a/frontend/checks.go b/frontend/fe_checks.go
index 30720f6..30720f6 100644
--- a/frontend/checks.go
+++ b/frontend/fe_checks.go
diff --git a/frontend/dir.go b/frontend/fe_dir.go
index ee5c474..75b665b 100644
--- a/frontend/dir.go
+++ b/frontend/fe_dir.go
@@ -1,5 +1,16 @@
package frontend
+// frontend sits between FUSE and ClueFS
+// and uses cryptfs for all crypto operations
+//
+// cryptfs
+// ^
+// |
+// v
+// FUSE <-> frontend <-> ClueFS
+//
+// This file handles directories
+
import (
"fmt"
"github.com/rfjakob/gocryptfs/cryptfs"
diff --git a/frontend/file.go b/frontend/fe_file.go
index ef74491..4bd623f 100644
--- a/frontend/file.go
+++ b/frontend/fe_file.go
@@ -1,5 +1,16 @@
package frontend
+// frontend sits between FUSE and ClueFS
+// and uses cryptfs for all crypto operations
+//
+// cryptfs
+// ^
+// |
+// v
+// FUSE <-> frontend <-> ClueFS
+//
+// This file handles files access
+
import (
"fmt"
"github.com/rfjakob/gocryptfs/cryptfs"
diff --git a/frontend/fs.go b/frontend/fe_fs.go
index 83d1953..470095f 100644
--- a/frontend/fs.go
+++ b/frontend/fe_fs.go
@@ -1,5 +1,16 @@
package frontend
+// frontend sits between FUSE and ClueFS
+// and uses cryptfs for all crypto operations
+//
+// cryptfs
+// ^
+// |
+// v
+// FUSE <-> frontend <-> ClueFS
+//
+// This file handles just the root directory
+
import (
"fmt"
"github.com/rfjakob/gocryptfs/cryptfs"