emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 6139a05241: Add direct remote copying in Tramp's scp support


From: Michael Albinus
Subject: master 6139a05241: Add direct remote copying in Tramp's scp support
Date: Sat, 22 Jan 2022 11:12:25 -0500 (EST)

branch: master
commit 6139a05241aa3f082b49cbfe90ce61ceb0189bf7
Author: Michael Albinus <michael.albinus@gmx.de>
Commit: Michael Albinus <michael.albinus@gmx.de>

    Add direct remote copying in Tramp's scp support
    
    * doc/misc/tramp.texi: Use @trampfn{} function where possible.
    (Top, Configuration): Insert section 'Ssh setup' and remove
    section 'Windows setup hints' in menu.
    (Default Method): Mention tramp-use-scp-direct-remote-copying.
    (Ssh setup): New node.
    (Windows setup hints): Move it to that new node.
    (Frequently Asked Questions): Move items about ssh config to that node.
    
    * etc/NEWS: Add Tramp's direct remote copying feature.  Fix typos.
    
    * lisp/net/tramp-sh.el (tramp-use-scp-direct-remote-copying):
    New defcustom.
    (tramp-methods) <scp, scpx>: Add "%y" marker.
    (tramp-scp-direct-remote-copying): New defun.
    (tramp-do-copy-or-rename-file-out-of-band): Extend for direct
    remote copying.
    
    * lisp/net/tramp.el (tramp-methods): Extend docstring.
    (tramp-password-prompt-not-unique): New defvar.
    (tramp-read-passwd): Adapt docstring.
    (tramp-read-passwd-without-cache): New defun.
    (tramp-action-password): Call it.
---
 doc/misc/tramp.texi  | 435 ++++++++++++++++++++++++++--------------------
 etc/NEWS             |  58 ++++---
 lisp/net/tramp-sh.el | 474 ++++++++++++++++++++++++++++++---------------------
 lisp/net/tramp.el    |  37 +++-
 4 files changed, 598 insertions(+), 406 deletions(-)

diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi
index 6a198e9bfb..ea544218ec 100644
--- a/doc/misc/tramp.texi
+++ b/doc/misc/tramp.texi
@@ -133,19 +133,21 @@ Configuring @value{tramp} for use
 * Multi-hops::                  Connecting to a remote host using multiple 
hops.
 * Firewalls::                   Passing firewalls.
 * Customizing Methods::         Using Non-Standard Methods.
-* Customizing Completion::      Selecting config files for user/host name 
completion.
+* Customizing Completion::      Selecting config files for user/host name @c
+completion.
 * Password handling::           Reusing passwords for several connections.
 * Connection caching::          Reusing connection related information.
 * Predefined connection information::
                                 Setting own connection related information.
-* Remote programs::             How @value{tramp} finds and uses programs on 
the remote host.
+* Remote programs::             How @value{tramp} finds and uses programs @c
+on the remote host.
 * Remote shell setup::          Remote shell setup hints.
+* Ssh setup::                   Ssh setup hints.
 * FUSE setup::                  @acronym{FUSE} setup hints.
 * Android shell setup::         Android shell setup hints.
 * Auto-save File Lock and Backup::
                                 Auto-save, File Lock and Backup.
 * Keeping files encrypted::     Protect remote files by encryption.
-* Windows setup hints::         Issues with Cygwin ssh.
 
 Using @value{tramp}
 
@@ -523,7 +525,7 @@ performed on another host, it can be comnbined with a 
leading
 connects first to the other host with non-administrative credentials,
 and changes to administrative credentials on that host afterwards.  In
 a simple case, the syntax looks like
-@file{@value{prefix}ssh@value{postfixhop}user@@host|sudo@value{postfixhop}@value{postfix}/path/to/file}.
+@file{@trampfn{ssh@value{postfixhop}user@@host|sudo,,/path/to/file}}.
 @xref{Ad-hoc multi-hops}.
 
 
@@ -683,19 +685,21 @@ may be used in your init file:
 * Multi-hops::                  Connecting to a remote host using multiple 
hops.
 * Firewalls::                   Passing firewalls.
 * Customizing Methods::         Using Non-Standard Methods.
-* Customizing Completion::      Selecting config files for user/host name 
completion.
+* Customizing Completion::      Selecting config files for user/host name @c
+completion.
 * Password handling::           Reusing passwords for several connections.
 * Connection caching::          Reusing connection related information.
 * Predefined connection information::
                                 Setting own connection related information.
-* Remote programs::             How @value{tramp} finds and uses programs on 
the remote host.
+* Remote programs::             How @value{tramp} finds and uses programs @c
+on the remote host.
 * Remote shell setup::          Remote shell setup hints.
+* Ssh setup::                   Ssh setup hints.
 * FUSE setup::                  @acronym{FUSE} setup hints.
 * Android shell setup::         Android shell setup hints.
 * Auto-save File Lock and Backup::
                                 Auto-save, File Lock and Backup.
 * Keeping files encrypted::     Protect remote files by encryption.
-* Windows setup hints::         Issues with Cygwin ssh.
 @end menu
 
 
@@ -1234,7 +1238,8 @@ be populated in your @command{Online Accounts} 
application outside Emacs.
 Since Google Drive uses cryptic blob file names internally,
 @value{tramp} works with the @code{display-name} of the files.  This
 could produce unexpected behavior in case two files in the same
-directory have the same @code{display-name}, such a situation must be avoided.
+directory have the same @code{display-name}, such a situation must be
+avoided.
 
 @item @option{mtp}
 @cindex method @option{mtp}
@@ -1448,7 +1453,7 @@ External methods might be more efficient for large files, 
but most
 @value{tramp} users edit small files more often than large files.
 
 Enable compression, @code{tramp-inline-compress-start-size}, for a
-performance boost for large files.
+performance boost for large files with inline methods.
 
 Since @command{ssh} has become the most common method of remote host
 access and it has the most reasonable security protocols, use
@@ -1474,6 +1479,10 @@ For editing large files, @option{scp} is faster than 
@option{ssh}.
 @option{pscp} is faster than @option{plink}.  But this speed
 improvement is not always true.
 
+When copying large files between two different remote hosts via
+@option{scp}, set @code{tramp-use-scp-direct-remote-copying} to
+non-@code{nil}.
+
 
 @node Default User
 @section Selecting a default user
@@ -1689,8 +1698,8 @@ Sometimes, it is not possible to reach a remote host 
directly.  A
 firewall might be in the way, which could be passed via a proxy
 server.
 
-Both ssh and PuTTY support such proxy settings, using an HTTP tunnel
-via the @command{CONNECT} command (conforming to RFC 2616, 2817
+Both OpenSSH and PuTTY support such proxy settings, using an HTTP
+tunnel via the @command{CONNECT} command (conforming to RFC 2616, 2817
 specifications).  Proxy servers using HTTP 1.1 or later protocol
 support this command.
 
@@ -1804,7 +1813,7 @@ hadoop server.
 @cindex @option{vagrant} method
 Convenience method to access vagrant boxes.  It is often used in
 multi-hop file names like
-@file{@value{prefix}vagrant@value{postfixhop}box|sudo@value{postfixhop}box@value{postfix}/path/to/file},
+@file{@trampfn{vagrant@value{postfixhop}box|sudo,box,/path/to/file}},
 where @samp{box} is the name of the vagrant box.
 
 @end table
@@ -2606,6 +2615,211 @@ where @samp{192.168.0.1} is the remote host IP address
 @end table
 
 
+@node Ssh setup
+@section Ssh setup hints
+
+The most common @value{tramp} connection family is based on either
+@command{ssh} or @command{scp} of OpenSSH, or @command{plink} or
+@command{pscp} of PuTTY on MS Windows.  In the following, some
+configuration recommendations are given.
+
+
+@subsection Detection of session hangouts
+
+@vindex ServerAliveInterval@r{, ssh option}
+@vindex ServerAliveCountMax@r{, ssh option}
+@command{ssh} sessions on the local host hang when the network is
+down.  @value{tramp} cannot safely detect such hangs.  OpenSSH can be
+configured to kill such hangs with the following settings in
+@file{~/.ssh/config}:
+
+@example
+@group
+Host *
+     ServerAliveInterval 5
+     ServerAliveCountMax 2
+@end group
+@end example
+
+The corresponding PuTTY configuration is in the @option{Connection}
+entry, @option{Seconds between keepalives} option.  Set this to 5.
+There is no counter which could be set.
+
+
+@subsection Using ssh connection sharing
+
+@vindex ControlPath@r{, ssh option}
+@vindex ControlPersist@r{, ssh option}
+@value{tramp} uses the @option{ControlMaster=auto} OpenSSH option by
+default, if possible.  However, it overwrites @option{ControlPath}
+settings when initiating @command{ssh} sessions.  @value{tramp} does
+this to fend off a stall if a master session opened outside the Emacs
+session is no longer open.  That is why @value{tramp} prompts for the
+password again even if there is an @command{ssh} already open.
+
+@vindex tramp-ssh-controlmaster-options
+Some OpenSSH versions support a @option{ControlPersist} option, which
+allows you to set the @option{ControlPath} provided the variable
+@code{tramp-ssh-controlmaster-options} is customized as follows:
+
+@lisp
+@group
+(customize-set-variable
+ 'tramp-ssh-controlmaster-options
+ (concat
+   "-o ControlPath=/tmp/ssh-ControlPath-%%r@@%%h:%%p "
+   "-o ControlMaster=auto -o ControlPersist=yes"))
+@end group
+@end lisp
+
+Note how @samp{%r}, @samp{%h} and @samp{%p} must be encoded as
+@samp{%%r}, @samp{%%h} and @samp{%%p}.
+
+@vindex tramp-use-ssh-controlmaster-options
+If the @file{~/.ssh/config} file is configured appropriately for the
+above behavior, then any changes to @command{ssh} can be suppressed
+with this @code{nil} setting:
+
+@lisp
+(customize-set-variable 'tramp-use-ssh-controlmaster-options nil)
+@end lisp
+
+@vindex ProxyCommand@r{, ssh option}
+@vindex ProxyJump@r{, ssh option}
+This should also be set to @code{nil} if you use the
+@option{ProxyCommand} or @option{ProxyJump} options in your
+@command{ssh} configuration.
+
+In order to use the @option{ControlMaster} option, @value{tramp} must
+check whether the @command{ssh} client supports this option.  This is
+only possible on the local host, for the first hop.  @value{tramp}
+does not use this option on proxy hosts, therefore.
+
+If you want to use this option also for the other hops, you must
+configure @file{~/.ssh/config} on the proxy host:
+
+@example
+@group
+Host *
+     ControlMaster      auto
+     ControlPath        tramp.%C
+     ControlPersist     no
+@end group
+@end example
+
+Check the @samp{ssh_config(5)} man page whether these options are
+supported on your proxy host.
+
+On MS Windows, @code{tramp-use-ssh-controlmaster-options} is set to
+@code{nil} by default, because the MS Windows and MSYS2
+implementations of @command{OpenSSH} do not support this option properly.
+
+In PuTTY, you can achieve connection sharing in the @option{Connection/SSH}
+entry, enabling the @option{Share SSH connections if possible} option.
+
+
+@subsection Configure direct copying between two remote servers
+
+@vindex tramp-use-scp-direct-remote-copying
+@value{tramp} uses a temporary local copy when copying two files
+between different remote hosts via external methods.  This behavior is
+due to authentication problems @value{tramp} cannot handle
+sufficiently.  However, for @option{scp} connections this can be
+changed.  When a file shall be copied between two different remote
+hosts @samp{source} and @samp{target}, and
+
+@itemize @minus
+@item
+Variable @code{tramp-use-scp-direct-remote-copying} is non-@code{nil},
+
+@item
+Remote host @samp{source} doesn't use the @option{RemoteCommand}
+option in @file{~/.ssh/config},
+
+@item
+Remote host @samp{target} shows the same host key when seen from the
+local host and from host @samp{source}, and
+
+@item
+@command{scp} running on host @samp{source} can authenticate to host
+@samp{target} without requiring a password,
+@end itemize
+
+@noindent
+@value{tramp} applies direct remote copying between hosts
+@samp{source} and @samp{target} like
+
+@example
+scp -p -T -R -q -r source:/path/to/file target:/path/to/another/file
+@end example
+
+This protects also your local temporary directory from overrun when
+copying large files.
+
+If these conditions do not apply, and
+@code{tramp-use-scp-direct-remote-copying} is non-@code{nil}, the
+option @samp{-3} is used instead of @samp{-R}.
+
+@c FIXME
+When @value{tramp} uses direct remote copying, password caches are not
+consulted.
+
+
+@subsection Issues with Cygwin ssh
+@cindex cygwin, issues
+
+This section is incomplete.  Please share your solutions.
+
+@cindex method @option{sshx} with cygwin
+@cindex @option{sshx} method with cygwin
+
+Cygwin's @command{ssh} works only with a Cygwin version of Emacs.  To
+check for compatibility: type @kbd{M-x eshell @key{RET}}, and start
+@kbd{ssh test.host @key{RET}}.  Incompatibilities trigger this
+message:
+
+@example
+Pseudo-terminal will not be allocated because stdin is not a terminal.
+@end example
+
+Some older versions of Cygwin's @command{ssh} work with the
+@option{sshx} access method.  Consult Cygwin's FAQ at
+@uref{https://cygwin.com/faq/} for details.
+
+@cindex cygwin and @command{fakecygpty}
+@cindex @command{fakecygpty} and cygwin
+
+On @uref{https://www.emacswiki.org/emacs/SshWithNTEmacs, the Emacs
+Wiki} it is explained how to use the helper program
+@command{fakecygpty} to fix this problem.
+
+@cindex method @option{scpx} with cygwin
+@cindex @option{scpx} method with cygwin
+
+When using the @option{scpx} access method, Emacs may call
+@command{scp} with MS Windows file naming, such as @file{c:/foo}.  But
+the version of @command{scp} that is installed with Cygwin does not
+know about MS Windows file naming, which causes it to incorrectly look
+for a host named @samp{c}.
+
+A workaround: write a wrapper script for @option{scp} to convert
+Windows file names to Cygwin file names.
+
+@cindex cygwin and @command{ssh-agent}
+@cindex @env{SSH_AUTH_SOCK} and emacs on ms windows
+@vindex SSH_AUTH_SOCK@r{, environment variable}
+
+When using the @command{ssh-agent} on MS Windows for password-less
+interaction, @option{ssh} methods depend on the environment variable
+@env{SSH_AUTH_SOCK}.  But this variable is not set when Emacs is
+started from a Desktop shortcut and authentication fails.
+
+One workaround is to use an MS Windows based SSH Agent, such as
+@command{Pageant}.  It is part of the PuTTY Suite of tools.
+
+The fallback is to start Emacs from a shell.
+
+
 @node FUSE setup
 @section @acronym{FUSE} setup hints
 
@@ -2828,10 +3042,10 @@ Example:
 The backup file name of
 @file{@trampfn{su,root@@localhost,/etc/secretfile}} would be
 @ifset unified
-@file{@trampfn{su,root@@localhost,~/.emacs.d/backups/!su:root@@localhost:!etc!secretfile~}}
+@file{@trampfn{su,root@@localhost,~/.emacs.d/backups/!su:root@@localhost:!etc!secretfile~}}.
 @end ifset
 @ifset separate
-@file{@trampfn{su,root@@localhost,~/.emacs.d/backups/![su!root@@localhost]!etc!secretfile~}}
+@file{@trampfn{su,root@@localhost,~/.emacs.d/backups/![su!root@@localhost]!etc!secretfile~}}.
 @end ifset
 
 @vindex auto-save-file-name-transforms
@@ -2985,62 +3199,6 @@ subdirectories will remain encrypted.
 @end deffn
 
 
-@node Windows setup hints
-@section Issues with Cygwin ssh
-@cindex cygwin, issues
-
-This section is incomplete.  Please share your solutions.
-
-@cindex method @option{sshx} with cygwin
-@cindex @option{sshx} method with cygwin
-
-Cygwin's @command{ssh} works only with a Cygwin version of Emacs.  To
-check for compatibility: type @kbd{M-x eshell @key{RET}}, and start
-@kbd{ssh test.host @key{RET}}.  Incompatibilities trigger this
-message:
-
-@example
-Pseudo-terminal will not be allocated because stdin is not a terminal.
-@end example
-
-Some older versions of Cygwin's @command{ssh} work with the
-@option{sshx} access method.  Consult Cygwin's FAQ at
-@uref{https://cygwin.com/faq/} for details.
-
-@cindex cygwin and @command{fakecygpty}
-@cindex @command{fakecygpty} and cygwin
-
-On @uref{https://www.emacswiki.org/emacs/SshWithNTEmacs, the Emacs
-Wiki} it is explained how to use the helper program
-@command{fakecygpty} to fix this problem.
-
-@cindex method @option{scpx} with cygwin
-@cindex @option{scpx} method with cygwin
-
-When using the @option{scpx} access method, Emacs may call
-@command{scp} with MS Windows file naming, such as @file{c:/foo}.  But
-the version of @command{scp} that is installed with Cygwin does not
-know about MS Windows file naming, which causes it to incorrectly look
-for a host named @samp{c}.
-
-A workaround: write a wrapper script for @option{scp} to convert
-Windows file names to Cygwin file names.
-
-@cindex cygwin and @command{ssh-agent}
-@cindex @env{SSH_AUTH_SOCK} and emacs on ms windows
-@vindex SSH_AUTH_SOCK@r{, environment variable}
-
-When using the @command{ssh-agent} on MS Windows for password-less
-interaction, @option{ssh} methods depend on the environment variable
-@env{SSH_AUTH_SOCK}.  But this variable is not set when Emacs is
-started from a Desktop shortcut and authentication fails.
-
-One workaround is to use an MS Windows based SSH Agent, such as
-Pageant.  It is part of the Putty Suite of tools.
-
-The fallback is to start Emacs from a shell.
-
-
 @node Usage
 @chapter Using @value{tramp}
 @cindex using @value{tramp}
@@ -3085,23 +3243,23 @@ is a feature of Emacs that may cause missed prompts 
when using
 on the remote host @var{host}, using the method @var{method}.
 
 @table @file
-@item @value{prefix}ssh@value{postfixhop}melancholia@value{postfix}.emacs
+@item @trampfn{ssh,melancholia,.emacs}
 For the file @file{.emacs} located in the home directory, on the host
 @code{melancholia}, using method @code{ssh}.
 
-@item 
@value{prefix}ssh@value{postfixhop}melancholia.danann.net@value{postfix}.emacs
+@item @trampfn{ssh,melancholia.danann.net,.emacs}
 For the file @file{.emacs} specified using the fully qualified domain name of
 the host.
 
-@item @value{prefix}ssh@value{postfixhop}melancholia@value{postfix}~/.emacs
+@item @trampfn{ssh,melancholia,~/.emacs}
 For the file @file{.emacs} specified using the @file{~}, which is expanded.
 
-@item 
@value{prefix}ssh@value{postfixhop}melancholia@value{postfix}~daniel/.emacs
+@item @trampfn{ssh,melancholia,~daniel/.emacs}
 For the file @file{.emacs} located in @code{daniel}'s home directory
 on the host, @code{melancholia}.  The @file{~<user>} construct is
 expanded to the home directory of that user on the remote host.
 
-@item 
@value{prefix}ssh@value{postfixhop}melancholia@value{postfix}/etc/squid.conf
+@item @trampfn{ssh,melancholia,/etc/squid.conf}
 For the file @file{/etc/squid.conf} on the host @code{melancholia}.
 
 @end table
@@ -3115,12 +3273,9 @@ brackets @file{@value{ipv6prefix}} and 
@file{@value{ipv6postfix}}.
 @end ifset
 
 By default, @value{tramp} will use the current local user name as the
-remote user name for log in to the remote host.  Specifying a different
-name using the proper syntax will override this default behavior:
-
-@example
-@trampfn{method,user@@host,path/to/file}
-@end example
+remote user name for log in to the remote host.  Specifying a
+different name using the proper syntax will override this default
+behavior: @file{@trampfn{method,user@@host,path/to/file}}.
 
 @file{@trampfn{ssh,daniel@@melancholia,.emacs}} is for file
 @file{.emacs} in @code{daniel}'s home directory on the host,
@@ -3316,8 +3471,9 @@ remote host name and file name.  For example, hopping 
over a single
 proxy @samp{bird@@bastion} to a remote file on @samp{you@@remotehost}:
 
 @example
-@c @kbd{C-x C-f 
@trampfn{ssh@value{postfixhop}bird@@bastion|ssh,you,remotehost,/path} @key{RET}}
-@kbd{C-x C-f 
@value{prefix}ssh@value{postfixhop}bird@@bastion|ssh@value{postfixhop}you@@remotehost@value{postfix}/path
 @key{RET}}
+@c @kbd{C-x C-f 
@trampfn{ssh@value{postfixhop}bird@@bastion|ssh,you@@remotehost,/path} 
@key{RET}}
+@kbd{C-x C-f @value{prefix}ssh@value{postfixhop}bird@@bastion|@c
+ssh@value{postfixhop}you@@remotehost@value{postfix}/path @key{RET}}
 @end example
 
 Each involved method must be an inline method (@pxref{Inline methods}).
@@ -3345,12 +3501,12 @@ Ad-hoc proxies can take patterns @code{%h} or @code{%u} 
like in
 @code{tramp-default-proxies-alist}.  The following file name expands
 to user @samp{root} on host @samp{remotehost}, starting with an
 @option{ssh} session on host @samp{remotehost}:
-@samp{@value{prefix}ssh@value{postfixhop}%h|su@value{postfixhop}remotehost@value{postfix}}.
+@samp{@trampfn{ssh@value{postfixhop}%h|su,remotehost,}}.
 
-On the other hand, if a trailing hop does not specify a host name,
-the host name of the previous hop is reused.  Therefore, the following
+On the other hand, if a trailing hop does not specify a host name, the
+host name of the previous hop is reused.  Therefore, the following
 file name is equivalent to the previous example:
-@samp{@value{prefix}ssh@value{postfixhop}remotehost|su@value{postfixhop}@value{postfix}}.
+@samp{@trampfn{ssh@value{postfixhop}remotehost|su,,}}.
 
 
 @node Remote processes
@@ -3971,7 +4127,9 @@ would trigger renaming of buffer file names on 
@samp{badhost} to
 @samp{goodhost}, including changing the directory name.
 
 @lisp
-("@trampfn{ssh,.+\\\\.company\\\\.org,}" . 
"@value{prefix}ssh@value{postfixhop}multi.hop|ssh@value{postfixhop}%h@value{postfix}")
+("@trampfn{ssh,.+\\\\.company\\\\.org,}" @c
+. "@value{prefix}ssh@value{postfixhop}multi.hop|@c
+ssh@value{postfixhop}%h@value{postfix}")
 @end lisp
 
 routes all connections to a host in @samp{company.org} via
@@ -4231,7 +4389,8 @@ It is even possible to access file archives in file 
archives, as
 (progn
   (url-handler-mode 1)
   (find-file
-   
"https://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_8.28-1_amd64.deb/control.tar.gz/control";))
+   "https://ftp.debian.org/debian/pool/main/c/coreutils/\
+coreutils_8.28-1_amd64.deb/control.tar.gz/control"))
 @end group
 @end lisp
 
@@ -4537,97 +4696,6 @@ In order to disable those optimizations, set user option
 @code{tramp-local-host-regexp} to @code{nil}.
 
 
-@item
-@value{tramp} does not recognize if a @command{ssh} session hangs
-
-@vindex ServerAliveInterval@r{, ssh option}
-@command{ssh} sessions on the local host hang when the network is
-down.  @value{tramp} cannot safely detect such hangs.  The network
-configuration for @command{ssh} can be configured to kill such hangs
-with the following command in the @file{~/.ssh/config}:
-
-@example
-@group
-Host *
-     ServerAliveInterval 5
-@end group
-@end example
-
-
-@item
-@value{tramp} does not use default @command{ssh} @option{ControlPath}
-
-@vindex ControlPath@r{, ssh option}
-@vindex ControlPersist@r{, ssh option}
-@value{tramp} overwrites @option{ControlPath} settings when initiating
-@command{ssh} sessions.  @value{tramp} does this to fend off a stall
-if a master session opened outside the Emacs session is no longer
-open.  That is why @value{tramp} prompts for the password again even
-if there is an @command{ssh} already open.
-
-@vindex tramp-ssh-controlmaster-options
-Some @command{ssh} versions support a @option{ControlPersist} option,
-which allows you to set the @option{ControlPath} provided the variable
-@code{tramp-ssh-controlmaster-options} is customized as follows:
-
-@lisp
-@group
-(customize-set-variable
- 'tramp-ssh-controlmaster-options
- (concat
-   "-o ControlPath=/tmp/ssh-ControlPath-%%r@@%%h:%%p "
-   "-o ControlMaster=auto -o ControlPersist=yes"))
-@end group
-@end lisp
-
-Note how @samp{%r}, @samp{%h} and @samp{%p} must be encoded as
-@samp{%%r}, @samp{%%h} and @samp{%%p}.
-
-@vindex tramp-use-ssh-controlmaster-options
-If the @file{~/.ssh/config} file is configured appropriately for the
-above behavior, then any changes to @command{ssh} can be suppressed
-with this @code{nil} setting:
-
-@lisp
-(customize-set-variable 'tramp-use-ssh-controlmaster-options nil)
-@end lisp
-
-@vindex ProxyCommand@r{, ssh option}
-@vindex ProxyJump@r{, ssh option}
-This should also be set to @code{nil} if you use the
-@option{ProxyCommand} or @option{ProxyJump} options in your
-@command{ssh} configuration.
-
-On MS Windows, @code{tramp-use-ssh-controlmaster-options} is set to
-@code{nil} by default, because the MS Windows and MSYS2
-implementations of @command{OpenSSH} do not support this option properly.
-
-
-@item
-On multi-hop connections, @value{tramp} does not use @command{ssh}
-@option{ControlMaster}
-
-In order to use the @option{ControlMaster} option, @value{tramp} must
-check whether the @command{ssh} client supports this option.  This is
-only possible on the local host, for the first hop.  @value{tramp}
-does not use this option on proxy hosts.
-
-If you want to use this option also for the other hops, you must
-configure @file{~/.ssh/config} on the proxy host:
-
-@example
-@group
-Host *
-     ControlMaster      auto
-     ControlPath        tramp.%C
-     ControlPersist     no
-@end group
-@end example
-
-Check the @samp{ssh_config(5)} man page whether these options are
-supported on your proxy host.
-
-
 @item
 Does @value{tramp} support @acronym{SSH} security keys?
 
@@ -5075,7 +5143,8 @@ Why saved multi-hop file names do not work in a new Emacs 
session?
 When saving ad-hoc multi-hop @value{tramp} file names (@pxref{Ad-hoc
 multi-hops}) via bookmarks, recent files, filecache, bbdb, or another
 package, use the full ad-hoc file name including all hops, like
-@file{@trampfn{ssh,bird@@bastion|ssh@value{postfixhop}news.my.domain,/opt/news/etc}}.
+@file{@trampfn{ssh,bird@@bastion|ssh@value{postfixhop}@c
+news.my.domain,/opt/news/etc}}.
 
 Alternatively, when saving abbreviated multi-hop file names
 @file{@trampfn{ssh,news@@news.my.domain,/opt/news/etc}}, the user
@@ -5384,7 +5453,7 @@ bind it to non-@code{nil} value.
 Keeping a local cache of remote file attributes in sync with the
 remote host is a time-consuming operation.  Flushing and re-querying
 these attributes can tax @value{tramp} to a grinding halt on busy
-remote servers.
+remote hosts.
 
 To get around these types of slow-downs in @value{tramp}'s
 responsiveness, set the @code{process-file-side-effects} to @code{nil}
@@ -5539,6 +5608,8 @@ function call traces are written to the buffer 
@file{*trace-output*}.
 @c
 @c * Say something about the .login and .profile files of the remote
 @c   shells.
+@c
 @c * Explain how tramp.el works in principle: open a shell on a remote
 @c   host and then send commands to it.
+@c
 @c * Consistent small or capitalized words especially in menus.
diff --git a/etc/NEWS b/etc/NEWS
index 048f6d5598..5297db3e2d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -72,8 +72,8 @@ work on any underlying window system supported by GDK, such as
 Wayland and Broadway.
 
 ---
-** The docstrings of preloaded files are not in 'etc/DOC' any more.
-Instead, they're fetched as needed from the corresponding '.elc' file,
+** The docstrings of preloaded files are not in "etc/DOC" any more.
+Instead, they're fetched as needed from the corresponding ".elc" file,
 as was already the case for all the non-preloaded files.
 
 
@@ -94,10 +94,10 @@ time.
 
 ---
 ** Support for old EIEIO functions is not autoloaded any more.
-You need an explicit (require 'eieio-compat) to use 'defmethod'
-and 'defgeneric' (which have been made obsolete in Emacs-25 with
+You need an explicit '(require 'eieio-compat)' to use 'defmethod'
+and 'defgeneric' (which have been made obsolete in Emacs 25.1 with
 'cl-defmethod' and 'cl-defgeneric').
-Similarly you might need to (require 'eieio-compat) before loading
+Similarly you might need to '(require 'eieio-compat)' before loading
 files that were compiled with an old EIEIO (Emacs<25).
 
 ---
@@ -260,13 +260,13 @@ These will take you (respectively) to the next and 
previous "page".
 ** Outline Mode
 
 *** Support for customizing the default visibility state of headings.
-Customize the option 'outline-default-state' to define what headings
-will be visible after Outline mode is turned on.  When equal to a
-number, the option 'outline-default-rules' determines the visibility
-of the subtree starting at the corresponding level.  Values are
-provided to control showing a heading subtree depending on whether the
-heading matches a regexp, or on whether its subtree has long lines or
-is itself too long.
+Customize the user option 'outline-default-state' to define what
+headings will be visible after Outline mode is turned on.  When equal
+to a number, the user option 'outline-default-rules' determines the
+visibility of the subtree starting at the corresponding level.  Values
+are provided to control showing a heading subtree depending on whether
+the heading matches a regexp, or on whether its subtree has long lines
+or is itself too long.
 
 ** Outline Minor Mode
 
@@ -414,15 +414,15 @@ received.
 
 ** Minibuffer and Completions
 
-*** The *Completions* buffer can now be automatically selected.
-To enable this behavior, customize the option 'completion-auto-select'
-to t.  Then pressing TAB will switch to the *Completions* buffer when
-it pops up that buffer.
+*** The "*Completions*" buffer can now be automatically selected.
+To enable this behavior, customize the user option
+'completion-auto-select' to t.  Then pressing 'TAB' will switch to the
+"*Completions*" buffer when it pops up that buffer.
 
 *** New user option 'completion-wrap-movement'.
 When non-nil, the commands 'next-completion' and 'previous-completion'
 automatically wrap around on reaching the beginning or the end of
-the *Completions* buffer.
+the "*Completions*" buffer.
 
 ** Isearch and Replace
 
@@ -484,7 +484,7 @@ If non-nil, 'C-c C-a' will put attached files at the end of 
the message.
 ---
 *** Message Mode now supports image yanking.
 
----
++++
 *** New user option 'message-server-alist'.
 This controls automatic insertion of the "X-Message-SMTP-Method"
 header before sending a message.
@@ -836,6 +836,12 @@ When calling 'abbreviate-file-name' on a Tramp filename, 
the result
 will abbreviate the user's home directory, for example by abbreviating
 "/ssh:user@host:/home/user" to "/ssh:user@host:~".
 
++++
+*** New user option 'tramp-use-scp-direct-remote-copying'.
+When set to non-nil, Tramp does not copy files between two remote
+hosts via a local copy in its temporary directory, but let the 'scp'
+command do this job.
+
 ** Browse URL
 
 ---
@@ -991,7 +997,7 @@ read back by the Emacs Lisp reader.
 This variable allows changing how Emacs prints unreadable objects.
 
 ---
-** The variable 'polling-period' now accepts floating point values.
+** The user option 'polling-period' now accepts floating point values.
 This means Emacs can now poll for input during Lisp execution more
 frequently than once in a second.
 
@@ -1039,7 +1045,7 @@ wheel on some mice, or when the user's finger moves off 
the touchpad.
 
 +++
 ** New event type 'pinch'.
-This event is sent when a user peforms a pinch gesture on a touchpad,
+This event is sent when a user performs a pinch gesture on a touchpad,
 which is comprised of placing two fingers on the touchpad and moving
 them towards or away from each other.
 
@@ -1199,14 +1205,14 @@ This can be used to check whether a specific font has a 
glyph for a
 character.
 
 +++
-** 'window-text-pixel-size' now accepts a new argument 'ignore-line-at-end'.
+** 'window-text-pixel-size' now accepts a new argument IGNORE-LINE-AT-END.
 This controls whether or not the last screen line of the text being
 measured will be counted for the purpose of calculating the text
 dimensions.
 
 +++
-** 'window-text-pixel-size' understands a new meaning of 'from'.
-Specifying a cons as the from argument allows to start measuring text
+** 'window-text-pixel-size' understands a new meaning of FROM.
+Specifying a cons as the FROM argument allows to start measuring text
 from a specified amount of pixels above or below a position.
 
 ---
@@ -1383,9 +1389,9 @@ cookies set by web pages on disk.
 This variable is bound to t during the preparation of a "*Help*" buffer.
 
 +++
-** Timestamps like (1 . 1000) now work without warnings being generated.
-For example, (time-add nil '(1 . 1000)) no longer warns that the
-(1 . 1000) acts like (1000 . 1000000).  This warning, which was a
+** Timestamps like '(1 . 1000)' now work without warnings being generated.
+For example, '(time-add nil '(1 . 1000))' no longer warns that the
+'(1 . 1000)' acts like '(1000 . 1000000)'.  This warning, which was a
 temporary transition aid for Emacs 27, has served its purpose.
 
 +++
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index f0ceabe568..98192bd96d 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -38,6 +38,7 @@
 (declare-function dired-compress-file "dired-aux")
 (declare-function dired-remove-file "dired-aux")
 (defvar dired-compress-file-suffixes)
+;; Added in Emacs 28.1.
 (defvar process-file-return-signal-string)
 (defvar vc-handled-backends)
 (defvar vc-bzr-program)
@@ -136,6 +137,12 @@ be auto-detected by Tramp.
 
 The string is used in `tramp-methods'.")
 
+(defcustom tramp-use-scp-direct-remote-copying nil
+  "Whether to use direct copying between two remote hosts."
+  :group 'tramp
+  :version "29.1"
+  :type 'boolean)
+
 ;; Initialize `tramp-methods' with the supported methods.
 ;;;###tramp-autoload
 (tramp--with-startup
@@ -172,7 +179,7 @@ The string is used in `tramp-methods'.")
                 (tramp-remote-shell-args    ("-c"))
                 (tramp-copy-program         "scp")
                 (tramp-copy-args            (("-P" "%p") ("-p" "%k")
-                                            ("%x") ("-q") ("-r") ("%c")))
+                                            ("%x") ("%y") ("-q") ("-r") 
("%c")))
                 (tramp-copy-keep-date       t)
                 (tramp-copy-recursive       t)))
  (add-to-list 'tramp-methods
@@ -188,7 +195,7 @@ The string is used in `tramp-methods'.")
                 (tramp-remote-shell-args    ("-c"))
                 (tramp-copy-program         "scp")
                 (tramp-copy-args            (("-P" "%p") ("-p" "%k")
-                                            ("%x") ("-q") ("-r") ("%c")))
+                                            ("%x") ("%y") ("-q") ("-r") 
("%c")))
                 (tramp-copy-keep-date       t)
                 (tramp-copy-recursive       t)))
  (add-to-list 'tramp-methods
@@ -2240,200 +2247,210 @@ the uid and gid from FILENAME."
     (op filename newname ok-if-already-exists keep-date)
   "Invoke `scp' program to copy.
 The method used must be an out-of-band method."
-  (let* ((t1 (tramp-tramp-file-p filename))
-        (t2 (tramp-tramp-file-p newname))
-        (orig-vec (tramp-dissect-file-name (if t1 filename newname)))
+  (let* ((v1 (and (tramp-tramp-file-p filename)
+                 (tramp-dissect-file-name filename)))
+        (v2 (and (tramp-tramp-file-p newname)
+                 (tramp-dissect-file-name newname)))
+        (v (or v1 v2))
         copy-program copy-args copy-env copy-keep-date listener spec
         options source target remote-copy-program remote-copy-args p)
 
-    (with-parsed-tramp-file-name (if t1 filename newname) nil
-      (if (and t1 t2)
-
-         ;; Both are Tramp files.  We shall optimize it when the
-         ;; methods for FILENAME and NEWNAME are the same.
-         (let* ((dir-flag (file-directory-p filename))
-                (tmpfile (tramp-compat-make-temp-file localname dir-flag)))
-           (if dir-flag
-               (setq tmpfile
-                     (expand-file-name
-                      (file-name-nondirectory newname) tmpfile)))
-           (unwind-protect
-               (progn
-                 (tramp-do-copy-or-rename-file-out-of-band
-                  op filename tmpfile ok-if-already-exists keep-date)
-                 (tramp-do-copy-or-rename-file-out-of-band
-                  'rename tmpfile newname ok-if-already-exists keep-date))
-             ;; Save exit.
-             (ignore-errors
-               (if dir-flag
-                   (delete-directory
-                    (expand-file-name ".." tmpfile) 'recursive)
-                 (delete-file tmpfile)))))
-
-       ;; Check which ones of source and target are Tramp files.
-       (setq source (funcall
-                     (if (and (string-equal method "rsync")
-                              (file-directory-p filename)
-                              (not (file-exists-p newname)))
-                         #'file-name-as-directory
-                       #'identity)
-                     (if t1
-                         (tramp-make-copy-program-file-name v)
-                       (tramp-compat-file-name-unquote filename)))
-             target (if t2
-                        (tramp-make-copy-program-file-name v)
-                      (tramp-compat-file-name-unquote newname)))
-
-       ;; Check for user.  There might be an interactive setting.
-       (setq user (or (tramp-file-name-user v)
-                      (tramp-get-connection-property v "login-as" nil)))
-
-       ;; Check for listener port.
-       (when (tramp-get-method-parameter v 'tramp-remote-copy-args)
-         (setq listener (number-to-string (+ 50000 (random 10000))))
-         (while
-             (zerop (tramp-call-process v "nc" nil nil nil "-z" host listener))
-           (setq listener (number-to-string (+ 50000 (random 10000))))))
-
-       ;; Compose copy command.
-       (setq options
-             (format-spec
-              (tramp-ssh-controlmaster-options v)
-              (format-spec-make
-               ?t (tramp-get-connection-property
-                   (tramp-get-connection-process v) "temp-file" "")))
-             spec (list
-                   ?h (or host "") ?u (or user "") ?p (or port "")
-                   ?r listener ?c options ?k (if keep-date " " "")
-                    ?n (concat "2>" (tramp-get-remote-null-device v))
-                   ?x (tramp-scp-strict-file-name-checking v))
-             copy-program (tramp-get-method-parameter v 'tramp-copy-program)
-             copy-keep-date (tramp-get-method-parameter
-                             v 'tramp-copy-keep-date)
-             copy-args
-             ;; " " has either been a replacement of "%k" (when
-             ;; keep-date argument is non-nil), or a replacement for
-             ;; the whole keep-date sublist.
-             (delete " " (apply #'tramp-expand-args v 'tramp-copy-args spec))
-             ;; `tramp-ssh-controlmaster-options' is a string instead
-             ;; of a list.  Unflatten it.
-             copy-args
-             (tramp-compat-flatten-tree
-              (mapcar
-               (lambda (x) (if (tramp-compat-string-search " " x)
-                                (split-string x) x))
-               copy-args))
-             copy-env (apply #'tramp-expand-args v 'tramp-copy-env spec)
-             remote-copy-program
-             (tramp-get-method-parameter v 'tramp-remote-copy-program)
-             remote-copy-args
-             (apply #'tramp-expand-args v 'tramp-remote-copy-args spec))
-
-       ;; Check for local copy program.
-       (unless (executable-find copy-program)
-         (tramp-error
-          v 'file-error "Cannot find local copy program: %s" copy-program))
-
-       ;; Install listener on the remote side.  The prompt must be
-       ;; consumed later on, when the process does not listen anymore.
-       (when remote-copy-program
-         (unless (with-tramp-connection-property
-                     v (concat "remote-copy-program-" remote-copy-program)
-                   (tramp-find-executable
-                    v remote-copy-program (tramp-get-remote-path v)))
-           (tramp-error
-            v 'file-error
-            "Cannot find remote listener: %s" remote-copy-program))
-         (setq remote-copy-program
-               (mapconcat
-                #'identity
-                (append
-                 (list remote-copy-program) remote-copy-args
-                 (list (if t1 (concat "<" source) (concat ">" target)) "&"))
-                " "))
-         (tramp-send-command v remote-copy-program)
-         (with-timeout
-             (60 (tramp-error
-                  v 'file-error
-                  "Listener process not running on remote host: `%s'"
-                  remote-copy-program))
-           (tramp-send-command v (format "netstat -l | grep -q :%s" listener))
-           (while (not (tramp-send-command-and-check v nil))
-             (tramp-send-command
-              v (format "netstat -l | grep -q :%s" listener)))))
+    (if (and v1 v2 (zerop (length (tramp-scp-direct-remote-copying v1 v2))))
 
-       (with-temp-buffer
+       ;; Both are Tramp files.  We cannot use direct remote copying.
+       (let* ((dir-flag (file-directory-p filename))
+              (tmpfile (tramp-compat-make-temp-file
+                        (tramp-file-name-localname v1) dir-flag)))
+         (if dir-flag
+             (setq tmpfile
+                   (expand-file-name
+                    (file-name-nondirectory newname) tmpfile)))
          (unwind-protect
-             ;; The default directory must be remote.
-             (let ((default-directory
-                     (file-name-directory (if t1 filename newname)))
-                   (process-environment (copy-sequence process-environment)))
-               ;; Set the transfer process properties.
-               (tramp-set-connection-property
-                v "process-name" (buffer-name (current-buffer)))
-               (tramp-set-connection-property
-                v "process-buffer" (current-buffer))
-               (when copy-env
-                 (tramp-message
-                  orig-vec 6 "%s=\"%s\""
-                  (car copy-env) (string-join (cdr copy-env) " "))
-                 (setenv (car copy-env) (string-join (cdr copy-env) " ")))
-               (setq
-                copy-args
-                (append
-                 copy-args
-                 (if remote-copy-program
-                     (list (if t1 (concat ">" target) (concat "<" source)))
-                   (list source target)))
-                ;; Use an asynchronous process.  By this, password
-                ;; can be handled.  We don't set a timeout, because
-                ;; the copying of large files can last longer than 60
-                ;; secs.
-                p (let ((default-directory 
tramp-compat-temporary-file-directory))
-                    (apply
-                     #'start-process
-                     (tramp-get-connection-name v)
-                     (tramp-get-connection-buffer v)
-                     copy-program copy-args)))
-               (tramp-message orig-vec 6 "%s" (string-join (process-command p) 
" "))
-               (process-put p 'vector orig-vec)
-               (process-put p 'adjust-window-size-function #'ignore)
-               (set-process-query-on-exit-flag p nil)
+             (progn
+               (tramp-do-copy-or-rename-file-out-of-band
+                op filename tmpfile ok-if-already-exists keep-date)
+               (tramp-do-copy-or-rename-file-out-of-band
+                'rename tmpfile newname ok-if-already-exists keep-date))
+           ;; Save exit.
+           (ignore-errors
+             (if dir-flag
+                 (delete-directory
+                  (expand-file-name ".." tmpfile) 'recursive)
+               (delete-file tmpfile)))))
+
+      ;; Check which ones of source and target are Tramp files.
+      (setq source (funcall
+                   (if (and (string-equal (tramp-file-name-method v) "rsync")
+                            (file-directory-p filename)
+                            (not (file-exists-p newname)))
+                       #'file-name-as-directory
+                     #'identity)
+                   (if v1
+                       (tramp-make-copy-program-file-name v1)
+                     (tramp-compat-file-name-unquote filename)))
+           target (if v2
+                      (tramp-make-copy-program-file-name v2)
+                    (tramp-compat-file-name-unquote newname)))
+
+      ;; Check for listener port.
+      (when (tramp-get-method-parameter v 'tramp-remote-copy-args)
+       (setq listener (number-to-string (+ 50000 (random 10000))))
+       (while
+           (zerop (tramp-call-process
+                   v "nc" nil nil nil "-z" (tramp-file-name-host v) listener))
+         (setq listener (number-to-string (+ 50000 (random 10000))))))
+
+      ;; Compose copy command.
+      (setq options
+           (format-spec
+            (tramp-ssh-controlmaster-options v)
+            (format-spec-make
+             ?t (tramp-get-connection-property
+                 (tramp-get-connection-process v) "temp-file" "")))
+           spec (list
+                 ;; "%h" and "%u" do not happen in `tramp-copy-args'
+                 ;; of `scp', so it is save to use `v'.
+                 ?h (or (tramp-file-name-host v) "")
+                 ?u (or (tramp-file-name-user v)
+                        ;; There might be an interactive setting.
+                        (tramp-get-connection-property v "login-as" nil)
+                        "")
+                 ;; For direct remote copying, the port must be the
+                 ;; same for source and target.
+                 ?p (or (tramp-file-name-port v) "")
+                 ?r listener ?c options ?k (if keep-date " " "")
+                  ?n (concat "2>" (tramp-get-remote-null-device v))
+                 ?x (tramp-scp-strict-file-name-checking v)
+                 ?y (tramp-scp-direct-remote-copying v1 v2))
+           copy-program (tramp-get-method-parameter v 'tramp-copy-program)
+           copy-keep-date (tramp-get-method-parameter
+                           v 'tramp-copy-keep-date)
+           copy-args
+           ;; " " has either been a replacement of "%k" (when
+           ;; keep-date argument is non-nil), or a replacement for
+           ;; the whole keep-date sublist.
+           (delete " " (apply #'tramp-expand-args v 'tramp-copy-args spec))
+           ;; `tramp-ssh-controlmaster-options' is a string instead
+           ;; of a list.  Unflatten it.
+           copy-args
+           (tramp-compat-flatten-tree
+            (mapcar
+             (lambda (x) (if (tramp-compat-string-search " " x)
+                              (split-string x) x))
+             copy-args))
+           copy-env (apply #'tramp-expand-args v 'tramp-copy-env spec)
+           remote-copy-program
+           (tramp-get-method-parameter v 'tramp-remote-copy-program)
+           remote-copy-args
+           (apply #'tramp-expand-args v 'tramp-remote-copy-args spec))
+
+      ;; Check for local copy program.
+      (unless (executable-find copy-program)
+       (tramp-error
+        v 'file-error "Cannot find local copy program: %s" copy-program))
+
+      ;; Install listener on the remote side.  The prompt must be
+      ;; consumed later on, when the process does not listen anymore.
+      (when remote-copy-program
+       (unless (with-tramp-connection-property
+                   v (concat "remote-copy-program-" remote-copy-program)
+                 (tramp-find-executable
+                  v remote-copy-program (tramp-get-remote-path v)))
+         (tramp-error
+          v 'file-error
+          "Cannot find remote listener: %s" remote-copy-program))
+       (setq remote-copy-program
+             (mapconcat
+              #'identity
+              (append
+               (list remote-copy-program) remote-copy-args
+               (list (if v1 (concat "<" source) (concat ">" target)) "&"))
+              " "))
+       (tramp-send-command v remote-copy-program)
+       (with-timeout
+           (60 (tramp-error
+                v 'file-error
+                "Listener process not running on remote host: `%s'"
+                remote-copy-program))
+         (tramp-send-command v (format "netstat -l | grep -q :%s" listener))
+         (while (not (tramp-send-command-and-check v nil))
+           (tramp-send-command
+            v (format "netstat -l | grep -q :%s" listener)))))
+
+      (with-temp-buffer
+       (unwind-protect
+           ;; The default directory must be remote.
+           (let ((default-directory
+                  (file-name-directory (if v1 filename newname)))
+                 (process-environment (copy-sequence process-environment)))
+             ;; Set the transfer process properties.
+             (tramp-set-connection-property
+              v "process-name" (buffer-name (current-buffer)))
+             (tramp-set-connection-property
+              v "process-buffer" (current-buffer))
+             (when copy-env
+               (tramp-message
+                v 6 "%s=\"%s\""
+                (car copy-env) (string-join (cdr copy-env) " "))
+               (setenv (car copy-env) (string-join (cdr copy-env) " ")))
+             (setq
+              copy-args
+              (append
+               copy-args
+               (if remote-copy-program
+                   (list (if v1 (concat ">" target) (concat "<" source)))
+                 (list source target)))
+              ;; Use an asynchronous process.  By this, password can
+              ;; be handled.  We don't set a timeout, because the
+              ;; copying of large files can last longer than 60 secs.
+              p (let ((default-directory
+                       tramp-compat-temporary-file-directory))
+                  (apply
+                   #'start-process
+                   (tramp-get-connection-name v)
+                   (tramp-get-connection-buffer v)
+                   copy-program copy-args)))
+             (tramp-message v 6 "%s" (string-join (process-command p) " "))
+             (process-put p 'vector v)
+             (process-put p 'adjust-window-size-function #'ignore)
+             (set-process-query-on-exit-flag p nil)
+
+             ;; We must adapt `tramp-local-end-of-line' for sending
+             ;; the password.  Also, we indicate that perhaps several
+             ;; password prompts might appear.
+             (let ((tramp-local-end-of-line tramp-rsh-end-of-line)
+                   (tramp-password-prompt-not-unique (and v1 v2)))
+               (tramp-process-actions
+                p v nil tramp-actions-copy-out-of-band)))
+
+         ;; Reset the transfer process properties.
+         (tramp-flush-connection-property v "process-name")
+         (tramp-flush-connection-property v "process-buffer")
+         ;; Clear the remote prompt.
+         (when (and remote-copy-program
+                    (not (tramp-send-command-and-check v nil)))
+           ;; Houston, we have a problem!  Likely, the listener is
+           ;; still running, so let's clear everything (but the
+           ;; cached password).
+           (tramp-cleanup-connection v 'keep-debug 'keep-password))))
+
+      ;; Handle KEEP-DATE argument.
+      (when (and keep-date (not copy-keep-date))
+       (tramp-compat-set-file-times
+        newname
+        (file-attribute-modification-time (file-attributes filename))
+        (unless ok-if-already-exists 'nofollow)))
+
+      ;; Set the mode.
+      (unless (and keep-date copy-keep-date)
+       (ignore-errors
+         (set-file-modes newname (tramp-default-file-modes filename)))))
 
-               ;; We must adapt `tramp-local-end-of-line' for
-               ;; sending the password.
-               (let ((tramp-local-end-of-line tramp-rsh-end-of-line))
-                 (tramp-process-actions
-                  p v nil tramp-actions-copy-out-of-band)))
-
-           ;; Reset the transfer process properties.
-           (tramp-flush-connection-property v "process-name")
-           (tramp-flush-connection-property v "process-buffer")
-           ;; Clear the remote prompt.
-           (when (and remote-copy-program
-                      (not (tramp-send-command-and-check v nil)))
-             ;; Houston, we have a problem!  Likely, the listener is
-             ;; still running, so let's clear everything (but the
-             ;; cached password).
-             (tramp-cleanup-connection v 'keep-debug 'keep-password))))
-
-       ;; Handle KEEP-DATE argument.
-       (when (and keep-date (not copy-keep-date))
-         (tramp-compat-set-file-times
-          newname
-          (file-attribute-modification-time (file-attributes filename))
-          (unless ok-if-already-exists 'nofollow)))
-
-       ;; Set the mode.
-       (unless (and keep-date copy-keep-date)
-         (ignore-errors
-           (set-file-modes newname (tramp-default-file-modes filename)))))
-
-      ;; If the operation was `rename', delete the original file.
-      (unless (eq op 'copy)
-       (if (file-regular-p filename)
-           (delete-file filename)
-         (delete-directory filename 'recursive))))))
+    ;; If the operation was `rename', delete the original file.
+    (unless (eq op 'copy)
+      (if (file-regular-p filename)
+         (delete-file filename)
+       (delete-directory filename 'recursive)))))
 
 (defun tramp-sh-handle-make-directory (dir &optional parents)
   "Like `make-directory' for Tramp files."
@@ -4806,7 +4823,7 @@ Goes through the list `tramp-inline-compress-commands'."
    ((stringp tramp-scp-strict-file-name-checking)
     tramp-scp-strict-file-name-checking)
 
-   ;; Determine the options.
+   ;; Determine the option.
    (t (setq tramp-scp-strict-file-name-checking "")
       (let ((case-fold-search t))
        (ignore-errors
@@ -4822,6 +4839,78 @@ Goes through the list `tramp-inline-compress-commands'."
                  (setq tramp-scp-strict-file-name-checking "-T")))))))
       tramp-scp-strict-file-name-checking)))
 
+(defun tramp-scp-direct-remote-copying (vec1 vec2)
+  "Return the direct remote copying argument of the local scp."
+  (cond
+   ((or (not tramp-use-scp-direct-remote-copying) (null vec1) (null vec2)
+       (not (tramp-get-process vec1))
+       (not (equal (tramp-file-name-port vec1) (tramp-file-name-port vec2)))
+       (null (assoc "%y" (tramp-get-method-parameter vec1 'tramp-copy-args)))
+       (null (assoc "%y" (tramp-get-method-parameter vec2 'tramp-copy-args))))
+    "")
+
+   ((let ((case-fold-search t))
+      (and
+       ;; Check, whether "scp" supports "-R" option.
+       (with-tramp-connection-property nil "scp-R"
+        (when (executable-find "scp")
+          (with-temp-buffer
+            (tramp-call-process vec1 "scp" nil t nil "-R")
+            (goto-char (point-min))
+            (not (search-forward-regexp
+                  "\\(illegal\\|unknown\\) option -- R" nil 'noerror)))))
+
+       ;; Check, that RemoteCommand is not used.
+       (with-tramp-connection-property (tramp-get-process vec1) 
"remote-command"
+        (let ((command `("ssh" "-G" ,(tramp-file-name-host vec1))))
+          (with-temp-buffer
+            (tramp-call-process
+             vec1 tramp-encoding-shell nil t nil
+             tramp-encoding-command-switch
+             (mapconcat #'identity command " "))
+            (goto-char (point-min))
+            (not (search-forward "remotecommand" nil 'noerror)))))
+
+       ;; Check hostkeys.
+       (with-tramp-connection-property
+          (tramp-get-process vec1)
+          (concat "direct-remote-copying-"
+                  (tramp-make-tramp-file-name vec2 'noloc))
+        (let ((command
+               (append
+                `("ssh" "-G" ,(tramp-file-name-host vec2) "|"
+                  "grep" "-i" "^hostname" "|" "cut" "-d\" \"" "-f2" "|"
+                  "ssh-keyscan" "-f" "-")
+                (when (tramp-file-name-port vec2)
+                  `("-p" ,(tramp-file-name-port vec2)))))
+              found string)
+          (with-temp-buffer
+            ;; Check hostkey of VEC2, seen from VEC1.
+            (tramp-send-command vec1 (mapconcat #'identity command " "))
+            ;; Check hostkey of VEC2, seen locally.
+            (tramp-call-process
+             vec1 tramp-encoding-shell nil t nil tramp-encoding-command-switch
+             (mapconcat #'identity command " "))
+            (goto-char (point-min))
+            (while (and (not found) (not (eobp)))
+              (setq string
+                    (buffer-substring
+                     (line-beginning-position) (line-end-position))
+                    string
+                    (and
+                     (string-match "^[^# ]+ \\S-+ \\(\\S-+\\)$" string)
+                     (match-string 1 string))
+                    found
+                    (and string
+                         (with-current-buffer (tramp-get-buffer vec1)
+                           (goto-char (point-min))
+                           (search-forward string nil 'noerror))))
+              (forward-line))
+            found)))))
+    "-R")
+
+   (t "-3")))
+
 (defun tramp-timeout-session (vec)
   "Close the connection VEC after a session timeout.
 If there is just some editing, retry it after 5 seconds."
@@ -5975,9 +6064,6 @@ function cell is returned to be applied on a buffer."
 ;;
 ;; * Use lsh instead of ssh.  (Alfred M. Szmidt)
 ;;
-;; * Optimize out-of-band copying when both methods are scp-like (not
-;;   rsync).
-;;
 ;; * Keep a second connection open for out-of-band methods like scp or
 ;;   rsync.
 ;;
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index b258121549..c6e55ff688 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -255,6 +255,8 @@ pair of the form (KEY VALUE).  The following KEYs are 
defined:
     - \"%n\" expands to \"2>/dev/null\".
     - \"%x\" is replaced by the `tramp-scp-strict-file-name-checking'
       argument if it is supported.
+    - \"%y\" is replaced by the `tramp-scp-direct-remote-copying'
+      argument if it is supported.
 
     The existence of `tramp-login-args', combined with the
     absence of `tramp-copy-args', is an indication that the
@@ -1387,6 +1389,11 @@ Will be called once the password has been verified by 
successful
 authentication.")
 (put 'tramp-password-save-function 'tramp-suppress-trace t)
 
+(defvar tramp-password-prompt-not-unique nil
+  "Whether several passwords might be requested.
+This shouldn't be set explicitly.  It is let-bound, for example
+during direct remote copying with scp.")
+
 (defconst tramp-completion-file-name-handler-alist
   '((file-name-all-completions
      . tramp-completion-handle-file-name-all-completions)
@@ -4751,7 +4758,9 @@ of."
       ;; Let's check whether a wrong password has been sent already.
       ;; Sometimes, the process returns a new password request
       ;; immediately after rejecting the previous (wrong) one.
-      (unless (tramp-get-connection-property vec "first-password-request" nil)
+      (unless (or tramp-password-prompt-not-unique
+                 (tramp-get-connection-property
+                  vec "first-password-request" nil))
        (tramp-clear-passwd vec))
       (goto-char (point-min))
       (tramp-check-for-regexp proc tramp-process-action-regexp)
@@ -4759,7 +4768,13 @@ of."
       ;; We don't call `tramp-send-string' in order to hide the
       ;; password from the debug buffer and the traces.
       (process-send-string
-       proc (concat (tramp-read-passwd proc) tramp-local-end-of-line))
+       proc
+       (concat
+       (funcall
+        (if tramp-password-prompt-not-unique
+            #'tramp-read-passwd-without-cache #'tramp-read-passwd)
+        proc)
+       tramp-local-end-of-line))
       ;; Hide password prompt.
       (narrow-to-region (point-max) (point-max))))
   t)
@@ -5705,8 +5720,7 @@ verbosity of 6."
 ;; tramp-cache-read-persistent-data t)'" instead.
 (defun tramp-read-passwd (proc &optional prompt)
   "Read a password from user (compat function).
-Consults the auth-source package.
-Invokes `password-read' if available, `read-passwd' else."
+Consults the auth-source package."
   (let* (;; If `auth-sources' contains "~/.authinfo.gpg", and
         ;; `exec-path' contains a relative file name like ".", it
         ;; could happen that the "gpg" command is not found.  So we
@@ -5783,6 +5797,21 @@ Invokes `password-read' if available, `read-passwd' 
else."
 
 (put #'tramp-read-passwd 'tramp-suppress-trace t)
 
+(defun tramp-read-passwd-without-cache (proc &optional prompt)
+  "Read a password from user (compat function)."
+  ;; We suspend the timers while reading the password.
+  (let ((stimers (with-timeout-suspend)))
+    (unwind-protect
+       (password-read
+        (or prompt
+            (with-current-buffer (process-buffer proc)
+              (tramp-check-for-regexp proc tramp-password-prompt-regexp)
+              (match-string 0))))
+      ;; Reenable the timers.
+      (with-timeout-unsuspend stimers))))
+
+(put #'tramp-read-passwd-without-cache 'tramp-suppress-trace t)
+
 (defun tramp-clear-passwd (vec)
   "Clear password cache for connection related to VEC."
   (let ((method (tramp-file-name-method vec))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]