From 9fe43ff672d02d6f43bd5bc7b08f40823c7a1851 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Fri, 18 Jan 2013 20:44:34 -0800 Subject: [PATCH] Work around bug in CIFS and vboxsf file systems. The bug was observed on Ubuntu operating inside a virtual machine, editing files mounted via CIFS or vboxsf from the MS Windows 7 host. The workaround introduces a race condition on non-buggy hosts, but it's an unlikely race and anyway there's a nearly identical nearby race that can't be fixed. * fileio.c (valid_timestamp_file_system, timestamp_file_system): New static vars. (Fwrite_region): Test for file system time stamp bug. (init_fileio): New function. * lisp.h (init_fileio): Declare it. * emacs.c (main): Call it. Fixes: debbugs:13149 --- src/ChangeLog | 13 +++++++++++++ src/emacs.c | 1 + src/fileio.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lisp.h | 1 + 4 files changed, 68 insertions(+) diff --git a/src/ChangeLog b/src/ChangeLog index 4cd417868a1..2954df9d6b8 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,18 @@ 2013-01-19 Paul Eggert + Work around bug in CIFS and vboxsf file systems (Bug#13149). + The bug was observed on Ubuntu operating inside a virtual machine, + editing files mounted via CIFS or vboxsf from the MS Windows 7 host. + The workaround introduces a race condition on non-buggy hosts, + but it's an unlikely race and anyway there's a nearly identical + nearby race that can't be fixed. + * fileio.c (valid_timestamp_file_system, timestamp_file_system): + New static vars. + (Fwrite_region): Test for file system time stamp bug. + (init_fileio): New function. + * lisp.h (init_fileio): Declare it. + * emacs.c (main): Call it. + * fileio.c (Finsert_file_contents): Simplify new diagnostic and make it more consistent with other stat-failure diagnostics. diff --git a/src/emacs.c b/src/emacs.c index 91a7ce6390d..6536c3708af 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1317,6 +1317,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem } init_callproc (); /* Must follow init_cmdargs but not init_sys_modes. */ + init_fileio (); init_lread (); #ifdef WINDOWSNT /* Check to see if Emacs has been installed correctly. */ diff --git a/src/fileio.c b/src/fileio.c index bd845c2f1e0..616f1c50f91 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -103,6 +103,11 @@ static mode_t auto_save_mode_bits; /* Set by auto_save_1 if an error occurred during the last auto-save. */ static bool auto_save_error_occurred; +/* If VALID_TIMESTAMP_FILE_SYSTEM, then TIMESTAMP_FILE_SYSTEM is the device + number of a file system where time stamps were observed to to work. */ +static bool valid_timestamp_file_system; +static dev_t timestamp_file_system; + /* The symbol bound to coding-system-for-read when insert-file-contents is called for recovering a file. This is not an actual coding system name, but just an indicator to tell @@ -4971,6 +4976,48 @@ This calls `write-region-annotate-functions' at the start, and /* Discard the unwind protect for close_file_unwind. */ specpdl_ptr = specpdl + count1; + /* Some file systems have a bug where st_mtime is not updated + properly after a write. For example, CIFS might not see the + st_mtime change until after the file is opened again. + + Attempt to detect this file system bug, and update MODTIME to the + newer st_mtime if the bug appears to be present. This introduces + a race condition, so to avoid most instances of the race condition + on non-buggy file systems, skip this check if the most recently + encountered non-buggy file system was the current file system. + + A race condition can occur if some other process modifies the + file between the fstat above and the fstat below, but the race is + unlikely and a similar race between the last write and the fstat + above cannot possibly be closed anyway. */ + + if (EMACS_TIME_VALID_P (modtime) + && ! (valid_timestamp_file_system && st.st_dev == timestamp_file_system)) + { + int desc1 = emacs_open (fn, O_WRONLY, 0); + if (0 <= desc1) + { + struct stat st1; + if (fstat (desc1, &st1) == 0 + && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino) + { + EMACS_TIME modtime1 = get_stat_mtime (&st1); + if (EMACS_TIME_EQ (modtime, modtime1) + && st.st_size == st1.st_size) + { + timestamp_file_system = st.st_dev; + valid_timestamp_file_system = 1; + } + else + { + st.st_size = st1.st_size; + modtime = modtime1; + } + } + emacs_close (desc1); + } + } + /* Call write-region-post-annotation-function. */ while (CONSP (Vwrite_region_annotation_buffers)) { @@ -5767,6 +5814,12 @@ Fread_file_name (Lisp_Object prompt, Lisp_Object dir, Lisp_Object default_filena } +void +init_fileio (void) +{ + valid_timestamp_file_system = 0; +} + void syms_of_fileio (void) { diff --git a/src/lisp.h b/src/lisp.h index 5c81bc5dd8a..48fba653203 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3299,6 +3299,7 @@ extern _Noreturn void report_file_error (const char *, Lisp_Object); extern bool internal_delete_file (Lisp_Object); extern bool file_directory_p (const char *); extern bool file_accessible_directory_p (const char *); +extern void init_fileio (void); extern void syms_of_fileio (void); extern Lisp_Object make_temp_name (Lisp_Object, bool); extern Lisp_Object Qdelete_file; -- 2.39.5