diff options
Diffstat (limited to 'internal/fusefrontend_reverse_v1api/rfile.go')
-rw-r--r-- | internal/fusefrontend_reverse_v1api/rfile.go | 209 |
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() -} |