summaryrefslogtreecommitdiff
path: root/internal/fusefrontend_reverse_v1api/rfile.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/fusefrontend_reverse_v1api/rfile.go')
-rw-r--r--internal/fusefrontend_reverse_v1api/rfile.go209
1 files changed, 0 insertions, 209 deletions
diff --git a/internal/fusefrontend_reverse_v1api/rfile.go b/internal/fusefrontend_reverse_v1api/rfile.go
deleted file mode 100644
index 68f7309..0000000
--- a/internal/fusefrontend_reverse_v1api/rfile.go
+++ /dev/null
@@ -1,209 +0,0 @@
-package fusefrontend_reverse
-
-import (
- "bytes"
- "io"
- "os"
- "path/filepath"
- "syscall"
-
- // In newer Go versions, this has moved to just "sync/syncmap".
- "golang.org/x/sync/syncmap"
-
- "github.com/hanwen/go-fuse/v2/fuse"
- "github.com/hanwen/go-fuse/v2/fuse/nodefs"
-
- "github.com/rfjakob/gocryptfs/internal/contentenc"
- "github.com/rfjakob/gocryptfs/internal/pathiv"
- "github.com/rfjakob/gocryptfs/internal/syscallcompat"
- "github.com/rfjakob/gocryptfs/internal/tlog"
-)
-
-type reverseFile struct {
- // Embed nodefs.defaultFile for a ENOSYS implementation of all methods
- nodefs.File
- // Backing FD
- fd *os.File
- // File header (contains the IV)
- header contentenc.FileHeader
- // IV for block 0
- block0IV []byte
- // Content encryption helper
- contentEnc *contentenc.ContentEnc
-}
-
-var inodeTable syncmap.Map
-
-// newFile receives a ciphered path "relPath" and its corresponding
-// decrypted path "pRelPath", opens it and returns a reverseFile
-// object. The backing file descriptor is always read-only.
-func (rfs *ReverseFS) newFile(relPath string, pRelPath string) (*reverseFile, fuse.Status) {
- if rfs.isExcludedPlain(pRelPath) {
- // Excluded paths should have been filtered out beforehand. Better safe
- // than sorry.
- tlog.Warn.Printf("BUG: newFile: received excluded path %q. This should not happen.", relPath)
- return nil, fuse.ENOENT
- }
- dir := filepath.Dir(pRelPath)
- dirfd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, dir)
- if err != nil {
- return nil, fuse.ToStatus(err)
- }
- fd, err := syscallcompat.Openat(dirfd, filepath.Base(pRelPath), syscall.O_RDONLY|syscall.O_NOFOLLOW, 0)
- syscall.Close(dirfd)
- if err != nil {
- return nil, fuse.ToStatus(err)
- }
- var st syscall.Stat_t
- err = syscall.Fstat(fd, &st)
- if err != nil {
- tlog.Warn.Printf("newFile: Fstat error: %v", err)
- syscall.Close(fd)
- return nil, fuse.ToStatus(err)
- }
- // Reject access if the file descriptor does not refer to a regular file.
- var a fuse.Attr
- a.FromStat(&st)
- if !a.IsRegular() {
- tlog.Warn.Printf("ino%d: newFile: not a regular file", st.Ino)
- syscall.Close(fd)
- return nil, fuse.ToStatus(syscall.EACCES)
- }
- // See if we have that inode number already in the table
- // (even if Nlink has dropped to 1)
- var derivedIVs pathiv.FileIVs
- v, found := inodeTable.Load(st.Ino)
- if found {
- tlog.Debug.Printf("ino%d: newFile: found in the inode table", st.Ino)
- derivedIVs = v.(pathiv.FileIVs)
- } else {
- derivedIVs = pathiv.DeriveFile(relPath)
- // Nlink > 1 means there is more than one path to this file.
- // Store the derived values so we always return the same data,
- // regardless of the path that is used to access the file.
- // This means that the first path wins.
- if st.Nlink > 1 {
- v, found = inodeTable.LoadOrStore(st.Ino, derivedIVs)
- if found {
- // Another thread has stored a different value before we could.
- derivedIVs = v.(pathiv.FileIVs)
- } else {
- tlog.Debug.Printf("ino%d: newFile: Nlink=%d, stored in the inode table", st.Ino, st.Nlink)
- }
- }
- }
- header := contentenc.FileHeader{
- Version: contentenc.CurrentVersion,
- ID: derivedIVs.ID,
- }
- return &reverseFile{
- File: nodefs.NewDefaultFile(),
- fd: os.NewFile(uintptr(fd), pRelPath),
- header: header,
- block0IV: derivedIVs.Block0IV,
- contentEnc: rfs.contentEnc,
- }, fuse.OK
-}
-
-// GetAttr - FUSE call
-// Triggered by fstat() from userspace
-func (rf *reverseFile) GetAttr(*fuse.Attr) fuse.Status {
- tlog.Debug.Printf("reverseFile.GetAttr fd=%d\n", rf.fd.Fd())
- // The kernel should fall back to stat()
- return fuse.ENOSYS
-}
-
-// encryptBlocks - encrypt "plaintext" into a number of ciphertext blocks.
-// "plaintext" must already be block-aligned.
-func (rf *reverseFile) encryptBlocks(plaintext []byte, firstBlockNo uint64, fileID []byte, block0IV []byte) []byte {
- inBuf := bytes.NewBuffer(plaintext)
- var outBuf bytes.Buffer
- bs := int(rf.contentEnc.PlainBS())
- for blockNo := firstBlockNo; inBuf.Len() > 0; blockNo++ {
- inBlock := inBuf.Next(bs)
- iv := pathiv.BlockIV(block0IV, blockNo)
- outBlock := rf.contentEnc.EncryptBlockNonce(inBlock, blockNo, fileID, iv)
- outBuf.Write(outBlock)
- }
- return outBuf.Bytes()
-}
-
-// readBackingFile: read from the backing plaintext file, encrypt it, return the
-// ciphertext.
-// "off" ... ciphertext offset (must be >= HEADER_LEN)
-// "length" ... ciphertext length
-func (rf *reverseFile) readBackingFile(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)
- plaintext := make([]byte, int(alignedLength))
- n, err := rf.fd.ReadAt(plaintext, int64(alignedOffset))
- if err != nil && err != io.EOF {
- tlog.Warn.Printf("readBackingFile: ReadAt: %s", err.Error())
- return nil, err
- }
- // Truncate buffer down to actually read bytes
- plaintext = plaintext[0:n]
-
- // Encrypt blocks
- ciphertext := rf.encryptBlocks(plaintext, blocks[0].BlockNo, rf.header.ID, rf.block0IV)
-
- // Crop down to the relevant part
- lenHave := len(ciphertext)
- skip := blocks[0].Skip
- endWant := int(skip + length)
- if lenHave > endWant {
- out = ciphertext[skip:endWant]
- } else if lenHave > int(skip) {
- 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 header []byte
-
- // Synthesize file header
- if off < contentenc.HeaderLen {
- header = rf.header.Pack()
- // Truncate to requested part
- end := int(off) + len(buf)
- if end > len(header) {
- end = len(header)
- }
- header = header[off:end]
- // Write into output buffer and adjust offsets
- out.Write(header)
- hLen := uint64(len(header))
- off += hLen
- length -= hLen
- }
-
- // Read actual file data
- if length > 0 {
- fileData, err := rf.readBackingFile(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)
- }
-
- return fuse.ReadResultData(out.Bytes()), fuse.OK
-}
-
-// Release - FUSE call, close file
-func (rf *reverseFile) Release() {
- rf.fd.Close()
-}