reverse: switch from GCM-SIV to AES-SIV
GCM-SIV is not yet finalized, and the reference implemenation is painfully slow at about 2 MB/s. Switch to AES-SIV.
This commit is contained in:
parent
d9fc652df0
commit
e9bb8b800c
@ -30,6 +30,9 @@ DESCRIPTION
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
|
**-aessiv**
|
||||||
|
: Use the AES-SIV encryption mode (implied by -reverse)
|
||||||
|
|
||||||
**-allow_other**
|
**-allow_other**
|
||||||
: By default, the Linux kernel prevents any other user (even root) to
|
: By default, the Linux kernel prevents any other user (even root) to
|
||||||
access a mounted FUSE filesystem. Settings this option allows access for
|
access a mounted FUSE filesystem. Settings this option allows access for
|
||||||
@ -58,9 +61,6 @@ to mount the gocryptfs filesytem without user interaction.
|
|||||||
**-fusedebug**
|
**-fusedebug**
|
||||||
: Enable fuse library debug output
|
: Enable fuse library debug output
|
||||||
|
|
||||||
**-gcmsiv**
|
|
||||||
: Use the GCM-SIV encryption mode (implied by -reverse)
|
|
||||||
|
|
||||||
**-init**
|
**-init**
|
||||||
: Initialize encrypted directory
|
: Initialize encrypted directory
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
type argContainer struct {
|
type argContainer struct {
|
||||||
debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,
|
debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,
|
||||||
plaintextnames, quiet, nosyslog, wpanic,
|
plaintextnames, quiet, nosyslog, wpanic,
|
||||||
longnames, allow_other, ro, reverse, gcmsiv bool
|
longnames, allow_other, ro, reverse, aessiv bool
|
||||||
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
|
masterkey, mountpoint, cipherdir, cpuprofile, extpass,
|
||||||
memprofile, o string
|
memprofile, o string
|
||||||
// Configuration file name override
|
// Configuration file name override
|
||||||
@ -51,7 +51,7 @@ func parseCliOpts() (args argContainer) {
|
|||||||
"Only works if user_allow_other is set in /etc/fuse.conf.")
|
"Only works if user_allow_other is set in /etc/fuse.conf.")
|
||||||
flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only")
|
flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only")
|
||||||
flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode")
|
flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode")
|
||||||
flagSet.BoolVar(&args.gcmsiv, "gcmsiv", false, "GCM-SIV encryption")
|
flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption")
|
||||||
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
|
flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")
|
||||||
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
|
flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file")
|
||||||
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")
|
flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file")
|
||||||
|
@ -39,7 +39,7 @@ func initDir(args *argContainer) {
|
|||||||
}
|
}
|
||||||
password := readpassword.Twice(args.extpass)
|
password := readpassword.Twice(args.extpass)
|
||||||
creator := tlog.ProgramName + " " + GitVersion
|
creator := tlog.ProgramName + " " + GitVersion
|
||||||
err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.gcmsiv)
|
err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.aessiv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Fatal.Println(err)
|
tlog.Fatal.Println(err)
|
||||||
os.Exit(ERREXIT_INIT)
|
os.Exit(ERREXIT_INIT)
|
||||||
|
@ -45,7 +45,7 @@ type ConfFile struct {
|
|||||||
// CreateConfFile - create a new config with a random key encrypted with
|
// CreateConfFile - create a new config with a random key encrypted with
|
||||||
// "password" and write it to "filename".
|
// "password" and write it to "filename".
|
||||||
// Uses scrypt with cost parameter logN.
|
// Uses scrypt with cost parameter logN.
|
||||||
func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, gcmsiv bool) error {
|
func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, aessiv bool) error {
|
||||||
var cf ConfFile
|
var cf ConfFile
|
||||||
cf.filename = filename
|
cf.filename = filename
|
||||||
cf.Creator = creator
|
cf.Creator = creator
|
||||||
@ -59,7 +59,7 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN
|
|||||||
cf.EncryptKey(key, password, logN)
|
cf.EncryptKey(key, password, logN)
|
||||||
|
|
||||||
// Set feature flags
|
// Set feature flags
|
||||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128]) // 128-bit IVs
|
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128])
|
||||||
if plaintextNames {
|
if plaintextNames {
|
||||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagPlaintextNames])
|
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagPlaintextNames])
|
||||||
} else {
|
} else {
|
||||||
@ -67,8 +67,8 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN
|
|||||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])
|
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])
|
||||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])
|
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])
|
||||||
}
|
}
|
||||||
if gcmsiv {
|
if aessiv {
|
||||||
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMSIV]) // GCM-SIV encryption mode
|
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write file to disk
|
// Write file to disk
|
||||||
|
@ -71,7 +71,7 @@ func TestCreateConfFile(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateConfFileGCMSIV(t *testing.T) {
|
func TestCreateConfFileAESSIV(t *testing.T) {
|
||||||
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true)
|
err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -80,14 +80,14 @@ func TestCreateConfFileGCMSIV(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !c.IsFeatureFlagSet(FlagGCMSIV) {
|
if !c.IsFeatureFlagSet(FlagAESSIV) {
|
||||||
t.Error("GCMSIV flag should be set but is not")
|
t.Error("AESSIV flag should be set but is not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsFeatureFlagKnown(t *testing.T) {
|
func TestIsFeatureFlagKnown(t *testing.T) {
|
||||||
// Test a few hardcoded values
|
// Test a few hardcoded values
|
||||||
testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "GCMSIV"}
|
testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "AESSIV"}
|
||||||
// And also everything in knownFlags (yes, it is likely that we end up with
|
// And also everything in knownFlags (yes, it is likely that we end up with
|
||||||
// some duplicates. Does not matter.)
|
// some duplicates. Does not matter.)
|
||||||
for _, f := range knownFlags {
|
for _, f := range knownFlags {
|
||||||
|
@ -8,7 +8,7 @@ const (
|
|||||||
FlagEMENames
|
FlagEMENames
|
||||||
FlagGCMIV128
|
FlagGCMIV128
|
||||||
FlagLongNames
|
FlagLongNames
|
||||||
FlagGCMSIV
|
FlagAESSIV
|
||||||
)
|
)
|
||||||
|
|
||||||
// knownFlags stores the known feature flags and their string representation
|
// knownFlags stores the known feature flags and their string representation
|
||||||
@ -18,7 +18,7 @@ var knownFlags map[flagIota]string = map[flagIota]string{
|
|||||||
FlagEMENames: "EMENames",
|
FlagEMENames: "EMENames",
|
||||||
FlagGCMIV128: "GCMIV128",
|
FlagGCMIV128: "GCMIV128",
|
||||||
FlagLongNames: "LongNames",
|
FlagLongNames: "LongNames",
|
||||||
FlagGCMSIV: "GCMSIV",
|
FlagAESSIV: "AESSIV",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filesystems that do not have these feature flags set are deprecated.
|
// Filesystems that do not have these feature flags set are deprecated.
|
||||||
|
@ -105,8 +105,8 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b
|
|||||||
|
|
||||||
// Extract nonce
|
// Extract nonce
|
||||||
nonce := ciphertext[:be.cryptoCore.IVLen]
|
nonce := ciphertext[:be.cryptoCore.IVLen]
|
||||||
if bytes.Equal(nonce, be.allZeroNonce) && be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV {
|
if bytes.Equal(nonce, be.allZeroNonce) {
|
||||||
panic("Hit an all-zero nonce with GCMSIV off. This MUST NOT happen!")
|
panic("Hit an all-zero nonce. This MUST NOT happen!")
|
||||||
}
|
}
|
||||||
ciphertextOrig := ciphertext
|
ciphertextOrig := ciphertext
|
||||||
ciphertext = ciphertext[be.cryptoCore.IVLen:]
|
ciphertext = ciphertext[be.cryptoCore.IVLen:]
|
||||||
@ -150,13 +150,13 @@ func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []by
|
|||||||
var nonce []byte
|
var nonce []byte
|
||||||
switch nMode {
|
switch nMode {
|
||||||
case ExternalNonce:
|
case ExternalNonce:
|
||||||
if be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV {
|
if be.cryptoCore.AEADBackend != cryptocore.BackendAESSIV {
|
||||||
panic("MUST NOT use deterministic nonces unless in GCMSIV mode!")
|
panic("MUST NOT use deterministic nonces unless in AESSIV mode!")
|
||||||
}
|
}
|
||||||
nonce = externalNonce
|
nonce = externalNonce
|
||||||
case ReverseDeterministicNonce:
|
case ReverseDeterministicNonce:
|
||||||
if be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV {
|
if be.cryptoCore.AEADBackend != cryptocore.BackendAESSIV {
|
||||||
panic("MUST NOT use deterministic nonces unless in GCMSIV mode!")
|
panic("MUST NOT use deterministic nonces unless in AESSIV mode!")
|
||||||
}
|
}
|
||||||
l := be.cryptoCore.IVLen
|
l := be.cryptoCore.IVLen
|
||||||
nonce = make([]byte, l)
|
nonce = make([]byte, l)
|
||||||
|
@ -7,9 +7,8 @@ import (
|
|||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/siv_aead"
|
||||||
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
|
||||||
|
|
||||||
"github.com/rfjakob/gcmsiv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BackendTypeEnum int
|
type BackendTypeEnum int
|
||||||
@ -21,13 +20,13 @@ const (
|
|||||||
_ = iota // Skip zero
|
_ = iota // Skip zero
|
||||||
BackendOpenSSL BackendTypeEnum = iota
|
BackendOpenSSL BackendTypeEnum = iota
|
||||||
BackendGoGCM BackendTypeEnum = iota
|
BackendGoGCM BackendTypeEnum = iota
|
||||||
BackendGCMSIV BackendTypeEnum = iota
|
BackendAESSIV BackendTypeEnum = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
type CryptoCore struct {
|
type CryptoCore struct {
|
||||||
// AES-256 block cipher. This is used for EME filename encryption.
|
// AES-256 block cipher. This is used for EME filename encryption.
|
||||||
BlockCipher cipher.Block
|
BlockCipher cipher.Block
|
||||||
// GCM or GCM-SIV. This is used for content encryption.
|
// GCM or AES-SIV. This is used for content encryption.
|
||||||
AEADCipher cipher.AEAD
|
AEADCipher cipher.AEAD
|
||||||
// Which backend is behind AEADCipher?
|
// Which backend is behind AEADCipher?
|
||||||
AEADBackend BackendTypeEnum
|
AEADBackend BackendTypeEnum
|
||||||
@ -64,8 +63,8 @@ func New(key []byte, backend BackendTypeEnum, IVBitLen int) *CryptoCore {
|
|||||||
gcm = stupidgcm.New(key)
|
gcm = stupidgcm.New(key)
|
||||||
case BackendGoGCM:
|
case BackendGoGCM:
|
||||||
gcm, err = goGCMWrapper(blockCipher, IVLen)
|
gcm, err = goGCMWrapper(blockCipher, IVLen)
|
||||||
case BackendGCMSIV:
|
case BackendAESSIV:
|
||||||
gcm, err = gcmsiv.NewGCMSIV(key)
|
gcm = siv_aead.New(key)
|
||||||
default:
|
default:
|
||||||
panic("unknown backend cipher")
|
panic("unknown backend cipher")
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,8 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rfjakob/gcmsiv"
|
// For benchmark comparison
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/siv_aead"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get "n" random bytes from /dev/urandom or panic
|
// Get "n" random bytes from /dev/urandom or panic
|
||||||
@ -162,16 +163,13 @@ func Benchmark4kEncGoGCM(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark4kEncGCMSIV(b *testing.B) {
|
func Benchmark4kEncAESSIV(b *testing.B) {
|
||||||
key := randBytes(32)
|
key := randBytes(32)
|
||||||
authData := randBytes(24)
|
authData := randBytes(24)
|
||||||
iv := randBytes(16)
|
iv := randBytes(16)
|
||||||
in := make([]byte, 4096)
|
in := make([]byte, 4096)
|
||||||
b.SetBytes(int64(len(in)))
|
b.SetBytes(int64(len(in)))
|
||||||
gGCM, err := gcmsiv.NewGCMSIV(key)
|
gGCM := siv_aead.New(key)
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
// Encrypt and append to nonce
|
// Encrypt and append to nonce
|
||||||
gGCM.Seal(iv, iv, in, authData)
|
gGCM.Seal(iv, iv, in, authData)
|
||||||
|
19
main.go
19
main.go
@ -150,9 +150,9 @@ func main() {
|
|||||||
if args.quiet {
|
if args.quiet {
|
||||||
tlog.Info.Enabled = false
|
tlog.Info.Enabled = false
|
||||||
}
|
}
|
||||||
// "-reverse" implies "-gcmsiv"
|
// "-reverse" implies "-aessiv"
|
||||||
if args.reverse {
|
if args.reverse {
|
||||||
args.gcmsiv = true
|
args.aessiv = true
|
||||||
}
|
}
|
||||||
// "-config"
|
// "-config"
|
||||||
if args.config != "" {
|
if args.config != "" {
|
||||||
@ -290,8 +290,8 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
|
|||||||
if args.openssl {
|
if args.openssl {
|
||||||
cryptoBackend = cryptocore.BackendOpenSSL
|
cryptoBackend = cryptocore.BackendOpenSSL
|
||||||
}
|
}
|
||||||
if args.gcmsiv {
|
if args.aessiv {
|
||||||
cryptoBackend = cryptocore.BackendGCMSIV
|
cryptoBackend = cryptocore.BackendAESSIV
|
||||||
}
|
}
|
||||||
frontendArgs := fusefrontend.Args{
|
frontendArgs := fusefrontend.Args{
|
||||||
Cipherdir: args.cipherdir,
|
Cipherdir: args.cipherdir,
|
||||||
@ -304,10 +304,10 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
|
|||||||
if confFile != nil {
|
if confFile != nil {
|
||||||
// Settings from the config file override command line args
|
// Settings from the config file override command line args
|
||||||
frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
|
frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
|
||||||
if confFile.IsFeatureFlagSet(configfile.FlagGCMSIV) {
|
if confFile.IsFeatureFlagSet(configfile.FlagAESSIV) {
|
||||||
frontendArgs.CryptoBackend = cryptocore.BackendGCMSIV
|
frontendArgs.CryptoBackend = cryptocore.BackendAESSIV
|
||||||
} else if args.reverse {
|
} else if args.reverse {
|
||||||
tlog.Fatal.Printf("GCM-SIV is required by reverse mode, but not enabled in the config file")
|
tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file")
|
||||||
os.Exit(ERREXIT_USAGE)
|
os.Exit(ERREXIT_USAGE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -318,11 +318,6 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi
|
|||||||
}
|
}
|
||||||
jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t")
|
jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t")
|
||||||
tlog.Debug.Printf("frontendArgs: %s", string(jsonBytes))
|
tlog.Debug.Printf("frontendArgs: %s", string(jsonBytes))
|
||||||
if frontendArgs.CryptoBackend == cryptocore.BackendGCMSIV {
|
|
||||||
tlog.Info.Printf(tlog.ColorYellow +
|
|
||||||
"Warning: The GCM-SIV format used by reverse mode is not yet finalized.\n" +
|
|
||||||
"The on-disk format will change in the future." + tlog.ColorReset)
|
|
||||||
}
|
|
||||||
var finalFs pathfs.FileSystem
|
var finalFs pathfs.FileSystem
|
||||||
if args.reverse {
|
if args.reverse {
|
||||||
finalFs = fusefrontend_reverse.NewFS(frontendArgs)
|
finalFs = fusefrontend_reverse.NewFS(frontendArgs)
|
||||||
|
@ -27,20 +27,20 @@ func TestInit(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if c.IsFeatureFlagSet(configfile.FlagGCMSIV) {
|
if c.IsFeatureFlagSet(configfile.FlagAESSIV) {
|
||||||
t.Error("GCMSIV flag should not be set")
|
t.Error("AESSIV flag should not be set")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test -init with -gcmsiv
|
// Test -init with -aessiv
|
||||||
func TestInitGcmsiv(t *testing.T) {
|
func TestInitAessiv(t *testing.T) {
|
||||||
dir := test_helpers.InitFS(t, "-gcmsiv")
|
dir := test_helpers.InitFS(t, "-aessiv")
|
||||||
_, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, "test")
|
_, c, err := configfile.LoadConfFile(dir+"/"+configfile.ConfDefaultName, "test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !c.IsFeatureFlagSet(configfile.FlagGCMSIV) {
|
if !c.IsFeatureFlagSet(configfile.FlagAESSIV) {
|
||||||
t.Error("GCMSIV flag should be set but is not")
|
t.Error("AESSIV flag should be set but is not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +51,8 @@ func TestInitReverse(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !c.IsFeatureFlagSet(configfile.FlagGCMSIV) {
|
if !c.IsFeatureFlagSet(configfile.FlagAESSIV) {
|
||||||
t.Error("GCMSIV flag should be set but is not")
|
t.Error("AESSIV flag should be set but is not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,4 +28,5 @@ gocryptfs -q -extpass="echo test" b c
|
|||||||
# Check md5 sums
|
# Check md5 sums
|
||||||
cd c
|
cd c
|
||||||
echo "Checking md5 sums"
|
echo "Checking md5 sums"
|
||||||
md5sum --status -c $MD5
|
set -o pipefail
|
||||||
|
md5sum -c $MD5 | pv -l -s 36782 -N "files checked" | (grep -v ": OK" || true)
|
||||||
|
@ -26,7 +26,7 @@ func TestMain(m *testing.M) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
test_helpers.MountOrExit(dirA, dirB, "-zerokey", "-reverse")
|
test_helpers.MountOrExit(dirA, dirB, "-zerokey", "-reverse")
|
||||||
test_helpers.MountOrExit(dirB, dirC, "-zerokey", "-gcmsiv")
|
test_helpers.MountOrExit(dirB, dirC, "-zerokey", "-aessiv")
|
||||||
r := m.Run()
|
r := m.Run()
|
||||||
test_helpers.UnmountPanic(dirC)
|
test_helpers.UnmountPanic(dirC)
|
||||||
test_helpers.UnmountPanic(dirB)
|
test_helpers.UnmountPanic(dirB)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user