-deterministic-names: implement for reverse mode, too
This commit is contained in:
parent
14bf80301b
commit
fbccb16043
@ -7,7 +7,6 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/rfjakob/gocryptfs/internal/ctlsocksrv"
|
||||
"github.com/rfjakob/gocryptfs/internal/pathiv"
|
||||
)
|
||||
|
||||
// Verify that the interface is implemented.
|
||||
@ -22,7 +21,7 @@ func (rn *RootNode) EncryptPath(plainPath string) (string, error) {
|
||||
cipherPath := ""
|
||||
parts := strings.Split(plainPath, "/")
|
||||
for _, part := range parts {
|
||||
dirIV := pathiv.Derive(cipherPath, pathiv.PurposeDirIV)
|
||||
dirIV := rn.deriveDirIV(cipherPath)
|
||||
encryptedPart, err := rn.nameTransform.EncryptName(part, dirIV)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -13,7 +13,6 @@ import (
|
||||
"github.com/rfjakob/gocryptfs/internal/configfile"
|
||||
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||
"github.com/rfjakob/gocryptfs/internal/pathiv"
|
||||
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||
)
|
||||
@ -23,20 +22,16 @@ import (
|
||||
// This function is symlink-safe through use of openBackingDir() and
|
||||
// ReadDirIVAt().
|
||||
func (n *Node) Readdir(ctx context.Context) (stream fs.DirStream, errno syscall.Errno) {
|
||||
// Virtual files: at least one gocryptfs.diriv file
|
||||
virtualFiles := []fuse.DirEntry{
|
||||
{Mode: virtualFileMode, Name: nametransform.DirIVFilename},
|
||||
}
|
||||
rn := n.rootNode()
|
||||
// Should we present a virtual gocryptfs.diriv?
|
||||
var virtualFiles []fuse.DirEntry
|
||||
if !rn.args.PlaintextNames && !rn.args.DeterministicNames {
|
||||
virtualFiles = append(virtualFiles, fuse.DirEntry{Mode: virtualFileMode, Name: nametransform.DirIVFilename})
|
||||
}
|
||||
|
||||
// This directory is a mountpoint. Present it as empty.
|
||||
if rn.args.OneFileSystem && n.isOtherFilesystem {
|
||||
if rn.args.PlaintextNames {
|
||||
return fs.NewListDirStream(nil), 0
|
||||
} else {
|
||||
// An "empty" directory still has a gocryptfs.diriv file!
|
||||
return fs.NewListDirStream(virtualFiles), 0
|
||||
}
|
||||
return fs.NewListDirStream(virtualFiles), 0
|
||||
}
|
||||
|
||||
d, errno := n.prepareAtSyscall("")
|
||||
@ -64,7 +59,7 @@ func (n *Node) Readdir(ctx context.Context) (stream fs.DirStream, errno syscall.
|
||||
return n.readdirPlaintextnames(entries)
|
||||
}
|
||||
|
||||
dirIV := pathiv.Derive(d.cPath, pathiv.PurposeDirIV)
|
||||
dirIV := rn.deriveDirIV(d.cPath)
|
||||
// Encrypt names
|
||||
for i := range entries {
|
||||
var cName string
|
||||
|
@ -2,6 +2,7 @@ package fusefrontend_reverse
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
@ -129,8 +130,8 @@ func (n *Node) lookupLongnameName(ctx context.Context, nameFile string, out *fus
|
||||
return
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
diriv := pathiv.Derive(d.cPath, pathiv.PurposeDirIV)
|
||||
rn := n.rootNode()
|
||||
diriv := rn.deriveDirIV(d.cPath)
|
||||
pName, cFullname, errno := rn.findLongnameParent(fd, diriv, nameFile)
|
||||
if errno != 0 {
|
||||
return
|
||||
@ -160,6 +161,10 @@ func (n *Node) lookupLongnameName(ctx context.Context, nameFile string, out *fus
|
||||
|
||||
// lookupDiriv returns a new Inode for a gocryptfs.diriv file inside `n`.
|
||||
func (n *Node) lookupDiriv(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) {
|
||||
if rn := n.rootNode(); rn.args.DeterministicNames {
|
||||
log.Panic("BUG: lookupDiriv called but DeterministicNames is set")
|
||||
}
|
||||
|
||||
d, errno := n.prepareAtSyscall("")
|
||||
if errno != 0 {
|
||||
return
|
||||
|
@ -2,6 +2,7 @@ package fusefrontend_reverse
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
@ -72,7 +73,7 @@ func (rn *RootNode) decryptPath(cPath string) (string, error) {
|
||||
// Start at the top and recurse
|
||||
currentCipherDir := filepath.Join(parts[:i]...)
|
||||
currentPlainDir := filepath.Join(transformedParts[:i]...)
|
||||
dirIV := pathiv.Derive(currentCipherDir, pathiv.PurposeDirIV)
|
||||
dirIV := rn.deriveDirIV(currentCipherDir)
|
||||
transformedPart, err := rn.rDecryptName(parts[i], dirIV, currentPlainDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -83,6 +84,17 @@ func (rn *RootNode) decryptPath(cPath string) (string, error) {
|
||||
return pRelPath, nil
|
||||
}
|
||||
|
||||
// deriveDirIV wraps pathiv.Derive but takes DeterministicNames into account.
|
||||
func (rn *RootNode) deriveDirIV(cPath string) []byte {
|
||||
if rn.args.PlaintextNames {
|
||||
log.Panic("BUG: deriveDirIV called but PlaintextNames is set")
|
||||
}
|
||||
if rn.args.DeterministicNames {
|
||||
return make([]byte, nametransform.DirIVLen)
|
||||
}
|
||||
return pathiv.Derive(cPath, pathiv.PurposeDirIV)
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -43,9 +43,11 @@ func (n *Node) lookupFileType(cName string) fileType {
|
||||
rn := n.rootNode()
|
||||
// In -plaintextname mode, neither diriv nor longname files exist.
|
||||
if !rn.args.PlaintextNames {
|
||||
// Is it a gocryptfs.diriv file?
|
||||
if cName == nametransform.DirIVFilename {
|
||||
return typeDiriv
|
||||
if !rn.args.DeterministicNames {
|
||||
// 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 {
|
||||
|
@ -995,9 +995,3 @@ func TestMountCreat(t *testing.T) {
|
||||
test_helpers.UnmountPanic(mnt)
|
||||
}
|
||||
}
|
||||
|
||||
// Test -init -deterministic-names
|
||||
func TestInitDeterministicNames(t *testing.T) {
|
||||
dir := test_helpers.InitFS(t, "-deterministic-names")
|
||||
|
||||
}
|
||||
|
@ -119,10 +119,10 @@ func TestConfigMapping(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the access() syscall works on virtual files
|
||||
func TestAccessVirtual(t *testing.T) {
|
||||
if plaintextnames {
|
||||
t.Skip("test makes no sense for plaintextnames")
|
||||
// Check that the access() syscall works on virtual gocryptfs.diriv files
|
||||
func TestAccessVirtualDirIV(t *testing.T) {
|
||||
if plaintextnames || deterministic_names {
|
||||
t.Skip("test makes no sense for plaintextnames or deterministic_names")
|
||||
}
|
||||
var R_OK uint32 = 4
|
||||
var W_OK uint32 = 2
|
||||
|
@ -130,8 +130,10 @@ func TestVirtualFileIno(t *testing.T) {
|
||||
}
|
||||
// Lower 48 bits should come from the backing file
|
||||
const mask = 0xffffffffffff
|
||||
if origInos.parent&mask != cipherInos.diriv&mask {
|
||||
t.Errorf("diriv ino mismatch: %#x vs %#x", origInos.parent, cipherInos.diriv)
|
||||
if !deterministic_names { // no diriv files with -deterministic-names
|
||||
if origInos.parent&mask != cipherInos.diriv&mask {
|
||||
t.Errorf("diriv ino mismatch: %#x vs %#x", origInos.parent, cipherInos.diriv)
|
||||
}
|
||||
}
|
||||
if origInos.child != cipherInos.child {
|
||||
t.Errorf("child ino mismatch: %d vs %d", origInos.child, cipherInos.child)
|
||||
|
@ -2,6 +2,7 @@ package reverse_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@ -9,8 +10,13 @@ import (
|
||||
)
|
||||
|
||||
var x240 = string(bytes.Repeat([]byte("x"), 240))
|
||||
|
||||
// plaintextnames is true when the currently running test has -plaintextnames active
|
||||
var plaintextnames bool
|
||||
|
||||
// deterministic_names is true when the currently running test has -deterministic-names active
|
||||
var deterministic_names bool
|
||||
|
||||
// dirA is a normal directory
|
||||
var dirA string
|
||||
|
||||
@ -24,10 +30,22 @@ var dirC string
|
||||
// to "dirC".
|
||||
func TestMain(m *testing.M) {
|
||||
var r int
|
||||
for _, plaintextnames = range []bool{false, true} {
|
||||
|
||||
testcases := []struct {
|
||||
plaintextnames bool
|
||||
deterministic_names bool
|
||||
}{
|
||||
{false, false},
|
||||
{true, false},
|
||||
{false, true},
|
||||
}
|
||||
for i, tc := range testcases {
|
||||
argsA := []string{"-reverse"}
|
||||
if plaintextnames {
|
||||
plaintextnames, deterministic_names = tc.plaintextnames, tc.deterministic_names
|
||||
if tc.plaintextnames {
|
||||
argsA = append(argsA, "-plaintextnames")
|
||||
} else if tc.deterministic_names {
|
||||
argsA = append(argsA, "-deterministic-names")
|
||||
}
|
||||
dirA = test_helpers.InitFS(nil, argsA...)
|
||||
dirB = test_helpers.TmpDir + "/b"
|
||||
@ -49,6 +67,7 @@ func TestMain(m *testing.M) {
|
||||
os.RemoveAll(dirC)
|
||||
|
||||
if r != 0 {
|
||||
fmt.Printf("testcases[%d] = %#v failed\n", i, tc)
|
||||
os.Exit(r)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package reverse
|
||||
package reverse_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
@ -10,7 +11,10 @@ import (
|
||||
"github.com/rfjakob/gocryptfs/tests/test_helpers"
|
||||
)
|
||||
|
||||
func doTestOneFileSystem(t *testing.T, plaintextnames bool) {
|
||||
func TestOneFileSystem(t *testing.T) {
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip("only works on linux")
|
||||
}
|
||||
// Let's not explode with "TempDir: pattern contains path separator"
|
||||
myEscapedName := url.PathEscape(t.Name())
|
||||
mnt, err := ioutil.TempDir(test_helpers.TmpDir, myEscapedName)
|
||||
@ -20,6 +24,8 @@ func doTestOneFileSystem(t *testing.T, plaintextnames bool) {
|
||||
cliArgs := []string{"-reverse", "-zerokey", "-one-file-system"}
|
||||
if plaintextnames {
|
||||
cliArgs = append(cliArgs, "-plaintextnames")
|
||||
} else if deterministic_names {
|
||||
cliArgs = append(cliArgs, "-deterministic-names")
|
||||
}
|
||||
test_helpers.MountOrFatal(t, "/", mnt, cliArgs...)
|
||||
defer test_helpers.UnmountErr(mnt)
|
||||
@ -48,25 +54,22 @@ func doTestOneFileSystem(t *testing.T, plaintextnames bool) {
|
||||
t.Skip("no mountpoints found, nothing to test")
|
||||
}
|
||||
for _, m := range mountpoints {
|
||||
e, err := ioutil.ReadDir(mnt + "/" + m)
|
||||
dir, err := os.Open(mnt + "/" + m)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer dir.Close()
|
||||
e, err := dir.Readdirnames(-1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expected := 1
|
||||
if plaintextnames {
|
||||
if plaintextnames || deterministic_names {
|
||||
expected = 0
|
||||
}
|
||||
if len(e) != expected {
|
||||
t.Errorf("mountpoint %q does not look empty: %v", m, e)
|
||||
t.Errorf("mountpoint %q should have %d entries, actually has: %v", m, expected, e)
|
||||
}
|
||||
}
|
||||
t.Logf("tested %d mountpoints: %v", len(mountpoints), mountpoints)
|
||||
}
|
||||
|
||||
func TestOneFileSystem(t *testing.T) {
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip("only works on linux")
|
||||
}
|
||||
t.Run("normal", func(t *testing.T) { doTestOneFileSystem(t, false) })
|
||||
t.Run("plaintextnames", func(t *testing.T) { doTestOneFileSystem(t, true) })
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user