diff options
| author | Jakob Unterwurzacher | 2016-02-07 14:02:09 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2016-02-07 14:02:09 +0100 | 
| commit | 653d4a619cb7b937d81deab4f20d3c8d4baa4898 (patch) | |
| tree | 4ae1132e0a082d72fa5f008f91e451ce138ec273 | |
| parent | 2a11906963d4e9be8876757690eee53c737dcef9 (diff) | |
longnames part II: Rename, Unlink, Rmdir, Mknod, Mkdir + testsv0.9-rc1
| -rw-r--r-- | integration_tests/main_test.go | 49 | ||||
| -rw-r--r-- | internal/contentenc/offsets.go | 2 | ||||
| -rw-r--r-- | internal/fusefrontend/fs.go | 55 | ||||
| -rw-r--r-- | internal/fusefrontend/fs_dir.go | 11 | ||||
| -rw-r--r-- | internal/nametransform/longnames.go | 39 | ||||
| -rw-r--r-- | internal/nametransform/name_api.go | 2 | ||||
| -rw-r--r-- | internal/nametransform/names_diriv.go | 17 | ||||
| -rw-r--r-- | internal/toggledlog/log.go | 5 | 
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...))  	}  } | 
