diff options
| author | Paul Eggert | 2017-09-10 15:39:24 -0700 |
|---|---|---|
| committer | Paul Eggert | 2017-09-10 15:46:51 -0700 |
| commit | 01c885f21f343045783eb9ad1ff5f9b83d6cd789 (patch) | |
| tree | 366e65100af42e6583e5bbaee70f4d5e853e4f92 /src | |
| parent | 52739ffe773eb403f58a6223b7ef64175df58dd7 (diff) | |
| download | emacs-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.c | 55 |
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 | |
| 602 | static bool | 602 | "/"). */ |
| 603 | directory_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 | ||
| 612 | static Lisp_Object | 604 | static Lisp_Object |
| 613 | expand_cp_target (Lisp_Object file, Lisp_Object newname) | 605 | expand_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) | |||
| 1833 | DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, | 1825 | DEFUN ("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. |
| 1836 | If NEWNAME names a directory, copy FILE there. | 1828 | If NEWNAME is a directory name, copy FILE to a like-named file under |
| 1829 | NEWNAME. | ||
| 1837 | 1830 | ||
| 1838 | This function always sets the file modes of the output file to match | 1831 | This function always sets the file modes of the output file to match |
| 1839 | the input file. | 1832 | the 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. |
| 2259 | If file has names other than FILE, it continues to have those names. | 2252 | If file has names other than FILE, it continues to have those names. |
| 2253 | If NEWNAME is a directory name, rename FILE to a like-named file under | ||
| 2254 | NEWNAME. | ||
| 2255 | |||
| 2260 | Signal a `file-already-exists' error if a file NEWNAME already exists | 2256 | Signal a `file-already-exists' error if a file NEWNAME already exists |
| 2261 | unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. | 2257 | unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. |
| 2262 | An integer third arg means request confirmation if NEWNAME already exists. | 2258 | 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. */) | |||
| 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. */) | |||
| 2364 | DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3, | 2358 | DEFUN ("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. |
| 2361 | If NEWNAME is a directory name, give FILE a like-named new name under | ||
| 2362 | NEWNAME. | ||
| 2363 | |||
| 2367 | Signal a `file-already-exists' error if a file NEWNAME already exists | 2364 | Signal a `file-already-exists' error if a file NEWNAME already exists |
| 2368 | unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. | 2365 | unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. |
| 2369 | An integer third arg means request confirmation if NEWNAME already exists. | 2366 | 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. */) | |||
| 2412 | 2409 | ||
| 2413 | DEFUN ("make-symbolic-link", Fmake_symbolic_link, Smake_symbolic_link, 2, 3, | 2410 | DEFUN ("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. |
| 2416 | Both args must be strings. | 2413 | If NEWNAME is a directory name, make a like-named symbolic link under |
| 2417 | Signal a `file-already-exists' error if a file LINKNAME already exists | 2414 | NEWNAME. |
| 2415 | |||
| 2416 | Signal a `file-already-exists' error if a file NEWNAME already exists | ||
| 2418 | unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. | 2417 | unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. |
| 2419 | An integer third arg means request confirmation if LINKNAME already | 2418 | An integer third arg means request confirmation if NEWNAME already |
| 2420 | exists, and expand leading "~" or strip leading "/:" in TARGET. | 2419 | exists, and expand leading "~" or strip leading "/:" in TARGET. |
| 2421 | This happens for interactive use with M-x. */) | 2420 | This 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) |