mirror of
https://github.com/rclone/rclone.git
synced 2024-12-22 11:41:17 +08:00
5d6b8141ec
As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code.
279 lines
6.6 KiB
Go
279 lines
6.6 KiB
Go
package vfstest
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rclone/rclone/cmd/mountlib"
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fs/cache"
|
|
"github.com/rclone/rclone/fstest"
|
|
"github.com/rclone/rclone/lib/file"
|
|
"github.com/rclone/rclone/vfs"
|
|
"github.com/rclone/rclone/vfs/vfscommon"
|
|
)
|
|
|
|
// Functions to run and control the mount subprocess
|
|
|
|
var (
|
|
runMount = flag.String("run-mount", "", "If set, run the mount subprocess with the options (internal use only)")
|
|
)
|
|
|
|
// Options for the mount sub processes passed with the -run-mount flag
|
|
type runMountOpt struct {
|
|
MountPoint string
|
|
MountOpt mountlib.Options
|
|
VFSOpt vfscommon.Options
|
|
Remote string
|
|
}
|
|
|
|
// Start the mount subprocess and wait for it to start
|
|
func (r *Run) startMountSubProcess() {
|
|
// If testing the VFS we don't start a subprocess, we just use
|
|
// the VFS directly
|
|
if r.useVFS {
|
|
vfs := vfs.New(r.fremote, r.vfsOpt)
|
|
r.os = vfsOs{vfs}
|
|
return
|
|
}
|
|
r.os = realOs{}
|
|
r.mountPath = findMountPath()
|
|
log.Printf("startMountSubProcess %q (%q) %q", r.fremote, r.fremoteName, r.mountPath)
|
|
|
|
opt := runMountOpt{
|
|
MountPoint: r.mountPath,
|
|
MountOpt: mountlib.Opt,
|
|
VFSOpt: *r.vfsOpt,
|
|
Remote: r.fremoteName,
|
|
}
|
|
|
|
opts, err := json.Marshal(&opt)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Re-run this executable with a new option -run-mount
|
|
args := append(os.Args, "-run-mount", string(opts))
|
|
r.cmd = exec.Command(args[0], args[1:]...)
|
|
r.cmd.Stderr = os.Stderr
|
|
r.out, err = r.cmd.StdinPipe()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
r.in, err = r.cmd.StdoutPipe()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = r.cmd.Start()
|
|
if err != nil {
|
|
log.Fatal("startMountSubProcess failed", err)
|
|
}
|
|
r.scanner = bufio.NewScanner(r.in)
|
|
|
|
// Wait it for startup
|
|
log.Print("Waiting for mount to start")
|
|
for r.scanner.Scan() {
|
|
rx := strings.TrimSpace(r.scanner.Text())
|
|
if rx == "STARTED" {
|
|
break
|
|
}
|
|
log.Printf("..Mount said: %s", rx)
|
|
}
|
|
if r.scanner.Err() != nil {
|
|
log.Printf("scanner err %v", r.scanner.Err())
|
|
}
|
|
|
|
log.Printf("startMountSubProcess: end")
|
|
}
|
|
|
|
// Find a free path to run the mount on
|
|
func findMountPath() string {
|
|
if runtime.GOOS != "windows" {
|
|
mountPath, err := os.MkdirTemp("", "rclonefs-mount")
|
|
if err != nil {
|
|
log.Fatalf("Failed to create mount dir: %v", err)
|
|
}
|
|
return mountPath
|
|
}
|
|
|
|
// Find a free drive letter
|
|
letter := file.FindUnusedDriveLetter()
|
|
drive := ""
|
|
if letter == 0 {
|
|
log.Fatalf("Couldn't find free drive letter for test")
|
|
} else {
|
|
drive = string(letter) + ":"
|
|
}
|
|
return drive
|
|
}
|
|
|
|
// Return true if we are running as a subprocess to run the mount
|
|
func isSubProcess() bool {
|
|
return *runMount != ""
|
|
}
|
|
|
|
// Run the mount - this is running in a subprocesses and the config
|
|
// is passed JSON encoded as the -run-mount parameter
|
|
//
|
|
// It reads commands from standard input and writes results to
|
|
// standard output.
|
|
func startMount(mountFn mountlib.MountFn, useVFS bool, opts string) {
|
|
log.Print("startMount")
|
|
ctx := context.Background()
|
|
|
|
var opt runMountOpt
|
|
err := json.Unmarshal([]byte(opts), &opt)
|
|
if err != nil {
|
|
log.Fatalf("Unmarshal failed: %v", err)
|
|
}
|
|
|
|
fstest.Initialise()
|
|
|
|
f, err := cache.Get(ctx, opt.Remote)
|
|
if err != nil {
|
|
log.Fatalf("Failed to open remote %q: %v", opt.Remote, err)
|
|
}
|
|
|
|
err = f.Mkdir(ctx, "")
|
|
if err != nil {
|
|
log.Fatalf("Failed to mkdir %q: %v", opt.Remote, err)
|
|
}
|
|
|
|
log.Printf("startMount: Mounting %q on %q with %q", opt.Remote, opt.MountPoint, opt.VFSOpt.CacheMode)
|
|
mnt := mountlib.NewMountPoint(mountFn, opt.MountPoint, f, &opt.MountOpt, &opt.VFSOpt)
|
|
|
|
_, err = mnt.Mount()
|
|
if err != nil {
|
|
log.Fatalf("mount FAILED %q: %v", opt.Remote, err)
|
|
}
|
|
defer umount(mnt)
|
|
log.Printf("startMount: mount OK")
|
|
fmt.Println("STARTED") // signal to parent all is good
|
|
|
|
// Read commands from stdin
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
exit := false
|
|
for !exit && scanner.Scan() {
|
|
rx := strings.Trim(scanner.Text(), "\r\n")
|
|
var tx string
|
|
tx, exit = doMountCommand(mnt.VFS, rx)
|
|
fmt.Println(tx)
|
|
}
|
|
|
|
err = scanner.Err()
|
|
if err != nil {
|
|
log.Fatalf("scanner failed %q: %v", opt.Remote, err)
|
|
}
|
|
}
|
|
|
|
// Do a mount command which is a line read from stdin and return a
|
|
// line to send to stdout with an exit flag.
|
|
//
|
|
// The format of the lines is
|
|
//
|
|
// command \t parameter (optional)
|
|
//
|
|
// The response should be
|
|
//
|
|
// OK|ERR \t result (optional)
|
|
func doMountCommand(vfs *vfs.VFS, rx string) (tx string, exit bool) {
|
|
command := strings.Split(rx, "\t")
|
|
// log.Printf("doMountCommand: %q received", command)
|
|
var out = []string{"OK", ""}
|
|
switch command[0] {
|
|
case "waitForWriters":
|
|
vfs.WaitForWriters(waitForWritersDelay)
|
|
case "forget":
|
|
root, err := vfs.Root()
|
|
if err != nil {
|
|
out = []string{"ERR", err.Error()}
|
|
} else {
|
|
root.ForgetPath(command[1], fs.EntryDirectory)
|
|
}
|
|
case "exit":
|
|
exit = true
|
|
default:
|
|
out = []string{"ERR", "command not found"}
|
|
}
|
|
return strings.Join(out, "\t"), exit
|
|
}
|
|
|
|
// Send a command to the mount subprocess and await a response
|
|
func (r *Run) sendMountCommand(args ...string) {
|
|
r.cmdMu.Lock()
|
|
defer r.cmdMu.Unlock()
|
|
tx := strings.Join(args, "\t")
|
|
// log.Printf("Send mount command: %q", tx)
|
|
var rx string
|
|
if r.useVFS {
|
|
// if using VFS do the VFS command directly
|
|
rx, _ = doMountCommand(r.os.(vfsOs).VFS, tx)
|
|
} else {
|
|
_, err := io.WriteString(r.out, tx+"\n")
|
|
if err != nil {
|
|
log.Fatalf("WriteString err %v", err)
|
|
}
|
|
if !r.scanner.Scan() {
|
|
log.Fatalf("Mount has gone away")
|
|
}
|
|
rx = strings.Trim(r.scanner.Text(), "\r\n")
|
|
}
|
|
in := strings.Split(rx, "\t")
|
|
// log.Printf("Answer is %q", in)
|
|
if in[0] != "OK" {
|
|
log.Fatalf("Error from mount: %q", in[1:])
|
|
}
|
|
}
|
|
|
|
// wait for any files being written to be released by fuse
|
|
func (r *Run) waitForWriters() {
|
|
r.sendMountCommand("waitForWriters")
|
|
}
|
|
|
|
// forget the directory passed in
|
|
func (r *Run) forget(dir string) {
|
|
r.sendMountCommand("forget", dir)
|
|
}
|
|
|
|
// Unmount the mount
|
|
func umount(mnt *mountlib.MountPoint) {
|
|
/*
|
|
log.Printf("Calling fusermount -u %q", mountPath)
|
|
err := exec.Command("fusermount", "-u", mountPath).Run()
|
|
if err != nil {
|
|
log.Printf("fusermount failed: %v", err)
|
|
}
|
|
*/
|
|
log.Printf("Unmounting %q", mnt.MountPoint)
|
|
err := mnt.Unmount()
|
|
if err != nil {
|
|
log.Printf("signal to umount failed - retrying: %v", err)
|
|
time.Sleep(3 * time.Second)
|
|
err = mnt.Unmount()
|
|
}
|
|
if err != nil {
|
|
log.Fatalf("signal to umount failed: %v", err)
|
|
}
|
|
log.Printf("Waiting for umount")
|
|
err = <-mnt.ErrChan
|
|
if err != nil {
|
|
log.Fatalf("umount failed: %v", err)
|
|
}
|
|
|
|
// Cleanup the VFS cache - umount has called Shutdown
|
|
err = mnt.VFS.CleanUp()
|
|
if err != nil {
|
|
log.Printf("Failed to cleanup the VFS cache: %v", err)
|
|
}
|
|
}
|