aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2021-10-21 09:37:04 +0200
committerJakob Unterwurzacher2021-10-21 14:55:30 +0200
commitdc32710045f6f46913ae336b6fb77bf90b6bdb85 (patch)
tree890dde8bdef6facc61ee9cd3c4d0246225fee6ad
parenta652be805e1562948aff4dc232bd1c516ff01d00 (diff)
nametransform: add longNameMax parameter
Determines when to start hashing long names instead of hardcoded 255. Will be used to alleviate "name too long" issues some users see on cloud storage. https://github.com/rfjakob/gocryptfs/issues/499
-rw-r--r--internal/fusefrontend/xattr_unit_test.go2
-rw-r--r--internal/nametransform/badname.go2
-rw-r--r--internal/nametransform/longnames_test.go41
-rw-r--r--internal/nametransform/names.go28
-rw-r--r--mount.go2
5 files changed, 65 insertions, 10 deletions
diff --git a/internal/fusefrontend/xattr_unit_test.go b/internal/fusefrontend/xattr_unit_test.go
index 5bffd5e..86c87a7 100644
--- a/internal/fusefrontend/xattr_unit_test.go
+++ b/internal/fusefrontend/xattr_unit_test.go
@@ -19,7 +19,7 @@ func newTestFS(args Args) *RootNode {
key := make([]byte, cryptocore.KeyLen)
cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true)
cEnc := contentenc.New(cCore, contentenc.DefaultBS)
- n := nametransform.New(cCore.EMECipher, true, true, nil, false)
+ n := nametransform.New(cCore.EMECipher, true, 0, true, nil, false)
rn := NewRootNode(args, cEnc, n)
oneSec := time.Second
options := &fs.Options{
diff --git a/internal/nametransform/badname.go b/internal/nametransform/badname.go
index eed0061..6e77561 100644
--- a/internal/nametransform/badname.go
+++ b/internal/nametransform/badname.go
@@ -48,7 +48,7 @@ func (be *NameTransform) EncryptAndHashBadName(name string, iv []byte, dirfd int
//expand suffix on error
continue
}
- if be.longNames && len(cName) > NameMax {
+ if len(cName) > be.longNameMax {
cNamePart = be.HashLongName(cName)
}
cNameBadReverse := cNamePart + name[charpos:len(name)-len(BadnameSuffix)]
diff --git a/internal/nametransform/longnames_test.go b/internal/nametransform/longnames_test.go
index 4210492..7a4e915 100644
--- a/internal/nametransform/longnames_test.go
+++ b/internal/nametransform/longnames_test.go
@@ -1,7 +1,11 @@
package nametransform
import (
+ "strings"
"testing"
+
+ "github.com/rfjakob/gocryptfs/v2/internal/contentenc"
+ "github.com/rfjakob/gocryptfs/v2/internal/cryptocore"
)
func TestIsLongName(t *testing.T) {
@@ -28,3 +32,40 @@ func TestRemoveLongNameSuffix(t *testing.T) {
t.Error(".name suffix not removed")
}
}
+
+func newLognamesTestInstance(longNameMax uint8) *NameTransform {
+ key := make([]byte, cryptocore.KeyLen)
+ cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true)
+ return New(cCore.EMECipher, true, longNameMax, true, nil, false)
+}
+
+func TestLongNameMax(t *testing.T) {
+ iv := make([]byte, 16)
+ for max := 0; max <= NameMax; max++ {
+ n := newLognamesTestInstance(uint8(max))
+ if max == 0 {
+ // effective value is 255
+ max = NameMax
+ }
+ for l := 0; l <= NameMax+10; l++ {
+ name := strings.Repeat("x", l)
+ out, err := n.EncryptAndHashName(name, iv)
+ if l == 0 || l > NameMax {
+ if err == nil {
+ t.Errorf("should have rejected a name of length %d, but did not", l)
+ }
+ continue
+ }
+ cName, _ := n.EncryptName(name, iv)
+ rawLen := len(cName)
+ want := LongNameNone
+ if rawLen > max {
+ want = LongNameContent
+ }
+ have := NameType(out)
+ if have != want {
+ t.Errorf("l=%d max=%d: wanted %v, got %v\nname=%q\nout=%q", l, max, want, have, name, out)
+ }
+ }
+ }
+}
diff --git a/internal/nametransform/names.go b/internal/nametransform/names.go
index d766d2f..939d31e 100644
--- a/internal/nametransform/names.go
+++ b/internal/nametransform/names.go
@@ -4,6 +4,7 @@ package nametransform
import (
"crypto/aes"
"encoding/base64"
+ "math"
"path/filepath"
"syscall"
@@ -20,7 +21,9 @@ const (
// NameTransform is used to transform filenames.
type NameTransform struct {
emeCipher *eme.EMECipher
- longNames bool
+ // Names longer than `longNameMax` are hashed. Set to MaxInt when
+ // longnames are disabled.
+ longNameMax int
// B64 = either base64.URLEncoding or base64.RawURLEncoding, depending
// on the Raw64 feature flag
B64 *base64.Encoding
@@ -30,17 +33,28 @@ type NameTransform struct {
}
// New returns a new NameTransform instance.
-func New(e *eme.EMECipher, longNames bool, raw64 bool, badname []string, deterministicNames bool) *NameTransform {
- tlog.Debug.Printf("nametransform.New: longNames=%v, raw64=%v, badname=%q",
- longNames, raw64, badname)
-
+//
+// If `longNames` is set, names longer than `longNameMax` are hashed to
+// `gocryptfs.longname.[sha256]`.
+// Pass `longNameMax = 0` to use the default value (255).
+func New(e *eme.EMECipher, longNames bool, longNameMax uint8, raw64 bool, badname []string, deterministicNames bool) *NameTransform {
+ tlog.Debug.Printf("nametransform.New: longNameMax=%v, raw64=%v, badname=%q",
+ longNameMax, raw64, badname)
b64 := base64.URLEncoding
if raw64 {
b64 = base64.RawURLEncoding
}
+ var effectiveLongNameMax int = math.MaxInt
+ if longNames {
+ if longNameMax == 0 {
+ effectiveLongNameMax = NameMax
+ } else {
+ effectiveLongNameMax = int(longNameMax)
+ }
+ }
return &NameTransform{
emeCipher: e,
- longNames: longNames,
+ longNameMax: effectiveLongNameMax,
B64: b64,
badnamePatterns: badname,
deterministicNames: deterministicNames,
@@ -115,7 +129,7 @@ func (be *NameTransform) EncryptAndHashName(name string, iv []byte) (string, err
if err != nil {
return "", err
}
- if be.longNames && len(cName) > NameMax {
+ if len(cName) > be.longNameMax {
return be.HashLongName(cName), nil
}
return cName, nil
diff --git a/mount.go b/mount.go
index dfabbc9..004c646 100644
--- a/mount.go
+++ b/mount.go
@@ -324,7 +324,7 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
// Init crypto backend
cCore := cryptocore.New(masterkey, cryptoBackend, IVBits, args.hkdf)
cEnc := contentenc.New(cCore, contentenc.DefaultBS)
- nameTransform := nametransform.New(cCore.EMECipher, frontendArgs.LongNames,
+ nameTransform := nametransform.New(cCore.EMECipher, frontendArgs.LongNames, 0,
args.raw64, []string(args.badname), frontendArgs.DeterministicNames)
// After the crypto backend is initialized,
// we can purge the master key from memory.