Function: tramp-do-copy-or-rename-file-directly

tramp-do-copy-or-rename-file-directly is a byte-compiled function defined in tramp-sh.el.gz.

Signature

(tramp-do-copy-or-rename-file-directly OP FILENAME NEWNAME OK-IF-ALREADY-EXISTS KEEP-DATE PRESERVE-UID-GID)

Documentation

Invokes cp or mv on the remote system.

OP must be one of copy or rename, indicating cp or mv, respectively. FILENAME specifies the file to copy or rename, NEWNAME is the name of the new file (for copy) or the new name of the file (for rename). Both files must reside on the same host. KEEP-DATE means to make sure that NEWNAME has the same timestamp as FILENAME. PRESERVE-UID-GID, when non-nil, instructs to keep the uid and gid from FILENAME.

Source Code

;; Defined in /usr/src/emacs/lisp/net/tramp-sh.el.gz
(defun tramp-do-copy-or-rename-file-directly
 (op filename newname ok-if-already-exists keep-date preserve-uid-gid)
  "Invokes `cp' or `mv' on the remote system.
OP must be one of `copy' or `rename', indicating `cp' or `mv',
respectively.  FILENAME specifies the file to copy or rename,
NEWNAME is the name of the new file (for copy) or the new name of
the file (for rename).  Both files must reside on the same host.
KEEP-DATE means to make sure that NEWNAME has the same timestamp
as FILENAME.  PRESERVE-UID-GID, when non-nil, instructs to keep
the uid and gid from FILENAME."
  ;; FILENAME and NEWNAME are already expanded.
  (let ((t1 (tramp-tramp-file-p filename))
	(t2 (tramp-tramp-file-p newname))
	(file-times (file-attribute-modification-time
		     (file-attributes filename)))
	(file-modes (tramp-default-file-modes filename)))
    (with-parsed-tramp-file-name (if t1 filename newname) nil
      (let* ((cmd (cond ((and (eq op 'copy) preserve-uid-gid) "cp -f -p")
			((eq op 'copy) "cp -f")
			((eq op 'rename) "mv -f")
			(t (tramp-error
			    v 'file-error
			    "Unknown operation `%s', must be `copy' or `rename'"
			    op))))
	     (localname1 (tramp-file-local-name filename))
	     (localname2 (tramp-file-local-name newname))
	     (prefix (file-remote-p (if t1 filename newname)))
             cmd-result)
	(when (and (eq op 'copy) (file-directory-p filename))
	  (setq cmd (concat cmd " -R")))

	(cond
	 ;; Both files are on a remote host, with same user.
	 ((and t1 t2)
          (setq cmd-result
                (tramp-send-command-and-check
                 v (format "%s %s %s" cmd
			   (tramp-shell-quote-argument localname1)
			   (tramp-shell-quote-argument localname2))))
	  (with-current-buffer (tramp-get-buffer v)
	    (goto-char (point-min))
	    (unless
		(or
		 (and keep-date
		      ;; Mask cp -f error.
		      (re-search-forward
		       tramp-operation-not-permitted-regexp nil t))
		 cmd-result)
	      (tramp-error-with-buffer
	       nil v 'file-error
	       "Copying directly failed, see buffer `%s' for details"
	       (buffer-name)))))

	 ;; We are on the local host.
	 ((or t1 t2)
	  (cond
	   ;; We can do it directly.
	   ((let (file-name-handler-alist)
	      (and (file-readable-p localname1)
		   ;; No sticky bit when renaming.
		   (or (eq op 'copy)
		       (zerop
			(logand
			 (file-modes (file-name-directory localname1)) #o1000)))
		   (file-writable-p (file-name-directory localname2))
		   (or (file-directory-p localname2)
		       (file-writable-p localname2))))
	    (if (eq op 'copy)
		(copy-file
		 localname1 localname2 ok-if-already-exists
		 keep-date preserve-uid-gid)
	      (tramp-run-real-handler
	       #'rename-file
	       (list localname1 localname2 ok-if-already-exists))))

	   ;; We can do it directly with `tramp-send-command'
	   ((and (file-readable-p (concat prefix localname1))
		 (file-writable-p
		  (file-name-directory (concat prefix localname2)))
		 (or (file-directory-p (concat prefix localname2))
		     (file-writable-p (concat prefix localname2))))
	    (with-parsed-tramp-file-name prefix nil
	      (tramp-flush-file-properties v localname2))
	    (tramp-do-copy-or-rename-file-directly
	     op (concat prefix localname1) (concat prefix localname2)
	     ok-if-already-exists keep-date preserve-uid-gid)
	    ;; We must change the ownership to the local user.
	    (tramp-set-file-uid-gid
	     (concat prefix localname2)
	     (tramp-get-local-uid 'integer)
	     (tramp-get-local-gid 'integer)))

	   ;; We need a temporary file in between.
	   (t
	    ;; Create the temporary file.
	    (let ((tmpfile (tramp-compat-make-temp-file localname1)))
	      (unwind-protect
		  (progn
		    (cond
		     (t1
		      (tramp-barf-unless-okay
		       v (format
			  "%s %s %s" cmd
			  (tramp-shell-quote-argument localname1)
			  (tramp-shell-quote-argument tmpfile))
		       "Copying directly failed, see buffer `%s' for details"
		       (tramp-get-buffer v))
		      ;; We must change the ownership as remote user.
		      ;; Since this does not work reliable, we also
		      ;; give read permissions.
		      (set-file-modes (concat prefix tmpfile) #o0777)
		      (tramp-set-file-uid-gid
		       (concat prefix tmpfile)
		       (tramp-get-local-uid 'integer)
		       (tramp-get-local-gid 'integer)))
		     (t2
		      (if (eq op 'copy)
			  (copy-file
			   localname1 tmpfile t keep-date preserve-uid-gid)
			(tramp-run-real-handler
			 #'rename-file (list localname1 tmpfile t)))
		      ;; We must change the ownership as local user.
		      ;; Since this does not work reliable, we also
		      ;; give read permissions.
		      (set-file-modes tmpfile #o0777)
		      (tramp-set-file-uid-gid
		       tmpfile
		       (tramp-get-remote-uid v 'integer)
		       (tramp-get-remote-gid v 'integer))))

		    ;; Move the temporary file to its destination.
		    (cond
		     (t2
		      (tramp-barf-unless-okay
		       v (format
			  "cp -f -p %s %s"
			  (tramp-shell-quote-argument tmpfile)
			  (tramp-shell-quote-argument localname2))
		       "Copying directly failed, see buffer `%s' for details"
		       (tramp-get-buffer v)))
		     (t1
		      (tramp-run-real-handler
		       #'rename-file
		       (list tmpfile localname2 ok-if-already-exists)))))

		;; Save exit.
		(ignore-errors (delete-file tmpfile)))))))))

      ;; When newname did exist, we have wrong cached values.
      (when t2
	(with-parsed-tramp-file-name newname v2
	  (tramp-flush-file-properties v2 v2-localname)))

      ;; Set the time and mode.  Mask possible errors.
      (ignore-errors
	  (when keep-date
	    (tramp-compat-set-file-times
	     newname file-times (unless ok-if-already-exists 'nofollow))
	    (set-file-modes newname file-modes))))))