mount_unix.go 2.09 KB
Newer Older
Jeromy's avatar
Jeromy committed
1 2 3 4 5 6 7 8
package ipns

import (
	"fmt"
	"os/exec"
	"runtime"
	"time"

9 10 11 12 13
	fuse "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
	fs "github.com/jbenet/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"

	core "github.com/jbenet/go-ipfs/core"
	mount "github.com/jbenet/go-ipfs/fuse/mount"
Jeromy's avatar
Jeromy committed
14 15 16 17
)

// Mount mounts an IpfsNode instance at a particular path. It
// serves until the process receives exit signals (to Unmount).
18 19
func Mount(ipfs *core.IpfsNode, fpath string, ipfspath string) (mount.Mount, error) {
	log.Infof("Mounting ipns at %s...", fpath)
Jeromy's avatar
Jeromy committed
20

21 22
	// setup the Mount abstraction.
	m := mount.New(ipfs.Context(), fpath, unmount)
Jeromy's avatar
Jeromy committed
23

24 25 26 27 28 29
	// go serve the mount
	mount.ServeMount(m, func(m mount.Mount) error {

		c, err := fuse.Mount(fpath)
		if err != nil {
			return err
Jeromy's avatar
Jeromy committed
30
		}
31
		defer c.Close()
Jeromy's avatar
Jeromy committed
32

33 34 35 36
		fsys, err := NewIpns(ipfs, ipfspath)
		if err != nil {
			return err
		}
Jeromy's avatar
Jeromy committed
37

38 39 40 41
		log.Infof("Mounted ipns at %s.", fpath)
		if err := fs.Serve(c, fsys); err != nil {
			return err
		}
Jeromy's avatar
Jeromy committed
42

43 44 45 46 47 48 49
		// check if the mount process has an error to report
		<-c.Ready
		if err := c.MountError; err != nil {
			return err
		}
		return nil
	})
Jeromy's avatar
Jeromy committed
50

51 52 53 54 55
	select {
	case <-m.Closed():
		return nil, fmt.Errorf("failed to mount")
	case <-time.After(time.Second):
		// assume it worked...
Jeromy's avatar
Jeromy committed
56
	}
57 58 59 60 61

	// bind the mount (ContextCloser) to the node, so that when the node exits
	// the fsclosers are automatically closed.
	ipfs.AddCloserChild(m)
	return m, nil
Jeromy's avatar
Jeromy committed
62 63
}

64
// unmount attempts to unmount the provided FUSE mount point, forcibly
Jeromy's avatar
Jeromy committed
65
// if necessary.
66 67
func unmount(point string) error {
	log.Infof("Unmounting ipns at %s...", point)
Jeromy's avatar
Jeromy committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

	var cmd *exec.Cmd
	switch runtime.GOOS {
	case "darwin":
		cmd = exec.Command("diskutil", "umount", "force", point)
	case "linux":
		cmd = exec.Command("fusermount", "-u", point)
	default:
		return fmt.Errorf("unmount: unimplemented")
	}

	errc := make(chan error, 1)
	go func() {
		if err := exec.Command("umount", point).Run(); err == nil {
			errc <- err
		}
		// retry to unmount with the fallback cmd
		errc <- cmd.Run()
	}()

	select {
	case <-time.After(1 * time.Second):
		return fmt.Errorf("umount timeout")
	case err := <-errc:
		return err
	}
}