aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2017-09-10 15:39:24 -0700
committerPaul Eggert2017-09-10 15:46:51 -0700
commit01c885f21f343045783eb9ad1ff5f9b83d6cd789 (patch)
tree366e65100af42e6583e5bbaee70f4d5e853e4f92 /src
parent52739ffe773eb403f58a6223b7ef64175df58dd7 (diff)
downloademacs-01c885f21f343045783eb9ad1ff5f9b83d6cd789.tar.gz
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')
-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,
595 return IS_DIRECTORY_SEP (c) ? Qt : Qnil; 595 return IS_DIRECTORY_SEP (c) ? Qt : Qnil;
596} 596}
597 597
598/* Return true if NAME must be that of a directory if it exists. 598/* Return the expansion of NEWNAME, except that if NEWNAME is a
599 When NAME is a directory name, this avoids system calls compared to 599 directory name then return the expansion of FILE's basename under
600 just calling Ffile_directory_p. */ 600 NEWNAME. This resembles how 'cp FILE NEWNAME' works, except that
601 601 it requires NEWNAME to be a directory name (typically, by ending in
602static bool 602 "/"). */
603directory_like (Lisp_Object name)
604{
605 return !NILP (Fdirectory_name_p (name)) || !NILP (Ffile_directory_p (name));
606}
607
608/* Return the expansion of NEWNAME, except that if NEWNAME is like a
609 directory then return the expansion of FILE's basename under
610 NEWNAME. This is like how 'cp FILE NEWNAME' works. */
611 603
612static Lisp_Object 604static Lisp_Object
613expand_cp_target (Lisp_Object file, Lisp_Object newname) 605expand_cp_target (Lisp_Object file, Lisp_Object newname)
614{ 606{
615 return (directory_like (newname) 607 return (!NILP (Fdirectory_name_p (newname))
616 ? Fexpand_file_name (Ffile_name_nondirectory (file), newname) 608 ? Fexpand_file_name (Ffile_name_nondirectory (file), newname)
617 : Fexpand_file_name (newname, Qnil)); 609 : Fexpand_file_name (newname, Qnil));
618} 610}
@@ -1833,7 +1825,8 @@ clone_file (int dest, int source)
1833DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, 1825DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6,
1834 "fCopy file: \nGCopy %s to file: \np\nP", 1826 "fCopy file: \nGCopy %s to file: \np\nP",
1835 doc: /* Copy FILE to NEWNAME. Both args must be strings. 1827 doc: /* Copy FILE to NEWNAME. Both args must be strings.
1836If NEWNAME names a directory, copy FILE there. 1828If NEWNAME is a directory name, copy FILE to a like-named file under
1829NEWNAME.
1837 1830
1838This function always sets the file modes of the output file to match 1831This function always sets the file modes of the output file to match
1839the input file. 1832the input file.
@@ -2257,6 +2250,9 @@ DEFUN ("rename-file", Frename_file, Srename_file, 2, 3,
2257 "fRename file: \nGRename %s to file: \np", 2250 "fRename file: \nGRename %s to file: \np",
2258 doc: /* Rename FILE as NEWNAME. Both args must be strings. 2251 doc: /* Rename FILE as NEWNAME. Both args must be strings.
2259If file has names other than FILE, it continues to have those names. 2252If file has names other than FILE, it continues to have those names.
2253If NEWNAME is a directory name, rename FILE to a like-named file under
2254NEWNAME.
2255
2260Signal a `file-already-exists' error if a file NEWNAME already exists 2256Signal a `file-already-exists' error if a file NEWNAME already exists
2261unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. 2257unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.
2262An integer third arg means request confirmation if NEWNAME already exists. 2258An integer third arg means request confirmation if NEWNAME already exists.
@@ -2265,7 +2261,6 @@ This is what happens in interactive use with M-x. */)
2265{ 2261{
2266 Lisp_Object handler; 2262 Lisp_Object handler;
2267 Lisp_Object encoded_file, encoded_newname, symlink_target; 2263 Lisp_Object encoded_file, encoded_newname, symlink_target;
2268 int dirp = -1;
2269 2264
2270 file = Fexpand_file_name (file, Qnil); 2265 file = Fexpand_file_name (file, Qnil);
2271 2266
@@ -2339,22 +2334,21 @@ This is what happens in interactive use with M-x. */)
2339 if (rename_errno != EXDEV) 2334 if (rename_errno != EXDEV)
2340 report_file_errno ("Renaming", list2 (file, newname), rename_errno); 2335 report_file_errno ("Renaming", list2 (file, newname), rename_errno);
2341 2336
2342 symlink_target = Ffile_symlink_p (file); 2337 bool dirp = !NILP (Fdirectory_name_p (file));
2343 if (!NILP (symlink_target)) 2338 if (dirp)
2344 Fmake_symbolic_link (symlink_target, newname, ok_if_already_exists); 2339 call4 (Qcopy_directory, file, newname, Qt, Qnil);
2345 else 2340 else
2346 { 2341 {
2347 if (dirp < 0) 2342 symlink_target = Ffile_symlink_p (file);
2348 dirp = directory_like (file); 2343 if (!NILP (symlink_target))
2349 if (dirp) 2344 Fmake_symbolic_link (symlink_target, newname, ok_if_already_exists);
2350 call4 (Qcopy_directory, file, newname, Qt, Qnil);
2351 else 2345 else
2352 Fcopy_file (file, newname, ok_if_already_exists, Qt, Qt, Qt); 2346 Fcopy_file (file, newname, ok_if_already_exists, Qt, Qt, Qt);
2353 } 2347 }
2354 2348
2355 ptrdiff_t count = SPECPDL_INDEX (); 2349 ptrdiff_t count = SPECPDL_INDEX ();
2356 specbind (Qdelete_by_moving_to_trash, Qnil); 2350 specbind (Qdelete_by_moving_to_trash, Qnil);
2357 if (dirp && NILP (symlink_target)) 2351 if (dirp)
2358 call2 (Qdelete_directory, file, Qt); 2352 call2 (Qdelete_directory, file, Qt);
2359 else 2353 else
2360 Fdelete_file (file, Qnil); 2354 Fdelete_file (file, Qnil);
@@ -2364,6 +2358,9 @@ This is what happens in interactive use with M-x. */)
2364DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3, 2358DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3,
2365 "fAdd name to file: \nGName to add to %s: \np", 2359 "fAdd name to file: \nGName to add to %s: \np",
2366 doc: /* Give FILE additional name NEWNAME. Both args must be strings. 2360 doc: /* Give FILE additional name NEWNAME. Both args must be strings.
2361If NEWNAME is a directory name, give FILE a like-named new name under
2362NEWNAME.
2363
2367Signal a `file-already-exists' error if a file NEWNAME already exists 2364Signal a `file-already-exists' error if a file NEWNAME already exists
2368unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. 2365unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.
2369An integer third arg means request confirmation if NEWNAME already exists. 2366An integer third arg means request confirmation if NEWNAME already exists.
@@ -2412,11 +2409,13 @@ This is what happens in interactive use with M-x. */)
2412 2409
2413DEFUN ("make-symbolic-link", Fmake_symbolic_link, Smake_symbolic_link, 2, 3, 2410DEFUN ("make-symbolic-link", Fmake_symbolic_link, Smake_symbolic_link, 2, 3,
2414 "FMake symbolic link to file: \nGMake symbolic link to file %s: \np", 2411 "FMake symbolic link to file: \nGMake symbolic link to file %s: \np",
2415 doc: /* Make a symbolic link to TARGET, named LINKNAME. 2412 doc: /* Make a symbolic link to TARGET, named NEWNAME.
2416Both args must be strings. 2413If NEWNAME is a directory name, make a like-named symbolic link under
2417Signal a `file-already-exists' error if a file LINKNAME already exists 2414NEWNAME.
2415
2416Signal a `file-already-exists' error if a file NEWNAME already exists
2418unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. 2417unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.
2419An integer third arg means request confirmation if LINKNAME already 2418An integer third arg means request confirmation if NEWNAME already
2420exists, and expand leading "~" or strip leading "/:" in TARGET. 2419exists, and expand leading "~" or strip leading "/:" in TARGET.
2421This happens for interactive use with M-x. */) 2420This happens for interactive use with M-x. */)
2422 (Lisp_Object target, Lisp_Object linkname, Lisp_Object ok_if_already_exists) 2421 (Lisp_Object target, Lisp_Object linkname, Lisp_Object ok_if_already_exists)