reverse: make gocryptfs.conf mapping plaintextnames-aware

Only in plaintextnames-mode AND with the config file at the
default location it will be mapped into the mountpoint.

Also adds a test for that.
This commit is contained in:
Jakob Unterwurzacher 2016-10-08 20:57:38 +02:00
parent dde4a66454
commit f054353bd3
7 changed files with 95 additions and 28 deletions

View File

@ -20,6 +20,9 @@ type argContainer struct {
// Configuration file name override // Configuration file name override
config string config string
notifypid, scryptn int notifypid, scryptn int
// _configCustom is true when the user sets a custom config file name.
// This is not a CLI option.
_configCustom bool
} }
var flagSet *flag.FlagSet var flagSet *flag.FlagSet

View File

@ -14,4 +14,8 @@ type Args struct {
// Should we chown a file after it has been created? // Should we chown a file after it has been created?
// This only makes sense if (1) allow_other is set and (2) we run as root. // This only makes sense if (1) allow_other is set and (2) we run as root.
PreserveOwner bool PreserveOwner bool
// ConfigCustom is true when the user select a non-default config file
// location. If it is false, reverse mode maps ".gocryptfs.reverse.conf"
// to "gocryptfs.conf" in the plaintext dir.
ConfigCustom bool
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/rfjakob/gocryptfs/internal/cryptocore" "github.com/rfjakob/gocryptfs/internal/cryptocore"
"github.com/rfjakob/gocryptfs/internal/fusefrontend" "github.com/rfjakob/gocryptfs/internal/fusefrontend"
"github.com/rfjakob/gocryptfs/internal/nametransform" "github.com/rfjakob/gocryptfs/internal/nametransform"
"github.com/rfjakob/gocryptfs/internal/tlog"
) )
const ( const (
@ -110,13 +111,19 @@ func (rfs *reverseFS) dirIVAttr(relPath string, context *fuse.Context) (*fuse.At
} }
// isDirIV determines if the path points to a gocryptfs.diriv file // isDirIV determines if the path points to a gocryptfs.diriv file
func isDirIV(relPath string) bool { func (rfs *reverseFS) isDirIV(relPath string) bool {
if rfs.args.PlaintextNames {
return false
}
return filepath.Base(relPath) == nametransform.DirIVFilename return filepath.Base(relPath) == nametransform.DirIVFilename
} }
// isNameFile determines if the path points to a gocryptfs.longname.*.name // isNameFile determines if the path points to a gocryptfs.longname.*.name
// file // file
func isNameFile(relPath string) bool { func (rfs *reverseFS) isNameFile(relPath string) bool {
if rfs.args.PlaintextNames {
return false
}
fileType := nametransform.NameType(filepath.Base(relPath)) fileType := nametransform.NameType(filepath.Base(relPath))
return fileType == nametransform.LongNameFilename return fileType == nametransform.LongNameFilename
} }
@ -161,19 +168,15 @@ func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
if relPath == configfile.ConfDefaultName { if relPath == configfile.ConfDefaultName {
return rfs.inoAwareStat(configfile.ConfReverseName) return rfs.inoAwareStat(configfile.ConfReverseName)
} }
if rfs.isFiltered(relPath) {
return nil, fuse.EPERM
}
// Handle virtual files // Handle virtual files
var f nodefs.File var f nodefs.File
var status fuse.Status var status fuse.Status
virtual := false virtual := false
if isDirIV(relPath) { if rfs.isDirIV(relPath) {
virtual = true virtual = true
f, status = rfs.newDirIVFile(relPath) f, status = rfs.newDirIVFile(relPath)
} }
if isNameFile(relPath) { if rfs.isNameFile(relPath) {
virtual = true virtual = true
f, status = rfs.newNameFile(relPath) f, status = rfs.newNameFile(relPath)
} }
@ -204,7 +207,7 @@ func (rfs *reverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
// Access - FUSE call // Access - FUSE call
func (rfs *reverseFS) Access(relPath string, mode uint32, context *fuse.Context) fuse.Status { func (rfs *reverseFS) Access(relPath string, mode uint32, context *fuse.Context) fuse.Status {
if isDirIV(relPath) { if rfs.isDirIV(relPath) {
return fuse.OK return fuse.OK
} }
if rfs.isFiltered(relPath) { if rfs.isFiltered(relPath) {
@ -223,10 +226,10 @@ func (rfs *reverseFS) Open(relPath string, flags uint32, context *fuse.Context)
// gocryptfs.conf maps to .gocryptfs.reverse.conf in the plaintext directory // gocryptfs.conf maps to .gocryptfs.reverse.conf in the plaintext directory
return rfs.loopbackfs.Open(configfile.ConfReverseName, flags, context) return rfs.loopbackfs.Open(configfile.ConfReverseName, flags, context)
} }
if isDirIV(relPath) { if rfs.isDirIV(relPath) {
return rfs.newDirIVFile(relPath) return rfs.newDirIVFile(relPath)
} }
if isNameFile(relPath) { if rfs.isNameFile(relPath) {
return rfs.newNameFile(relPath) return rfs.newNameFile(relPath)
} }
if rfs.isFiltered(relPath) { if rfs.isFiltered(relPath) {
@ -235,6 +238,31 @@ func (rfs *reverseFS) Open(relPath string, flags uint32, context *fuse.Context)
return rfs.NewFile(relPath, flags) return rfs.NewFile(relPath, flags)
} }
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 %s is mapped to %s and shadows another file. Please rename %s in %s .",
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 // OpenDir - FUSE readdir call
func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
relPath, err := rfs.decryptPath(cipherPath) relPath, err := rfs.decryptPath(cipherPath)
@ -246,6 +274,9 @@ func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.
if entries == nil { if entries == nil {
return nil, status return nil, status
} }
if rfs.args.PlaintextNames {
return rfs.openDirPlaintextnames(cipherPath, entries)
}
// Allocate maximum possible number of virtual files. // Allocate maximum possible number of virtual files.
// If all files have long names we need a virtual ".name" file for each, // If all files have long names we need a virtual ".name" file for each,
// plus one for gocryptfs.diriv. // plus one for gocryptfs.diriv.

View File

@ -155,6 +155,7 @@ func main() {
os.Exit(ErrExitInit) os.Exit(ErrExitInit)
} }
tlog.Info.Printf("Using config file at custom location %s", args.config) tlog.Info.Printf("Using config file at custom location %s", args.config)
args._configCustom = true
} else if args.reverse { } else if args.reverse {
args.config = filepath.Join(args.cipherdir, configfile.ConfReverseName) args.config = filepath.Join(args.cipherdir, configfile.ConfReverseName)
} else { } else {

View File

@ -125,6 +125,7 @@ func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfF
PlaintextNames: args.plaintextnames, PlaintextNames: args.plaintextnames,
LongNames: args.longnames, LongNames: args.longnames,
CryptoBackend: cryptoBackend, CryptoBackend: cryptoBackend,
ConfigCustom: args._configCustom,
} }
// confFile is nil when "-zerokey" or "-masterkey" was used // confFile is nil when "-zerokey" or "-masterkey" was used
if confFile != nil { if confFile != nil {

View File

@ -1,6 +1,7 @@
package reverse_test package reverse_test
import ( import (
"io/ioutil"
"os" "os"
"testing" "testing"
//"time" //"time"
@ -49,3 +50,17 @@ func TestSymlinks(t *testing.T) {
t.Errorf("wrong symlink target: want=%q have=%q", target, actualTarget) t.Errorf("wrong symlink target: want=%q have=%q", target, actualTarget)
} }
} }
// .gocryptfs.reverse.conf in the plaintext dir should be visible as
// gocryptfs.conf
func TestConfigMapping(t *testing.T) {
c := dirB + "/gocryptfs.conf"
test_helpers.VerifyExistence(c)
data, err := ioutil.ReadFile(c)
if err != nil {
t.Fatal(err)
}
if len(data) == 0 {
t.Errorf("empty file")
}
}

View File

@ -10,26 +10,38 @@ import (
var dirA, dirB, dirC string var dirA, dirB, dirC string
var x240 string var x240 string
var plaintextnames bool
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
x240 = string(bytes.Repeat([]byte("x"), 240)) x240 = string(bytes.Repeat([]byte("x"), 240))
dirA = test_helpers.TmpDir + "/a" var r int
dirB = test_helpers.TmpDir + "/b" for _, plaintextnames = range []bool{false, true} {
dirC = test_helpers.TmpDir + "/c" argsA := []string{"-reverse"}
if err := os.Mkdir(dirA, 0700); err != nil { if plaintextnames {
panic(err) argsA = append(argsA, "-plaintextnames")
}
dirA = test_helpers.InitFS(nil, argsA...)
dirB = test_helpers.TmpDir + "/b"
dirC = test_helpers.TmpDir + "/c"
if err := os.Mkdir(dirB, 0700); err != nil {
panic(err)
}
if err := os.Mkdir(dirC, 0700); err != nil {
panic(err)
}
test_helpers.MountOrExit(dirA, dirB, "-reverse", "-extpass", "echo test")
test_helpers.MountOrExit(dirB, dirC, "-extpass", "echo test")
r = m.Run()
test_helpers.UnmountPanic(dirC)
test_helpers.UnmountPanic(dirB)
os.RemoveAll(dirA)
os.RemoveAll(dirB)
os.RemoveAll(dirC)
if r != 0 {
os.Exit(r)
}
} }
if err := os.Mkdir(dirB, 0700); err != nil {
panic(err)
}
if err := os.Mkdir(dirC, 0700); err != nil {
panic(err)
}
test_helpers.MountOrExit(dirA, dirB, "-zerokey", "-reverse")
test_helpers.MountOrExit(dirB, dirC, "-zerokey", "-aessiv")
r := m.Run()
test_helpers.UnmountPanic(dirC)
test_helpers.UnmountPanic(dirB)
os.RemoveAll(test_helpers.TmpDir)
os.Exit(r) os.Exit(r)
} }