diff options
| -rw-r--r-- | internal/fusefrontend_reverse/excluder.go | 20 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/excluder_test.go | 83 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/mocks_test.go | 32 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/root_node.go | 10 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rpath.go | 4 | 
5 files changed, 139 insertions, 10 deletions
| diff --git a/internal/fusefrontend_reverse/excluder.go b/internal/fusefrontend_reverse/excluder.go index 337c3d2..b6cb961 100644 --- a/internal/fusefrontend_reverse/excluder.go +++ b/internal/fusefrontend_reverse/excluder.go @@ -15,15 +15,19 @@ import (  // prepareExcluder creates an object to check if paths are excluded  // based on the patterns specified in the command line.  func prepareExcluder(args fusefrontend.Args) *ignore.GitIgnore { -	if len(args.Exclude) > 0 || len(args.ExcludeWildcard) > 0 || len(args.ExcludeFrom) > 0 { -		excluder, err := ignore.CompileIgnoreLines(getExclusionPatterns(args)...) -		if err != nil { -			tlog.Fatal.Printf("Error compiling exclusion rules: %q", err) -			os.Exit(exitcodes.ExcludeError) -		} -		return excluder +	if len(args.Exclude) == 0 && len(args.ExcludeWildcard) == 0 && len(args.ExcludeFrom) == 0 { +		return nil +	} +	patterns := getExclusionPatterns(args) +	if len(patterns) == 0 { +		panic(patterns) +	} +	excluder, err := ignore.CompileIgnoreLines(patterns...) +	if err != nil { +		tlog.Fatal.Printf("Error compiling exclusion rules: %v", err) +		os.Exit(exitcodes.ExcludeError)  	} -	return nil +	return excluder  }  // getExclusionPatters prepares a list of patterns to be excluded. diff --git a/internal/fusefrontend_reverse/excluder_test.go b/internal/fusefrontend_reverse/excluder_test.go new file mode 100644 index 0000000..47b430a --- /dev/null +++ b/internal/fusefrontend_reverse/excluder_test.go @@ -0,0 +1,83 @@ +package fusefrontend_reverse + +import ( +	"io/ioutil" +	"os" +	"reflect" +	"testing" + +	"github.com/rfjakob/gocryptfs/internal/fusefrontend" +) + +func TestShouldNoCreateExcluderIfNoPattersWereSpecified(t *testing.T) { +	var args fusefrontend.Args +	excluder := prepareExcluder(args) +	if excluder != nil { +		t.Error("Should not have created excluder") +	} +} + +func TestShouldPrefixExcludeValuesWithSlash(t *testing.T) { +	var args fusefrontend.Args +	args.Exclude = []string{"file1", "dir1/file2.txt"} +	args.ExcludeWildcard = []string{"*~", "build/*.o"} + +	expected := []string{"/file1", "/dir1/file2.txt", "*~", "build/*.o"} + +	patterns := getExclusionPatterns(args) +	if !reflect.DeepEqual(patterns, expected) { +		t.Errorf("expected %q, got %q", expected, patterns) +	} +} + +func TestShouldReadExcludePatternsFromFiles(t *testing.T) { +	tmpfile1, err := ioutil.TempFile("", "excludetest") +	if err != nil { +		t.Fatal(err) +	} +	exclude1 := tmpfile1.Name() +	defer os.Remove(exclude1) +	defer tmpfile1.Close() + +	tmpfile2, err := ioutil.TempFile("", "excludetest") +	if err != nil { +		t.Fatal(err) +	} +	exclude2 := tmpfile2.Name() +	defer os.Remove(exclude2) +	defer tmpfile2.Close() + +	tmpfile1.WriteString("file1.1\n") +	tmpfile1.WriteString("file1.2\n") +	tmpfile2.WriteString("file2.1\n") +	tmpfile2.WriteString("file2.2\n") + +	var args fusefrontend.Args +	args.ExcludeWildcard = []string{"cmdline1"} +	args.ExcludeFrom = []string{exclude1, exclude2} + +	// An empty string is returned for the last empty line +	// It's ignored when the patterns are actually compiled +	expected := []string{"cmdline1", "file1.1", "file1.2", "", "file2.1", "file2.2", ""} + +	patterns := getExclusionPatterns(args) +	if !reflect.DeepEqual(patterns, expected) { +		t.Errorf("expected %q, got %q", expected, patterns) +	} +} + +func TestShouldReturnFalseIfThereAreNoExclusions(t *testing.T) { +	var rfs RootNode +	if rfs.isExcludedPlain("any/path") { +		t.Error("Should not exclude any path if no exclusions were specified") +	} +} + +func TestShouldCallIgnoreParserToCheckExclusion(t *testing.T) { +	rfs, ignorerMock := createRFSWithMocks() + +	rfs.isExcludedPlain("some/path") +	if ignorerMock.calledWith != "some/path" { +		t.Error("Failed to call IgnoreParser") +	} +} diff --git a/internal/fusefrontend_reverse/mocks_test.go b/internal/fusefrontend_reverse/mocks_test.go new file mode 100644 index 0000000..2d14c1d --- /dev/null +++ b/internal/fusefrontend_reverse/mocks_test.go @@ -0,0 +1,32 @@ +package fusefrontend_reverse + +import ( +	"github.com/rfjakob/gocryptfs/internal/nametransform" +) + +type IgnoreParserMock struct { +	toExclude  string +	calledWith string +} + +func (parser *IgnoreParserMock) MatchesPath(f string) bool { +	parser.calledWith = f +	return f == parser.toExclude +} + +type NameTransformMock struct { +	nametransform.NameTransform +} + +func (n *NameTransformMock) DecryptName(cipherName string, iv []byte) (string, error) { +	return "mockdecrypt_" + cipherName, nil +} + +func createRFSWithMocks() (*RootNode, *IgnoreParserMock) { +	ignorerMock := &IgnoreParserMock{} +	nameTransformMock := &NameTransformMock{} +	var rfs RootNode +	rfs.excluder = ignorerMock +	rfs.nameTransform = nameTransformMock +	return &rfs, ignorerMock +} diff --git a/internal/fusefrontend_reverse/root_node.go b/internal/fusefrontend_reverse/root_node.go index 4297ecf..4346306 100644 --- a/internal/fusefrontend_reverse/root_node.go +++ b/internal/fusefrontend_reverse/root_node.go @@ -27,8 +27,8 @@ type RootNode struct {  	nameTransform nametransform.NameTransformer  	// Content encryption helper  	contentEnc *contentenc.ContentEnc -	// Tests whether a path is excluded (hiden) from the user. Used by -exclude. -	excluder ignore.IgnoreParser +	// Tests whether a path is excluded (hidden) from the user. Used by -exclude. +	excluder *ignore.GitIgnore  	// inoMap translates inode numbers from different devices to unique inode  	// numbers.  	inoMap *inomap.InoMap @@ -78,3 +78,9 @@ func (rn *RootNode) findLongnameParent(fd int, diriv []byte, longname string) (p  	}  	return  } + +// isExcludedPlain finds out if the plaintext path "pPath" is +// excluded (used when -exclude is passed by the user). +func (rn *RootNode) isExcludedPlain(pPath string) bool { +	return rn.excluder != nil && rn.excluder.MatchesPath(pPath) +} diff --git a/internal/fusefrontend_reverse/rpath.go b/internal/fusefrontend_reverse/rpath.go index 2ac65be..1e44638 100644 --- a/internal/fusefrontend_reverse/rpath.go +++ b/internal/fusefrontend_reverse/rpath.go @@ -109,6 +109,10 @@ func (rn *RootNode) openBackingDir(cPath string) (dirfd int, pName string, err e  	if err != nil {  		return  	} +	if rn.isExcludedPlain(pRelPath) { +		err = syscall.EPERM +		return +	}  	// Open directory, safe against symlink races  	pDir := filepath.Dir(pRelPath)  	dirfd, err = syscallcompat.OpenDirNofollow(rn.args.Cipherdir, pDir) | 
