Function: tramp-do-copy-or-rename-file-out-of-band
tramp-do-copy-or-rename-file-out-of-band is a byte-compiled function
defined in tramp-sh.el.gz.
Signature
(tramp-do-copy-or-rename-file-out-of-band OP FILENAME NEWNAME OK-IF-ALREADY-EXISTS KEEP-DATE)
Documentation
Invoke scp program to copy.
The method used must be an out-of-band method.
Source Code
;; Defined in /usr/src/emacs/lisp/net/tramp-sh.el.gz
(defun tramp-do-copy-or-rename-file-out-of-band
(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)))
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)
?y (tramp-scp-force-scp-protocol 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)))))
(with-temp-buffer
(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)
;; 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
(tramp-compat-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))))))