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;
int ifd, 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_open (SSDATA (encoded_file), O_RDONLY, 0);
if (ifd < 0)
report_file_error ("Opening input file", file);
record_unwind_protect_int (close_file_unwind, ifd);
if (fstat (ifd, &st) != 0)
report_file_error ("Input file status", file);
if (!NILP (preserve_permissions))
{
#if HAVE_LIBSELINUX
if (is_selinux_enabled ())
{
conlength = fgetfilecon (ifd, &con);
if (conlength == -1)
report_file_error ("Doing fgetfilecon", file);
}
#endif
}
/* 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 (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 (clone_file (ofd, ifd))
newsize = st.st_size;
else
{
off_t insize = st.st_size;
ssize_t copied;
#ifndef MSDOS
for (newsize = 0; 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 (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. 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_read_quit (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)
? qcopy_acl (SSDATA (encoded_file), 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;
/* See https://debbugs.gnu.org/11245 for ENOTSUP. */
if (fail && errno != ENOTSUP)
report_file_error ("Doing fsetfilecon", newname);
freecon (con);
}
#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)
xsignal2 (Qfile_date_error,
build_string ("Cannot set file date"), newname);
}
if (emacs_close (ofd) < 0)
report_file_error ("Write error", newname);
emacs_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;
}