From b016e3cd778fd557c6831a7494e1176a041aafd2 Mon Sep 17 00:00:00 2001 From: Russ Magee Date: Tue, 16 Mar 2021 19:32:18 -0700 Subject: [PATCH] Added notes on how to avoid/detect client->server copy issues (cf. issue #31) Signed-off-by: Russ Magee --- README.md | 10 ++++++++++ xsd/xsd.go | 12 ++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c3f925b..b0dd5b7 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,16 @@ NOTE: Renaming while copying (eg., 'cp /foo/bar/fileA ./fileB') is NOT supported If the 'pv' pipeview utility is available (http://www.ivarch.com/programs/pv.shtml) file transfer progress and bandwidth control will be available (suppress the former with the -q option, set the latter with -L <bytes_per_second>). +Special care should be taken when doing client->server copies: since the tarpipe (should) always succeed at least sending data to the remote side, a destination with no write permission will not return a nonzero status and the client closes its end after sending all data, giving the server no opportunity to send an error code to the client. +It is recommended to test beforehand if the server-side destination is writable (and optionally if the destination already exists, if one does not want to clobber an existing path) by: + +``` +$ xs -x "test -w /dest/path" me@myserver ## If clobbering /dest/path is OK, or +$ xs -x "test -w /dest/path -o ! -e /dest/path" me@myserver ## To prevent clobbering +``` + +Perhaps in future a more complex handshake will be devised to allow the client to half-close the tarpipe, allowing the server to complete its side of the operation and send back its success or failure code, but the current connection protocol does not allow this. If this is a deal-breaking feature, please contact the maintainer. + ### Tunnels Simple tunnels (client -> server, no reverse tunnels for now) are supported. diff --git a/xsd/xsd.go b/xsd/xsd.go index 1abf813..1e9b15e 100755 --- a/xsd/xsd.go +++ b/xsd/xsd.go @@ -800,15 +800,15 @@ func main() { log.Printf("[Client->Server copy]\n") addr := hc.RemoteAddr() hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[Running copy for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + logger.LogNotice(fmt.Sprintf("[c->s copy for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck cmdStatus, runErr := runClientToServerCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) // Returned hopefully via an EOF or exit/logout; // Clear current op so user can enter next, or EOF rec.SetOp([]byte{0}) if runErr != nil { - logger.LogErr(fmt.Sprintf("[Error running cp for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + logger.LogErr(fmt.Sprintf("[c->s copy error for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck } else { - logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck + logger.LogNotice(fmt.Sprintf("[c->s copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck } // TODO: Test this with huge files.. see Bug #22 - do we need to // sync w/sender (client) that we've gotten all data? @@ -824,13 +824,13 @@ func main() { log.Printf("[Server->Client copy]\n") addr := hc.RemoteAddr() hname := goutmp.GetHost(addr.String()) - logger.LogNotice(fmt.Sprintf("[Running copy for [%s@%s]]\n", rec.Who(), hname)) // nolint: gosec,errcheck + logger.LogNotice(fmt.Sprintf("[s->c copy for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck cmdStatus, runErr := runServerToClientCopyAs(string(rec.Who()), string(rec.TermType()), hc, string(rec.Cmd()), chaffEnabled) if runErr != nil { - logger.LogErr(fmt.Sprintf("[Error spawning cp for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck + logger.LogErr(fmt.Sprintf("[s->c copy error for %s@%s]\n", rec.Who(), hname)) // nolint: gosec,errcheck } else { // Returned hopefully via an EOF or exit/logout; - logger.LogNotice(fmt.Sprintf("[Command completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck + logger.LogNotice(fmt.Sprintf("[s->c copy completed for %s@%s, status %d]\n", rec.Who(), hname, cmdStatus)) // nolint: gosec,errcheck } // HACK: Bug #22: (xc) Need to wait for rcvr to get final data // TODO: Await specific msg from client to inform they have gotten all data from the tarpipe