aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2017-08-02 01:53:46 -0700
committerPaul Eggert2017-08-02 01:59:32 -0700
commit1f9f514e7c2ba41b0954d0141f99652f6a53a107 (patch)
tree37d6a253a4db4b6ff2a46e9a7ac512cbe1938c71 /src
parent5656492d04aa1a82747ff167d8063bbd7950597e (diff)
downloademacs-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.c83
-rw-r--r--src/filelock.c3
-rw-r--r--src/lisp.h6
-rw-r--r--src/sysdep.c20
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
2389DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3, 2398DEFUN ("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);
4298extern ptrdiff_t emacs_write_sig (int, void const *, ptrdiff_t); 4298extern ptrdiff_t emacs_write_sig (int, void const *, ptrdiff_t);
4299extern ptrdiff_t emacs_write_quit (int, void const *, ptrdiff_t); 4299extern ptrdiff_t emacs_write_quit (int, void const *, ptrdiff_t);
4300extern void emacs_perror (char const *); 4300extern void emacs_perror (char const *);
4301extern int renameat_noreplace (int, char const *, int, char const *);
4302extern int str_collate (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);
4301 4303
4302extern void unlock_all_files (void); 4304/* Defined in filelock.c. */
4303extern void lock_file (Lisp_Object); 4305extern void lock_file (Lisp_Object);
4304extern void unlock_file (Lisp_Object); 4306extern void unlock_file (Lisp_Object);
4307extern void unlock_all_files (void);
4305extern void unlock_buffer (struct buffer *); 4308extern void unlock_buffer (struct buffer *);
4306extern void syms_of_filelock (void); 4309extern void syms_of_filelock (void);
4307extern int str_collate (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);
4308 4310
4309/* Defined in sound.c. */ 4311/* Defined in sound.c. */
4310extern void syms_of_sound (void); 4312extern 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. */
2691int
2692renameat_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. */