1
mirror of https://github.com/rclone/rclone synced 2024-12-03 13:14:37 +01:00
rclone/cmd/nfsmount/nfsmount.go
Saleh Dindar ef2ef8ef84 nfsmount: New mount command to provide mount mechanism on macOS without FUSE
Summary:
In cases where cmount is not available in macOS, we alias nfsmount to mount command and transparently start the NFS server and mount it to the target dir.

The NFS server is started on localhost on a random port so it is reasonably secure.

Test Plan:
```
go run rclone.go mount --http-url https://beta.rclone.org :http: nfs-test
```

Added mount tests:
```
go test ./cmd/nfsmount
```
2023-10-06 14:08:20 +01:00

70 lines
1.9 KiB
Go

//go:build darwin && !cmount
// +build darwin,!cmount
// Package nfsmount implements mounting functionality using serve nfs command
//
// NFS mount is only needed for macOS since it has no
// support for FUSE-based file systems
package nfsmount
import (
"context"
"fmt"
"net"
"os/exec"
"runtime"
"strings"
"github.com/rclone/rclone/cmd/mountlib"
"github.com/rclone/rclone/cmd/serve/nfs"
"github.com/rclone/rclone/vfs"
)
func init() {
cmd := mountlib.NewMountCommand("mount", false, mount)
cmd.Aliases = append(cmd.Aliases, "nfsmount")
mountlib.AddRc("nfsmount", mount)
}
func mount(VFS *vfs.VFS, mountpoint string, opt *mountlib.Options) (asyncerrors <-chan error, unmount func() error, err error) {
s, err := nfs.NewServer(context.Background(), VFS, &nfs.Options{})
if err != nil {
return
}
errChan := make(chan error, 1)
go func() {
errChan <- s.Serve()
}()
// The port is always picked at random after the NFS server has started
// we need to query the server for the port number so we can mount it
_, port, err := net.SplitHostPort(s.Addr().String())
if err != nil {
err = fmt.Errorf("cannot find port number in %s", s.Addr().String())
return
}
optionsString := strings.Join(opt.ExtraOptions, ",")
err = exec.Command("mount", fmt.Sprintf("-oport=%s,mountport=%s,%s", port, port, optionsString), "localhost:", mountpoint).Run()
if err != nil {
err = fmt.Errorf("failed to mount NFS volume %e", err)
return
}
asyncerrors = errChan
unmount = func() error {
var umountErr error
if runtime.GOOS == "darwin" {
umountErr = exec.Command("diskutil", "umount", "force", mountpoint).Run()
} else {
umountErr = exec.Command("umount", "-f", mountpoint).Run()
}
shutdownErr := s.Shutdown()
VFS.Shutdown()
if umountErr != nil {
return fmt.Errorf("failed to umount the NFS volume %e", umountErr)
} else if shutdownErr != nil {
return fmt.Errorf("failed to shutdown NFS server: %e", shutdownErr)
}
return nil
}
return
}