package main import ( "fmt" "os" ) // windowJob raw-copies a byte window [srcBase,srcBase+size) of the source // disk onto [dstBase,dstBase+size) of the target, through the same // push/pull block-diff engine `sync` uses. Used for boot regions and for // partitions cloned with method "raw". type windowJob struct { srcPath, dstPath string srcBase, dstBase int64 size int64 bothLocal bool blockSize int64 cfg *SyncConfig srcHost, srcUser string dstHost, dstUser string srcRemoteBin string dstRemoteBin string srcSudo, dstSudo bool } func runWindowTransfer(srcCtrl, dstCtrl *Controller, j windowJob) error { pp := newProgressPrinter(j.blockSize) cb := transferCallbacks{onProgress: pp.print} pushReq := CtrlMsg{ Path: j.srcPath, Base: j.srcBase, PeerBase: j.dstBase, Size: j.size, BlockSize: j.blockSize, PeerHost: j.dstHost, PeerUser: j.dstUser, PeerPath: j.dstPath, PeerLocal: j.bothLocal, RemoteBin: j.dstRemoteBin, SSHBin: j.cfg.SSHBin, SSHOpts: j.cfg.SSHOpts, ConnectTimeoutSec: j.cfg.ConnectTimeoutSec, Sudo: j.dstSudo, } ok, reason, err := srcCtrl.ConnectPush(pushReq, cb) if err != nil { pp.finish() return err } if !ok { pp.finish() fmt.Fprintf(os.Stderr, " push not possible (%s); pulling instead ...\n", reason) pullReq := CtrlMsg{ Path: j.dstPath, Base: j.dstBase, PeerBase: j.srcBase, Size: j.size, BlockSize: j.blockSize, PeerHost: j.srcHost, PeerUser: j.srcUser, PeerPath: j.srcPath, PeerLocal: j.bothLocal, RemoteBin: j.srcRemoteBin, SSHBin: j.cfg.SSHBin, SSHOpts: j.cfg.SSHOpts, ConnectTimeoutSec: j.cfg.ConnectTimeoutSec, Sudo: j.srcSudo, } ok2, reason2, err2 := dstCtrl.ConnectPull(pullReq, cb) if err2 != nil { pp.finish() return err2 } if !ok2 { pp.finish() return fmt.Errorf("no direct connection either way (push: %s; pull: %s)", reason, reason2) } } pp.finish() return nil } // cloneCtx carries the transport context for an fs-image partition clone. type cloneCtx struct { srcPath, dstPath string bothLocal bool cfg *SyncConfig srcHost, srcUser string dstHost, dstUser string srcRemoteBin string dstRemoteBin string srcSudo, dstSudo bool } // runFSImagePartition streams a filesystem image for one partition, trying // push (source drives) then pull (dest drives). func runFSImagePartition(srcCtrl, dstCtrl *Controller, pp plannedPart, c cloneCtx) error { opts := fsCloneOpts{FSType: pp.FSType, Tool: pp.Tool, SizeBytes: pp.SrcSizeB, ShrinkToBytes: pp.ShrinkToB} prog := newProgressPrinter(1 << 20) cb := transferCallbacks{onProgress: prog.printBytes} pushReq := CtrlMsg{ Method: "fs-image", PartIndex: pp.Num, Options: opts.encode(), Path: c.srcPath, PeerPath: c.dstPath, PeerLocal: c.bothLocal, PeerHost: c.dstHost, PeerUser: c.dstUser, RemoteBin: c.dstRemoteBin, SSHBin: c.cfg.SSHBin, SSHOpts: c.cfg.SSHOpts, ConnectTimeoutSec: c.cfg.ConnectTimeoutSec, Sudo: c.dstSudo, } ok, reason, err := srcCtrl.ClonePartition(pushReq, cb) if err != nil { prog.finish() return err } if !ok { prog.finish() fmt.Fprintf(os.Stderr, " fs-image push not possible (%s); pulling instead ...\n", reason) pullReq := CtrlMsg{ Method: "fs-image", Reason: "pull", PartIndex: pp.Num, Options: opts.encode(), Path: c.dstPath, PeerPath: c.srcPath, PeerLocal: c.bothLocal, PeerHost: c.srcHost, PeerUser: c.srcUser, RemoteBin: c.srcRemoteBin, SSHBin: c.cfg.SSHBin, SSHOpts: c.cfg.SSHOpts, ConnectTimeoutSec: c.cfg.ConnectTimeoutSec, Sudo: c.srcSudo, } ok2, reason2, err2 := dstCtrl.ClonePartition(pullReq, cb) if err2 != nil { prog.finish() return err2 } if !ok2 { prog.finish() return fmt.Errorf("fs-image failed both ways (push: %s; pull: %s)", reason, reason2) } } prog.finish() return nil }