cli: add -longnamemax
Fixes https://github.com/rfjakob/gocryptfs/issues/499
This commit is contained in:
parent
d583bdb79e
commit
d14c9340d6
@ -123,6 +123,28 @@ and https://github.com/rfjakob/gocryptfs/issues/596 for background info.
|
||||
Use HKDF to derive separate keys for content and name encryption from
|
||||
the master key. Default true.
|
||||
|
||||
#### -longnamemax
|
||||
|
||||
integer value, allowed range 62...255
|
||||
|
||||
Hash file names that (in encrypted form) exceed this length. The default
|
||||
is 255, which aligns with the usual name length limit on Linux and
|
||||
provides best performance.
|
||||
|
||||
However, online storage may impose lower limits on file name and/or
|
||||
path length. In this case, setting -longnamemax to a lower value
|
||||
can be helpful.
|
||||
|
||||
The lower the value, the more extra `.name` files
|
||||
must be created, which slows down directory listings.
|
||||
|
||||
Values below 62 are not allowed as then the hashed name
|
||||
would be longer than the original name.
|
||||
|
||||
Example:
|
||||
|
||||
-longnamemax 100
|
||||
|
||||
#### -plaintextnames
|
||||
Do not encrypt file names and symlink targets.
|
||||
|
||||
|
@ -196,6 +196,12 @@ RM: 2,367
|
||||
Changelog
|
||||
---------
|
||||
|
||||
#### vNEXT
|
||||
* Add **`-longnamemax`** flag to `-init` ([#499](https://github.com/rfjakob/gocryptfs/issues/499)).
|
||||
Can be used to work around file or path length restrictions on online storage.
|
||||
See the [man page](https://github.com/rfjakob/gocryptfs/blob/master/Documentation/MANPAGE.md#-longnamemax)
|
||||
for details.
|
||||
|
||||
#### v2.2.1, 2021-10-20
|
||||
* Fix `-force_owner` only taking effect after 2 seconds ([#609](https://github.com/rfjakob/gocryptfs/issues/609)).
|
||||
This was a regression introduced in v2.0.
|
||||
|
@ -45,6 +45,8 @@ type argContainer struct {
|
||||
notifypid, scryptn int
|
||||
// Idle time before autounmount
|
||||
idle time.Duration
|
||||
// -longnamemax (hash encrypted names that are longer than this)
|
||||
longnamemax uint8
|
||||
// Helper variables that are NOT cli options all start with an underscore
|
||||
// _configCustom is true when the user sets a custom config file name.
|
||||
_configCustom bool
|
||||
@ -215,6 +217,8 @@ func parseCliOpts(osArgs []string) (args argContainer) {
|
||||
flagSet.StringSliceVar(&args.badname, "badname", nil, "Glob pattern invalid file names that should be shown")
|
||||
flagSet.StringSliceVar(&args.passfile, "passfile", nil, "Read password from file")
|
||||
|
||||
flagSet.Uint8Var(&args.longnamemax, "longnamemax", 255, "Hash encrypted names that are longer than this")
|
||||
|
||||
flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+
|
||||
"successful mount - used internally for daemonization")
|
||||
const scryptn = "scryptn"
|
||||
@ -292,6 +296,10 @@ func parseCliOpts(osArgs []string) (args argContainer) {
|
||||
os.Exit(exitcodes.Usage)
|
||||
}
|
||||
}
|
||||
if args.longnamemax > 0 && args.longnamemax < 62 {
|
||||
tlog.Fatal.Printf("-longnamemax: value %d is outside allowed range 62 ... 255", args.longnamemax)
|
||||
os.Exit(exitcodes.Usage)
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
@ -116,11 +116,12 @@ func TestConvertToDoubleDash(t *testing.T) {
|
||||
|
||||
func TestParseCliOpts(t *testing.T) {
|
||||
defaultArgs := argContainer{
|
||||
longnames: true,
|
||||
raw64: true,
|
||||
hkdf: true,
|
||||
openssl: stupidgcm.PreferOpenSSLAES256GCM(), // depends on CPU and build flags
|
||||
scryptn: 16,
|
||||
longnames: true,
|
||||
longnamemax: 255,
|
||||
raw64: true,
|
||||
hkdf: true,
|
||||
openssl: stupidgcm.PreferOpenSSLAES256GCM(), // depends on CPU and build flags
|
||||
scryptn: 16,
|
||||
}
|
||||
|
||||
type testcaseContainer struct {
|
||||
|
@ -102,7 +102,9 @@ func initDir(args *argContainer) {
|
||||
Fido2CredentialID: fido2CredentialID,
|
||||
Fido2HmacSalt: fido2HmacSalt,
|
||||
DeterministicNames: args.deterministic_names,
|
||||
XChaCha20Poly1305: args.xchacha})
|
||||
XChaCha20Poly1305: args.xchacha,
|
||||
LongNameMax: args.longnamemax,
|
||||
})
|
||||
if err != nil {
|
||||
tlog.Fatal.Println(err)
|
||||
os.Exit(exitcodes.WriteConf)
|
||||
|
@ -47,10 +47,10 @@ func (cf *ConfFile) Validate() error {
|
||||
}
|
||||
// Filename encryption
|
||||
{
|
||||
if cf.IsFeatureFlagSet(FlagPlaintextNames) && cf.IsFeatureFlagSet(FlagEMENames) {
|
||||
return fmt.Errorf("Can't have both PlaintextNames and EMENames feature flags")
|
||||
}
|
||||
if cf.IsFeatureFlagSet(FlagPlaintextNames) {
|
||||
if cf.IsFeatureFlagSet(FlagEMENames) {
|
||||
return fmt.Errorf("PlaintextNames conflicts with EMENames feature flag")
|
||||
}
|
||||
if cf.IsFeatureFlagSet(FlagDirIV) {
|
||||
return fmt.Errorf("PlaintextNames conflicts with DirIV feature flag")
|
||||
}
|
||||
@ -60,10 +60,19 @@ func (cf *ConfFile) Validate() error {
|
||||
if cf.IsFeatureFlagSet(FlagRaw64) {
|
||||
return fmt.Errorf("PlaintextNames conflicts with Raw64 feature flag")
|
||||
}
|
||||
if cf.IsFeatureFlagSet(FlagLongNameMax) {
|
||||
return fmt.Errorf("PlaintextNames conflicts with LongNameMax feature flag")
|
||||
}
|
||||
}
|
||||
if cf.IsFeatureFlagSet(FlagEMENames) {
|
||||
// All combinations of DirIV, LongNames, Raw64 allowed
|
||||
}
|
||||
if cf.LongNameMax != 0 && !cf.IsFeatureFlagSet(FlagLongNameMax) {
|
||||
return fmt.Errorf("LongNameMax=%d but the LongNameMax feature flag is NOT set", cf.LongNameMax)
|
||||
}
|
||||
if cf.LongNameMax == 0 && cf.IsFeatureFlagSet(FlagLongNameMax) {
|
||||
return fmt.Errorf("LongNameMax=0 but the LongNameMax feature flag IS set")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
4
mount.go
4
mount.go
@ -292,6 +292,8 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
|
||||
// Settings from the config file override command line args
|
||||
frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
|
||||
frontendArgs.DeterministicNames = !confFile.IsFeatureFlagSet(configfile.FlagDirIV)
|
||||
// Things that don't have to be in frontendArgs are only in args
|
||||
args.longnamemax = confFile.LongNameMax
|
||||
args.raw64 = confFile.IsFeatureFlagSet(configfile.FlagRaw64)
|
||||
args.hkdf = confFile.IsFeatureFlagSet(configfile.FlagHKDF)
|
||||
// Note: this will always return the non-openssl variant
|
||||
@ -324,7 +326,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, 0,
|
||||
nameTransform := nametransform.New(cCore.EMECipher, frontendArgs.LongNames, args.longnamemax,
|
||||
args.raw64, []string(args.badname), frontendArgs.DeterministicNames)
|
||||
// After the crypto backend is initialized,
|
||||
// we can purge the master key from memory.
|
||||
|
61
tests/cli/longnamemax_test.go
Normal file
61
tests/cli/longnamemax_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/rfjakob/gocryptfs/v2/internal/configfile"
|
||||
|
||||
"github.com/rfjakob/gocryptfs/v2/tests/test_helpers"
|
||||
)
|
||||
|
||||
// Create & test fs with -longnamemax=100
|
||||
func TestLongnamemax100(t *testing.T) {
|
||||
cDir := test_helpers.InitFS(nil, "-longnamemax", "100")
|
||||
pDir := cDir + ".mnt"
|
||||
|
||||
// Check config file sanity
|
||||
_, c, err := configfile.LoadAndDecrypt(cDir+"/"+configfile.ConfDefaultName, testPw)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if !c.IsFeatureFlagSet(configfile.FlagLongNameMax) {
|
||||
t.Error("FlagLongNameMax should be on")
|
||||
}
|
||||
if c.LongNameMax != 100 {
|
||||
t.Errorf("LongNameMax=%d, want 100", c.LongNameMax)
|
||||
}
|
||||
|
||||
// Check that it takes effect
|
||||
test_helpers.MountOrExit(cDir, pDir, "-extpass", "echo test")
|
||||
defer test_helpers.UnmountPanic(pDir)
|
||||
|
||||
for l := 1; l <= 255; l++ {
|
||||
path := pDir + "/" + strings.Repeat("x", l)
|
||||
if err := ioutil.WriteFile(path, nil, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
matches, err := filepath.Glob(cDir + "/gocryptfs.longname.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = syscall.Unlink(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// As determined experimentally, a name of length >= 64 causes a longname
|
||||
// to be created.
|
||||
if l <= 63 && len(matches) != 0 {
|
||||
t.Errorf("l=%d: should not see a longname yet", l)
|
||||
}
|
||||
if l >= 64 && len(matches) != 2 {
|
||||
t.Errorf("l=%d: should see a longname now", l)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user