From fcb28e4ff35e6bc262bfda23f3779ef3a8e7708a Mon Sep 17 00:00:00 2001
From: Jakob Unterwurzacher
Date: Thu, 15 Oct 2020 23:18:21 +0200
Subject: v2pai: delete fusefrontend_reverse_v1api

Served its mission a copy-paste source but can now be deleted.
---
 .../ctlsock_interface.go                           |  38 --
 internal/fusefrontend_reverse_v1api/excluder.go    |  66 ---
 .../fusefrontend_reverse_v1api/excluder_test.go    |  84 ----
 internal/fusefrontend_reverse_v1api/mocks_test.go  |  32 --
 .../reverse_longnames.go                           | 126 ------
 internal/fusefrontend_reverse_v1api/rfile.go       | 209 ---------
 internal/fusefrontend_reverse_v1api/rfs.go         | 472 ---------------------
 internal/fusefrontend_reverse_v1api/rfs_test.go    | 124 ------
 internal/fusefrontend_reverse_v1api/rpath.go       | 113 -----
 internal/fusefrontend_reverse_v1api/rpath_cache.go |  45 --
 internal/fusefrontend_reverse_v1api/virtualfile.go | 110 -----
 11 files changed, 1419 deletions(-)
 delete mode 100644 internal/fusefrontend_reverse_v1api/ctlsock_interface.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/excluder.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/excluder_test.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/mocks_test.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/reverse_longnames.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/rfile.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/rfs.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/rfs_test.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/rpath.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/rpath_cache.go
 delete mode 100644 internal/fusefrontend_reverse_v1api/virtualfile.go

(limited to 'internal/fusefrontend_reverse_v1api')

diff --git a/internal/fusefrontend_reverse_v1api/ctlsock_interface.go b/internal/fusefrontend_reverse_v1api/ctlsock_interface.go
deleted file mode 100644
index f7b8afd..0000000
--- a/internal/fusefrontend_reverse_v1api/ctlsock_interface.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"path/filepath"
-	"strings"
-
-	"golang.org/x/sys/unix"
-
-	"github.com/rfjakob/gocryptfs/internal/ctlsocksrv"
-	"github.com/rfjakob/gocryptfs/internal/pathiv"
-)
-
-var _ ctlsocksrv.Interface = &ReverseFS{} // Verify that interface is implemented.
-
-// EncryptPath implements ctlsock.Backend.
-// This is used for the control socket and for the "-exclude" logic.
-func (rfs *ReverseFS) EncryptPath(plainPath string) (string, error) {
-	if rfs.args.PlaintextNames || plainPath == "" {
-		return plainPath, nil
-	}
-	cipherPath := ""
-	parts := strings.Split(plainPath, "/")
-	for _, part := range parts {
-		dirIV := pathiv.Derive(cipherPath, pathiv.PurposeDirIV)
-		encryptedPart := rfs.nameTransform.EncryptName(part, dirIV)
-		if rfs.args.LongNames && len(encryptedPart) > unix.NAME_MAX {
-			encryptedPart = rfs.nameTransform.HashLongName(encryptedPart)
-		}
-		cipherPath = filepath.Join(cipherPath, encryptedPart)
-	}
-	return cipherPath, nil
-}
-
-// DecryptPath implements ctlsock.Backend
-func (rfs *ReverseFS) DecryptPath(cipherPath string) (string, error) {
-	p, err := rfs.decryptPath(cipherPath)
-	return p, err
-}
diff --git a/internal/fusefrontend_reverse_v1api/excluder.go b/internal/fusefrontend_reverse_v1api/excluder.go
deleted file mode 100644
index c44816c..0000000
--- a/internal/fusefrontend_reverse_v1api/excluder.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"io/ioutil"
-	"os"
-	"strings"
-
-	"github.com/rfjakob/gocryptfs/internal/exitcodes"
-	"github.com/rfjakob/gocryptfs/internal/fusefrontend"
-	"github.com/rfjakob/gocryptfs/internal/tlog"
-
-	"github.com/sabhiram/go-gitignore"
-)
-
-// prepareExcluder creates an object to check if paths are excluded
-// based on the patterns specified in the command line.
-func (rfs *ReverseFS) prepareExcluder(args fusefrontend.Args) {
-	if len(args.Exclude) > 0 || len(args.ExcludeWildcard) > 0 || len(args.ExcludeFrom) > 0 {
-		excluder, err := ignore.CompileIgnoreLines(getExclusionPatterns(args)...)
-		if err != nil {
-			tlog.Fatal.Printf("Error compiling exclusion rules: %q", err)
-			os.Exit(exitcodes.ExcludeError)
-		}
-		rfs.excluder = excluder
-	}
-}
-
-// getExclusionPatters prepares a list of patterns to be excluded.
-// Patterns passed in the -exclude command line option are prefixed
-// with a leading '/' to preserve backwards compatibility (before
-// wildcard matching was implemented, exclusions always were matched
-// against the full path).
-func getExclusionPatterns(args fusefrontend.Args) []string {
-	patterns := make([]string, len(args.Exclude)+len(args.ExcludeWildcard))
-	// add -exclude
-	for i, p := range args.Exclude {
-		patterns[i] = "/" + p
-	}
-	// add -exclude-wildcard
-	copy(patterns[len(args.Exclude):], args.ExcludeWildcard)
-	// add -exclude-from
-	for _, file := range args.ExcludeFrom {
-		lines, err := getLines(file)
-		if err != nil {
-			tlog.Fatal.Printf("Error reading exclusion patterns: %q", err)
-			os.Exit(exitcodes.ExcludeError)
-		}
-		patterns = append(patterns, lines...)
-	}
-	return patterns
-}
-
-// getLines reads a file and splits it into lines
-func getLines(file string) ([]string, error) {
-	buffer, err := ioutil.ReadFile(file)
-	if err != nil {
-		return nil, err
-	}
-	return strings.Split(string(buffer), "\n"), nil
-}
-
-// isExcludedPlain finds out if the plaintext path "pPath" is
-// excluded (used when -exclude is passed by the user).
-func (rfs *ReverseFS) isExcludedPlain(pPath string) bool {
-	return rfs.excluder != nil && rfs.excluder.MatchesPath(pPath)
-}
diff --git a/internal/fusefrontend_reverse_v1api/excluder_test.go b/internal/fusefrontend_reverse_v1api/excluder_test.go
deleted file mode 100644
index e19b99f..0000000
--- a/internal/fusefrontend_reverse_v1api/excluder_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"io/ioutil"
-	"os"
-	"reflect"
-	"testing"
-
-	"github.com/rfjakob/gocryptfs/internal/fusefrontend"
-)
-
-func TestShouldNoCreateExcluderIfNoPattersWereSpecified(t *testing.T) {
-	var rfs ReverseFS
-	var args fusefrontend.Args
-	rfs.prepareExcluder(args)
-	if rfs.excluder != nil {
-		t.Error("Should not have created excluder")
-	}
-}
-
-func TestShouldPrefixExcludeValuesWithSlash(t *testing.T) {
-	var args fusefrontend.Args
-	args.Exclude = []string{"file1", "dir1/file2.txt"}
-	args.ExcludeWildcard = []string{"*~", "build/*.o"}
-
-	expected := []string{"/file1", "/dir1/file2.txt", "*~", "build/*.o"}
-
-	patterns := getExclusionPatterns(args)
-	if !reflect.DeepEqual(patterns, expected) {
-		t.Errorf("expected %q, got %q", expected, patterns)
-	}
-}
-
-func TestShouldReadExcludePatternsFromFiles(t *testing.T) {
-	tmpfile1, err := ioutil.TempFile("", "excludetest")
-	if err != nil {
-		t.Fatal(err)
-	}
-	exclude1 := tmpfile1.Name()
-	defer os.Remove(exclude1)
-	defer tmpfile1.Close()
-
-	tmpfile2, err := ioutil.TempFile("", "excludetest")
-	if err != nil {
-		t.Fatal(err)
-	}
-	exclude2 := tmpfile2.Name()
-	defer os.Remove(exclude2)
-	defer tmpfile2.Close()
-
-	tmpfile1.WriteString("file1.1\n")
-	tmpfile1.WriteString("file1.2\n")
-	tmpfile2.WriteString("file2.1\n")
-	tmpfile2.WriteString("file2.2\n")
-
-	var args fusefrontend.Args
-	args.ExcludeWildcard = []string{"cmdline1"}
-	args.ExcludeFrom = []string{exclude1, exclude2}
-
-	// An empty string is returned for the last empty line
-	// It's ignored when the patterns are actually compiled
-	expected := []string{"cmdline1", "file1.1", "file1.2", "", "file2.1", "file2.2", ""}
-
-	patterns := getExclusionPatterns(args)
-	if !reflect.DeepEqual(patterns, expected) {
-		t.Errorf("expected %q, got %q", expected, patterns)
-	}
-}
-
-func TestShouldReturnFalseIfThereAreNoExclusions(t *testing.T) {
-	var rfs ReverseFS
-	if rfs.isExcludedPlain("any/path") {
-		t.Error("Should not exclude any path if no exclusions were specified")
-	}
-}
-
-func TestShouldCallIgnoreParserToCheckExclusion(t *testing.T) {
-	rfs, ignorerMock := createRFSWithMocks()
-
-	rfs.isExcludedPlain("some/path")
-	if ignorerMock.calledWith != "some/path" {
-		t.Error("Failed to call IgnoreParser")
-	}
-}
diff --git a/internal/fusefrontend_reverse_v1api/mocks_test.go b/internal/fusefrontend_reverse_v1api/mocks_test.go
deleted file mode 100644
index fd2f940..0000000
--- a/internal/fusefrontend_reverse_v1api/mocks_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"github.com/rfjakob/gocryptfs/internal/nametransform"
-)
-
-type IgnoreParserMock struct {
-	toExclude  string
-	calledWith string
-}
-
-func (parser *IgnoreParserMock) MatchesPath(f string) bool {
-	parser.calledWith = f
-	return f == parser.toExclude
-}
-
-type NameTransformMock struct {
-	nametransform.NameTransform
-}
-
-func (n *NameTransformMock) DecryptName(cipherName string, iv []byte) (string, error) {
-	return "mockdecrypt_" + cipherName, nil
-}
-
-func createRFSWithMocks() (*ReverseFS, *IgnoreParserMock) {
-	ignorerMock := &IgnoreParserMock{}
-	nameTransformMock := &NameTransformMock{}
-	var rfs ReverseFS
-	rfs.excluder = ignorerMock
-	rfs.nameTransform = nameTransformMock
-	return &rfs, ignorerMock
-}
diff --git a/internal/fusefrontend_reverse_v1api/reverse_longnames.go b/internal/fusefrontend_reverse_v1api/reverse_longnames.go
deleted file mode 100644
index f1576d2..0000000
--- a/internal/fusefrontend_reverse_v1api/reverse_longnames.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"log"
-	"path/filepath"
-	"sync"
-	"syscall"
-	"time"
-
-	"golang.org/x/sys/unix"
-
-	"github.com/hanwen/go-fuse/v2/fuse"
-	"github.com/hanwen/go-fuse/v2/fuse/nodefs"
-
-	"github.com/rfjakob/gocryptfs/internal/nametransform"
-	"github.com/rfjakob/gocryptfs/internal/pathiv"
-	"github.com/rfjakob/gocryptfs/internal/syscallcompat"
-	"github.com/rfjakob/gocryptfs/internal/tlog"
-)
-
-const (
-	// File names are padded to 16-byte multiples, encrypted and
-	// base64-encoded. We can encode at most 176 bytes to stay below the 255
-	// bytes limit:
-	// * base64(176 bytes) = 235 bytes
-	// * base64(192 bytes) = 256 bytes (over 255!)
-	// But the PKCS#7 padding is at least one byte. This means we can only use
-	// 175 bytes for the file name.
-	shortNameMax = 175
-)
-
-// longnameParentCache maps dir+"/"+longname to plaintextname.
-// Yes, the combination of relative plaintext dir path and encrypted
-// longname is strange, but works fine as a map index.
-var longnameParentCache map[string]string
-var longnameCacheLock sync.Mutex
-
-// Very simple cache cleaner: Nuke it every hour
-func longnameCacheCleaner() {
-	for {
-		time.Sleep(time.Hour)
-		longnameCacheLock.Lock()
-		longnameParentCache = map[string]string{}
-		longnameCacheLock.Unlock()
-	}
-}
-
-func initLongnameCache() {
-	if longnameParentCache != nil {
-		return
-	}
-	longnameParentCache = map[string]string{}
-	go longnameCacheCleaner()
-}
-
-// findLongnameParent converts "longname" = "gocryptfs.longname.XYZ" to the
-// plaintext name. "dir" = relative plaintext path to the directory the
-// longname file is in, "dirIV" = directory IV of the directory.
-func (rfs *ReverseFS) findLongnameParent(dir string, dirIV []byte, longname string) (plaintextName string, err error) {
-	longnameCacheLock.Lock()
-	hit := longnameParentCache[dir+"/"+longname]
-	longnameCacheLock.Unlock()
-	if hit != "" {
-		return hit, nil
-	}
-	dirfd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, filepath.Dir(dir))
-	if err != nil {
-		tlog.Warn.Printf("findLongnameParent: OpenDirNofollow failed: %v\n", err)
-		return "", err
-	}
-	fd, err := syscallcompat.Openat(dirfd, filepath.Base(dir), syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
-	syscall.Close(dirfd)
-	if err != nil {
-		tlog.Warn.Printf("findLongnameParent: Openat failed: %v\n", err)
-		return "", err
-	}
-	dirEntries, err := syscallcompat.Getdents(fd)
-	syscall.Close(fd)
-	if err != nil {
-		tlog.Warn.Printf("findLongnameParent: Getdents failed: %v\n", err)
-		return "", err
-	}
-	longnameCacheLock.Lock()
-	defer longnameCacheLock.Unlock()
-	for _, entry := range dirEntries {
-		plaintextName := entry.Name
-		if len(plaintextName) <= shortNameMax {
-			continue
-		}
-		cName := rfs.nameTransform.EncryptName(plaintextName, dirIV)
-		if len(cName) <= unix.NAME_MAX {
-			// Entry should have been skipped by the "continue" above
-			log.Panic("logic error or wrong shortNameMax constant?")
-		}
-		hName := rfs.nameTransform.HashLongName(cName)
-		longnameParentCache[dir+"/"+hName] = plaintextName
-		if longname == hName {
-			hit = plaintextName
-		}
-	}
-	if hit == "" {
-		return "", syscall.ENOENT
-	}
-	return hit, nil
-}
-
-func (rfs *ReverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) {
-	dotName := filepath.Base(relPath)                       // gocryptfs.longname.XYZ.name
-	longname := nametransform.RemoveLongNameSuffix(dotName) // gocryptfs.longname.XYZ
-	// cipher directory
-	cDir := nametransform.Dir(relPath)
-	// plain directory
-	pDir, err := rfs.decryptPath(cDir)
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	dirIV := pathiv.Derive(cDir, pathiv.PurposeDirIV)
-	// plain name
-	pName, err := rfs.findLongnameParent(pDir, dirIV, longname)
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	content := []byte(rfs.nameTransform.EncryptName(pName, dirIV))
-	parentFile := filepath.Join(pDir, pName)
-	return rfs.newVirtualFile(content, rfs.args.Cipherdir, parentFile, inoTagNameFile)
-}
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()
-}
diff --git a/internal/fusefrontend_reverse_v1api/rfs.go b/internal/fusefrontend_reverse_v1api/rfs.go
deleted file mode 100644
index 9f7d1f6..0000000
--- a/internal/fusefrontend_reverse_v1api/rfs.go
+++ /dev/null
@@ -1,472 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"fmt"
-	"path/filepath"
-	"syscall"
-
-	"golang.org/x/sys/unix"
-
-	"github.com/hanwen/go-fuse/v2/fuse"
-	"github.com/hanwen/go-fuse/v2/fuse/nodefs"
-	"github.com/hanwen/go-fuse/v2/fuse/pathfs"
-
-	"github.com/rfjakob/gocryptfs/internal/configfile"
-	"github.com/rfjakob/gocryptfs/internal/contentenc"
-	"github.com/rfjakob/gocryptfs/internal/cryptocore"
-	"github.com/rfjakob/gocryptfs/internal/fusefrontend"
-	"github.com/rfjakob/gocryptfs/internal/inomap"
-	"github.com/rfjakob/gocryptfs/internal/nametransform"
-	"github.com/rfjakob/gocryptfs/internal/pathiv"
-	"github.com/rfjakob/gocryptfs/internal/syscallcompat"
-	"github.com/rfjakob/gocryptfs/internal/tlog"
-
-	"github.com/sabhiram/go-gitignore"
-)
-
-// ReverseFS implements the pathfs.FileSystem interface and provides an
-// encrypted view of a plaintext directory.
-type ReverseFS struct {
-	// Embed pathfs.defaultFileSystem for a ENOSYS implementation of all methods
-	pathfs.FileSystem
-	// pathfs.loopbackFileSystem, see go-fuse/fuse/pathfs/loopback.go
-	loopbackfs pathfs.FileSystem
-	// Stores configuration arguments
-	args fusefrontend.Args
-	// Filename encryption helper
-	nameTransform nametransform.NameTransformer
-	// Content encryption helper
-	contentEnc *contentenc.ContentEnc
-	// Tests whether a path is excluded (hiden) from the user. Used by -exclude.
-	excluder ignore.IgnoreParser
-	// inoMap translates inode numbers from different devices to unique inode
-	// numbers.
-	inoMap *inomap.InoMap
-}
-
-var _ pathfs.FileSystem = &ReverseFS{}
-
-// NewFS returns an encrypted FUSE overlay filesystem.
-// In this case (reverse mode) the backing directory is plain-text and
-// ReverseFS provides an encrypted view.
-func NewFS(args fusefrontend.Args, c *contentenc.ContentEnc, n nametransform.NameTransformer) *ReverseFS {
-	initLongnameCache()
-	fs := &ReverseFS{
-		// pathfs.defaultFileSystem returns ENOSYS for all operations
-		FileSystem:    pathfs.NewDefaultFileSystem(),
-		loopbackfs:    pathfs.NewLoopbackFileSystem(args.Cipherdir),
-		args:          args,
-		nameTransform: n,
-		contentEnc:    c,
-		inoMap:        inomap.New(),
-	}
-	fs.prepareExcluder(args)
-	return fs
-}
-
-// relDir is identical to filepath.Dir excepts that it returns "" when
-// filepath.Dir would return ".".
-// In the FUSE API, the root directory is called "", and we actually want that.
-func relDir(path string) string {
-	dir := filepath.Dir(path)
-	if dir == "." {
-		return ""
-	}
-	return dir
-}
-
-// getFileInfo returns information on a ciphertext path "relPath":
-// - ftype: file type (as returned by getFileType)
-// - excluded: if the path is excluded
-// - pPath: if it's not a special file, the decrypted path
-// - err: non nil if any error happens
-func (rfs *ReverseFS) getFileInfo(relPath string) (ftype fileType, excluded bool, pPath string, err error) {
-	ftype = rfs.getFileType(relPath)
-	if ftype == typeConfig {
-		excluded, pPath, err = false, "", nil
-		return
-	}
-	if ftype == typeDiriv {
-		parentDir := nametransform.Dir(relPath)
-		_, excluded, _, err = rfs.getFileInfo(parentDir)
-		pPath = ""
-		return
-	}
-	if ftype == typeName {
-		parentDir := nametransform.Dir(relPath)
-		var parentExcluded bool
-		_, parentExcluded, _, err = rfs.getFileInfo(parentDir)
-		if parentExcluded || err != nil {
-			excluded, pPath = parentExcluded, ""
-			return
-		}
-		relPath = nametransform.RemoveLongNameSuffix(relPath)
-	}
-	pPath, err = rfs.decryptPath(relPath)
-	excluded = err == nil && rfs.isExcludedPlain(pPath)
-	return
-}
-
-type fileType int
-
-// Values returned by getFileType
-const (
-	// A regular file/directory/symlink
-	typeRegular fileType = iota
-	// A DirIV (gocryptfs.diriv) file
-	typeDiriv
-	// A gocryptfs.longname.*.name file for a file with a long name
-	typeName
-	// The config file gocryptfs.conf
-	typeConfig
-)
-
-// getFileType returns the type of file (one of the fileType constants above).
-func (rfs *ReverseFS) getFileType(cPath string) fileType {
-	if !rfs.args.PlaintextNames {
-		cName := filepath.Base(cPath)
-		// Is it a gocryptfs.diriv file?
-		if cName == nametransform.DirIVFilename {
-			return typeDiriv
-		}
-		// Is it a gocryptfs.longname.*.name file?
-		if t := nametransform.NameType(cName); t == nametransform.LongNameFilename {
-			return typeName
-		}
-	}
-	if rfs.isTranslatedConfig(cPath) {
-		return typeConfig
-	}
-	return typeRegular
-}
-
-// isTranslatedConfig returns true if the default config file name is in use
-// and the ciphertext path is "gocryptfs.conf".
-// "gocryptfs.conf" then maps to ".gocryptfs.reverse.conf" in the plaintext
-// directory.
-func (rfs *ReverseFS) isTranslatedConfig(relPath string) bool {
-	if rfs.args.ConfigCustom {
-		return false
-	}
-	if relPath == configfile.ConfDefaultName {
-		return true
-	}
-	return false
-}
-
-// GetAttr - FUSE call
-// "relPath" is the relative ciphertext path
-func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
-	ftype, excluded, pPath, err := rfs.getFileInfo(relPath)
-	if excluded {
-		return nil, fuse.ENOENT
-	}
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	// Handle "gocryptfs.conf"
-	if ftype == typeConfig {
-		absConfPath, _ := rfs.abs(configfile.ConfReverseName, nil)
-		var st syscall.Stat_t
-		err = syscall.Lstat(absConfPath, &st)
-		if err != nil {
-			return nil, fuse.ToStatus(err)
-		}
-		rfs.inoMap.TranslateStat(&st)
-		var a fuse.Attr
-		a.FromStat(&st)
-		if rfs.args.ForceOwner != nil {
-			a.Owner = *rfs.args.ForceOwner
-		}
-		return &a, fuse.OK
-	}
-	// Handle virtual files (gocryptfs.diriv, *.name)
-	var f nodefs.File
-	var status fuse.Status
-	virtual := false
-	if ftype == typeDiriv {
-		virtual = true
-		f, status = rfs.newDirIVFile(relPath)
-	}
-	if ftype == typeName {
-		virtual = true
-		f, status = rfs.newNameFile(relPath)
-	}
-	if virtual {
-		if !status.Ok() {
-			tlog.Warn.Printf("GetAttr %q: newXFile failed: %v\n", relPath, status)
-			return nil, status
-		}
-		var a fuse.Attr
-		status = f.GetAttr(&a)
-		if rfs.args.ForceOwner != nil {
-			a.Owner = *rfs.args.ForceOwner
-		}
-		return &a, status
-	}
-	// Normal file / directory
-	dirfd, name, err := rfs.openBackingDir(pPath)
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	// Stat the backing file/dir using Fstatat
-	var st syscall.Stat_t
-	{
-		var st2 unix.Stat_t
-		err = syscallcompat.Fstatat(dirfd, name, &st2, unix.AT_SYMLINK_NOFOLLOW)
-		syscall.Close(dirfd)
-		if err != nil {
-			return nil, fuse.ToStatus(err)
-		}
-		st = syscallcompat.Unix2syscall(st2)
-	}
-	rfs.inoMap.TranslateStat(&st)
-	var a fuse.Attr
-	a.FromStat(&st)
-	// Calculate encrypted file size
-	if a.IsRegular() {
-		a.Size = rfs.contentEnc.PlainSizeToCipherSize(a.Size)
-	} else if a.IsSymlink() {
-		var linkTarget string
-		var readlinkStatus fuse.Status
-
-		linkTarget, readlinkStatus = rfs.Readlink(relPath, context)
-		if !readlinkStatus.Ok() {
-			return nil, readlinkStatus
-		}
-
-		a.Size = uint64(len(linkTarget))
-	}
-	if rfs.args.ForceOwner != nil {
-		a.Owner = *rfs.args.ForceOwner
-	}
-	return &a, fuse.OK
-}
-
-// Access - FUSE call
-func (rfs *ReverseFS) Access(relPath string, mode uint32, context *fuse.Context) fuse.Status {
-	ftype, excluded, pPath, err := rfs.getFileInfo(relPath)
-	if excluded {
-		return fuse.ENOENT
-	}
-	if err != nil {
-		return fuse.ToStatus(err)
-	}
-	if ftype != typeRegular {
-		// access(2) R_OK flag for checking if the file is readable, always 4 as defined in POSIX.
-		ROK := uint32(0x4)
-		// Virtual files can always be read and never written
-		if mode == ROK || mode == 0 {
-			return fuse.OK
-		}
-		return fuse.EPERM
-	}
-	dirfd, name, err := rfs.openBackingDir(pPath)
-	if err != nil {
-		return fuse.ToStatus(err)
-	}
-	err = syscallcompat.Faccessat(dirfd, name, mode)
-	syscall.Close(dirfd)
-	return fuse.ToStatus(err)
-}
-
-// Open - FUSE call
-func (rfs *ReverseFS) Open(relPath string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
-	ftype, excluded, pPath, err := rfs.getFileInfo(relPath)
-	if excluded {
-		return nil, fuse.ENOENT
-	}
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	if ftype == typeConfig {
-		return rfs.loopbackfs.Open(configfile.ConfReverseName, flags, context)
-	}
-	if ftype == typeDiriv {
-		return rfs.newDirIVFile(relPath)
-	}
-	if ftype == typeName {
-		return rfs.newNameFile(relPath)
-	}
-	return rfs.newFile(relPath, pPath)
-}
-
-func (rfs *ReverseFS) openDirPlaintextnames(relPath string, entries []fuse.DirEntry) ([]fuse.DirEntry, fuse.Status) {
-	if relPath != "" || rfs.args.ConfigCustom {
-		return entries, fuse.OK
-	}
-	// We are in the root dir and the default config file name
-	// ".gocryptfs.reverse.conf" is used. We map it to "gocryptfs.conf".
-	dupe := -1
-	status := fuse.OK
-	for i := range entries {
-		if entries[i].Name == configfile.ConfReverseName {
-			entries[i].Name = configfile.ConfDefaultName
-		} else if entries[i].Name == configfile.ConfDefaultName {
-			dupe = i
-		}
-	}
-	if dupe >= 0 {
-		// Warn the user loudly: The gocryptfs.conf_NAME_COLLISION file will
-		// throw ENOENT errors that are hard to miss.
-		tlog.Warn.Printf("The file %q is mapped to %q and shadows another file. Please rename %q in directory %q.",
-			configfile.ConfReverseName, configfile.ConfDefaultName, configfile.ConfDefaultName, rfs.args.Cipherdir)
-		entries[dupe].Name = "gocryptfs.conf_NAME_COLLISION_" + fmt.Sprintf("%d", cryptocore.RandUint64())
-	}
-	return entries, status
-}
-
-// OpenDir - FUSE readdir call
-func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
-	ftype, excluded, relPath, err := rfs.getFileInfo(cipherPath)
-	if excluded {
-		return nil, fuse.ENOENT
-	}
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	if ftype != typeRegular {
-		return nil, fuse.ENOTDIR
-	}
-	// Read plaintext dir
-	dirfd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, filepath.Dir(relPath))
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	fd, err := syscallcompat.Openat(dirfd, filepath.Base(relPath), syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
-	syscall.Close(dirfd)
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	entries, err := syscallcompat.Getdents(fd)
-	syscall.Close(fd)
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	if rfs.args.PlaintextNames {
-		var status fuse.Status
-		entries, status = rfs.openDirPlaintextnames(cipherPath, entries)
-		if !status.Ok() {
-			return nil, status
-		}
-		entries = rfs.excludeDirEntries(relPath, entries)
-		return entries, fuse.OK
-	}
-	// Filter out excluded entries
-	entries = rfs.excludeDirEntries(relPath, entries)
-	// Allocate maximum possible number of virtual files.
-	// If all files have long names we need a virtual ".name" file for each,
-	// plus one for gocryptfs.diriv.
-	virtualFiles := make([]fuse.DirEntry, len(entries)+1)
-	// Virtual gocryptfs.diriv file
-	virtualFiles[0] = fuse.DirEntry{
-		Mode: virtualFileMode,
-		Name: nametransform.DirIVFilename,
-	}
-	// Actually used entries
-	nVirtual := 1
-
-	// Encrypt names
-	dirIV := pathiv.Derive(cipherPath, pathiv.PurposeDirIV)
-	for i := range entries {
-		var cName string
-		// ".gocryptfs.reverse.conf" in the root directory is mapped to "gocryptfs.conf"
-		if cipherPath == "" && entries[i].Name == configfile.ConfReverseName &&
-			!rfs.args.ConfigCustom {
-			cName = configfile.ConfDefaultName
-		} else {
-			cName = rfs.nameTransform.EncryptName(entries[i].Name, dirIV)
-			if len(cName) > unix.NAME_MAX {
-				cName = rfs.nameTransform.HashLongName(cName)
-				dotNameFile := fuse.DirEntry{
-					Mode: virtualFileMode,
-					Name: cName + nametransform.LongNameSuffix,
-				}
-				virtualFiles[nVirtual] = dotNameFile
-				nVirtual++
-			}
-		}
-		entries[i].Name = cName
-	}
-	// Add virtual files
-	entries = append(entries, virtualFiles[:nVirtual]...)
-	return entries, fuse.OK
-}
-
-// excludeDirEntries filters out directory entries that are "-exclude"d.
-// pDir is the relative plaintext path to the directory these entries are
-// from. The entries should be plaintext files.
-func (rfs *ReverseFS) excludeDirEntries(pDir string, entries []fuse.DirEntry) (filtered []fuse.DirEntry) {
-	if rfs.excluder == nil {
-		return entries
-	}
-	filtered = make([]fuse.DirEntry, 0, len(entries))
-	for _, entry := range entries {
-		// filepath.Join handles the case of pDir="" correctly:
-		// Join("", "foo") -> "foo". This does not: pDir + "/" + name"
-		p := filepath.Join(pDir, entry.Name)
-		if rfs.isExcludedPlain(p) {
-			// Skip file
-			continue
-		}
-		filtered = append(filtered, entry)
-	}
-	return filtered
-}
-
-// StatFs - FUSE call. Returns information about the filesystem (free space
-// etc).
-// Securing statfs against symlink races seems to be more trouble than
-// it's worth, so we just ignore the path and always return info about the
-// backing storage root dir.
-func (rfs *ReverseFS) StatFs(relPath string) *fuse.StatfsOut {
-	_, excluded, _, err := rfs.getFileInfo(relPath)
-	if excluded || err != nil {
-		return nil
-	}
-	var s syscall.Statfs_t
-	err = syscall.Statfs(rfs.args.Cipherdir, &s)
-	if err != nil {
-		return nil
-	}
-	out := &fuse.StatfsOut{}
-	out.FromStatfsT(&s)
-	return out
-}
-
-// Readlink - FUSE call
-func (rfs *ReverseFS) Readlink(relPath string, context *fuse.Context) (string, fuse.Status) {
-	ftype, excluded, pPath, err := rfs.getFileInfo(relPath)
-	if excluded {
-		return "", fuse.ENOENT
-	}
-	if err != nil {
-		return "", fuse.ToStatus(err)
-	}
-	if ftype != typeRegular {
-		return "", fuse.EINVAL
-	}
-	dirfd, name, err := rfs.openBackingDir(pPath)
-	if err != nil {
-		return "", fuse.ToStatus(err)
-	}
-	// read the link target using Readlinkat
-	plainTarget, err := syscallcompat.Readlinkat(dirfd, name)
-	syscall.Close(dirfd)
-	if err != nil {
-		return "", fuse.ToStatus(err)
-	}
-	if rfs.args.PlaintextNames {
-		return plainTarget, fuse.OK
-	}
-	nonce := pathiv.Derive(relPath, pathiv.PurposeSymlinkIV)
-	// Symlinks are encrypted like file contents and base64-encoded
-	cBinTarget := rfs.contentEnc.EncryptBlockNonce([]byte(plainTarget), 0, nil, nonce)
-	cTarget := rfs.nameTransform.B64EncodeToString(cBinTarget)
-	// The kernel will reject a symlink target above 4096 chars and return
-	// and I/O error to the user. Better emit the proper error ourselves.
-	if len(cTarget) > syscallcompat.PATH_MAX {
-		return "", fuse.Status(syscall.ENAMETOOLONG)
-	}
-	return cTarget, fuse.OK
-}
diff --git a/internal/fusefrontend_reverse_v1api/rfs_test.go b/internal/fusefrontend_reverse_v1api/rfs_test.go
deleted file mode 100644
index eccaab9..0000000
--- a/internal/fusefrontend_reverse_v1api/rfs_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"testing"
-
-	"github.com/rfjakob/gocryptfs/internal/configfile"
-	"github.com/rfjakob/gocryptfs/internal/nametransform"
-)
-
-func TestShouldDetectDirIV(t *testing.T) {
-	var rfs ReverseFS
-	ftype := rfs.getFileType("some/path/" + nametransform.DirIVFilename)
-	if ftype != typeDiriv {
-		t.Errorf("Expecting %d, got %d\n", typeDiriv, ftype)
-	}
-}
-
-func TestShouldDetectNameFile(t *testing.T) {
-	var rfs ReverseFS
-	ftype := rfs.getFileType("dir1/dir2/gocryptfs.longname.URrM8kgxTKYMgCk4hKk7RO9Lcfr30XQof4L_5bD9Iro=" + nametransform.LongNameSuffix)
-	if ftype != typeName {
-		t.Errorf("Expecting %d, got %d\n", typeName, ftype)
-	}
-}
-
-func TestShouldDetectConfigFile(t *testing.T) {
-	var rfs ReverseFS
-	ftype := rfs.getFileType(configfile.ConfDefaultName)
-	if ftype != typeConfig {
-		t.Errorf("Expecting %d, got %d\n", typeConfig, ftype)
-	}
-}
-
-func TestShouldDetectRegularFile(t *testing.T) {
-	var rfs ReverseFS
-	ftype := rfs.getFileType("documents/text_file.txt")
-	if ftype != typeRegular {
-		t.Errorf("Expecting %d, got %d\n", typeRegular, ftype)
-	}
-}
-
-// Note: For path exclusion, see also the integration tests in
-// tests/reverse/exclude_test.go
-func TestShouldNotCallIgnoreParserForTranslatedConfig(t *testing.T) {
-	rfs, ignorerMock := createRFSWithMocks()
-
-	ftype, excluded, _, err := rfs.getFileInfo(configfile.ConfDefaultName)
-	if err != nil {
-		t.Errorf("Unexpected error %q\n", err)
-	}
-	if ftype != typeConfig {
-		t.Errorf("Wrong file type, expecting %d, got %d\n", typeConfig, ftype)
-	}
-	if excluded {
-		t.Error("Should not exclude translated config")
-	}
-	if ignorerMock.calledWith != "" {
-		t.Error("Should not call IgnoreParser for translated config")
-	}
-}
-
-func TestShouldCheckIfParentIsExcludedForDirIV(t *testing.T) {
-	rfs, ignorerMock := createRFSWithMocks()
-	path := "dir"
-	ignorerMock.toExclude = "mockdecrypt_dir"
-	dirIV := path + "/" + nametransform.DirIVFilename
-
-	ftype, excluded, _, err := rfs.getFileInfo(dirIV)
-	if err != nil {
-		t.Errorf("Unexpected error %q\n", err)
-	}
-	if ftype != typeDiriv {
-		t.Errorf("Wrong file type, expecting %d, got %d\n", typeDiriv, ftype)
-	}
-	if !excluded {
-		t.Error("Should have excluded DirIV based on parent")
-	}
-	if ignorerMock.calledWith != "mockdecrypt_dir" {
-		t.Errorf("Should have checked parent dir, checked %q", ignorerMock.calledWith)
-	}
-}
-
-func TestShouldCheckIfParentIsExcludedForLongName(t *testing.T) {
-	rfs, ignorerMock := createRFSWithMocks()
-	path := "parent"
-	ignorerMock.toExclude = "mockdecrypt_parent"
-	dirIV := path + "/" + "gocryptfs.longname.fake.name"
-
-	ftype, excluded, _, err := rfs.getFileInfo(dirIV)
-	if err != nil {
-		t.Errorf("Unexpected error %q\n", err)
-	}
-	if ftype != typeName {
-		t.Errorf("Wrong file type, expecting %d, got %d\n", typeName, ftype)
-	}
-	if !excluded {
-		t.Error("Should have excluded LongName based on parent")
-	}
-	if ignorerMock.calledWith != "mockdecrypt_parent" {
-		t.Errorf("Should have checked parent dir, checked %q", ignorerMock.calledWith)
-	}
-}
-
-func TestShouldDecryptPathAndReturnTrueForExcludedPath(t *testing.T) {
-	rfs, ignorerMock := createRFSWithMocks()
-	ignorerMock.toExclude = "mockdecrypt_file.txt"
-
-	ftype, excluded, pPath, err := rfs.getFileInfo("file.txt")
-	if err != nil {
-		t.Errorf("Unexpected error %q\n", err)
-	}
-	if ftype != typeRegular {
-		t.Errorf("Wrong file type, expecting %d, got %d\n", typeRegular, ftype)
-	}
-	if !excluded {
-		t.Error("Should have excluded")
-	}
-	if pPath != "mockdecrypt_file.txt" {
-		t.Errorf("Wrong pPath returned, got %q\n", pPath)
-	}
-	if ignorerMock.calledWith != "mockdecrypt_file.txt" {
-		t.Error("Didn't call IgnoreParser with decrypted path")
-	}
-}
diff --git a/internal/fusefrontend_reverse_v1api/rpath.go b/internal/fusefrontend_reverse_v1api/rpath.go
deleted file mode 100644
index 2da8379..0000000
--- a/internal/fusefrontend_reverse_v1api/rpath.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"encoding/base64"
-	"path/filepath"
-	"strings"
-	"syscall"
-
-	"github.com/rfjakob/gocryptfs/internal/nametransform"
-	"github.com/rfjakob/gocryptfs/internal/pathiv"
-	"github.com/rfjakob/gocryptfs/internal/syscallcompat"
-	"github.com/rfjakob/gocryptfs/internal/tlog"
-)
-
-// abs basically returns storage dir + "/" + relPath.
-// It takes an error parameter so it can directly wrap decryptPath like this:
-// a, err := rfs.abs(rfs.decryptPath(relPath))
-// abs never generates an error on its own. In other words, abs(p, nil) never
-// fails.
-func (rfs *ReverseFS) abs(relPath string, err error) (string, error) {
-	if err != nil {
-		return "", err
-	}
-	return filepath.Join(rfs.args.Cipherdir, relPath), nil
-}
-
-// rDecryptName decrypts the ciphertext name "cName", given the dirIV of the
-// directory "cName" lies in. The relative plaintext path to the directory
-// "pDir" is used if a "gocryptfs.longname.XYZ.name" must be resolved.
-func (rfs *ReverseFS) rDecryptName(cName string, dirIV []byte, pDir string) (pName string, err error) {
-	nameType := nametransform.NameType(cName)
-	if nameType == nametransform.LongNameNone {
-		pName, err = rfs.nameTransform.DecryptName(cName, dirIV)
-		if err != nil {
-			// We get lots of decrypt requests for names like ".Trash" that
-			// are invalid base64. Convert them to ENOENT so the correct
-			// error gets returned to the user.
-			if _, ok := err.(base64.CorruptInputError); ok {
-				return "", syscall.ENOENT
-			}
-			// Stat attempts on the link target of encrypted symlinks.
-			// These are always valid base64 but the length is not a
-			// multiple of 16.
-			if err == syscall.EBADMSG {
-				return "", syscall.ENOENT
-			}
-			return "", err
-		}
-	} else if nameType == nametransform.LongNameContent {
-		pName, err = rfs.findLongnameParent(pDir, dirIV, cName)
-		if err != nil {
-			return "", err
-		}
-	} else {
-		// It makes no sense to decrypt a ".name" file. This is a virtual file
-		// that has no representation in the plaintext filesystem. ".name"
-		// files should have already been handled in virtualfile.go.
-		tlog.Warn.Printf("rDecryptName: cannot decrypt virtual file %q", cName)
-		return "", syscall.EINVAL
-	}
-	return pName, nil
-}
-
-// decryptPath decrypts a relative ciphertext path to a relative plaintext
-// path.
-func (rfs *ReverseFS) decryptPath(relPath string) (string, error) {
-	if rfs.args.PlaintextNames || relPath == "" {
-		return relPath, nil
-	}
-	// Check if the parent dir is in the cache
-	cDir := nametransform.Dir(relPath)
-	dirIV, pDir := rPathCache.lookup(cDir)
-	if dirIV != nil {
-		cName := filepath.Base(relPath)
-		pName, err := rfs.rDecryptName(cName, dirIV, pDir)
-		if err != nil {
-			return "", err
-		}
-		return filepath.Join(pDir, pName), nil
-	}
-	parts := strings.Split(relPath, "/")
-	var transformedParts []string
-	for i := range parts {
-		// Start at the top and recurse
-		currentCipherDir := filepath.Join(parts[:i]...)
-		currentPlainDir := filepath.Join(transformedParts[:i]...)
-		dirIV = pathiv.Derive(currentCipherDir, pathiv.PurposeDirIV)
-		transformedPart, err := rfs.rDecryptName(parts[i], dirIV, currentPlainDir)
-		if err != nil {
-			return "", err
-		}
-		transformedParts = append(transformedParts, transformedPart)
-	}
-	pRelPath := filepath.Join(transformedParts...)
-	rPathCache.store(cDir, dirIV, nametransform.Dir(pRelPath))
-	return pRelPath, nil
-}
-
-// openBackingDir receives an already decrypted relative path
-// "pRelPath", opens the directory that contains the target file/dir
-// and returns the fd to the directory and the decrypted name of the
-// target file. The fd/name pair is intended for use with fchownat and
-// friends.
-func (rfs *ReverseFS) openBackingDir(pRelPath string) (dirfd int, pName string, err error) {
-	// Open directory, safe against symlink races
-	pDir := filepath.Dir(pRelPath)
-	dirfd, err = syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, pDir)
-	if err != nil {
-		return -1, "", err
-	}
-	pName = filepath.Base(pRelPath)
-	return dirfd, pName, nil
-}
diff --git a/internal/fusefrontend_reverse_v1api/rpath_cache.go b/internal/fusefrontend_reverse_v1api/rpath_cache.go
deleted file mode 100644
index 221f578..0000000
--- a/internal/fusefrontend_reverse_v1api/rpath_cache.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"sync"
-)
-
-// rPathCacheContainer is a simple one entry path cache. Because the dirIV
-// is generated deterministically from the directory path, there is no need
-// to ever invalidate entries.
-type rPathCacheContainer struct {
-	sync.Mutex
-	// Relative ciphertext path to the directory
-	cPath string
-	// Relative plaintext path
-	pPath string
-	// Directory IV of the directory
-	dirIV []byte
-}
-
-// lookup relative ciphertext path "cPath". Returns dirIV, relative
-// plaintext path.
-func (c *rPathCacheContainer) lookup(cPath string) ([]byte, string) {
-	c.Lock()
-	defer c.Unlock()
-	if cPath == c.cPath {
-		// hit
-		return c.dirIV, c.pPath
-	}
-	// miss
-	return nil, ""
-}
-
-// store - write entry for the directory at relative ciphertext path "cPath"
-// into the cache.
-// "dirIV" = directory IV of the directory, "pPath" = relative plaintext path
-func (c *rPathCacheContainer) store(cPath string, dirIV []byte, pPath string) {
-	c.Lock()
-	defer c.Unlock()
-	c.cPath = cPath
-	c.dirIV = dirIV
-	c.pPath = pPath
-}
-
-// rPathCache: see rPathCacheContainer above for a detailed description
-var rPathCache rPathCacheContainer
diff --git a/internal/fusefrontend_reverse_v1api/virtualfile.go b/internal/fusefrontend_reverse_v1api/virtualfile.go
deleted file mode 100644
index f258e7f..0000000
--- a/internal/fusefrontend_reverse_v1api/virtualfile.go
+++ /dev/null
@@ -1,110 +0,0 @@
-package fusefrontend_reverse
-
-import (
-	"log"
-	"path/filepath"
-	"syscall"
-
-	"golang.org/x/sys/unix"
-
-	"github.com/hanwen/go-fuse/v2/fuse"
-	"github.com/hanwen/go-fuse/v2/fuse/nodefs"
-
-	"github.com/rfjakob/gocryptfs/internal/inomap"
-	"github.com/rfjakob/gocryptfs/internal/nametransform"
-	"github.com/rfjakob/gocryptfs/internal/pathiv"
-	"github.com/rfjakob/gocryptfs/internal/syscallcompat"
-	"github.com/rfjakob/gocryptfs/internal/tlog"
-)
-
-const (
-	// virtualFileMode is the mode to use for virtual files (gocryptfs.diriv and
-	// *.name). They are always readable, as stated in func Access
-	virtualFileMode = syscall.S_IFREG | 0444
-	// We use inomap's `Tag` feature to generate unique inode numbers for
-	// virtual files. These are the tags we use.
-	inoTagDirIV    = 1
-	inoTagNameFile = 2
-)
-
-func (rfs *ReverseFS) newDirIVFile(cRelPath string) (nodefs.File, fuse.Status) {
-	cDir := nametransform.Dir(cRelPath)
-	dir, err := rfs.decryptPath(cDir)
-	if err != nil {
-		return nil, fuse.ToStatus(err)
-	}
-	iv := pathiv.Derive(cDir, pathiv.PurposeDirIV)
-	return rfs.newVirtualFile(iv, rfs.args.Cipherdir, dir, inoTagDirIV)
-}
-
-type virtualFile struct {
-	// Embed nodefs.defaultFile for a ENOSYS implementation of all methods
-	nodefs.File
-	// pointer to parent filesystem
-	rfs *ReverseFS
-	// file content
-	content []byte
-	// backing directory
-	cipherdir string
-	// path to a parent file (relative to cipherdir)
-	parentFile string
-	// inomap `Tag`.
-	// Depending on the file type, either `inoTagDirIV` or `inoTagNameFile`.
-	inoTag uint8
-}
-
-// newVirtualFile creates a new in-memory file that does not have a representation
-// on disk. "content" is the file content. Timestamps and file owner are copied
-// from "parentFile" (plaintext path relative to "cipherdir").
-// For a "gocryptfs.diriv" file, you would use the parent directory as
-// "parentFile".
-func (rfs *ReverseFS) newVirtualFile(content []byte, cipherdir string, parentFile string, inoTag uint8) (nodefs.File, fuse.Status) {
-	if inoTag == 0 {
-		log.Panicf("BUG: inoTag for virtual file is zero - this will cause ino collisions!")
-	}
-	return &virtualFile{
-		File:       nodefs.NewDefaultFile(),
-		rfs:        rfs,
-		content:    content,
-		cipherdir:  cipherdir,
-		parentFile: parentFile,
-		inoTag:     inoTag,
-	}, fuse.OK
-}
-
-// Read - FUSE call
-func (f *virtualFile) Read(buf []byte, off int64) (resultData fuse.ReadResult, status fuse.Status) {
-	if off >= int64(len(f.content)) {
-		return nil, fuse.OK
-	}
-	end := int(off) + len(buf)
-	if end > len(f.content) {
-		end = len(f.content)
-	}
-	return fuse.ReadResultData(f.content[off:end]), fuse.OK
-}
-
-// GetAttr - FUSE call
-func (f *virtualFile) GetAttr(a *fuse.Attr) fuse.Status {
-	dir := filepath.Dir(f.parentFile)
-	dirfd, err := syscallcompat.OpenDirNofollow(f.cipherdir, dir)
-	if err != nil {
-		return fuse.ToStatus(err)
-	}
-	defer syscall.Close(dirfd)
-	name := filepath.Base(f.parentFile)
-	var st2 unix.Stat_t
-	err = syscallcompat.Fstatat(dirfd, name, &st2, unix.AT_SYMLINK_NOFOLLOW)
-	if err != nil {
-		tlog.Debug.Printf("GetAttr: Fstatat %q: %v\n", f.parentFile, err)
-		return fuse.ToStatus(err)
-	}
-	st := syscallcompat.Unix2syscall(st2)
-	q := inomap.NewQIno(uint64(st.Dev), f.inoTag, uint64(st.Ino))
-	st.Ino = f.rfs.inoMap.Translate(q)
-	st.Size = int64(len(f.content))
-	st.Mode = virtualFileMode
-	st.Nlink = 1
-	a.FromStat(&st)
-	return fuse.OK
-}
-- 
cgit v1.2.3