summaryrefslogtreecommitdiff
path: root/src/fileio.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2017-09-10 15:39:24 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2017-09-10 15:46:51 -0700
commit01c885f21f343045783eb9ad1ff5f9b83d6cd789 (patch)
tree366e65100af42e6583e5bbaee70f4d5e853e4f92 /src/fileio.c
parent52739ffe773eb403f58a6223b7ef64175df58dd7 (diff)
downloademacs-01c885f21f343045783eb9ad1ff5f9b83d6cd789.tar.gz
emacs-01c885f21f343045783eb9ad1ff5f9b83d6cd789.tar.bz2
emacs-01c885f21f343045783eb9ad1ff5f9b83d6cd789.zip
Fix race with rename-file etc. with dir NEWNAME
This changes the behavior of rename-file etc. slightly. The old behavior mostly disagreed with the documentation, and had a race condition bug that could allow attackers to modify victims' write-protected directories (Bug#27986). * doc/lispref/files.texi (Changing Files): Document that in rename-file etc., NEWFILE is special if it is a directory name. * etc/NEWS: Document the change in behavior. * src/fileio.c (directory_like): Remove. All uses removed. (expand_cp_target): Test only whether NEWNAME is a directory name, not whether it is currently a directory. This avoids a race. (Fcopy_file, Frename_file, Fadd_name_to_file, Fmake_symbolic_link): Document behavior if NEWNAME is a directory name. (Frename_file): Simplify now that the destdir behavior occurs only when NEWNAME is a directory name. * test/lisp/net/tramp-tests.el (tramp-test11-copy-file) (tramp-test12-rename-file, tramp--test-check-files): Adjust tests to match new behavior.
Diffstat (limited to 'src/fileio.c')
-rw-r--r--src/fileio.c55
1 files changed, 27 insertions, 28 deletions
diff --git a/src/fileio.c b/src/fileio.c
index a1cea94c0b6..3195348a8ce 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -595,24 +595,16 @@ DEFUN ("directory-name-p", Fdirectory_name_p, Sdirectory_name_p, 1, 1, 0,
return IS_DIRECTORY_SEP (c) ? Qt : Qnil;
}
-/* Return true if NAME must be that of a directory if it exists.
- When NAME is a directory name, this avoids system calls compared to
- just calling Ffile_directory_p. */
-
-static bool
-directory_like (Lisp_Object name)
-{
- return !NILP (Fdirectory_name_p (name)) || !NILP (Ffile_directory_p (name));
-}
-
-/* Return the expansion of NEWNAME, except that if NEWNAME is like a
- directory then return the expansion of FILE's basename under
- NEWNAME. This is like how 'cp FILE NEWNAME' works. */
+/* Return the expansion of NEWNAME, except that if NEWNAME is a
+ directory name then return the expansion of FILE's basename under
+ NEWNAME. This resembles how 'cp FILE NEWNAME' works, except that
+ it requires NEWNAME to be a directory name (typically, by ending in
+ "/"). */
static Lisp_Object
expand_cp_target (Lisp_Object file, Lisp_Object newname)
{
- return (directory_like (newname)
+ return (!NILP (Fdirectory_name_p (newname))
? Fexpand_file_name (Ffile_name_nondirectory (file), newname)
: Fexpand_file_name (newname, Qnil));
}
@@ -1833,7 +1825,8 @@ clone_file (int dest, int source)
DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6,
"fCopy file: \nGCopy %s to file: \np\nP",
doc: /* Copy FILE to NEWNAME. Both args must be strings.
-If NEWNAME names a directory, copy FILE there.
+If NEWNAME is a directory name, copy FILE to a like-named file under
+NEWNAME.
This function always sets the file modes of the output file to match
the input file.
@@ -2257,6 +2250,9 @@ DEFUN ("rename-file", Frename_file, Srename_file, 2, 3,
"fRename file: \nGRename %s to file: \np",
doc: /* Rename FILE as NEWNAME. Both args must be strings.
If file has names other than FILE, it continues to have those names.
+If NEWNAME is a directory name, rename FILE to a like-named file under
+NEWNAME.
+
Signal a `file-already-exists' error if a file NEWNAME already exists
unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.
An integer third arg means request confirmation if NEWNAME already exists.
@@ -2265,7 +2261,6 @@ This is what happens in interactive use with M-x. */)
{
Lisp_Object handler;
Lisp_Object encoded_file, encoded_newname, symlink_target;
- int dirp = -1;
file = Fexpand_file_name (file, Qnil);
@@ -2339,22 +2334,21 @@ This is what happens in interactive use with M-x. */)
if (rename_errno != EXDEV)
report_file_errno ("Renaming", list2 (file, newname), rename_errno);
- symlink_target = Ffile_symlink_p (file);
- if (!NILP (symlink_target))
- Fmake_symbolic_link (symlink_target, newname, ok_if_already_exists);
+ bool dirp = !NILP (Fdirectory_name_p (file));
+ if (dirp)
+ call4 (Qcopy_directory, file, newname, Qt, Qnil);
else
{
- if (dirp < 0)
- dirp = directory_like (file);
- if (dirp)
- call4 (Qcopy_directory, file, newname, Qt, Qnil);
+ symlink_target = Ffile_symlink_p (file);
+ if (!NILP (symlink_target))
+ Fmake_symbolic_link (symlink_target, newname, ok_if_already_exists);
else
Fcopy_file (file, newname, ok_if_already_exists, Qt, Qt, Qt);
}
ptrdiff_t count = SPECPDL_INDEX ();
specbind (Qdelete_by_moving_to_trash, Qnil);
- if (dirp && NILP (symlink_target))
+ if (dirp)
call2 (Qdelete_directory, file, Qt);
else
Fdelete_file (file, Qnil);
@@ -2364,6 +2358,9 @@ This is what happens in interactive use with M-x. */)
DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3,
"fAdd name to file: \nGName to add to %s: \np",
doc: /* Give FILE additional name NEWNAME. Both args must be strings.
+If NEWNAME is a directory name, give FILE a like-named new name under
+NEWNAME.
+
Signal a `file-already-exists' error if a file NEWNAME already exists
unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.
An integer third arg means request confirmation if NEWNAME already exists.
@@ -2412,11 +2409,13 @@ This is what happens in interactive use with M-x. */)
DEFUN ("make-symbolic-link", Fmake_symbolic_link, Smake_symbolic_link, 2, 3,
"FMake symbolic link to file: \nGMake symbolic link to file %s: \np",
- doc: /* Make a symbolic link to TARGET, named LINKNAME.
-Both args must be strings.
-Signal a `file-already-exists' error if a file LINKNAME already exists
+ doc: /* Make a symbolic link to TARGET, named NEWNAME.
+If NEWNAME is a directory name, make a like-named symbolic link under
+NEWNAME.
+
+Signal a `file-already-exists' error if a file NEWNAME already exists
unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.
-An integer third arg means request confirmation if LINKNAME already
+An integer third arg means request confirmation if NEWNAME already
exists, and expand leading "~" or strip leading "/:" in TARGET.
This happens for interactive use with M-x. */)
(Lisp_Object target, Lisp_Object linkname, Lisp_Object ok_if_already_exists)