diff options
| -rwxr-xr-x | test.bash | 3 | ||||
| -rw-r--r-- | tests/cli/cli_test.go | 33 | ||||
| -rw-r--r-- | tests/root_test/root_test.go | 178 | 
3 files changed, 203 insertions, 11 deletions
| @@ -85,8 +85,7 @@ if grep -R "panic(" ./*.go internal ; then  fi  # All functions from the commit msg in https://go-review.googlesource.com/c/go/+/210639 -if grep -R -E 'syscall.(Setegid|Seteuid|Setgroups|Setgid|Setregid|Setreuid|Setresgid|Setresuid|Setuid)\(' \ -	./*.go internal ; then +if find . -type f -name \*.go -print0 | xargs -0 grep -E 'syscall.(Setegid|Seteuid|Setgroups|Setgid|Setregid|Setreuid|Setresgid|Setresuid|Setuid)\(' ; then  	echo "$MYNAME: You probably want to use unix.Setgroups and friends. See the comments in OpenatUser() for why."  	exit 1  fi diff --git a/tests/cli/cli_test.go b/tests/cli/cli_test.go index 23cea05..9d25062 100644 --- a/tests/cli/cli_test.go +++ b/tests/cli/cli_test.go @@ -9,6 +9,7 @@ import (  	"os/exec"  	"strconv"  	"strings" +	"sync"  	"syscall"  	"testing"  	"time" @@ -869,3 +870,35 @@ func TestSharedstorage(t *testing.T) {  		t.Fatal(st2.Size)  	}  } + +// Test that the filesystem is immediately ready for Creat() after mount returns +func TestMountCreat(t *testing.T) { +	const concurrency = 2 +	const repeat = 2 + +	dir := test_helpers.InitFS(t) +	mnt := dir + ".mnt" +	err := os.Mkdir(mnt, 0700) +	if err != nil { +		t.Fatal(err) +	} + +	for j := 0; j < repeat; j++ { +		test_helpers.MountOrFatal(t, dir, mnt, "-extpass=echo test") +		var wg sync.WaitGroup +		wg.Add(concurrency) +		for i := 0; i < concurrency; i++ { +			go func(i int) { +				path := fmt.Sprintf("%s/%d", mnt, i) +				fd, err := syscall.Creat(path, 0600) +				syscall.Close(fd) +				if err != nil { +					t.Errorf("Creat %q: %v", path, err) +				} +				wg.Done() +			}(i) +		} +		wg.Wait() +		test_helpers.UnmountPanic(mnt) +	} +} diff --git a/tests/root_test/root_test.go b/tests/root_test/root_test.go index 26caafc..079c03b 100644 --- a/tests/root_test/root_test.go +++ b/tests/root_test/root_test.go @@ -1,11 +1,19 @@ +// Package root_test contains tests that need root +// permissions to run  package root_test  import ( +	"io/ioutil"  	"os" +	"os/exec" +	"path/filepath"  	"runtime" +	"sync"  	"syscall"  	"testing" +	"golang.org/x/sys/unix" +  	"github.com/rfjakob/gocryptfs/tests/test_helpers"  ) @@ -13,25 +21,57 @@ func asUser(uid int, gid int, supplementaryGroups []int, f func() error) error {  	runtime.LockOSThread()  	defer runtime.UnlockOSThread() -	err := syscall.Setgroups(supplementaryGroups) +	err := unix.Setgroups(supplementaryGroups)  	if err != nil {  		return err  	} -	defer syscall.Setgroups(nil) - -	err = syscall.Setregid(-1, gid) +	defer func() { +		err = unix.Setgroups(nil) +		if err != nil { +			panic(err) +		} +	}() +	err = unix.Setregid(-1, gid)  	if err != nil {  		return err  	} -	defer syscall.Setregid(-1, 0) - -	err = syscall.Setreuid(-1, uid) +	defer func() { +		err = unix.Setregid(-1, 0) +		if err != nil { +			panic(err) +		} +	}() +	err = unix.Setreuid(-1, uid)  	if err != nil {  		return err  	} -	defer syscall.Setreuid(-1, 0) +	defer func() { +		err = unix.Setreuid(-1, 0) +		if err != nil { +			panic(err) +		} +	}() + +	ret := f() -	return f() +	// Also reset the saved user id (suid) and saved group id (sgid) to prevent +	// bizarre failures in later tests. +	// +	// Yes, the kernel checks that *all of them* match: +	// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/fuse/dir.c?h=v5.12-rc2#n1193 +	// +	// How to check: +	// ps -o tid,pid,euid,ruid,suid,egid,rgid,sgid,cmd -eL +	err = unix.Setresuid(0, 0, 0) +	if err != nil { +		panic(err) +	} +	err = unix.Setresgid(0, 0, 0) +	if err != nil { +		panic(err) +	} + +	return ret  }  func TestSupplementaryGroups(t *testing.T) { @@ -73,3 +113,123 @@ func TestSupplementaryGroups(t *testing.T) {  		t.Error(err)  	}  } + +func writeTillFull(t *testing.T, path string) (int, syscall.Errno) { +	runtime.LockOSThread() +	defer runtime.UnlockOSThread() + +	fd, err := syscall.Creat(path, 0600) +	if err != nil { +		return 0, err.(syscall.Errno) +	} +	defer syscall.Close(fd) +	// Write in 100.000 byte-blocks, which is not aligend to the +	// underlying block size +	buf := make([]byte, 100000) +	var sz int +	for { +		n, err := syscall.Write(fd, buf) +		if err != nil { +			return sz, err.(syscall.Errno) +		} +		sz += n +	} +	return sz, 0 +} + +func TestDiskFull(t *testing.T) { +	if os.Getuid() != 0 { +		t.Skip("must run as root") +	} + +	// Create 10 MB file full of zeros +	ext4img := filepath.Join(test_helpers.TmpDir, t.Name()+".ext4") +	f, err := os.Create(ext4img) +	if err != nil { +		t.Fatal(err) +	} +	defer f.Close() +	err = f.Truncate(10 * 1024 * 1024) +	if err != nil { +		t.Fatal(err) +	} + +	// Format as ext4 +	cmd := exec.Command("mkfs.ext4", ext4img) +	out, err := cmd.CombinedOutput() +	if err != nil { +		t.Log(string(out)) +		t.Fatal(err) +	} + +	// Mount ext4 +	ext4mnt := ext4img + ".mnt" +	err = os.Mkdir(ext4mnt, 0600) +	if err != nil { +		t.Fatal(err) +	} +	cmd = exec.Command("mount", ext4img, ext4mnt) +	out, err = cmd.CombinedOutput() +	if err != nil { +		t.Log(string(out)) +		t.Fatal(err) +	} +	defer syscall.Unlink(ext4img) +	defer syscall.Unmount(ext4mnt, 0) + +	// gocryptfs -init +	cipherdir := ext4mnt + "/a" +	if err = os.Mkdir(cipherdir, 0600); err != nil { +		t.Fatal(err) +	} +	cmd = exec.Command(test_helpers.GocryptfsBinary, "-q", "-init", "-extpass", "echo test", "-scryptn=10", cipherdir) +	out, err = cmd.CombinedOutput() +	if err != nil { +		t.Log(string(out)) +		t.Fatal(err) +	} + +	// Mount gocryptfs +	mnt := ext4mnt + "/b" +	err = os.Mkdir(mnt, 0600) +	if err != nil { +		t.Fatal(err) +	} +	test_helpers.MountOrFatal(t, cipherdir, mnt, "-extpass", "echo test") +	defer test_helpers.UnmountPanic(mnt) + +	// Write till we get ENOSPC +	var err1, err2 error +	var sz1, sz2 int +	var wg sync.WaitGroup +	wg.Add(2) +	go func() { +		sz1, err1 = writeTillFull(t, mnt+"/foo1") +		wg.Done() +	}() +	go func() { +		sz2, err2 = writeTillFull(t, mnt+"/foo2") +		wg.Done() +	}() +	wg.Wait() +	if err1 != syscall.ENOSPC || err2 != syscall.ENOSPC { +		t.Fatalf("err1=%v, err2=%v", err1, err2) +	} +	t.Logf("sz1=%d, sz2=%d", sz1, sz2) + +	foo1, err := ioutil.ReadFile(mnt + "/foo1") +	if err != nil { +		t.Fatal(err) +	} +	if len(foo1) != sz1 { +		t.Fail() +	} + +	foo2, err := ioutil.ReadFile(mnt + "/foo2") +	if err != nil { +		t.Fatal(err) +	} +	if len(foo2) != sz2 { +		t.Fail() +	} +} | 
