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.
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 calln (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 | O_TRUNC, 0);
}
if (ofd < 0)
report_file_error ("Opening output file", newname);
record_unwind_protect_int (close_file_unwind, ofd);
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);
}
maybe_quit ();
if (emacs_fd_to_int (ifd) == -1
|| !clone_file (ofd, emacs_fd_to_int (ifd)))
{
MAYBE_UNUSED off_t newsize = 0;
#ifndef MSDOS
if (emacs_fd_to_int (ifd) != -1)
{
for (ssize_t copied; ; newsize += copied)
{
/* Copy at most COPY_MAX bytes at a time; this is min
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = min (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
copied = copy_file_range (emacs_fd_to_int (ifd), NULL,
ofd, NULL, copy_max, 0);
if (copied <= 0)
break;
maybe_quit ();
}
}
#endif /* MSDOS */
/* Follow up with read+write regardless of any copy_file_range failure.
Many copy_file_range implementations fail for no good reason,
or "succeed" even when they did nothing (e.g., in /proc files).
Also, if read+write fails it will report an error more
precisely than copy_file_range would. */
char buf[MAX_ALLOCA];
for (ptrdiff_t copied;
(copied = emacs_full_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);
if (copied < sizeof buf)
break;
}
}
#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;
}