From 45795d4717589cdd1c0c0cdf7bb14e86c44e84ee Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 29 Feb 2016 02:34:31 +0000 Subject: [PATCH 1/7] TestSuccessfulMount --- mount_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 mount_test.go diff --git a/mount_test.go b/mount_test.go new file mode 100644 index 0000000..590125e --- /dev/null +++ b/mount_test.go @@ -0,0 +1,61 @@ +package fuse_test + +import ( + "io/ioutil" + "os" + "testing" + + "golang.org/x/net/context" + + "github.com/jacobsa/fuse" + "github.com/jacobsa/fuse/fuseutil" +) + +//////////////////////////////////////////////////////////////////////// +// minimalFS +//////////////////////////////////////////////////////////////////////// + +// A minimal fuseutil.FileSystem that can successfully mount but do nothing +// else. +type minimalFS struct { + fuseutil.NotImplementedFileSystem +} + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func TestSuccessfulMount(t *testing.T) { + ctx := context.Background() + + // Set up a temporary directory. + dir, err := ioutil.TempDir("", "mount_test") + if err != nil { + t.Fatal("ioutil.TempDir: %v", err) + } + + defer os.RemoveAll(dir) + + // Mount. + fs := &minimalFS{} + mfs, err := fuse.Mount( + dir, + fuseutil.NewFileSystemServer(fs), + &fuse.MountConfig{}) + + if err != nil { + t.Fatalf("fuse.Mount: %v", err) + } + + defer func() { + if err := mfs.Join(ctx); err != nil { + t.Errorf("Joining: %v", err) + } + }() + + defer fuse.Unmount(mfs.Dir()) +} + +func TestNonEmptyMountPoint(t *testing.T) { + t.Fatal("TODO") +} From 3d617264014d9cf912958bf63a1b2bf691e240f8 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 29 Feb 2016 02:36:18 +0000 Subject: [PATCH 2/7] TestNonEmptyMountPoint --- mount_test.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/mount_test.go b/mount_test.go index 590125e..13d6bbe 100644 --- a/mount_test.go +++ b/mount_test.go @@ -3,6 +3,8 @@ package fuse_test import ( "io/ioutil" "os" + "path" + "strings" "testing" "golang.org/x/net/context" @@ -57,5 +59,29 @@ func TestSuccessfulMount(t *testing.T) { } func TestNonEmptyMountPoint(t *testing.T) { - t.Fatal("TODO") + // Set up a temporary directory. + dir, err := ioutil.TempDir("", "mount_test") + if err != nil { + t.Fatal("ioutil.TempDir: %v", err) + } + + defer os.RemoveAll(dir) + + // Add a file within it. + err = ioutil.WriteFile(path.Join(dir, "foo"), []byte{}, 0600) + if err != nil { + t.Fatalf("ioutil.WriteFile: %v", err) + } + + // Attempt to mount. + fs := &minimalFS{} + _, err = fuse.Mount( + dir, + fuseutil.NewFileSystemServer(fs), + &fuse.MountConfig{}) + + const want = "not empty" + if err == nil || !strings.Contains(err.Error(), want) { + t.Errorf("Unexpected error: %v", err) + } } From 38175a2e8bc62d9fa2024f06bad7a3c4ccdf0c8c Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 29 Feb 2016 13:38:01 +1100 Subject: [PATCH 3/7] Fix minimalFS on OS X. --- mount_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mount_test.go b/mount_test.go index 13d6bbe..88b5388 100644 --- a/mount_test.go +++ b/mount_test.go @@ -10,6 +10,7 @@ import ( "golang.org/x/net/context" "github.com/jacobsa/fuse" + "github.com/jacobsa/fuse/fuseops" "github.com/jacobsa/fuse/fuseutil" ) @@ -23,6 +24,12 @@ type minimalFS struct { fuseutil.NotImplementedFileSystem } +func (fs *minimalFS) StatFS( + ctx context.Context, + op *fuseops.StatFSOp) (err error) { + return +} + //////////////////////////////////////////////////////////////////////// // Tests //////////////////////////////////////////////////////////////////////// From 8b7833ba01887382f298a1c9b287f2e96924eb4a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 29 Feb 2016 13:40:11 +1100 Subject: [PATCH 4/7] TestNonEmptyMountPoint: clean up in the event of a successful mount. --- mount_test.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mount_test.go b/mount_test.go index 88b5388..f7234d5 100644 --- a/mount_test.go +++ b/mount_test.go @@ -66,6 +66,8 @@ func TestSuccessfulMount(t *testing.T) { } func TestNonEmptyMountPoint(t *testing.T) { + ctx := context.Background() + // Set up a temporary directory. dir, err := ioutil.TempDir("", "mount_test") if err != nil { @@ -82,13 +84,19 @@ func TestNonEmptyMountPoint(t *testing.T) { // Attempt to mount. fs := &minimalFS{} - _, err = fuse.Mount( + mfs, err := fuse.Mount( dir, fuseutil.NewFileSystemServer(fs), &fuse.MountConfig{}) + if err == nil { + fuse.Unmount(mfs.Dir()) + mfs.Join(ctx) + t.Fatal("fuse.Mount returned nil") + } + const want = "not empty" - if err == nil || !strings.Contains(err.Error(), want) { - t.Errorf("Unexpected error: %v", err) + if got := err.Error(); !strings.Contains(got, want) { + t.Errorf("Unexpected error: %v", got) } } From 035636830fa3f224d786bd1b90b8796162bf5124 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 29 Feb 2016 13:41:38 +1100 Subject: [PATCH 5/7] Switch to a test that can pass on OS X, too. It appears osxfuse is happy to mount over a non-empty directory. --- mount_test.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/mount_test.go b/mount_test.go index f7234d5..c27e882 100644 --- a/mount_test.go +++ b/mount_test.go @@ -65,7 +65,7 @@ func TestSuccessfulMount(t *testing.T) { defer fuse.Unmount(mfs.Dir()) } -func TestNonEmptyMountPoint(t *testing.T) { +func TestNonexistentMountPoint(t *testing.T) { ctx := context.Background() // Set up a temporary directory. @@ -76,16 +76,10 @@ func TestNonEmptyMountPoint(t *testing.T) { defer os.RemoveAll(dir) - // Add a file within it. - err = ioutil.WriteFile(path.Join(dir, "foo"), []byte{}, 0600) - if err != nil { - t.Fatalf("ioutil.WriteFile: %v", err) - } - - // Attempt to mount. + // Attempt to mount into a sub-directory that doesn't exist. fs := &minimalFS{} mfs, err := fuse.Mount( - dir, + path.Join(dir, "foo"), fuseutil.NewFileSystemServer(fs), &fuse.MountConfig{}) @@ -95,7 +89,7 @@ func TestNonEmptyMountPoint(t *testing.T) { t.Fatal("fuse.Mount returned nil") } - const want = "not empty" + const want = "no such file" if got := err.Error(); !strings.Contains(got, want) { t.Errorf("Unexpected error: %v", got) } From 3da7af8c8806810583e0cad92ef5654d8b6cb4b2 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 29 Feb 2016 13:44:01 +1100 Subject: [PATCH 6/7] Restore TestNonEmptyMountPoint for Linux. I had forgotten that we explicitly handle the "mount point doesn't exist" case, so that wasn't tickling what I had hoped in fusermount. --- mount_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/mount_test.go b/mount_test.go index c27e882..f7c7edf 100644 --- a/mount_test.go +++ b/mount_test.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "os" "path" + "runtime" "strings" "testing" @@ -65,6 +66,51 @@ func TestSuccessfulMount(t *testing.T) { defer fuse.Unmount(mfs.Dir()) } +func TestNonEmptyMountPoint(t *testing.T) { + ctx := context.Background() + + // osxfuse appears to be happy to mount over a non-empty mount point. + // + // We leave this test in for Linux, because it tickles the behavior of + // fusermount writing to stderr and exiting with an error code. We want to + // make sure that a descriptive error makes it back to the user. + if runtime.GOOS == "darwin" { + return + } + + // Set up a temporary directory. + dir, err := ioutil.TempDir("", "mount_test") + if err != nil { + t.Fatal("ioutil.TempDir: %v", err) + } + + defer os.RemoveAll(dir) + + // Add a file within it. + err = ioutil.WriteFile(path.Join(dir, "foo"), []byte{}, 0600) + if err != nil { + t.Fatalf("ioutil.WriteFile: %v", err) + } + + // Attempt to mount. + fs := &minimalFS{} + mfs, err := fuse.Mount( + dir, + fuseutil.NewFileSystemServer(fs), + &fuse.MountConfig{}) + + if err == nil { + fuse.Unmount(mfs.Dir()) + mfs.Join(ctx) + t.Fatal("fuse.Mount returned nil") + } + + const want = "not empty" + if got := err.Error(); !strings.Contains(got, want) { + t.Errorf("Unexpected error: %v", got) + } +} + func TestNonexistentMountPoint(t *testing.T) { ctx := context.Background() From 2c0c70f477d1f84c59224f3e211b545d7e7352a5 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 29 Feb 2016 02:47:56 +0000 Subject: [PATCH 7/7] Kill off silly fusermount screen scraping. --- mount_linux.go | 61 +++++++------------------------------------------- 1 file changed, 8 insertions(+), 53 deletions(-) diff --git a/mount_linux.go b/mount_linux.go index 94858bd..2df4e78 100644 --- a/mount_linux.go +++ b/mount_linux.go @@ -1,37 +1,14 @@ package fuse import ( - "bufio" + "bytes" "fmt" - "io" - "log" "net" "os" "os/exec" - "sync" "syscall" ) -func lineLogger(wg *sync.WaitGroup, prefix string, r io.ReadCloser) { - defer wg.Done() - - scanner := bufio.NewScanner(r) - for scanner.Scan() { - switch line := scanner.Text(); line { - case `fusermount: failed to open /etc/fuse.conf: Permission denied`: - // Silence this particular message, it occurs way too - // commonly and isn't very relevant to whether the mount - // succeeds or not. - continue - default: - log.Printf("%s: %s", prefix, line) - } - } - if err := scanner.Err(); err != nil { - log.Printf("%s, error reading: %v", prefix, err) - } -} - // Begin the process of mounting at the given directory, returning a connection // to the kernel. Mounting continues in the background, and is complete when an // error is written to the supplied channel. The file system may need to @@ -57,7 +34,9 @@ func mount( readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads") defer readFile.Close() - // Start fusermount, passing it pipes for stdout and stderr. + // Start fusermount, passing it a buffer in which to write stderr. + var stderr bytes.Buffer + cmd := exec.Command( "fusermount", "-o", cfg.toOptionsString(), @@ -67,36 +46,12 @@ func mount( cmd.Env = append(os.Environ(), "_FUSE_COMMFD=3") cmd.ExtraFiles = []*os.File{writeFile} + cmd.Stderr = &stderr - stdout, err := cmd.StdoutPipe() + // Run the command. + err = cmd.Run() if err != nil { - err = fmt.Errorf("StdoutPipe: %v", err) - return - } - - stderr, err := cmd.StderrPipe() - if err != nil { - err = fmt.Errorf("StderrPipe: %v", err) - return - } - - err = cmd.Start() - if err != nil { - err = fmt.Errorf("Starting fusermount: %v", err) - return - } - - // Log fusermount output until it closes stdout and stderr. - var wg sync.WaitGroup - wg.Add(2) - go lineLogger(&wg, "mount helper output", stdout) - go lineLogger(&wg, "mount helper error", stderr) - wg.Wait() - - // Wait for the command. - err = cmd.Wait() - if err != nil { - err = fmt.Errorf("fusermount: %v", err) + err = fmt.Errorf("running fusermount: %v\n\nstderr:\n%s", err, stderr.Bytes()) return }