Add support for osxfuse 3.
There are a few changes to tests and documentation to reflect changes to osxfuse behavior. All of them should probably be seen as backwards compatible. The tests now test the osxfuse 3 behavior, and for this reason we should consider killing osxfuse 2 support after some time. Closes #18. Closes #19.geesefs-0-30-9
commit
3b8b4e55df
|
@ -59,7 +59,7 @@ type StatFSOp struct {
|
||||||
//
|
//
|
||||||
// On Linux this can be any value, and will be faithfully returned to the
|
// On Linux this can be any value, and will be faithfully returned to the
|
||||||
// caller of statfs(2) (see the code walk above). On OS X it appears that
|
// caller of statfs(2) (see the code walk above). On OS X it appears that
|
||||||
// only powers of 2 in the range [2^9, 2^17] are preserved, and a value of
|
// only powers of 2 in the range [2^7, 2^20] are preserved, and a value of
|
||||||
// zero is treated as 4096.
|
// zero is treated as 4096.
|
||||||
//
|
//
|
||||||
// This interface does not distinguish between blocks and block fragments.
|
// This interface does not distinguish between blocks and block fragments.
|
||||||
|
@ -85,7 +85,7 @@ type StatFSOp struct {
|
||||||
// transfer block size".
|
// transfer block size".
|
||||||
//
|
//
|
||||||
// On Linux this can be any value. On OS X it appears that only powers of 2
|
// On Linux this can be any value. On OS X it appears that only powers of 2
|
||||||
// in the range [2^12, 2^20] are faithfully preserved, and a value of zero is
|
// in the range [2^12, 2^25] are faithfully preserved, and a value of zero is
|
||||||
// treated as 65536.
|
// treated as 65536.
|
||||||
IoSize uint32
|
IoSize uint32
|
||||||
|
|
||||||
|
@ -696,9 +696,10 @@ type SyncFileOp struct {
|
||||||
// * On OS X, if a user modifies a mapped file via the mapping before
|
// * On OS X, if a user modifies a mapped file via the mapping before
|
||||||
// closing the file with close(2), the WriteFileOps for the modifications
|
// closing the file with close(2), the WriteFileOps for the modifications
|
||||||
// may not be received before the FlushFileOp for the close(2) (cf.
|
// may not be received before the FlushFileOp for the close(2) (cf.
|
||||||
// http://goo.gl/kVmNcx).
|
// https://github.com/osxfuse/osxfuse/issues/202). It appears that this may
|
||||||
|
// be fixed in osxfuse 3 (cf. https://goo.gl/rtvbko).
|
||||||
//
|
//
|
||||||
// * However, even on OS X you can arrange for writes via a mapping to be
|
// * However, you safely can arrange for writes via a mapping to be
|
||||||
// flushed by calling msync(2) followed by close(2). On OS X msync(2)
|
// flushed by calling msync(2) followed by close(2). On OS X msync(2)
|
||||||
// will cause a WriteFileOps to go through and close(2) will cause a
|
// will cause a WriteFileOps to go through and close(2) will cause a
|
||||||
// FlushFile as usual (cf. http://goo.gl/kVmNcx). On Linux, msync(2) does
|
// FlushFile as usual (cf. http://goo.gl/kVmNcx). On Linux, msync(2) does
|
||||||
|
|
116
mount_darwin.go
116
mount_darwin.go
|
@ -14,10 +14,53 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var errNoAvail = errors.New("no available fuse devices")
|
var errNoAvail = errors.New("no available fuse devices")
|
||||||
var errNotLoaded = errors.New("osxfusefs is not loaded")
|
var errNotLoaded = errors.New("osxfuse is not loaded")
|
||||||
|
|
||||||
func loadOSXFUSE() error {
|
// errOSXFUSENotFound is returned from Mount when the OSXFUSE installation is
|
||||||
cmd := exec.Command("/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs")
|
// not detected. Make sure OSXFUSE is installed.
|
||||||
|
var errOSXFUSENotFound = errors.New("cannot locate OSXFUSE")
|
||||||
|
|
||||||
|
// osxfuseInstallation describes the paths used by an installed OSXFUSE
|
||||||
|
// version.
|
||||||
|
type osxfuseInstallation struct {
|
||||||
|
// Prefix for the device file. At mount time, an incrementing number is
|
||||||
|
// suffixed until a free FUSE device is found.
|
||||||
|
DevicePrefix string
|
||||||
|
|
||||||
|
// Path of the load helper, used to load the kernel extension if no device
|
||||||
|
// files are found.
|
||||||
|
Load string
|
||||||
|
|
||||||
|
// Path of the mount helper, used for the actual mount operation.
|
||||||
|
Mount string
|
||||||
|
|
||||||
|
// Environment variable used to pass the path to the executable calling the
|
||||||
|
// mount helper.
|
||||||
|
DaemonVar string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
osxfuseInstallations = []osxfuseInstallation{
|
||||||
|
// v3
|
||||||
|
{
|
||||||
|
DevicePrefix: "/dev/osxfuse",
|
||||||
|
Load: "/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse",
|
||||||
|
Mount: "/Library/Filesystems/osxfuse.fs/Contents/Resources/mount_osxfuse",
|
||||||
|
DaemonVar: "MOUNT_OSXFUSE_DAEMON_PATH",
|
||||||
|
},
|
||||||
|
|
||||||
|
// v2
|
||||||
|
{
|
||||||
|
DevicePrefix: "/dev/osxfuse",
|
||||||
|
Load: "/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs",
|
||||||
|
Mount: "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs",
|
||||||
|
DaemonVar: "MOUNT_FUSEFS_DAEMON_PATH",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadOSXFUSE(bin string) error {
|
||||||
|
cmd := exec.Command(bin)
|
||||||
cmd.Dir = "/"
|
cmd.Dir = "/"
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
@ -25,10 +68,10 @@ func loadOSXFUSE() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func openOSXFUSEDev() (dev *os.File, err error) {
|
func openOSXFUSEDev(devPrefix string) (dev *os.File, err error) {
|
||||||
// Try each device name.
|
// Try each device name.
|
||||||
for i := uint64(0); ; i++ {
|
for i := uint64(0); ; i++ {
|
||||||
path := fmt.Sprintf("/dev/osxfuse%d", i)
|
path := devPrefix + strconv.FormatUint(i, 10)
|
||||||
dev, err = os.OpenFile(path, os.O_RDWR, 0000)
|
dev, err = os.OpenFile(path, os.O_RDWR, 0000)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
@ -52,11 +95,12 @@ func openOSXFUSEDev() (dev *os.File, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func callMount(
|
func callMount(
|
||||||
|
bin string,
|
||||||
|
daemonVar string,
|
||||||
dir string,
|
dir string,
|
||||||
cfg *MountConfig,
|
cfg *MountConfig,
|
||||||
dev *os.File,
|
dev *os.File,
|
||||||
ready chan<- error) (err error) {
|
ready chan<- error) (err error) {
|
||||||
const bin = "/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs"
|
|
||||||
|
|
||||||
// The mount helper doesn't understand any escaping.
|
// The mount helper doesn't understand any escaping.
|
||||||
for k, v := range cfg.toMap() {
|
for k, v := range cfg.toMap() {
|
||||||
|
@ -85,8 +129,15 @@ func callMount(
|
||||||
)
|
)
|
||||||
cmd.ExtraFiles = []*os.File{dev}
|
cmd.ExtraFiles = []*os.File{dev}
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
|
// OSXFUSE <3.3.0
|
||||||
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
|
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_CALL_BY_LIB=")
|
||||||
cmd.Env = append(cmd.Env, "MOUNT_FUSEFS_DAEMON_PATH="+bin)
|
// OSXFUSE >=3.3.0
|
||||||
|
cmd.Env = append(cmd.Env, "MOUNT_OSXFUSE_CALL_BY_LIB=")
|
||||||
|
|
||||||
|
daemon := os.Args[0]
|
||||||
|
if daemonVar != "" {
|
||||||
|
cmd.Env = append(cmd.Env, daemonVar+"="+daemon)
|
||||||
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
cmd.Stdout = &buf
|
cmd.Stdout = &buf
|
||||||
|
@ -122,34 +173,45 @@ func mount(
|
||||||
dir string,
|
dir string,
|
||||||
cfg *MountConfig,
|
cfg *MountConfig,
|
||||||
ready chan<- error) (dev *os.File, err error) {
|
ready chan<- error) (dev *os.File, err error) {
|
||||||
// Open the device.
|
// Find the version of osxfuse installed on this machine.
|
||||||
dev, err = openOSXFUSEDev()
|
for _, loc := range osxfuseInstallations {
|
||||||
|
if _, err := os.Stat(loc.Mount); os.IsNotExist(err) {
|
||||||
|
// try the other locations
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Special case: we may need to explicitly load osxfuse. Load it, then try
|
// Open the device.
|
||||||
// again.
|
dev, err = openOSXFUSEDev(loc.DevicePrefix)
|
||||||
if err == errNotLoaded {
|
|
||||||
err = loadOSXFUSE()
|
// Special case: we may need to explicitly load osxfuse. Load it, then
|
||||||
|
// try again.
|
||||||
|
if err == errNotLoaded {
|
||||||
|
err = loadOSXFUSE(loc.Load)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("loadOSXFUSE: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dev, err = openOSXFUSEDev(loc.DevicePrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagate errors.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("loadOSXFUSE: %v", err)
|
err = fmt.Errorf("openOSXFUSEDev: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dev, err = openOSXFUSEDev()
|
// Call the mount binary with the device.
|
||||||
}
|
err = callMount(loc.Mount, loc.DaemonVar, dir, cfg, dev, ready)
|
||||||
|
if err != nil {
|
||||||
|
dev.Close()
|
||||||
|
err = fmt.Errorf("callMount: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Propagate errors.
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("openOSXFUSEDev: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the mount binary with the device.
|
|
||||||
err = callMount(dir, cfg, dev, ready)
|
|
||||||
if err != nil {
|
|
||||||
dev.Close()
|
|
||||||
err = fmt.Errorf("callMount: %v", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = errOSXFUSENotFound
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -605,19 +605,13 @@ func (t *NoErrorsTest) Mmap_NoMsync_MunmapBeforeClose() {
|
||||||
ExpectThat(t.getFlushes(), ElementsAre())
|
ExpectThat(t.getFlushes(), ElementsAre())
|
||||||
ExpectThat(t.getFsyncs(), ElementsAre())
|
ExpectThat(t.getFsyncs(), ElementsAre())
|
||||||
|
|
||||||
// Close the file. We should see a flush. On Darwin, this will contain out of
|
// Close the file. We should see a flush with up to date contents.
|
||||||
// date contents (cf. https://github.com/osxfuse/osxfuse/issues/202).
|
|
||||||
err = t.f1.Close()
|
err = t.f1.Close()
|
||||||
t.f1 = nil
|
t.f1 = nil
|
||||||
AssertEq(nil, err)
|
AssertEq(nil, err)
|
||||||
|
|
||||||
if isDarwin {
|
ExpectThat(t.getFlushes(), ElementsAre("paco"))
|
||||||
ExpectThat(t.getFlushes(), ElementsAre("taco"))
|
ExpectThat(t.getFsyncs(), ElementsAre())
|
||||||
ExpectThat(t.getFsyncs(), ElementsAre())
|
|
||||||
} else {
|
|
||||||
ExpectThat(t.getFlushes(), ElementsAre("paco"))
|
|
||||||
ExpectThat(t.getFsyncs(), ElementsAre())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *NoErrorsTest) Mmap_NoMsync_CloseBeforeMunmap() {
|
func (t *NoErrorsTest) Mmap_NoMsync_CloseBeforeMunmap() {
|
||||||
|
|
|
@ -70,7 +70,7 @@ func (t *StatFSTest) Syscall_ZeroValues() {
|
||||||
ExpectEq(0, stat.Bavail)
|
ExpectEq(0, stat.Bavail)
|
||||||
ExpectEq(0, stat.Files)
|
ExpectEq(0, stat.Files)
|
||||||
ExpectEq(0, stat.Ffree)
|
ExpectEq(0, stat.Ffree)
|
||||||
ExpectEq("osxfusefs", convertName(stat.Fstypename[:]))
|
ExpectEq("osxfuse", convertName(stat.Fstypename[:]))
|
||||||
ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
|
ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
|
||||||
ExpectEq(fsName, convertName(stat.Mntfromname[:]))
|
ExpectEq(fsName, convertName(stat.Mntfromname[:]))
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ func (t *StatFSTest) Syscall_NonZeroValues() {
|
||||||
ExpectEq(canned.BlocksAvailable, stat.Bavail)
|
ExpectEq(canned.BlocksAvailable, stat.Bavail)
|
||||||
ExpectEq(canned.Inodes, stat.Files)
|
ExpectEq(canned.Inodes, stat.Files)
|
||||||
ExpectEq(canned.InodesFree, stat.Ffree)
|
ExpectEq(canned.InodesFree, stat.Ffree)
|
||||||
ExpectEq("osxfusefs", convertName(stat.Fstypename[:]))
|
ExpectEq("osxfuse", convertName(stat.Fstypename[:]))
|
||||||
ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
|
ExpectEq(t.canonicalDir, convertName(stat.Mntonname[:]))
|
||||||
ExpectEq(fsName, convertName(stat.Mntfromname[:]))
|
ExpectEq(fsName, convertName(stat.Mntfromname[:]))
|
||||||
}
|
}
|
||||||
|
@ -120,8 +120,8 @@ func (t *StatFSTest) BlockSizes() {
|
||||||
expectedBsize uint32
|
expectedBsize uint32
|
||||||
}{
|
}{
|
||||||
0: {0, 4096},
|
0: {0, 4096},
|
||||||
1: {1, 512},
|
1: {1, 128},
|
||||||
2: {3, 512},
|
2: {3, 128},
|
||||||
3: {511, 512},
|
3: {511, 512},
|
||||||
4: {512, 512},
|
4: {512, 512},
|
||||||
5: {513, 1024},
|
5: {513, 1024},
|
||||||
|
@ -129,16 +129,22 @@ func (t *StatFSTest) BlockSizes() {
|
||||||
7: {1024, 1024},
|
7: {1024, 1024},
|
||||||
8: {4095, 4096},
|
8: {4095, 4096},
|
||||||
9: {1 << 16, 1 << 16},
|
9: {1 << 16, 1 << 16},
|
||||||
10: {1<<17 - 1, 1 << 17},
|
10: {1 << 17, 1 << 17},
|
||||||
11: {1 << 17, 1 << 17},
|
11: {1 << 18, 1 << 18},
|
||||||
12: {1<<17 + 1, 1 << 17},
|
12: {1 << 19, 1 << 19},
|
||||||
13: {1 << 18, 1 << 17},
|
|
||||||
14: {1 << 20, 1 << 17},
|
13: {1<<20 - 1, 1 << 20},
|
||||||
15: {math.MaxInt32 - 1, 1 << 17},
|
14: {1 << 20, 1 << 20},
|
||||||
16: {math.MaxInt32, 1 << 17},
|
15: {1<<20 + 1, 1 << 20},
|
||||||
17: {math.MaxInt32 + 1, 512},
|
|
||||||
18: {math.MaxInt32 + 1<<15, 1 << 15},
|
16: {1 << 21, 1 << 20},
|
||||||
19: {math.MaxUint32, 1 << 17},
|
17: {1 << 22, 1 << 20},
|
||||||
|
|
||||||
|
18: {math.MaxInt32 - 1, 1 << 20},
|
||||||
|
19: {math.MaxInt32, 1 << 20},
|
||||||
|
20: {math.MaxInt32 + 1, 128},
|
||||||
|
21: {math.MaxInt32 + 1<<15, 1 << 15},
|
||||||
|
22: {math.MaxUint32, 1 << 20},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
|
@ -170,23 +176,29 @@ func (t *StatFSTest) IoSizes() {
|
||||||
fsIoSize uint32
|
fsIoSize uint32
|
||||||
expectedIosize uint32
|
expectedIosize uint32
|
||||||
}{
|
}{
|
||||||
0: {0, 65536},
|
0: {0, 65536},
|
||||||
1: {1, 4096},
|
1: {1, 4096},
|
||||||
2: {3, 4096},
|
2: {3, 4096},
|
||||||
3: {4095, 4096},
|
3: {4095, 4096},
|
||||||
4: {4096, 4096},
|
4: {4096, 4096},
|
||||||
5: {4097, 8192},
|
5: {4097, 8192},
|
||||||
6: {8191, 8192},
|
6: {8191, 8192},
|
||||||
7: {8192, 8192},
|
7: {8192, 8192},
|
||||||
8: {8193, 16384},
|
8: {8193, 16384},
|
||||||
9: {1<<20 - 1, 1 << 20},
|
|
||||||
|
9: {1 << 18, 1 << 18},
|
||||||
10: {1 << 20, 1 << 20},
|
10: {1 << 20, 1 << 20},
|
||||||
11: {1<<20 + 1, 1 << 20},
|
11: {1 << 23, 1 << 23},
|
||||||
12: {math.MaxInt32 - 1, 1 << 20},
|
|
||||||
13: {math.MaxInt32, 1 << 20},
|
12: {1<<25 - 1, 1 << 25},
|
||||||
14: {math.MaxInt32 + 1, 4096},
|
13: {1 << 25, 1 << 25},
|
||||||
15: {math.MaxInt32 + 1<<15, 1 << 15},
|
14: {1<<25 + 1, 1 << 25},
|
||||||
16: {math.MaxUint32, 1 << 20},
|
|
||||||
|
15: {math.MaxInt32 - 1, 1 << 25},
|
||||||
|
16: {math.MaxInt32, 1 << 25},
|
||||||
|
17: {math.MaxInt32 + 1, 4096},
|
||||||
|
18: {math.MaxInt32 + 1<<15, 1 << 15},
|
||||||
|
19: {math.MaxUint32, 1 << 25},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
|
|
Loading…
Reference in New Issue