Function: copy-file

copy-file is an interactive function defined in fileio.c.

Signature

(copy-file FILE NEWNAME &optional OK-IF-ALREADY-EXISTS KEEP-TIME PRESERVE-UID-GID PRESERVE-PERMISSIONS)

Documentation

Copy FILE to NEWNAME. Both args must be strings.

If NEWNAME is a directory name, copy FILE to a like-named file under NEWNAME. For NEWNAME to be recognized as a directory name, it should end in a slash.

This function always sets the file modes of the output file to match the input file.

The optional third argument OK-IF-ALREADY-EXISTS specifies what to do if file NEWNAME already exists. If OK-IF-ALREADY-EXISTS is nil, signal a file-already-exists error without overwriting. If OK-IF-ALREADY-EXISTS is an integer, request confirmation from the user about overwriting; this is what happens in interactive use with M-x. Any other value for OK-IF-ALREADY-EXISTS means to overwrite the existing file.

Fourth arg KEEP-TIME non-nil means give the output file the same last-modified time as the old one. (This works on only some systems.)

A prefix arg makes KEEP-TIME non-nil.

If PRESERVE-UID-GID is non-nil, try to transfer the uid and gid of FILE to NEWNAME.

If PRESERVE-PERMISSIONS is non-nil, copy permissions of FILE to NEWNAME; this includes the file modes, along with ACL entries and SELinux context if present. Otherwise, if NEWNAME is created its file permission bits are those of FILE, masked by the default file permissions.

Other relevant functions are documented in the file group.

View in manual

Probably introduced at or before Emacs version 1.6.

Key Bindings

Shortdoc

;; file
(copy-file "/tmp/foo" "/tmp/foocopy")

Source Code

// Defined in /usr/src/emacs/src/fileio.c
// Skipping highlighting due to helpful-max-highlight.
{
  Lisp_Object handler;
  specpdl_ref count = SPECPDL_INDEX ();
  Lisp_Object encoded_file, encoded_newname;
#if HAVE_LIBSELINUX
  char *con;
  int conlength = 0;
#endif
#ifdef WINDOWSNT
  int result;
#else
  bool already_exists = false;
  mode_t new_mask;
  emacs_fd ifd;
  int ofd;
  struct stat st;
#endif

  file = Fexpand_file_name (file, Qnil);
  newname = expand_cp_target (file, newname);

  /* If the input file name has special constructs in it,
     call the corresponding file name handler.  */
  handler = Ffind_file_name_handler (file, Qcopy_file);
  /* Likewise for output file name.  */
  if (NILP (handler))
    handler = Ffind_file_name_handler (newname, Qcopy_file);
  if (!NILP (handler))
    return call7 (handler, Qcopy_file, file, newname,
		  ok_if_already_exists, keep_time, preserve_uid_gid,
		  preserve_permissions);

  encoded_file = ENCODE_FILE (file);
  encoded_newname = ENCODE_FILE (newname);

#ifdef WINDOWSNT
  if (NILP (ok_if_already_exists)
      || FIXNUMP (ok_if_already_exists))
    barf_or_query_if_file_exists (newname, false, "copy to it",
				  FIXNUMP (ok_if_already_exists), false);

  result = w32_copy_file (SSDATA (encoded_file), SSDATA (encoded_newname),
			  !NILP (keep_time), !NILP (preserve_uid_gid),
			  !NILP (preserve_permissions));
  switch (result)
    {
    case -1:
      report_file_error ("Copying file", list2 (file, newname));
    case -2:
      report_file_error ("Copying permissions from", file);
    case -3:
      xsignal2 (Qfile_date_error,
		build_string ("Cannot set file date"), newname);
    case -4:
      report_file_error ("Copying permissions to", newname);
    }
#else /* not WINDOWSNT */
  ifd = emacs_fd_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0);

  if (!emacs_fd_valid_p (ifd))
    report_file_error ("Opening input file", file);

  record_unwind_protect_ptr (close_file_unwind_emacs_fd, &ifd);

  if (emacs_fd_fstat (ifd, &st) != 0)
    report_file_error ("Input file status", file);

  if (!NILP (preserve_permissions))
    {
#if HAVE_LIBSELINUX
      if (selinux_enabled_p (SSDATA (encoded_file))
	  /* Eschew copying SELinux contexts if they're inapplicable
	     to the destination file.  */
	  && selinux_enabled_p (SSDATA (encoded_newname))
	  && emacs_fd_to_int (ifd) != -1)
	{
	  conlength = fgetfilecon (emacs_fd_to_int (ifd),
				   &con);
	  if (conlength == -1)
	    report_file_error ("Doing fgetfilecon", file);
	}
#endif /* HAVE_LIBSELINUX */
    }

  /* We can copy only regular files.  */
  if (!S_ISREG (st.st_mode))
    report_file_errno ("Non-regular file", file,
		       S_ISDIR (st.st_mode) ? EISDIR : EINVAL);

#ifndef MSDOS
  new_mask = st.st_mode & (!NILP (preserve_uid_gid) ? 0700 : 0777);
#else
  new_mask = S_IREAD | S_IWRITE;
#endif

  ofd = emacs_open (SSDATA (encoded_newname), O_WRONLY | O_CREAT | O_EXCL,
		    new_mask);
  if (ofd < 0 && errno == EEXIST)
    {
      if (NILP (ok_if_already_exists) || FIXNUMP (ok_if_already_exists))
	barf_or_query_if_file_exists (newname, true, "copy to it",
				      FIXNUMP (ok_if_already_exists), false);
      already_exists = true;
      ofd = emacs_open (SSDATA (encoded_newname), O_WRONLY, 0);
    }
  if (ofd < 0)
    report_file_error ("Opening output file", newname);

  record_unwind_protect_int (close_file_unwind, ofd);

  off_t oldsize = 0, newsize;

  if (already_exists)
    {
      struct stat out_st;
      if (sys_fstat (ofd, &out_st) != 0)
	report_file_error ("Output file status", newname);
      if (st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
	report_file_errno ("Input and output files are the same",
			   list2 (file, newname), 0);
      if (S_ISREG (out_st.st_mode))
	oldsize = out_st.st_size;
    }

  maybe_quit ();

  if (emacs_fd_to_int (ifd) != -1
      && clone_file (ofd, emacs_fd_to_int (ifd)))
    newsize = st.st_size;
  else
    {
      off_t insize = st.st_size;
      ssize_t copied;

#ifndef MSDOS
      newsize = 0;

      if (emacs_fd_to_int (ifd) != -1)
	{
	  for (; newsize < insize; newsize += copied)
	    {
	      /* Copy at most COPY_MAX bytes at a time; this is min
		 (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
		 surely aligned well.  */
	      ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
	      ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
	      off_t intail = insize - newsize;
	      ptrdiff_t len = min (intail, copy_max);
	      copied = copy_file_range (emacs_fd_to_int (ifd), NULL,
					ofd, NULL, len, 0);
	      if (copied <= 0)
		break;
	      maybe_quit ();
	    }
	}
#endif /* MSDOS */

      /* Fall back on read+write if copy_file_range failed, or if the
	 input is empty and so could be a /proc file, or if ifd is an
	 invention of android.c.  read+write will either succeed, or
	 report an error more precisely than copy_file_range
	 would.  */
      if (newsize != insize || insize == 0)
	{
	  char buf[MAX_ALLOCA];

	  for (; (copied = emacs_fd_read (ifd, buf, sizeof buf));
	       newsize += copied)
	    {
	      if (copied < 0)
		report_file_error ("Read error", file);
	      if (emacs_write_quit (ofd, buf, copied) != copied)
		report_file_error ("Write error", newname);
	    }
	}
    }

  /* Truncate any existing output file after writing the data.  This
     is more likely to work than truncation before writing, if the
     file system is out of space or the user is over disk quota.  */
  if (newsize < oldsize && ftruncate (ofd, newsize) != 0)
    report_file_error ("Truncating output file", newname);

#ifndef MSDOS
  /* Preserve the original file permissions, and if requested, also its
     owner and group.  */
  {
    mode_t preserved_permissions = st.st_mode & 07777;
    mode_t default_permissions = st.st_mode & 0777 & ~realmask;
    if (!NILP (preserve_uid_gid))
      {
	/* Attempt to change owner and group.  If that doesn't work
	   attempt to change just the group, as that is sometimes allowed.
	   Adjust the mode mask to eliminate setuid or setgid bits
	   or group permissions bits that are inappropriate if the
	   owner or group are wrong.  */
	if (fchown (ofd, st.st_uid, st.st_gid) != 0)
	  {
	    if (fchown (ofd, -1, st.st_gid) == 0)
	      preserved_permissions &= ~04000;
	    else
	      {
		preserved_permissions &= ~06000;

		/* Copy the other bits to the group bits, since the
		   group is wrong.  */
		preserved_permissions &= ~070;
		preserved_permissions |= (preserved_permissions & 7) << 3;
		default_permissions &= ~070;
		default_permissions |= (default_permissions & 7) << 3;
	      }
	  }
      }

    switch ((!NILP (preserve_permissions)
	     && emacs_fd_to_int (ifd) != -1)
	    ? qcopy_acl (SSDATA (encoded_file),
			 emacs_fd_to_int (ifd),
			 SSDATA (encoded_newname), ofd,
			 preserved_permissions)
	    : (already_exists
	       || (new_mask & ~realmask) == default_permissions)
	    ? 0
	    : fchmod (ofd, default_permissions))
      {
      case -2: report_file_error ("Copying permissions from", file);
      case -1: report_file_error ("Copying permissions to", newname);
      }
  }
#endif	/* not MSDOS */

#if HAVE_LIBSELINUX
  if (conlength > 0)
    {
      /* Set the modified context back to the file.  */
      bool fail = fsetfilecon (ofd, con) != 0;
      freecon (con);

      /* See https://debbugs.gnu.org/11245 for ENOTSUP.  */
      if (fail
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
	  /* Treat SELinux errors copying files leniently on Android,
	     since the system usually forbids user programs from
	     changing file contexts.  */
	  && errno != EACCES
#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
	  && errno != ENOTSUP)
	report_file_error ("Doing fsetfilecon", newname);
    }
#endif

  if (!NILP (keep_time))
    {
      struct timespec ts[2];
      ts[0] = get_stat_atime (&st);
      ts[1] = get_stat_mtime (&st);
      if (futimens (ofd, ts) != 0
	  /* Various versions of the Android C library are missing
	     futimens, prompting Gnulib to install a fallback that
	     uses fdutimens instead.  However, fdutimens is not
	     supported on many Android kernels, so just silently fail
	     if errno is ENOTSUP or ENOSYS.  */
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
	  && errno != ENOTSUP
	  && errno != ENOSYS
#endif
	  )
	xsignal2 (Qfile_date_error,
		  build_string ("Cannot set file date"), newname);
    }

  if (emacs_close (ofd) < 0)
    report_file_error ("Write error", newname);

  /* Note that ifd is not closed twice because unwind_protects are
     discarded at the end of this function.  */
  emacs_fd_close (ifd);

#ifdef MSDOS
  /* In DJGPP v2.0 and later, fstat usually returns true file mode bits,
     and if it can't, it tells so.  Otherwise, under MSDOS we usually
     get only the READ bit, which will make the copied file read-only,
     so it's better not to chmod at all.  */
  if ((_djstat_flags & _STFAIL_WRITEBIT) == 0)
    chmod (SDATA (encoded_newname), st.st_mode & 07777);
#endif /* MSDOS */
#endif /* not WINDOWSNT */

  /* Discard the unwind protects.  */
  specpdl_ptr = specpdl_ref_to_ptr (count);

  return Qnil;
}