-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix scp regressions for 6.x #5729
Conversation
@andrejtokarcik Can you review this? |
@a-palchikov If we can't make it to rc.2, let's aim for rc.3? |
32d26d7
to
ce424dd
Compare
Ok - I'll work on the tests to be on the safe side. |
if cmd.FileSystem.IsDir(cmd.Flags.Target[0]) { | ||
// Copying into an existing directory? append to it: | ||
st.push(fc.Name, st.stat) | ||
} else { | ||
// If target specifies a new directory, we need to reset | ||
// state with it | ||
st.path = newPathFromDirAndTimes(cmd.Flags.Target[0], st.stat) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm mentioning this here just so we're aware of the current behaviour not that it's necessary to change it, feel free to close this note with no action:
This could cause unexpected data duplication, especially in automated scripts which may simply rely on the fact that tsh scp
always overwrites its target. For example:
$ stat /tmp/zz
stat: cannot stat '/tmp/zz': No such file or directory
$ tsh scp -r ./large-tree/ /tmp/zz/
[...]
$ diff ./large-tree/ /tmp/zz/ && echo same tree
same tree
$ tsh scp -r ./large-tree/ /tmp/zz/ # re-run the same tsh scp command
[...]
$ diff ./large-tree/ /tmp/zz/
Only in /tmp/zz/: large-tree
The files of large-tree
would end up in two copies under /tmp/zz
: once directly under /tmp/zz
, once under /tmp/zz/large-tree
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is consistent with OpenSSH client's behavior - initial attempt to copy a directory into a new non-existing location causes a rename, but existing location appends to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't test it right now but IIRC, OpenSSH scp
changes its dir-copying behaviour based on the trailing slash which I think tsh scp
would not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That could be possible - I'll test that as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
client (ubuntu):
$ ssh -V
OpenSSH_8.4p1, OpenSSL 1.1.1f 31 Mar 2020
server (centos):
$ ssh -V
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017
Trailing slash does not change the behavior as described in the linked issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@a-palchikov I this code is always run on the client? Server process is sshd
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the server is sshd
. It does not have a version flag but outputs the version anyways:
$ sshd -V
unknown option -- V
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017
BTW I had to apply the following to make diff --git a/lib/srv/exec.go b/lib/srv/exec.go
index e8cf6232d..a822b0293 100644
--- a/lib/srv/exec.go
+++ b/lib/srv/exec.go
@@ -247,7 +247,7 @@ func (e *localExec) transformSecureCopy() error {
if err != nil {
return trace.Wrap(err)
}
- e.Command = fmt.Sprintf("%s scp --remote-addr=%s --local-addr=%s %v",
+ e.Command = fmt.Sprintf("%s scp --remote-addr=%q --local-addr=%q %v",
teleportBin,
e.Ctx.ServerConn.RemoteAddr().String(),
e.Ctx.ServerConn.LocalAddr().String(), The encountered error was:
|
3a02ae8
to
3f8e3fb
Compare
args := append(tt.args, source) | ||
|
||
// Source is missing, expect an error. | ||
err = runSCP(cmd, args...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I factored this into a dedicated test - no need to test this for each local testcase.
@@ -49,7 +49,7 @@ func (l *localFileSystem) Chtimes(path string, atime, mtime time.Time) error { | |||
// MkDir creates a directory | |||
func (l *localFileSystem) MkDir(path string, mode int) error { | |||
fileMode := os.FileMode(mode & int(os.ModePerm)) | |||
err := os.MkdirAll(path, fileMode) | |||
err := os.Mkdir(path, fileMode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This differed from OpenSSH's behavior which does not automatically create intermediate directories. If that's a concern, we can revert this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adhering to OpenSSH behaviour seems more logical to me. When people type scp
they expect it to behave just like scp does when it comes to things like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, we should be as consistent as possible with OpenSSH.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alex-kovoy Any reason you made this MkdirAll
? #2055
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@russjones That was original implementation, it appeared in my PR because I move some code around while working on HTTP transfers. See the original (deleted) scp.go
file in that PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hrm, interesting the original commit seems to indicate MkdirAll
lines up with OpenSSH behavior: 80df1bb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for the behavior of OpenSSH (7.4p1 from above) and the following source layout:
$ tree foo
foo
├── foo1
└── foo-dir
└── foo-file
Copying to a non-existing directory (zz
, 1 level), renames the source into it (or creates it remotely and copies the contents of the source directory):
$ scp -r ./foo [email protected]:zz
foo1 100% 0 0.0KB/s 00:00
foo-file 100% 0 0.0KB/s 00:00
with the resulting remote layout:
# tree zz
zz
├── foo1
└── foo-dir
└── foo-file
while opening into a directory with an intermediate non-existing directory:
$ scp -r ./foo [email protected]:non-existing/zz
scp: non-existing/zz: No such file or directory
but it works with tsh in 5.2.1
.
@xacrimon @andrejtokarcik Can you to re-review? |
@@ -49,7 +49,7 @@ func (l *localFileSystem) Chtimes(path string, atime, mtime time.Time) error { | |||
// MkDir creates a directory | |||
func (l *localFileSystem) MkDir(path string, mode int) error { | |||
fileMode := os.FileMode(mode & int(os.ModePerm)) | |||
err := os.MkdirAll(path, fileMode) | |||
err := os.Mkdir(path, fileMode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adhering to OpenSSH behaviour seems more logical to me. When people type scp
they expect it to behave just like scp does when it comes to things like this.
9ce6407
to
3796e10
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bot.
@a-palchikov Just to clarify, we now match OpenSSH behavior right? I agree, I think that's what users expectation here is. |
Yes, w.r.t to copying a directory to a remote path with intermediates that do not exist. |
Handle target directory/file renames. Fixes #5695.
…ote location with intermediate directories
8ff0b44
to
ebd0a26
Compare
Use correct target directory path in error message.
Handle target directory/file renames properly.
I've only tested these changes manually and I was working on automatic test coverage for the rest of the use-cases but I'm not sure I can make it in time for rc.2.
Fixes #5695.