diff options
| author | Paul Eggert | 2017-08-02 01:53:46 -0700 |
|---|---|---|
| committer | Paul Eggert | 2017-08-02 01:59:32 -0700 |
| commit | 1f9f514e7c2ba41b0954d0141f99652f6a53a107 (patch) | |
| tree | 37d6a253a4db4b6ff2a46e9a7ac512cbe1938c71 /src | |
| parent | 5656492d04aa1a82747ff167d8063bbd7950597e (diff) | |
| download | emacs-1f9f514e7c2ba41b0954d0141f99652f6a53a107.tar.gz emacs-1f9f514e7c2ba41b0954d0141f99652f6a53a107.zip | |
When renaming a file, ask only if EEXIST or ENOSYS
* src/fileio.c (Frename_file): Avoid calling Ffile_directory_p
more than once on FILE. Use renameat_noreplace, so that we can
ask the user (and unlink and retry) only if this fails with errno
== EEXIST or ENOSYS. This avoids the need to ask the user for
permission to do an operation that will fail anyway. Simplify
computation of ok_if_already_exists for subsidiary functions.
* src/filelock.c (rename_lock_file): Prefer renameat_noreplace
if it works, as this avoids the need to link and unlink.
* src/lisp.h (renameat_noreplace): New decl.
* src/sysdep.c [HAVE_LINUX_FS_H]: Include linux/fs.h and sys/syscall.h.
(renameat_noreplace): New function.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fileio.c | 83 | ||||
| -rw-r--r-- | src/filelock.c | 3 | ||||
| -rw-r--r-- | src/lisp.h | 6 | ||||
| -rw-r--r-- | src/sysdep.c | 20 |
4 files changed, 73 insertions, 39 deletions
diff --git a/src/fileio.c b/src/fileio.c index 96c5639a096..0264c9fa1d8 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -2311,6 +2311,7 @@ This is what happens in interactive use with M-x. */) | |||
| 2311 | { | 2311 | { |
| 2312 | Lisp_Object handler; | 2312 | Lisp_Object handler; |
| 2313 | Lisp_Object encoded_file, encoded_newname, symlink_target; | 2313 | Lisp_Object encoded_file, encoded_newname, symlink_target; |
| 2314 | int dirp = -1; | ||
| 2314 | 2315 | ||
| 2315 | symlink_target = encoded_file = encoded_newname = Qnil; | 2316 | symlink_target = encoded_file = encoded_newname = Qnil; |
| 2316 | CHECK_STRING (file); | 2317 | CHECK_STRING (file); |
| @@ -2324,8 +2325,8 @@ This is what happens in interactive use with M-x. */) | |||
| 2324 | && (NILP (Ffile_name_case_insensitive_p (file)) | 2325 | && (NILP (Ffile_name_case_insensitive_p (file)) |
| 2325 | || NILP (Fstring_equal (Fdowncase (file), Fdowncase (newname))))) | 2326 | || NILP (Fstring_equal (Fdowncase (file), Fdowncase (newname))))) |
| 2326 | { | 2327 | { |
| 2327 | Lisp_Object fname = (NILP (Ffile_directory_p (file)) | 2328 | dirp = !NILP (Ffile_directory_p (file)); |
| 2328 | ? file : Fdirectory_file_name (file)); | 2329 | Lisp_Object fname = dirp ? Fdirectory_file_name (file) : file; |
| 2329 | newname = Fexpand_file_name (Ffile_name_nondirectory (fname), newname); | 2330 | newname = Fexpand_file_name (Ffile_name_nondirectory (fname), newname); |
| 2330 | } | 2331 | } |
| 2331 | else | 2332 | else |
| @@ -2343,47 +2344,55 @@ This is what happens in interactive use with M-x. */) | |||
| 2343 | encoded_file = ENCODE_FILE (file); | 2344 | encoded_file = ENCODE_FILE (file); |
| 2344 | encoded_newname = ENCODE_FILE (newname); | 2345 | encoded_newname = ENCODE_FILE (newname); |
| 2345 | 2346 | ||
| 2346 | /* If the filesystem is case-insensitive and the file names are | 2347 | if (renameat_noreplace (AT_FDCWD, SSDATA (encoded_file), |
| 2347 | identical but for the case, don't ask for confirmation: they | 2348 | AT_FDCWD, SSDATA (encoded_newname)) |
| 2348 | simply want to change the letter-case of the file name. */ | 2349 | == 0) |
| 2349 | if ((!(file_name_case_insensitive_p (SSDATA (encoded_file))) | 2350 | return Qnil; |
| 2350 | || NILP (Fstring_equal (Fdowncase (file), Fdowncase (newname)))) | 2351 | int rename_errno = errno; |
| 2351 | && ((NILP (ok_if_already_exists) || INTEGERP (ok_if_already_exists)))) | 2352 | |
| 2352 | barf_or_query_if_file_exists (newname, false, "rename to it", | 2353 | if (rename_errno == EEXIST || rename_errno == ENOSYS) |
| 2353 | INTEGERP (ok_if_already_exists), false); | ||
| 2354 | if (rename (SSDATA (encoded_file), SSDATA (encoded_newname)) < 0) | ||
| 2355 | { | 2354 | { |
| 2356 | int rename_errno = errno; | 2355 | /* If the filesystem is case-insensitive and the file names are |
| 2357 | if (rename_errno == EXDEV) | 2356 | identical but for the case, don't ask for confirmation: they |
| 2358 | { | 2357 | simply want to change the letter-case of the file name. */ |
| 2359 | ptrdiff_t count; | 2358 | if ((NILP (ok_if_already_exists) || INTEGERP (ok_if_already_exists)) |
| 2360 | symlink_target = Ffile_symlink_p (file); | 2359 | && (! file_name_case_insensitive_p (SSDATA (encoded_file)) |
| 2361 | if (! NILP (symlink_target)) | 2360 | || NILP (Fstring_equal (Fdowncase (file), Fdowncase (newname))))) |
| 2362 | Fmake_symbolic_link (symlink_target, newname, | 2361 | barf_or_query_if_file_exists (newname, rename_errno == EEXIST, |
| 2363 | NILP (ok_if_already_exists) ? Qnil : Qt); | 2362 | "rename to it", |
| 2364 | else if (!NILP (Ffile_directory_p (file))) | 2363 | INTEGERP (ok_if_already_exists), false); |
| 2365 | call4 (Qcopy_directory, file, newname, Qt, Qnil); | 2364 | if (rename (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0) |
| 2366 | else | 2365 | return Qnil; |
| 2367 | /* We have already prompted if it was an integer, so don't | 2366 | rename_errno = errno; |
| 2368 | have copy-file prompt again. */ | 2367 | /* Don't prompt again. */ |
| 2369 | Fcopy_file (file, newname, | 2368 | ok_if_already_exists = Qt; |
| 2370 | NILP (ok_if_already_exists) ? Qnil : Qt, | 2369 | } |
| 2371 | Qt, Qt, Qt); | 2370 | else if (!NILP (ok_if_already_exists)) |
| 2371 | ok_if_already_exists = Qt; | ||
| 2372 | 2372 | ||
| 2373 | count = SPECPDL_INDEX (); | 2373 | if (rename_errno != EXDEV) |
| 2374 | specbind (Qdelete_by_moving_to_trash, Qnil); | 2374 | report_file_errno ("Renaming", list2 (file, newname), rename_errno); |
| 2375 | 2375 | ||
| 2376 | if (!NILP (Ffile_directory_p (file)) && NILP (symlink_target)) | 2376 | symlink_target = Ffile_symlink_p (file); |
| 2377 | call2 (Qdelete_directory, file, Qt); | 2377 | if (!NILP (symlink_target)) |
| 2378 | else | 2378 | Fmake_symbolic_link (symlink_target, newname, ok_if_already_exists); |
| 2379 | Fdelete_file (file, Qnil); | 2379 | else |
| 2380 | unbind_to (count, Qnil); | 2380 | { |
| 2381 | } | 2381 | if (dirp < 0) |
| 2382 | dirp = !NILP (Ffile_directory_p (file)); | ||
| 2383 | if (dirp) | ||
| 2384 | call4 (Qcopy_directory, file, newname, Qt, Qnil); | ||
| 2382 | else | 2385 | else |
| 2383 | report_file_errno ("Renaming", list2 (file, newname), rename_errno); | 2386 | Fcopy_file (file, newname, ok_if_already_exists, Qt, Qt, Qt); |
| 2384 | } | 2387 | } |
| 2385 | 2388 | ||
| 2386 | return Qnil; | 2389 | ptrdiff_t count = SPECPDL_INDEX (); |
| 2390 | specbind (Qdelete_by_moving_to_trash, Qnil); | ||
| 2391 | if (dirp && NILP (symlink_target)) | ||
| 2392 | call2 (Qdelete_directory, file, Qt); | ||
| 2393 | else | ||
| 2394 | Fdelete_file (file, Qnil); | ||
| 2395 | return unbind_to (count, Qnil); | ||
| 2387 | } | 2396 | } |
| 2388 | 2397 | ||
| 2389 | DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3, | 2398 | DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3, |
diff --git a/src/filelock.c b/src/filelock.c index bfa1d63d833..dd8cb28c425 100644 --- a/src/filelock.c +++ b/src/filelock.c | |||
| @@ -339,6 +339,9 @@ rename_lock_file (char const *old, char const *new, bool force) | |||
| 339 | { | 339 | { |
| 340 | struct stat st; | 340 | struct stat st; |
| 341 | 341 | ||
| 342 | int r = renameat_noreplace (AT_FDCWD, old, AT_FDCWD, new); | ||
| 343 | if (! (r < 0 && errno == ENOSYS)) | ||
| 344 | return r; | ||
| 342 | if (link (old, new) == 0) | 345 | if (link (old, new) == 0) |
| 343 | return unlink (old) == 0 || errno == ENOENT ? 0 : -1; | 346 | return unlink (old) == 0 || errno == ENOENT ? 0 : -1; |
| 344 | if (errno != ENOSYS && errno != LINKS_MIGHT_NOT_WORK) | 347 | if (errno != ENOSYS && errno != LINKS_MIGHT_NOT_WORK) |
diff --git a/src/lisp.h b/src/lisp.h index cffaf954b3b..4de6fc85ec1 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4298,13 +4298,15 @@ extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t); | |||
| 4298 | extern ptrdiff_t emacs_write_sig (int, void const *, ptrdiff_t); | 4298 | extern ptrdiff_t emacs_write_sig (int, void const *, ptrdiff_t); |
| 4299 | extern ptrdiff_t emacs_write_quit (int, void const *, ptrdiff_t); | 4299 | extern ptrdiff_t emacs_write_quit (int, void const *, ptrdiff_t); |
| 4300 | extern void emacs_perror (char const *); | 4300 | extern void emacs_perror (char const *); |
| 4301 | extern int renameat_noreplace (int, char const *, int, char const *); | ||
| 4302 | extern int str_collate (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object); | ||
| 4301 | 4303 | ||
| 4302 | extern void unlock_all_files (void); | 4304 | /* Defined in filelock.c. */ |
| 4303 | extern void lock_file (Lisp_Object); | 4305 | extern void lock_file (Lisp_Object); |
| 4304 | extern void unlock_file (Lisp_Object); | 4306 | extern void unlock_file (Lisp_Object); |
| 4307 | extern void unlock_all_files (void); | ||
| 4305 | extern void unlock_buffer (struct buffer *); | 4308 | extern void unlock_buffer (struct buffer *); |
| 4306 | extern void syms_of_filelock (void); | 4309 | extern void syms_of_filelock (void); |
| 4307 | extern int str_collate (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object); | ||
| 4308 | 4310 | ||
| 4309 | /* Defined in sound.c. */ | 4311 | /* Defined in sound.c. */ |
| 4310 | extern void syms_of_sound (void); | 4312 | extern void syms_of_sound (void); |
diff --git a/src/sysdep.c b/src/sysdep.c index db99f53299c..22446b25d16 100644 --- a/src/sysdep.c +++ b/src/sysdep.c | |||
| @@ -37,6 +37,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 37 | #include "sysselect.h" | 37 | #include "sysselect.h" |
| 38 | #include "blockinput.h" | 38 | #include "blockinput.h" |
| 39 | 39 | ||
| 40 | #ifdef HAVE_LINUX_FS_H | ||
| 41 | # include <linux/fs.h> | ||
| 42 | # include <sys/syscall.h> | ||
| 43 | #endif | ||
| 44 | |||
| 40 | #if defined DARWIN_OS || defined __FreeBSD__ | 45 | #if defined DARWIN_OS || defined __FreeBSD__ |
| 41 | # include <sys/sysctl.h> | 46 | # include <sys/sysctl.h> |
| 42 | #endif | 47 | #endif |
| @@ -2678,6 +2683,21 @@ set_file_times (int fd, const char *filename, | |||
| 2678 | timespec[1] = mtime; | 2683 | timespec[1] = mtime; |
| 2679 | return fdutimens (fd, filename, timespec); | 2684 | return fdutimens (fd, filename, timespec); |
| 2680 | } | 2685 | } |
| 2686 | |||
| 2687 | /* Rename directory SRCFD's entry SRC to directory DSTFD's entry DST. | ||
| 2688 | This is like renameat except that it fails if DST already exists, | ||
| 2689 | or if this operation is not supported atomically. Return 0 if | ||
| 2690 | successful, -1 (setting errno) otherwise. */ | ||
| 2691 | int | ||
| 2692 | renameat_noreplace (int srcfd, char const *src, int dstfd, char const *dst) | ||
| 2693 | { | ||
| 2694 | #ifdef SYS_renameat2 | ||
| 2695 | return syscall (SYS_renameat2, srcfd, src, dstfd, dst, RENAME_NOREPLACE); | ||
| 2696 | #else | ||
| 2697 | errno = ENOSYS; | ||
| 2698 | return -1; | ||
| 2699 | #endif | ||
| 2700 | } | ||
| 2681 | 2701 | ||
| 2682 | /* Like strsignal, except async-signal-safe, and this function typically | 2702 | /* Like strsignal, except async-signal-safe, and this function typically |
| 2683 | returns a string in the C locale rather than the current locale. */ | 2703 | returns a string in the C locale rather than the current locale. */ |