summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/fusefrontend_reverse/rfile.go71
-rw-r--r--internal/fusefrontend_reverse/rfs.go54
2 files changed, 107 insertions, 18 deletions
diff --git a/internal/fusefrontend_reverse/rfile.go b/internal/fusefrontend_reverse/rfile.go
index 15f2764..d363df9 100644
--- a/internal/fusefrontend_reverse/rfile.go
+++ b/internal/fusefrontend_reverse/rfile.go
@@ -1,6 +1,7 @@
package fusefrontend_reverse
import (
+ "bytes"
"fmt"
"io"
"os"
@@ -12,10 +13,12 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog"
)
-var zeroFileId []byte
+// File header that contains an all-zero File ID
+var zeroFileHeader *contentenc.FileHeader
func init() {
- zeroFileId = make([]byte, 16)
+ zeroFileHeader = contentenc.RandomHeader()
+ zeroFileHeader.Id = make([]byte, contentenc.HEADER_ID_LEN)
}
type reverseFile struct {
@@ -41,12 +44,12 @@ func (rf *reverseFile) GetAttr(*fuse.Attr) fuse.Status {
return fuse.ENOSYS
}
-// Read - FUSE call
-func (rf *reverseFile) Read(buf []byte, off int64) (resultData fuse.ReadResult, status fuse.Status) {
- // TODO prefix file header
-
- length := uint64(len(buf))
- blocks := rf.contentEnc.ExplodeCipherRange(uint64(off), length)
+// readFile - read from the backing plaintext file, encrypt it, return the
+// ciphertext.
+// "off" ... ciphertext offset (must be >= HEADER_LEN)
+// "length" ... ciphertext length
+func (rf *reverseFile) readFile(off uint64, length uint64) (out []byte, err error) {
+ blocks := rf.contentEnc.ExplodeCipherRange(off, length)
// Read the backing plaintext in one go
alignedOffset, alignedLength := contentenc.JointPlaintextRange(blocks)
@@ -54,26 +57,62 @@ func (rf *reverseFile) Read(buf []byte, off int64) (resultData fuse.ReadResult,
plaintext := make([]byte, int(alignedLength))
n, err := rf.fd.ReadAt(plaintext, int64(alignedOffset))
if err != nil && err != io.EOF {
- tlog.Warn.Printf("reverseFile.Read: ReadAt: %s", err.Error())
- return nil, fuse.ToStatus(err)
+ tlog.Warn.Printf("reverseFile.readFile: ReadAt: %s", err.Error())
+ return nil, err
}
// Truncate buffer down to actually read bytes
plaintext = plaintext[0:n]
// Encrypt blocks
- ciphertext := rf.contentEnc.EncryptBlocks(plaintext, blocks[0].BlockNo, zeroFileId)
+ ciphertext := rf.contentEnc.EncryptBlocks(plaintext, blocks[0].BlockNo, zeroFileHeader.Id)
// Crop down to the relevant part
- var out []byte
lenHave := len(ciphertext)
skip := blocks[0].Skip
endWant := int(skip + length)
if lenHave > endWant {
- out = plaintext[skip:endWant]
+ out = ciphertext[skip:endWant]
} else if lenHave > int(skip) {
- out = plaintext[skip:lenHave]
+ out = ciphertext[skip:lenHave]
+ } // else: out stays empty, file was smaller than the requested offset
+
+ return out, nil
+}
+
+// Read - FUSE call
+func (rf *reverseFile) Read(buf []byte, ioff int64) (resultData fuse.ReadResult, status fuse.Status) {
+ length := uint64(len(buf))
+ off := uint64(ioff)
+ var out bytes.Buffer
+ var headerPart []byte
+
+ // Create a virtual file header
+ if off < contentenc.HEADER_LEN {
+ headerPart = zeroFileHeader.Pack()
+ headerPart = headerPart[off:]
+ if off+length < contentenc.HEADER_LEN {
+ headerPart = headerPart[:length]
+ }
+ }
+ out.Write(headerPart)
+ hLen := uint64(len(headerPart))
+ off += hLen
+ length -= hLen
+
+ // Read actual file data
+ if length > 0 {
+ fileData, err := rf.readFile(off, length)
+ if err != nil {
+ return nil, fuse.ToStatus(err)
+ }
+ if len(fileData) == 0 {
+ // If we could not read any actual data, we also don't want to
+ // return the file header. An empty file stays empty in encrypted
+ // form.
+ return nil, fuse.OK
+ }
+ out.Write(fileData)
}
- // else: out stays empty, file was smaller than the requested offset
- return fuse.ReadResultData(out), fuse.OK
+ return fuse.ReadResultData(out.Bytes()), fuse.OK
}
diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go
index 68a5ac8..3243dfd 100644
--- a/internal/fusefrontend_reverse/rfs.go
+++ b/internal/fusefrontend_reverse/rfs.go
@@ -1,7 +1,10 @@
package fusefrontend_reverse
import (
+ "fmt"
"os"
+ "path"
+ "strings"
"syscall"
"github.com/hanwen/go-fuse/fuse"
@@ -14,6 +17,10 @@ import (
"github.com/rfjakob/gocryptfs/internal/nametransform"
)
+const (
+ DirIVMode = syscall.S_IFREG | 0400
+)
+
type reverseFS struct {
// Embed pathfs.defaultFileSystem for a ENOSYS implementation of all methods
pathfs.FileSystem
@@ -44,6 +51,46 @@ func NewFS(args fusefrontend.Args) *reverseFS {
}
func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
+ // Handle gocryptfs.diriv
+ if relPath == nametransform.DirIVFilename ||
+ strings.HasSuffix(relPath, nametransform.DirIVFilename) {
+
+ fmt.Printf("Handling gocryptfs.diriv\n")
+
+ cDir := path.Dir(relPath)
+ if cDir == "." {
+ cDir = ""
+ }
+ dir, err := rfs.decryptPath(cDir)
+ if err != nil {
+ fmt.Printf("decrypt err %q\n", cDir)
+ return nil, fuse.ToStatus(err)
+ }
+ // Does the parent dir exist?
+ a, status := rfs.loopbackfs.GetAttr(dir, context)
+ if !status.Ok() {
+ fmt.Printf("missing parent\n")
+ return nil, status
+ }
+ // Is it a dir at all?
+ if !a.IsDir() {
+ fmt.Printf("not isdir\n")
+ return nil, fuse.ENOTDIR
+ }
+ // Does the user have execute permissions?
+ if a.Mode&syscall.S_IXUSR == 0 {
+ fmt.Printf("not exec")
+ return nil, fuse.EPERM
+ }
+ // All good. Let's fake the file.
+ // We use the inode number of the parent dir (can this cause problems?).
+ a.Mode = DirIVMode
+ a.Size = nametransform.DirIVLen
+ a.Nlink = 1
+
+ return a, fuse.OK
+ }
+
if rfs.isFiltered(relPath) {
return nil, fuse.EPERM
}
@@ -52,8 +99,8 @@ func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
return nil, fuse.ToStatus(err)
}
a, status := rfs.loopbackfs.GetAttr(relPath, context)
- if a == nil {
- return a, status
+ if !status.Ok() {
+ return nil, status
}
// Calculate encrypted file size
if a.IsRegular() {
@@ -105,5 +152,8 @@ func (rfs *reverseFS) OpenDir(relPath string, context *fuse.Context) ([]fuse.Dir
return nil, fuse.ToStatus(err)
}
}
+ // Add virtual gocryptfs.diriv
+ entries = append(entries, fuse.DirEntry{syscall.S_IFREG | 0400, nametransform.DirIVFilename})
+
return entries, fuse.OK
}