From 552c32c5e9378b85e52c420c4dd2d7ccc827556f Mon Sep 17 00:00:00 2001
From: Jakob Unterwurzacher
Date: Mon, 5 Oct 2015 20:32:10 +0200
Subject: Move main binary to gocryptfs_main

That way the wrapper shell script can be named just "gocryptfs"
---
 .gitignore                  |   2 +-
 all.bash                    |   8 ++
 benchmark.bash              |   2 +
 gocryptfs                   |  22 ++++
 gocryptfs.sh                |  14 ---
 gocryptfs_main/main.go      | 222 +++++++++++++++++++++++++++++++++++++
 gocryptfs_main/main_test.go | 265 ++++++++++++++++++++++++++++++++++++++++++++
 main.go                     | 222 -------------------------------------
 main_test.go                | 265 --------------------------------------------
 9 files changed, 520 insertions(+), 502 deletions(-)
 create mode 100755 all.bash
 create mode 100755 gocryptfs
 delete mode 100755 gocryptfs.sh
 create mode 100644 gocryptfs_main/main.go
 create mode 100644 gocryptfs_main/main_test.go
 delete mode 100644 main.go
 delete mode 100644 main_test.go

diff --git a/.gitignore b/.gitignore
index 6072ebf..4e4ddae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
 # binary
-/gocryptfs
+/gocryptfs_main/gocryptfs_main
 
 # temporary files created by the tests
 /tmp
diff --git a/all.bash b/all.bash
new file mode 100755
index 0000000..08bc7cf
--- /dev/null
+++ b/all.bash
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+set -eu
+
+cd gocryptfs_main
+echo -n "Compiling... "
+go build
+echo "done."
diff --git a/benchmark.bash b/benchmark.bash
index 447581a..baba0f1 100755
--- a/benchmark.bash
+++ b/benchmark.bash
@@ -2,5 +2,7 @@
 
 set -eux
 
+cd gocryptfs_main
+
 go build
 go test -bench=.
diff --git a/gocryptfs b/gocryptfs
new file mode 100755
index 0000000..ce15b97
--- /dev/null
+++ b/gocryptfs
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Simple wrapper that runs the gocryptfs process in the background
+
+set -eu
+
+dir=$(dirname "$0")
+main="$dir/gocryptfs_main/gocryptfs_main"
+
+if [ ! -x $main ]; then
+	echo "Error: gocryptfs_main executable not found. Run ./all.bash to build it."
+	exit 1
+fi
+
+# This needs user input and cannot run in the background
+if [[ $* == *--init* ]]; then
+	"$main" $*
+else
+	"$main" $* &
+	sleep 0.1
+	disown
+fi
diff --git a/gocryptfs.sh b/gocryptfs.sh
deleted file mode 100755
index 689708c..0000000
--- a/gocryptfs.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-# Run the gocryptfs process in the background
-
-set -eu
-
-dir=$(dirname "$0")
-
-# This needs user input and cannot run in the background
-if [[ $* == *--init* ]]; then
-	"$dir/gocryptfs" $*
-else
-	"$dir/gocryptfs" $* & disown
-fi
diff --git a/gocryptfs_main/main.go b/gocryptfs_main/main.go
new file mode 100644
index 0000000..ae3974e
--- /dev/null
+++ b/gocryptfs_main/main.go
@@ -0,0 +1,222 @@
+package main
+
+import (
+	"encoding/hex"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"runtime"
+	"runtime/pprof"
+	"time"
+
+	"github.com/rfjakob/gocryptfs/cryptfs"
+	"github.com/rfjakob/gocryptfs/pathfs_frontend"
+
+	"golang.org/x/crypto/ssh/terminal"
+
+	"github.com/hanwen/go-fuse/fuse"
+	"github.com/hanwen/go-fuse/fuse/nodefs"
+	"github.com/hanwen/go-fuse/fuse/pathfs"
+)
+
+const (
+	USE_CLUEFS   = false // Use cluefs or pathfs FUSE frontend
+	USE_OPENSSL  = true  // 3x speed increase compared to Go's built-in GCM
+	PATHFS_DEBUG = false
+
+	PROGRAM_NAME = "gocryptfs"
+
+	// Exit codes
+	ERREXIT_USAGE     = 1
+	ERREXIT_NEWFS     = 2
+	ERREXIT_MOUNT     = 3
+	ERREXIT_SERVE     = 4
+	ERREXIT_MOUNT2    = 5
+	ERREXIT_CIPHERDIR = 6
+	ERREXIT_INIT      = 7
+	ERREXIT_LOADCONF  = 8
+	ERREXIT_PASSWORD  = 9
+)
+
+func initDir(dirArg string) {
+	dir, _ := filepath.Abs(dirArg)
+
+	if dirEmpty(dir) == false {
+		fmt.Printf("Error: Directory \"%s\" is not empty\n", dirArg)
+		os.Exit(ERREXIT_INIT)
+	}
+
+	confName := filepath.Join(dir, cryptfs.ConfDefaultName)
+	fmt.Printf("Choose a password for protecting your files.\n")
+	password := readPasswordTwice()
+	err := cryptfs.CreateConfFile(confName, password)
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(ERREXIT_INIT)
+	}
+	fmt.Printf("The filesystem is now ready for mounting.\n")
+	os.Exit(0)
+}
+
+func main() {
+	runtime.GOMAXPROCS(4)
+
+	// Parse command line arguments
+	var debug, init, zerokey, fusedebug bool
+
+	flag.BoolVar(&debug, "debug", false, "Enable debug output")
+	flag.BoolVar(&fusedebug, "fusedebug", false, "Enable fuse library debug output")
+	flag.BoolVar(&init, "init", false, "Initialize encrypted directory")
+	flag.BoolVar(&zerokey, "zerokey", false, "Use all-zero dummy master key")
+	var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
+
+	flag.Parse()
+	if *cpuprofile != "" {
+		f, err := os.Create(*cpuprofile)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(ERREXIT_INIT)
+		}
+		fmt.Printf("Writing CPU profile to %s\n", *cpuprofile)
+		pprof.StartCPUProfile(f)
+		defer pprof.StopCPUProfile()
+	}
+	if debug {
+		cryptfs.Debug.Enable()
+		cryptfs.Debug.Printf("Debug output enabled\n")
+	}
+	if init {
+		if flag.NArg() != 1 {
+			fmt.Printf("usage: %s --init CIPHERDIR\n", PROGRAM_NAME)
+			os.Exit(ERREXIT_USAGE)
+		}
+		initDir(flag.Arg(0))
+	}
+	if flag.NArg() < 2 {
+		fmt.Printf("usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT\n", PROGRAM_NAME)
+		os.Exit(ERREXIT_USAGE)
+	}
+	cipherdir, _ := filepath.Abs(flag.Arg(0))
+	mountpoint, _ := filepath.Abs(flag.Arg(1))
+	cryptfs.Debug.Printf("cipherdir=%s\nmountpoint=%s\n", cipherdir, mountpoint)
+
+	_, err := os.Stat(cipherdir)
+	if err != nil {
+		fmt.Printf("Cipherdir: %s\n", err.Error())
+		os.Exit(ERREXIT_CIPHERDIR)
+	}
+
+	key := make([]byte, cryptfs.KEY_LEN)
+	if zerokey {
+		fmt.Printf("Zerokey mode active: using all-zero dummy master key.\n")
+		fmt.Printf("ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING.\n")
+	} else {
+		cfname := filepath.Join(cipherdir, cryptfs.ConfDefaultName)
+		_, err = os.Stat(cfname)
+		if err != nil {
+			fmt.Printf("Error: %s not found in CIPHERDIR\n", cryptfs.ConfDefaultName)
+			fmt.Printf("Please run \"%s --init %s\" first\n", PROGRAM_NAME, flag.Arg(0))
+			os.Exit(ERREXIT_LOADCONF)
+		}
+		fmt.Printf("Password: ")
+		password := readPassword()
+		fmt.Printf("\nDecrypting master key... ")
+		key, err = cryptfs.LoadConfFile(cfname, password)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(ERREXIT_LOADCONF)
+		}
+		fmt.Printf("Success\n")
+		printMasterKey(key)
+	}
+
+	pathfsFrontend(key, cipherdir, mountpoint, fusedebug)
+}
+
+// printMasterKey - remind the user that he should store the master key in
+// a safe place
+func printMasterKey(key []byte) {
+	h := hex.EncodeToString(key)
+	// Make it less scary by splitting it up in chunks
+	h = h[0:8] + "-" + h[8:16] + "-" + h[16:24] + "-" + h[24:32]
+
+	fmt.Printf(`
+WARNING:
+  If the gocryptfs config file becomes corrupted or you ever
+  forget your password, there is only one hope for recovery:
+  The master key. Print it to a piece of paper and store it in a drawer.
+
+  Master key: %s
+
+`, h)
+}
+
+func readPasswordTwice() string {
+	fmt.Printf("Password: ")
+	p1 := readPassword()
+	fmt.Printf("\nRepeat: ")
+	p2 := readPassword()
+	fmt.Printf("\n")
+	if p1 != p2 {
+		fmt.Printf("Passwords do not match\n")
+		os.Exit(ERREXIT_PASSWORD)
+	}
+	return p1
+}
+
+// Get password from terminal
+func readPassword() string {
+	fd := int(os.Stdin.Fd())
+	p, err := terminal.ReadPassword(fd)
+	if err != nil {
+		fmt.Printf("Error: Could not read password: %s\n")
+		os.Exit(ERREXIT_PASSWORD)
+	}
+	return string(p)
+}
+
+func dirEmpty(dir string) bool {
+	entries, err := ioutil.ReadDir(dir)
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(ERREXIT_CIPHERDIR)
+	}
+	if len(entries) == 0 {
+		return true
+	}
+	return false
+}
+
+func pathfsFrontend(key []byte, cipherdir string, mountpoint string, debug bool) {
+
+	finalFs := pathfs_frontend.NewFS(key, cipherdir, USE_OPENSSL)
+	pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
+	pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts)
+	fuseOpts := &nodefs.Options{
+		// These options are to be compatible with libfuse defaults,
+		// making benchmarking easier.
+		NegativeTimeout: time.Second,
+		AttrTimeout:     time.Second,
+		EntryTimeout:    time.Second,
+	}
+	conn := nodefs.NewFileSystemConnector(pathFs.Root(), fuseOpts)
+	var mOpts fuse.MountOptions
+	mOpts.AllowOther = false
+	// Set values shown in "df -T" and friends
+	// First column, "Filesystem"
+	mOpts.Options = append(mOpts.Options, "fsname="+cipherdir)
+	// Second column, "Type", will be shown as "fuse." + Name
+	mOpts.Name = "gocryptfs"
+
+	state, err := fuse.NewServer(conn.RawFS(), mountpoint, &mOpts)
+	if err != nil {
+		fmt.Printf("Mount fail: %v\n", err)
+		os.Exit(1)
+	}
+	state.SetDebug(debug)
+
+	fmt.Println("Mounted.")
+	state.Serve()
+}
diff --git a/gocryptfs_main/main_test.go b/gocryptfs_main/main_test.go
new file mode 100644
index 0000000..218c256
--- /dev/null
+++ b/gocryptfs_main/main_test.go
@@ -0,0 +1,265 @@
+package main
+
+import (
+	"bytes"
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"testing"
+	"time"
+)
+
+const tmpDir = "../tmp/"
+const plainDir = tmpDir + "plain/"
+const cipherDir = tmpDir + "cipher/"
+
+func unmount() error {
+	fu := exec.Command("fusermount", "-z", "-u", plainDir)
+	fu.Stdout = os.Stdout
+	fu.Stderr = os.Stderr
+	return fu.Run()
+}
+
+func md5fn(filename string) string {
+	buf, err := ioutil.ReadFile(filename)
+	if err != nil {
+		fmt.Printf("ReadFile: %v\n", err)
+		return ""
+	}
+	rawHash := md5.Sum(buf)
+	hash := hex.EncodeToString(rawHash[:])
+	return hash
+}
+
+func TestMain(m *testing.M) {
+
+	fu := exec.Command("fusermount", "-z", "-u", plainDir)
+	fu.Run()
+
+	os.RemoveAll(tmpDir)
+
+	err := os.MkdirAll(plainDir, 0777)
+	if err != nil {
+		panic("Could not create plainDir")
+	}
+
+	err = os.MkdirAll(cipherDir, 0777)
+	if err != nil {
+		panic("Could not create cipherDir")
+	}
+
+	//c := exec.Command("./gocryptfs", "--zerokey", "--cpuprofile", "/tmp/gcfs.cpu", cipherDir, plainDir)
+	c := exec.Command("./gocryptfs_main", "--zerokey", cipherDir, plainDir)
+	c.Stdout = os.Stdout
+	c.Stderr = os.Stderr
+	go c.Run()
+
+	time.Sleep(3 * time.Second)
+
+	r := m.Run()
+
+	unmount()
+	os.Exit(r)
+}
+
+func testWriteN(t *testing.T, fn string, n int) string {
+	file, err := os.Create(plainDir + fn)
+	if err != nil {
+		t.FailNow()
+	}
+
+	d := make([]byte, n)
+	written, err := file.Write(d)
+	if err != nil || written != len(d) {
+		fmt.Printf("err=\"%s\", written=%d\n", err, written)
+		t.Fail()
+	}
+	file.Close()
+
+	bin := md5.Sum(d)
+	hashWant := hex.EncodeToString(bin[:])
+
+	hashActual := md5fn(plainDir + fn)
+
+	if hashActual != hashWant {
+		fmt.Printf("hashWant=%s hashActual=%s\n", hashWant, hashActual)
+		t.Fail()
+	}
+
+	return hashActual
+}
+
+func TestWrite10(t *testing.T) {
+	testWriteN(t, "10", 10)
+}
+
+func TestWrite100(t *testing.T) {
+	testWriteN(t, "100", 100)
+}
+
+func TestWrite1M(t *testing.T) {
+	testWriteN(t, "1M", 1024*1024)
+}
+
+func TestWrite1Mx100(t *testing.T) {
+	hashWant := testWriteN(t, "1Mx100", 1024*1024)
+	// Read and check 100 times to catch race conditions
+	var i int
+	for i = 0; i < 100; i++ {
+		hashActual := md5fn(plainDir + "1M")
+		if hashActual != hashWant {
+			fmt.Printf("Read corruption in loop # %d\n", i)
+			t.FailNow()
+		} else {
+			//fmt.Print(".")
+		}
+	}
+}
+
+func TestTruncate(t *testing.T) {
+	fn := plainDir + "truncate"
+	file, err := os.Create(fn)
+	if err != nil {
+		t.FailNow()
+	}
+	// Grow to two blocks
+	file.Truncate(7000)
+	if md5fn(fn) != "95d4ec7038e3e4fdbd5f15c34c3f0b34" {
+		t.Errorf("Fail 7000")
+	}
+	// Shrink - needs RMW
+	file.Truncate(6999)
+	if md5fn(fn) != "35fd15873ec6c35380064a41b9b9683b" {
+		t.Errorf("Fail 6999")
+	}
+	// Shrink to one partial block
+	file.Truncate(465)
+	if md5fn(fn) != "a1534d6e98a6b21386456a8f66c55260" {
+		t.Errorf("Fail 465")
+	}
+	// Grow to exactly one block
+	file.Truncate(4096)
+	if md5fn(fn) != "620f0b67a91f7f74151bc5be745b7110" {
+		t.Errorf("Fail 4096")
+	}
+}
+
+func TestAppend(t *testing.T) {
+	fn := plainDir + "append"
+	file, err := os.Create(fn)
+	if err != nil {
+		t.FailNow()
+	}
+	data := []byte("testdata123456789") // length 17
+	var buf bytes.Buffer
+	var hashWant string
+	for i := 0; i <= 500; i++ {
+		file.Write(data)
+		buf.Write(data)
+		bin := md5.Sum(buf.Bytes())
+		hashWant = hex.EncodeToString(bin[:])
+		hashActual := md5fn(fn)
+		if hashWant != hashActual {
+			t.FailNow()
+		}
+	}
+
+	// Overwrite with the same data
+	// Hash must stay the same
+	file.Seek(0, 0)
+	for i := 0; i <= 500; i++ {
+		file.Write(data)
+		hashActual := md5fn(fn)
+		if hashWant != hashActual {
+			t.FailNow()
+		}
+	}
+}
+
+// Create a file with holes by writing to offset 0 (block #0) and
+// offset 4096 (block #1).
+func TestFileHoles(t *testing.T) {
+	fn := plainDir + "fileholes"
+	file, err := os.Create(fn)
+	if err != nil {
+		t.Errorf("file create failed")
+	}
+	foo := []byte("foo")
+	file.Write(foo)
+	file.WriteAt(foo, 4096)
+	_, err = ioutil.ReadFile(fn)
+	if err != nil {
+		t.Error(err)
+	}
+}
+
+func BenchmarkStreamWrite(t *testing.B) {
+	buf := make([]byte, 1024*1024)
+	t.SetBytes(int64(len(buf)))
+
+	file, err := os.Create(plainDir + "BenchmarkWrite")
+	if err != nil {
+		t.FailNow()
+	}
+
+	t.ResetTimer()
+	var i int
+	for i = 0; i < t.N; i++ {
+		written, err := file.Write(buf)
+		if err != nil {
+			fmt.Printf("err=\"%s\", written=%d\n", err.Error(), written)
+			t.FailNow()
+		}
+	}
+	file.Close()
+}
+
+func BenchmarkStreamRead(t *testing.B) {
+	buf := make([]byte, 1024*1024)
+	t.SetBytes(int64(len(buf)))
+
+	fn := plainDir + "BenchmarkWrite"
+	fi, _ := os.Stat(fn)
+	mb := int(fi.Size() / 1024 / 1024)
+
+	if t.N > mb {
+		// Grow file so we can satisfy the test
+		//fmt.Printf("Growing file to %d MB... ", t.N)
+		f2, err := os.OpenFile(fn, os.O_WRONLY|os.O_APPEND, 0666)
+		if err != nil {
+			fmt.Println(err)
+			t.FailNow()
+		}
+		for h := 0; h < t.N-mb; h++ {
+			_, err = f2.Write(buf)
+			if err != nil {
+				fmt.Println(err)
+				t.FailNow()
+			}
+		}
+		f2.Close()
+		//fmt.Printf("done\n")
+	}
+
+	file, err := os.Open(plainDir + "BenchmarkWrite")
+	if err != nil {
+		t.FailNow()
+	}
+	t.ResetTimer()
+	var i int
+	for i = 0; i < t.N; i++ {
+		_, err := file.Read(buf)
+		if err == io.EOF {
+			fmt.Printf("Test file too small\n")
+			t.SkipNow()
+		} else if err != nil {
+			fmt.Println(err)
+			t.FailNow()
+		}
+	}
+	file.Close()
+}
diff --git a/main.go b/main.go
deleted file mode 100644
index ae3974e..0000000
--- a/main.go
+++ /dev/null
@@ -1,222 +0,0 @@
-package main
-
-import (
-	"encoding/hex"
-	"flag"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"runtime"
-	"runtime/pprof"
-	"time"
-
-	"github.com/rfjakob/gocryptfs/cryptfs"
-	"github.com/rfjakob/gocryptfs/pathfs_frontend"
-
-	"golang.org/x/crypto/ssh/terminal"
-
-	"github.com/hanwen/go-fuse/fuse"
-	"github.com/hanwen/go-fuse/fuse/nodefs"
-	"github.com/hanwen/go-fuse/fuse/pathfs"
-)
-
-const (
-	USE_CLUEFS   = false // Use cluefs or pathfs FUSE frontend
-	USE_OPENSSL  = true  // 3x speed increase compared to Go's built-in GCM
-	PATHFS_DEBUG = false
-
-	PROGRAM_NAME = "gocryptfs"
-
-	// Exit codes
-	ERREXIT_USAGE     = 1
-	ERREXIT_NEWFS     = 2
-	ERREXIT_MOUNT     = 3
-	ERREXIT_SERVE     = 4
-	ERREXIT_MOUNT2    = 5
-	ERREXIT_CIPHERDIR = 6
-	ERREXIT_INIT      = 7
-	ERREXIT_LOADCONF  = 8
-	ERREXIT_PASSWORD  = 9
-)
-
-func initDir(dirArg string) {
-	dir, _ := filepath.Abs(dirArg)
-
-	if dirEmpty(dir) == false {
-		fmt.Printf("Error: Directory \"%s\" is not empty\n", dirArg)
-		os.Exit(ERREXIT_INIT)
-	}
-
-	confName := filepath.Join(dir, cryptfs.ConfDefaultName)
-	fmt.Printf("Choose a password for protecting your files.\n")
-	password := readPasswordTwice()
-	err := cryptfs.CreateConfFile(confName, password)
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(ERREXIT_INIT)
-	}
-	fmt.Printf("The filesystem is now ready for mounting.\n")
-	os.Exit(0)
-}
-
-func main() {
-	runtime.GOMAXPROCS(4)
-
-	// Parse command line arguments
-	var debug, init, zerokey, fusedebug bool
-
-	flag.BoolVar(&debug, "debug", false, "Enable debug output")
-	flag.BoolVar(&fusedebug, "fusedebug", false, "Enable fuse library debug output")
-	flag.BoolVar(&init, "init", false, "Initialize encrypted directory")
-	flag.BoolVar(&zerokey, "zerokey", false, "Use all-zero dummy master key")
-	var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
-
-	flag.Parse()
-	if *cpuprofile != "" {
-		f, err := os.Create(*cpuprofile)
-		if err != nil {
-			fmt.Println(err)
-			os.Exit(ERREXIT_INIT)
-		}
-		fmt.Printf("Writing CPU profile to %s\n", *cpuprofile)
-		pprof.StartCPUProfile(f)
-		defer pprof.StopCPUProfile()
-	}
-	if debug {
-		cryptfs.Debug.Enable()
-		cryptfs.Debug.Printf("Debug output enabled\n")
-	}
-	if init {
-		if flag.NArg() != 1 {
-			fmt.Printf("usage: %s --init CIPHERDIR\n", PROGRAM_NAME)
-			os.Exit(ERREXIT_USAGE)
-		}
-		initDir(flag.Arg(0))
-	}
-	if flag.NArg() < 2 {
-		fmt.Printf("usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT\n", PROGRAM_NAME)
-		os.Exit(ERREXIT_USAGE)
-	}
-	cipherdir, _ := filepath.Abs(flag.Arg(0))
-	mountpoint, _ := filepath.Abs(flag.Arg(1))
-	cryptfs.Debug.Printf("cipherdir=%s\nmountpoint=%s\n", cipherdir, mountpoint)
-
-	_, err := os.Stat(cipherdir)
-	if err != nil {
-		fmt.Printf("Cipherdir: %s\n", err.Error())
-		os.Exit(ERREXIT_CIPHERDIR)
-	}
-
-	key := make([]byte, cryptfs.KEY_LEN)
-	if zerokey {
-		fmt.Printf("Zerokey mode active: using all-zero dummy master key.\n")
-		fmt.Printf("ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING.\n")
-	} else {
-		cfname := filepath.Join(cipherdir, cryptfs.ConfDefaultName)
-		_, err = os.Stat(cfname)
-		if err != nil {
-			fmt.Printf("Error: %s not found in CIPHERDIR\n", cryptfs.ConfDefaultName)
-			fmt.Printf("Please run \"%s --init %s\" first\n", PROGRAM_NAME, flag.Arg(0))
-			os.Exit(ERREXIT_LOADCONF)
-		}
-		fmt.Printf("Password: ")
-		password := readPassword()
-		fmt.Printf("\nDecrypting master key... ")
-		key, err = cryptfs.LoadConfFile(cfname, password)
-		if err != nil {
-			fmt.Println(err)
-			os.Exit(ERREXIT_LOADCONF)
-		}
-		fmt.Printf("Success\n")
-		printMasterKey(key)
-	}
-
-	pathfsFrontend(key, cipherdir, mountpoint, fusedebug)
-}
-
-// printMasterKey - remind the user that he should store the master key in
-// a safe place
-func printMasterKey(key []byte) {
-	h := hex.EncodeToString(key)
-	// Make it less scary by splitting it up in chunks
-	h = h[0:8] + "-" + h[8:16] + "-" + h[16:24] + "-" + h[24:32]
-
-	fmt.Printf(`
-WARNING:
-  If the gocryptfs config file becomes corrupted or you ever
-  forget your password, there is only one hope for recovery:
-  The master key. Print it to a piece of paper and store it in a drawer.
-
-  Master key: %s
-
-`, h)
-}
-
-func readPasswordTwice() string {
-	fmt.Printf("Password: ")
-	p1 := readPassword()
-	fmt.Printf("\nRepeat: ")
-	p2 := readPassword()
-	fmt.Printf("\n")
-	if p1 != p2 {
-		fmt.Printf("Passwords do not match\n")
-		os.Exit(ERREXIT_PASSWORD)
-	}
-	return p1
-}
-
-// Get password from terminal
-func readPassword() string {
-	fd := int(os.Stdin.Fd())
-	p, err := terminal.ReadPassword(fd)
-	if err != nil {
-		fmt.Printf("Error: Could not read password: %s\n")
-		os.Exit(ERREXIT_PASSWORD)
-	}
-	return string(p)
-}
-
-func dirEmpty(dir string) bool {
-	entries, err := ioutil.ReadDir(dir)
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(ERREXIT_CIPHERDIR)
-	}
-	if len(entries) == 0 {
-		return true
-	}
-	return false
-}
-
-func pathfsFrontend(key []byte, cipherdir string, mountpoint string, debug bool) {
-
-	finalFs := pathfs_frontend.NewFS(key, cipherdir, USE_OPENSSL)
-	pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
-	pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts)
-	fuseOpts := &nodefs.Options{
-		// These options are to be compatible with libfuse defaults,
-		// making benchmarking easier.
-		NegativeTimeout: time.Second,
-		AttrTimeout:     time.Second,
-		EntryTimeout:    time.Second,
-	}
-	conn := nodefs.NewFileSystemConnector(pathFs.Root(), fuseOpts)
-	var mOpts fuse.MountOptions
-	mOpts.AllowOther = false
-	// Set values shown in "df -T" and friends
-	// First column, "Filesystem"
-	mOpts.Options = append(mOpts.Options, "fsname="+cipherdir)
-	// Second column, "Type", will be shown as "fuse." + Name
-	mOpts.Name = "gocryptfs"
-
-	state, err := fuse.NewServer(conn.RawFS(), mountpoint, &mOpts)
-	if err != nil {
-		fmt.Printf("Mount fail: %v\n", err)
-		os.Exit(1)
-	}
-	state.SetDebug(debug)
-
-	fmt.Println("Mounted.")
-	state.Serve()
-}
diff --git a/main_test.go b/main_test.go
deleted file mode 100644
index 2f606bc..0000000
--- a/main_test.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package main
-
-import (
-	"bytes"
-	"crypto/md5"
-	"encoding/hex"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"os/exec"
-	"testing"
-	"time"
-)
-
-const tmpDir = "tmp/"
-const plainDir = tmpDir + "plain/"
-const cipherDir = tmpDir + "cipher/"
-
-func unmount() error {
-	fu := exec.Command("fusermount", "-z", "-u", plainDir)
-	fu.Stdout = os.Stdout
-	fu.Stderr = os.Stderr
-	return fu.Run()
-}
-
-func md5fn(filename string) string {
-	buf, err := ioutil.ReadFile(filename)
-	if err != nil {
-		fmt.Printf("ReadFile: %v\n", err)
-		return ""
-	}
-	rawHash := md5.Sum(buf)
-	hash := hex.EncodeToString(rawHash[:])
-	return hash
-}
-
-func TestMain(m *testing.M) {
-
-	fu := exec.Command("fusermount", "-z", "-u", plainDir)
-	fu.Run()
-
-	os.RemoveAll(tmpDir)
-
-	err := os.MkdirAll(plainDir, 0777)
-	if err != nil {
-		panic("Could not create plainDir")
-	}
-
-	err = os.MkdirAll(cipherDir, 0777)
-	if err != nil {
-		panic("Could not create cipherDir")
-	}
-
-	//c := exec.Command("./gocryptfs", "--zerokey", "--cpuprofile", "/tmp/gcfs.cpu", cipherDir, plainDir)
-	c := exec.Command("./gocryptfs", "--zerokey", cipherDir, plainDir)
-	c.Stdout = os.Stdout
-	c.Stderr = os.Stderr
-	go c.Run()
-
-	time.Sleep(3 * time.Second)
-
-	r := m.Run()
-
-	unmount()
-	os.Exit(r)
-}
-
-func testWriteN(t *testing.T, fn string, n int) string {
-	file, err := os.Create(plainDir + fn)
-	if err != nil {
-		t.FailNow()
-	}
-
-	d := make([]byte, n)
-	written, err := file.Write(d)
-	if err != nil || written != len(d) {
-		fmt.Printf("err=\"%s\", written=%d\n", err, written)
-		t.Fail()
-	}
-	file.Close()
-
-	bin := md5.Sum(d)
-	hashWant := hex.EncodeToString(bin[:])
-
-	hashActual := md5fn(plainDir + fn)
-
-	if hashActual != hashWant {
-		fmt.Printf("hashWant=%s hashActual=%s\n", hashWant, hashActual)
-		t.Fail()
-	}
-
-	return hashActual
-}
-
-func TestWrite10(t *testing.T) {
-	testWriteN(t, "10", 10)
-}
-
-func TestWrite100(t *testing.T) {
-	testWriteN(t, "100", 100)
-}
-
-func TestWrite1M(t *testing.T) {
-	testWriteN(t, "1M", 1024*1024)
-}
-
-func TestWrite1Mx100(t *testing.T) {
-	hashWant := testWriteN(t, "1Mx100", 1024*1024)
-	// Read and check 100 times to catch race conditions
-	var i int
-	for i = 0; i < 100; i++ {
-		hashActual := md5fn(plainDir + "1M")
-		if hashActual != hashWant {
-			fmt.Printf("Read corruption in loop # %d\n", i)
-			t.FailNow()
-		} else {
-			//fmt.Print(".")
-		}
-	}
-}
-
-func TestTruncate(t *testing.T) {
-	fn := plainDir + "truncate"
-	file, err := os.Create(fn)
-	if err != nil {
-		t.FailNow()
-	}
-	// Grow to two blocks
-	file.Truncate(7000)
-	if md5fn(fn) != "95d4ec7038e3e4fdbd5f15c34c3f0b34" {
-		t.Errorf("Fail 7000")
-	}
-	// Shrink - needs RMW
-	file.Truncate(6999)
-	if md5fn(fn) != "35fd15873ec6c35380064a41b9b9683b" {
-		t.Errorf("Fail 6999")
-	}
-	// Shrink to one partial block
-	file.Truncate(465)
-	if md5fn(fn) != "a1534d6e98a6b21386456a8f66c55260" {
-		t.Errorf("Fail 465")
-	}
-	// Grow to exactly one block
-	file.Truncate(4096)
-	if md5fn(fn) != "620f0b67a91f7f74151bc5be745b7110" {
-		t.Errorf("Fail 4096")
-	}
-}
-
-func TestAppend(t *testing.T) {
-	fn := plainDir + "append"
-	file, err := os.Create(fn)
-	if err != nil {
-		t.FailNow()
-	}
-	data := []byte("testdata123456789") // length 17
-	var buf bytes.Buffer
-	var hashWant string
-	for i := 0; i <= 500; i++ {
-		file.Write(data)
-		buf.Write(data)
-		bin := md5.Sum(buf.Bytes())
-		hashWant = hex.EncodeToString(bin[:])
-		hashActual := md5fn(fn)
-		if hashWant != hashActual {
-			t.FailNow()
-		}
-	}
-
-	// Overwrite with the same data
-	// Hash must stay the same
-	file.Seek(0, 0)
-	for i := 0; i <= 500; i++ {
-		file.Write(data)
-		hashActual := md5fn(fn)
-		if hashWant != hashActual {
-			t.FailNow()
-		}
-	}
-}
-
-// Create a file with holes by writing to offset 0 (block #0) and
-// offset 4096 (block #1).
-func TestFileHoles(t *testing.T) {
-	fn := plainDir + "fileholes"
-	file, err := os.Create(fn)
-	if err != nil {
-		t.Errorf("file create failed")
-	}
-	foo := []byte("foo")
-	file.Write(foo)
-	file.WriteAt(foo, 4096)
-	_, err = ioutil.ReadFile(fn)
-	if err != nil {
-		t.Error(err)
-	}
-}
-
-func BenchmarkStreamWrite(t *testing.B) {
-	buf := make([]byte, 1024*1024)
-	t.SetBytes(int64(len(buf)))
-
-	file, err := os.Create(plainDir + "BenchmarkWrite")
-	if err != nil {
-		t.FailNow()
-	}
-
-	t.ResetTimer()
-	var i int
-	for i = 0; i < t.N; i++ {
-		written, err := file.Write(buf)
-		if err != nil {
-			fmt.Printf("err=\"%s\", written=%d\n", err.Error(), written)
-			t.FailNow()
-		}
-	}
-	file.Close()
-}
-
-func BenchmarkStreamRead(t *testing.B) {
-	buf := make([]byte, 1024*1024)
-	t.SetBytes(int64(len(buf)))
-
-	fn := plainDir + "BenchmarkWrite"
-	fi, _ := os.Stat(fn)
-	mb := int(fi.Size() / 1024 / 1024)
-
-	if t.N > mb {
-		// Grow file so we can satisfy the test
-		//fmt.Printf("Growing file to %d MB... ", t.N)
-		f2, err := os.OpenFile(fn, os.O_WRONLY|os.O_APPEND, 0666)
-		if err != nil {
-			fmt.Println(err)
-			t.FailNow()
-		}
-		for h := 0; h < t.N-mb; h++ {
-			_, err = f2.Write(buf)
-			if err != nil {
-				fmt.Println(err)
-				t.FailNow()
-			}
-		}
-		f2.Close()
-		//fmt.Printf("done\n")
-	}
-
-	file, err := os.Open(plainDir + "BenchmarkWrite")
-	if err != nil {
-		t.FailNow()
-	}
-	t.ResetTimer()
-	var i int
-	for i = 0; i < t.N; i++ {
-		_, err := file.Read(buf)
-		if err == io.EOF {
-			fmt.Printf("Test file too small\n")
-			t.SkipNow()
-		} else if err != nil {
-			fmt.Println(err)
-			t.FailNow()
-		}
-	}
-	file.Close()
-}
-- 
cgit v1.2.3