Skip to content

Commit

Permalink
Fix handling of relative paths for mount points.
Browse files Browse the repository at this point in the history
Flags that specify paths, which are less frequently used, must be
absolute. Unfortunately it would be difficult to fix them in a
straightforward way.

Fixes #147.
  • Loading branch information
jacobsa committed Jan 4, 2016
2 parents 08ac56d + db0c0e2 commit fa4cc5f
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 45 deletions.
6 changes: 3 additions & 3 deletions flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func newApp() (app *cli.App) {
cli.StringFlag{
Name: "key-file",
Value: "",
Usage: "Path to JSON key file for use with GCS. " +
Usage: "Absolute path to JSON key file for use with GCS. " +
"(default: none, Google application default credentials used)",
},

Expand Down Expand Up @@ -164,8 +164,8 @@ func newApp() (app *cli.App) {
cli.StringFlag{
Name: "temp-dir",
Value: "",
Usage: "Temporary directory for local GCS object copies. " +
"(default: system default, likely /tmp)",
Usage: "Absolute path to temporary directory for local GCS object " +
"copies. (default: system default, likely /tmp)",
},

/////////////////////////
Expand Down
58 changes: 39 additions & 19 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"os"
"os/signal"
"path"
"path/filepath"
"runtime"
"runtime/pprof"
"syscall"
Expand Down Expand Up @@ -222,23 +223,11 @@ func getConn(flags *flagStorage) (c gcs.Conn, err error) {
////////////////////////////////////////////////////////////////////////

// Mount the file system according to arguments in the supplied context.
func mountFromContext(
args []string,
func mountWithArgs(
bucketName string,
mountPoint string,
flags *flagStorage,
mountStatus *log.Logger) (mfs *fuse.MountedFileSystem, err error) {
// Extract arguments.
if len(args) != 2 {
err = fmt.Errorf(
"Error: %s takes exactly two arguments. Run `%s --help` for more info.",
path.Base(os.Args[0]),
path.Base(os.Args[0]))

return
}

bucketName := args[0]
mountPoint := args[1]

// Enable invariant checking if requested.
if flags.DebugInvariants {
syncutil.EnableInvariantChecking()
Expand All @@ -254,7 +243,7 @@ func mountFromContext(
}

// Mount the file system.
mfs, err = mount(
mfs, err = mountWithConn(
context.Background(),
bucketName,
mountPoint,
Expand All @@ -263,7 +252,7 @@ func mountFromContext(
mountStatus)

if err != nil {
err = fmt.Errorf("mount: %v", err)
err = fmt.Errorf("mountWithConn: %v", err)
return
}

Expand All @@ -273,21 +262,52 @@ func mountFromContext(
func runCLIApp(c *cli.Context) (err error) {
flags := populateFlags(c)

// Extract arguments.
if len(c.Args()) != 2 {
err = fmt.Errorf(
"%s takes exactly two arguments. Run `%s --help` for more info.",
path.Base(os.Args[0]),
path.Base(os.Args[0]))

return
}

bucketName := c.Args()[0]
mountPoint := c.Args()[1]

// Canonicalize the mount point, making it absolute. This is important when
// daemonizing below, since the daemon will change its working directory
// before running this code again.
mountPoint, err = filepath.Abs(mountPoint)
if err != nil {
err = fmt.Errorf("canonicalizing mount point: %v", err)
return
}

fmt.Fprintf(os.Stderr, "Using mount point: %s\n", mountPoint)

// If we haven't been asked to run in foreground mode, we should run a daemon
// with the foreground flag set and wait for it to mount.
if !flags.Foreground {
// Find the executable.
var path string
path, err = osext.Executable()
if err != nil {
err = fmt.Errorf("osext.Executable: %v", err)
return
}

// Set up arguments. Be sure to use foreground mode, and to send along the
// potentially-modified mount point.
args := append([]string{"--foreground"}, os.Args[1:]...)
args[len(args)-1] = mountPoint

// Pass along PATH so that the daemon can find fusermount on Linux.
env := []string{
fmt.Sprintf("PATH=%s", os.Getenv("PATH")),
}

// Run.
err = daemonize.Run(path, args, env, os.Stderr)
if err != nil {
err = fmt.Errorf("daemonize.Run: %v", err)
Expand All @@ -302,13 +322,13 @@ func runCLIApp(c *cli.Context) (err error) {
var mfs *fuse.MountedFileSystem
{
mountStatus := log.New(daemonize.StatusWriter, "", 0)
mfs, err = mountFromContext(c.Args(), flags, mountStatus)
mfs, err = mountWithArgs(bucketName, mountPoint, flags, mountStatus)

if err == nil {
mountStatus.Println("File system has been successfully mounted.")
daemonize.SignalOutcome(nil)
} else {
err = fmt.Errorf("mountFromContext: %v", err)
err = fmt.Errorf("mountWithArgs: %v", err)
daemonize.SignalOutcome(err)
return
}
Expand Down
2 changes: 1 addition & 1 deletion mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (

// Mount the file system based on the supplied arguments, returning a
// fuse.MountedFileSystem that can be joined to wait for unmounting.
func mount(
func mountWithConn(
ctx context.Context,
bucketName string,
mountPoint string,
Expand Down
54 changes: 39 additions & 15 deletions tools/integration_tests/gcsfuse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,24 @@ func (t *GcsfuseTest) TearDown() {
AssertEq(nil, err)
}

// Call gcsfuse with the supplied args, waiting for it to exit. Return nil only
// if it exits successfully.
func (t *GcsfuseTest) runGcsfuse(args []string) (err error) {
cmd := exec.Command(t.gcsfusePath, args...)
// Create an appropriate exec.Cmd for running gcsfuse, setting the required
// environment.
func (t *GcsfuseTest) gcsfuseCommand(args []string) (cmd *exec.Cmd) {
cmd = exec.Command(t.gcsfusePath, args...)

// Teach gcsfuse where fusermount lives.
cmd.Env = []string{
fmt.Sprintf("PATH=%s", path.Dir(gFusermountPath)),
}

return
}

// Call gcsfuse with the supplied args, waiting for it to exit. Return nil only
// if it exits successfully.
func (t *GcsfuseTest) runGcsfuse(args []string) (err error) {
cmd := t.gcsfuseCommand(args)

// Run.
output, err := cmd.CombinedOutput()
if err != nil {
Expand Down Expand Up @@ -141,8 +149,7 @@ func (t *GcsfuseTest) BadUsage() {

// Run each test case.
for i, tc := range testCases {
cmd := exec.Command(t.gcsfusePath)
cmd.Args = append(cmd.Args, tc.args...)
cmd := t.gcsfuseCommand(tc.args)

output, err := cmd.CombinedOutput()
ExpectThat(err, Error(HasSubstr("exit status")), "case %d", i)
Expand Down Expand Up @@ -404,13 +411,34 @@ func (t *GcsfuseTest) OnlyDir_WithImplicitDir() {
ExpectEq(len(canned.ImplicitDirFile_Contents), fi.Size())
}

func (t *GcsfuseTest) RelativeMountPoint() {
// Start gcsfuse with a relative mount point.
cmd := t.gcsfuseCommand([]string{
canned.FakeBucketName,
path.Base(t.dir),
})

cmd.Dir = path.Dir(t.dir)

output, err := cmd.CombinedOutput()
AssertEq(nil, err, "output:\n%s", output)

defer unmount(t.dir)

// The file system should be available.
fi, err := os.Lstat(path.Join(t.dir, canned.TopLevelFile))
AssertEq(nil, err)
ExpectEq(os.FileMode(0644), fi.Mode())
ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *GcsfuseTest) ForegroundMode() {
// Start gcsfuse, writing stderr to a pipe.
cmd := exec.Command(
t.gcsfusePath,
cmd := t.gcsfuseCommand([]string{
"--foreground",
canned.FakeBucketName,
t.dir)
t.dir,
})

cmd.Env = []string{
fmt.Sprintf("PATH=%s", path.Dir(gFusermountPath)),
Expand Down Expand Up @@ -468,9 +496,7 @@ func (t *GcsfuseTest) VersionFlags() {

// For each argument, gcsfuse should exist successfully.
for i, tc := range testCases {
cmd := exec.Command(t.gcsfusePath)
cmd.Args = append(cmd.Args, tc.args...)

cmd := t.gcsfuseCommand(tc.args)
output, err := cmd.CombinedOutput()
ExpectEq(nil, err, "case %d\nOutput:\n%s", i, output)
}
Expand All @@ -486,9 +512,7 @@ func (t *GcsfuseTest) HelpFlags() {

// For each argument, gcsfuse should exist successfully.
for i, tc := range testCases {
cmd := exec.Command(t.gcsfusePath)
cmd.Args = append(cmd.Args, tc.args...)

cmd := t.gcsfuseCommand(tc.args)
output, err := cmd.CombinedOutput()
ExpectEq(nil, err, "case %d\nOutput:\n%s", i, output)
}
Expand Down
40 changes: 33 additions & 7 deletions tools/integration_tests/mount_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,19 @@ func (t *MountHelperTest) TearDown() {
AssertEq(nil, err)
}

func (t *MountHelperTest) mount(args []string) (err error) {
cmd := exec.Command(t.helperPath)
func (t *MountHelperTest) mountHelperCommand(args []string) (cmd *exec.Cmd) {
cmd = exec.Command(t.helperPath)
cmd.Args = append(cmd.Args, args...)
cmd.Env = []string{
fmt.Sprintf("PATH=%s", path.Join(gBuildDir, "bin")),
}

return
}

func (t *MountHelperTest) mount(args []string) (err error) {
cmd := t.mountHelperCommand(args)

output, err := cmd.CombinedOutput()
if err != nil {
err = fmt.Errorf("CombinedOutput: %v\nOutput:\n%s", err, output)
Expand Down Expand Up @@ -120,11 +126,7 @@ func (t *MountHelperTest) BadUsage() {

// Run each test case.
for i, tc := range testCases {
cmd := exec.Command(t.helperPath)
cmd.Args = append(cmd.Args, tc.args...)
cmd.Env = []string{
fmt.Sprintf("PATH=%s", path.Join(gBuildDir, "bin")),
}
cmd := t.mountHelperCommand(tc.args)

output, err := cmd.CombinedOutput()
ExpectThat(err, Error(HasSubstr("exit status")), "case %d", i)
Expand All @@ -150,6 +152,30 @@ func (t *MountHelperTest) SuccessfulMount() {
ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) RelativeMountPoint() {
var err error
var fi os.FileInfo

// Mount with a relative mount point.
cmd := t.mountHelperCommand([]string{
canned.FakeBucketName,
path.Base(t.dir),
})

cmd.Dir = path.Dir(t.dir)

output, err := cmd.CombinedOutput()
AssertEq(nil, err, "output:\n%s", output)

defer unmount(t.dir)

// The file system should be available.
fi, err = os.Lstat(path.Join(t.dir, canned.TopLevelFile))
AssertEq(nil, err)
ExpectEq(os.FileMode(0644), fi.Mode())
ExpectEq(len(canned.TopLevelFile_Contents), fi.Size())
}

func (t *MountHelperTest) ReadOnlyMode() {
var err error

Expand Down

0 comments on commit fa4cc5f

Please sign in to comment.