mount_unix.go 2.28 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
	// setup the Mount abstraction.
22
	m := mount.New(ipfs.Context(), fpath)
Jeromy's avatar
Jeromy committed
23

24
	// go serve the mount
25 26 27
	m.Mount(func(m mount.Mount) error {
		return internalMount(ipfs, fpath, ipfspath)
	}, internalUnmount)
Jeromy's avatar
Jeromy committed
28

29 30 31 32 33
	select {
	case <-m.Closed():
		return nil, fmt.Errorf("failed to mount")
	case <-time.After(time.Second):
		// assume it worked...
Jeromy's avatar
Jeromy committed
34
	}
35

36
	// bind the mount (ContextGroup) to the node, so that when the node exits
37
	// the fsclosers are automatically closed.
38
	ipfs.AddChildGroup(m)
39
	return m, nil
Jeromy's avatar
Jeromy committed
40 41
}

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
// mount attempts to mount at the provided FUSE mount point
func internalMount(ipfs *core.IpfsNode, fpath string, ipfspath string) error {

	c, err := fuse.Mount(fpath)
	if err != nil {
		return err
	}
	defer c.Close()

	fsys, err := NewIpns(ipfs, ipfspath)
	if err != nil {
		return err
	}

	log.Infof("Mounted ipns at %s.", fpath)
	if err := fs.Serve(c, fsys); err != nil {
		return err
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		return err
	}
	return nil
}

69
// unmount attempts to unmount the provided FUSE mount point, forcibly
Jeromy's avatar
Jeromy committed
70
// if necessary.
71 72
func internalUnmount(m mount.Mount) error {
	point := m.MountPoint()
73
	log.Infof("Unmounting ipns at %s...", point)
Jeromy's avatar
Jeromy committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

	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
	}
}