aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2016-02-07 14:02:09 +0100
committerJakob Unterwurzacher2016-02-07 14:02:09 +0100
commit653d4a619cb7b937d81deab4f20d3c8d4baa4898 (patch)
tree4ae1132e0a082d72fa5f008f91e451ce138ec273
parent2a11906963d4e9be8876757690eee53c737dcef9 (diff)
longnames part II: Rename, Unlink, Rmdir, Mknod, Mkdir + testsv0.9-rc1
-rw-r--r--integration_tests/main_test.go49
-rw-r--r--internal/contentenc/offsets.go2
-rw-r--r--internal/fusefrontend/fs.go55
-rw-r--r--internal/fusefrontend/fs_dir.go11
-rw-r--r--internal/nametransform/longnames.go39
-rw-r--r--internal/nametransform/name_api.go2
-rw-r--r--internal/nametransform/names_diriv.go17
-rw-r--r--internal/toggledlog/log.go5
8 files changed, 147 insertions, 33 deletions
diff --git a/integration_tests/main_test.go b/integration_tests/main_test.go
index 2d150f2..cd2c228 100644
--- a/integration_tests/main_test.go
+++ b/integration_tests/main_test.go
@@ -353,12 +353,17 @@ func TestDirOverwrite(t *testing.T) {
}
func TestLongNames(t *testing.T) {
- // Create
+ fi, err := ioutil.ReadDir(defaultCipherDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cnt1 := len(fi)
wd := defaultPlainDir
+ // Create file with long name
n255x := string(bytes.Repeat([]byte("x"), 255))
f, err := os.Create(wd + n255x)
if err != nil {
- t.Fatalf("Could not create n255x")
+ t.Fatalf("Could not create n255x: %v", err)
}
f.Close()
if !verifyExistence(wd + n255x) {
@@ -368,7 +373,7 @@ func TestLongNames(t *testing.T) {
n255y := string(bytes.Repeat([]byte("y"), 255))
err = os.Rename(wd+n255x, wd+n255y)
if err != nil {
- t.Fatalf("Could not rename n255x to n255y")
+ t.Fatalf("Could not rename n255x to n255y: %v", err)
}
if !verifyExistence(wd + n255y) {
t.Errorf("n255y is not in directory listing")
@@ -376,7 +381,7 @@ func TestLongNames(t *testing.T) {
// Rename long to short
err = os.Rename(wd+n255y, wd+"short")
if err != nil {
- t.Fatalf("Could not rename n255y to short")
+ t.Fatalf("Could not rename n255y to short: %v", err)
}
if !verifyExistence(wd + "short") {
t.Errorf("short is not in directory listing")
@@ -384,7 +389,7 @@ func TestLongNames(t *testing.T) {
// Rename short to long
err = os.Rename(wd+"short", wd+n255x)
if err != nil {
- t.Fatalf("Could not rename short to n255x")
+ t.Fatalf("Could not rename short to n255x: %v", err)
}
if !verifyExistence(wd + n255x) {
t.Errorf("255x is not in directory listing II")
@@ -392,9 +397,41 @@ func TestLongNames(t *testing.T) {
// Unlink
err = syscall.Unlink(wd + n255x)
if err != nil {
- t.Fatalf("Could not unlink n255x")
+ t.Fatalf("Could not unlink n255x: %v", err)
}
if verifyExistence(wd + n255x) {
t.Errorf("n255x still there after unlink")
}
+ // Long symlink
+ n255s := string(bytes.Repeat([]byte("s"), 255))
+ err = os.Symlink("/etc/motd", wd+n255s)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !verifyExistence(wd + n255s) {
+ t.Errorf("n255s is not in directory listing")
+ }
+ err = syscall.Unlink(wd + n255s)
+ if err != nil {
+ t.Error(err)
+ }
+ // Long dir
+ n255d := string(bytes.Repeat([]byte("d"), 255))
+ err = os.Mkdir(wd+n255d, 0777)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = syscall.Rmdir(wd + n255d)
+ if err != nil {
+ t.Error(err)
+ }
+ // Check for orphaned files
+ fi, err = ioutil.ReadDir(defaultCipherDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cnt2 := len(fi)
+ if cnt1 != cnt2 {
+ t.Errorf("Leftover files, cnt1=%d cnt2=%d", cnt1, cnt2)
+ }
}
diff --git a/internal/contentenc/offsets.go b/internal/contentenc/offsets.go
index 1b5952f..cfb1c69 100644
--- a/internal/contentenc/offsets.go
+++ b/internal/contentenc/offsets.go
@@ -40,7 +40,7 @@ func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 {
}
if cipherSize < HEADER_LEN {
- toggledlog.Warn.Printf("cipherSize %d < header size: corrupt file\n", cipherSize)
+ toggledlog.Warn.Printf("cipherSize %d < header size %d: corrupt file\n", cipherSize, HEADER_LEN)
return 0
}
diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go
index f11c05e..fb82c6b 100644
--- a/internal/fusefrontend/fs.go
+++ b/internal/fusefrontend/fs.go
@@ -5,7 +5,6 @@ package fusefrontend
import (
"encoding/base64"
"os"
- "path/filepath"
"sync"
"syscall"
"time"
@@ -14,7 +13,6 @@ import (
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs"
- "github.com/rfjakob/gocryptfs/internal/configfile"
"github.com/rfjakob/gocryptfs/internal/contentenc"
"github.com/rfjakob/gocryptfs/internal/cryptocore"
"github.com/rfjakob/gocryptfs/internal/nametransform"
@@ -113,10 +111,9 @@ func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Conte
if err != nil {
return nil, fuse.ToStatus(err)
}
- cBaseName := filepath.Base(cPath)
- if fs.args.LongNames && nametransform.IsLongName(cBaseName) == 1 {
- // Create the ".name" file before creating the content
- err = fs.nameTransform.WriteLongName(filepath.Dir(cPath), cBaseName, filepath.Base(path))
+ // Create .name file to store the long file name if needed
+ if !fs.args.PlaintextNames {
+ err = fs.nameTransform.WriteLongName(cPath, path)
if err != nil {
return nil, fuse.ToStatus(err)
}
@@ -158,6 +155,13 @@ func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context)
if err != nil {
return fuse.ToStatus(err)
}
+ if !fs.args.PlaintextNames {
+ // Create .name file to store the long file name if needed
+ err = fs.nameTransform.WriteLongName(cPath, path)
+ if err != nil {
+ return fuse.ToStatus(err)
+ }
+ }
return fs.FileSystem.Mknod(cPath, mode, dev, context)
}
@@ -223,7 +227,12 @@ func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {
if err != nil {
return fuse.ToStatus(err)
}
- return fuse.ToStatus(syscall.Unlink(cPath))
+ err = syscall.Unlink(cPath)
+ // Delete .name file
+ if err == nil && !fs.args.PlaintextNames {
+ nametransform.DeleteLongName(cPath)
+ }
+ return fuse.ToStatus(err)
}
func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) {
@@ -236,6 +245,7 @@ func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (co
return fuse.ToStatus(err)
}
// Old filesystem: symlinks are encrypted like paths (CBC)
+ // TODO drop compatibility and simplify code
if !fs.args.DirIV {
cTarget, err := fs.encryptPath(target)
if err != nil {
@@ -248,7 +258,13 @@ func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (co
// Since gocryptfs v0.5 symlinks are encrypted like file contents (GCM)
cBinTarget := fs.contentEnc.EncryptBlock([]byte(target), 0, nil)
cTarget := base64.URLEncoding.EncodeToString(cBinTarget)
-
+ if !fs.args.PlaintextNames {
+ // Create .name file to store the long file name if needed
+ err = fs.nameTransform.WriteLongName(cPath, linkName)
+ if err != nil {
+ return fuse.ToStatus(err)
+ }
+ }
err = os.Symlink(cTarget, cPath)
toggledlog.Debug.Printf("Symlink: os.Symlink(%s, %s) = %v", cTarget, cPath, err)
return fuse.ToStatus(err)
@@ -270,6 +286,15 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
// That directory may still be in the DirIV cache, clear it.
fs.nameTransform.DirIVCache.Clear()
+ if !fs.args.PlaintextNames {
+ // Create .name file to store the new long file name if needed
+ err = fs.nameTransform.WriteLongName(cNewPath, newPath)
+ if err != nil {
+ return fuse.ToStatus(err)
+ }
+ }
+
+ // Actual rename
err = os.Rename(cOldPath, cNewPath)
if lerr, ok := err.(*os.LinkError); ok && lerr.Err == syscall.ENOTEMPTY {
@@ -281,6 +306,13 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
err = os.Rename(cOldPath, cNewPath)
}
}
+ if err == nil {
+ // Rename succeeded - delete old long name file
+ nametransform.DeleteLongName(cOldPath)
+ } else {
+ // Rename has failed - undo long name file creation
+ nametransform.DeleteLongName(cNewPath)
+ }
return fuse.ToStatus(err)
}
@@ -297,6 +329,13 @@ func (fs *FS) Link(oldPath string, newPath string, context *fuse.Context) (code
if err != nil {
return fuse.ToStatus(err)
}
+ if !fs.args.PlaintextNames {
+ // Create .name file to store the long file name if needed
+ err = fs.nameTransform.WriteLongName(cNewPath, newPath)
+ if err != nil {
+ return fuse.ToStatus(err)
+ }
+ }
return fuse.ToStatus(os.Link(cOldPath, cNewPath))
}
diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go
index f1ade25..ebf7015 100644
--- a/internal/fusefrontend/fs_dir.go
+++ b/internal/fusefrontend/fs_dir.go
@@ -10,6 +10,7 @@ import (
"github.com/hanwen/go-fuse/fuse"
+ "github.com/rfjakob/gocryptfs/internal/configfile"
"github.com/rfjakob/gocryptfs/internal/cryptocore"
"github.com/rfjakob/gocryptfs/internal/nametransform"
"github.com/rfjakob/gocryptfs/internal/toggledlog"
@@ -30,7 +31,11 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu
// We need write and execute permissions to create gocryptfs.diriv
origMode := mode
mode = mode | 0300
-
+ // Create .name file to store the long file name if needed
+ err = fs.nameTransform.WriteLongName(encPath, relPath)
+ if err != nil {
+ return fuse.ToStatus(err)
+ }
// The new directory may take the place of an older one that is still in the cache
fs.nameTransform.DirIVCache.Clear()
// Create directory
@@ -151,6 +156,10 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
if err != nil {
toggledlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err)
}
+ err = nametransform.DeleteLongName(encPath)
+ if err != nil {
+ toggledlog.Warn.Printf("Rmdir: Could not delete long name file: %v", err)
+ }
// The now-deleted directory may have been in the DirIV cache. Clear it.
fs.nameTransform.DirIVCache.Clear()
return fuse.OK
diff --git a/internal/nametransform/longnames.go b/internal/nametransform/longnames.go
index e442b64..dad269b 100644
--- a/internal/nametransform/longnames.go
+++ b/internal/nametransform/longnames.go
@@ -1,12 +1,12 @@
package nametransform
import (
- "syscall"
- "path/filepath"
- "io/ioutil"
"crypto/sha256"
"encoding/base64"
+ "io/ioutil"
+ "path/filepath"
"strings"
+ "syscall"
"github.com/rfjakob/gocryptfs/internal/toggledlog"
)
@@ -39,28 +39,47 @@ func IsLongName(cName string) int {
return 1
}
-// ReadLongName - read "path".name
+// ReadLongName - read path.name
func ReadLongName(path string) (string, error) {
- content, err := ioutil.ReadFile(path+longNameSuffix)
+ content, err := ioutil.ReadFile(path + longNameSuffix)
if err != nil {
toggledlog.Warn.Printf("ReadLongName: %v", err)
}
return string(content), err
}
-// WriteLongName -
-func (n *NameTransform) WriteLongName(cDir string, hashedName string, plainName string) (err error) {
- if len(plainName) > syscall.NAME_MAX {
- return syscall.ENAMETOOLONG
+// DeleteLongName - if cPath ends in "gocryptfs.longname.[sha256]",
+// delete the "gocryptfs.longname.[sha256].name" file
+func DeleteLongName(cPath string) error {
+ if IsLongName(filepath.Base(cPath)) == 1 {
+ err := syscall.Unlink(cPath + longNameSuffix)
+ if err != nil {
+ toggledlog.Warn.Printf("DeleteLongName: %v", err)
+ }
+ return err
}
+ return nil
+}
+// WriteLongName - if cPath ends in "gocryptfs.longname.[sha256]", write the
+// "gocryptfs.longname.[sha256].name" file
+func (n *NameTransform) WriteLongName(cPath string, plainPath string) (err error) {
+ cHashedName := filepath.Base(cPath)
+ if IsLongName(cHashedName) != 1 {
+ // This is not a hashed file name, nothing to do
+ return nil
+ }
+ // Encrypt (but do not hash) the plaintext name
+ cDir := filepath.Dir(cPath)
dirIV, err := ReadDirIV(cDir)
if err != nil {
toggledlog.Warn.Printf("WriteLongName: %v", err)
return err
}
+ plainName := filepath.Base(plainPath)
cName := n.EncryptName(plainName, dirIV)
- err = ioutil.WriteFile(filepath.Join(cDir, hashedName + longNameSuffix), []byte(cName), 0600)
+ // Write the encrypted name into gocryptfs.longname.[sha256].name
+ err = ioutil.WriteFile(filepath.Join(cDir, cHashedName+longNameSuffix), []byte(cName), 0600)
if err != nil {
toggledlog.Warn.Printf("WriteLongName: %v", err)
}
diff --git a/internal/nametransform/name_api.go b/internal/nametransform/name_api.go
index 391a5ce..7ac7d26 100644
--- a/internal/nametransform/name_api.go
+++ b/internal/nametransform/name_api.go
@@ -12,7 +12,7 @@ type NameTransform struct {
func New(c *cryptocore.CryptoCore, useEME bool, longNames bool) *NameTransform {
return &NameTransform{
cryptoCore: c,
- longNames: longNames,
+ longNames: longNames,
useEME: useEME,
}
}
diff --git a/internal/nametransform/names_diriv.go b/internal/nametransform/names_diriv.go
index d45f91b..9336f5d 100644
--- a/internal/nametransform/names_diriv.go
+++ b/internal/nametransform/names_diriv.go
@@ -1,12 +1,12 @@
package nametransform
import (
- "syscall"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
+ "syscall"
"github.com/rfjakob/gocryptfs/internal/cryptocore"
"github.com/rfjakob/gocryptfs/internal/toggledlog"
@@ -54,17 +54,23 @@ func WriteDirIV(dir string) error {
return ioutil.WriteFile(file, iv, 0444)
}
-// EncryptPathDirIV - encrypt path using EME with DirIV
+// EncryptPathDirIV - encrypt relative plaintext path using EME with DirIV.
+// Components that are longer than 255 bytes are hashed.
func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cipherPath string, err error) {
// Empty string means root directory
if plainPath == "" {
return plainPath, nil
}
+ // Reject names longer than 255 bytes already here. This relieves everybody
+ // who uses hashed long names from checking for that later.
+ baseName := filepath.Base(plainPath)
+ if len(baseName) > syscall.NAME_MAX {
+ return "", syscall.ENAMETOOLONG
+ }
// Check if the DirIV is cached
parentDir := filepath.Dir(plainPath)
found, iv, cParentDir := be.DirIVCache.lookup(parentDir)
if found {
- baseName := filepath.Base(plainPath)
cBaseName := be.EncryptName(baseName, iv)
if be.longNames && len(cBaseName) > syscall.NAME_MAX {
cBaseName = HashLongName(cBaseName)
@@ -72,7 +78,7 @@ func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cip
cipherPath = cParentDir + "/" + cBaseName
return cipherPath, nil
}
- // Walk the directory tree
+ // Not cached - walk the directory tree
var wd = rootDir
var encryptedNames []string
plainNames := strings.Split(plainPath, "/")
@@ -96,6 +102,9 @@ func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cip
}
// DecryptPathDirIV - decrypt path using EME with DirIV
+//
+// TODO This has only a single user, Readlink(), and only for compatability with
+// gocryptfs v0.5. Drop?
func (be *NameTransform) DecryptPathDirIV(encryptedPath string, rootDir string) (string, error) {
var wd = rootDir
var plainNames []string
diff --git a/internal/toggledlog/log.go b/internal/toggledlog/log.go
index 5e5191a..31a9eb6 100644
--- a/internal/toggledlog/log.go
+++ b/internal/toggledlog/log.go
@@ -9,6 +9,7 @@ import (
const (
ProgramName = "gocryptfs"
+ wpanicMsg = "-wpanic turns this warning into a panic: "
)
func JSONDump(obj interface{}) string {
@@ -35,7 +36,7 @@ func (l *toggledLogger) Printf(format string, v ...interface{}) {
}
l.Logger.Printf(format, v...)
if l.Wpanic {
- panic("-wpanic turns warning into panic: " + fmt.Sprintf(format, v...))
+ panic(wpanicMsg + fmt.Sprintf(format, v...))
}
}
func (l *toggledLogger) Println(v ...interface{}) {
@@ -44,7 +45,7 @@ func (l *toggledLogger) Println(v ...interface{}) {
}
l.Logger.Println(v...)
if l.Wpanic {
- panic("-wpanic turns warning into panic: " + fmt.Sprintln(v...))
+ panic(wpanicMsg + fmt.Sprintln(v...))
}
}