Subject: file synchronization fixes

From: Paul Eggert <[email-masked]>
Date: Wed, 13 Mar 2013 00:38:00 -0700

Tags: patch

The new code for creating regular-file lock files has a problem if
the system crashes while the lock file is being created: due to
races inside the file system, if the crash occurs at the wrong time
the file system may end up with an empty regular-file lock file,
which after the reboot will cause Emacs to bypass locking for that
file indefinitely (unless the empty lock file is removed manually
by the user).

On a POSIX-conforming system the problem can occur only if the
system crashes just as a lock file is being created.  Fixing this
requires using a synchronization primitive such as fsync on the
newly created file, before renaming it.

While looking into this I noticed some other file synchronization
problems.  fsync is used sometimes when fdatasync will do.  Emacs
does not consistently retry fsync after being interrupted.  It
sometimes incorrectly reports an error merely because fsync isn't
supported.  And it sometimes incorrectly skips fsync merely because
we are on a non-BSD system.

Attached a patch which I'd like to install.  If MS-Windows has fdatasync
already this should build on MS-Windows; if not, the MS-Windows port
needs to compile lib/fdatasync.c or support an fdatasync substitute
in some other way.
=== modified file 'ChangeLog'
--- ChangeLog	2013-03-13 07:15:17 +0000
+++ ChangeLog	2013-03-13 07:31:15 +0000
@@ -1,5 +1,12 @@
 2013-03-13  Paul Eggert  <[email-masked]>
 
+	File synchronization fixes.
+	* configure.ac (BSD_SYSTEM, BSD_SYSTEM_AHB): Remove; no longer needed.
+	(fsync): Remove check; now done by gnulib.
+	* lib/fdatasync.c, lib/fsync.c, m4/fdatasync.m4, m4/fsync.m4:
+	New files, from gnulib.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+
 	Merge from gnulib, incorporating:
 	2013-03-13 putenv: port to Solaris 10
 	2013-03-12 mktime: fix configure typo

=== modified file 'admin/CPP-DEFINES'
--- admin/CPP-DEFINES	2013-02-25 05:55:37 +0000
+++ admin/CPP-DEFINES	2013-03-13 05:52:50 +0000
@@ -9,7 +9,6 @@
 
 AIX
 _AIX
-BSD_SYSTEM
 CYGWIN		Compiling the Cygwin port.
 __CYGWIN__	Ditto
 GNU_LINUX
@@ -149,7 +148,6 @@
 HAVE_FREEIFADDRS
 HAVE_FREETYPE
 HAVE_FSEEKO
-HAVE_FSYNC
 HAVE_FUTIMENS
 HAVE_FUTIMES
 HAVE_FUTIMESAT

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2013-03-11 22:32:07 +0000
+++ admin/ChangeLog	2013-03-13 05:52:50 +0000
@@ -1,3 +1,9 @@
+2013-03-13  Paul Eggert  <[email-masked]>
+
+	File synchronization fixes.
+	* CPP-DEFINES (BSD_SYSTEM, HAVE_FSYNC): Remove.
+	* merge-gnulib (GNULIB_MODULES): Add fsync, fdatasync.
+
 2013-03-11  Paul Eggert  <[email-masked]>
 
 	* notes/unicode: Improve notes about Emacs source file encoding.

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2013-02-11 23:37:18 +0000
+++ admin/merge-gnulib	2013-03-13 05:23:47 +0000
@@ -29,7 +29,8 @@
   alloca-opt c-ctype c-strcase
   careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
   dtoastr dtotimespec dup2 environ execinfo faccessat
-  fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday
+  fcntl-h fdatasync fdopendir filemode fstatat fsync
+  getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
   manywarnings memrchr mktime
   pselect pthread_sigmask putenv readlink readlinkat

=== modified file 'configure.ac'
--- configure.ac	2013-03-06 08:01:47 +0000
+++ configure.ac	2013-03-13 05:52:50 +0000
@@ -2872,7 +2872,7 @@
 utimes getrlimit setrlimit shutdown getaddrinfo \
 strsignal setitimer \
 sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \
-gai_strerror mkstemp getline getdelim fsync sync \
+gai_strerror mkstemp getline getdelim sync \
 difftime posix_memalign \
 getpwent endpwent getgrent endgrent \
 touchlock \
@@ -3774,7 +3774,6 @@
 
 dnl Define symbols to identify the version of Unix this is.
 dnl Define all the symbols that apply correctly.
-AH_TEMPLATE(BSD_SYSTEM, [Define if the system is compatible with BSD 4.2.])
 AH_TEMPLATE(DOS_NT, [Define if the system is MS DOS or MS Windows.])
 AH_TEMPLATE(MSDOS, [Define if the system is MS DOS.])
 AH_TEMPLATE(USG, [Define if the system is compatible with System III.])
@@ -3798,30 +3797,12 @@
     ;;
 
   darwin)
-    dnl BSD4_3 and BSD4_4 are already defined in sys/param.h.
-    AC_DEFINE(BSD_SYSTEM, [])
-    dnl More specific than the above two.  We cannot use __APPLE__ as this
-    dnl may not be defined on non-OSX Darwin, and we cannot define DARWIN
-    dnl here because Panther and lower CoreFoundation.h uses DARWIN to
+    dnl Not __APPLE__, as this may not be defined on non-OSX Darwin.
+    dnl Not DARWIN, because Panther and lower CoreFoundation.h use DARWIN to
     dnl distinguish OS X from pure Darwin.
     AC_DEFINE(DARWIN_OS, [], [Define if the system is Darwin.])
     ;;
 
-  freebsd)
-    dnl Hack to avoid calling AC_PREPROC_IFELSE multiple times.
-    dnl Would not be needed with autoconf >= 2.67, where the
-    dnl preprocessed output is accessible in "conftest.i".
-    AC_DEFINE(BSD_SYSTEM_AHB, 1, [Define if AH_BOTTOM should change BSD_SYSTEM.])
-    ;;
-
-  gnu | netbsd | openbsd )
-    AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[
-#ifndef BSD_SYSTEM
-# error "BSD_SYSTEM not defined"
-#endif
-    ]], [[]])], [], AC_DEFINE(BSD_SYSTEM, 43) )
-    ;;
-
   gnu-linux | gnu-kfreebsd )
     AC_DEFINE(USG, [])
     AC_DEFINE(GNU_LINUX, [], [Define if ths system is compatible with GNU/Linux.])
@@ -4185,15 +4166,8 @@
    LD_SWITCH_SYSTEM_TEMACS="-fno-pie -prebind $libs_nsgui -Xlinker -headerpad -Xlinker $headerpad_extra"
 
    ## This is here because src/Makefile.in did some extra fiddling around
-   ## with LD_SWITCH_SYSTEM.  The cpp logic was:
-   ##   #ifndef LD_SWITCH_SYSTEM
-   ##   #if !defined (__GNUC__) && ((defined (BSD_SYSTEM) && !defined (COFF)))
-   ## Since all the *bsds define LD_SWITCH_SYSTEM, this simplifies to:
-   ## not using gcc, darwin.
-   ## Because this was done in src/Makefile.in, the resulting part of
-   ## LD_SWITCH_SYSTEM was not used in configure (ie, in ac_link).
-   ## It therefore seems cleaner to put this in LD_SWITCH_SYSTEM_TEMACS,
-   ## rather than LD_SWITCH_SYSTEM.
+   ## with LD_SWITCH_SYSTEM.  It seems cleaner to put this in
+   ## LD_SWITCH_SYSTEM_TEMACS instead,
    test "x$LD_SWITCH_SYSTEM" = "x" && test "x$GCC" != "xyes" && \
      LD_SWITCH_SYSTEM_TEMACS="-X $LD_SWITCH_SYSTEM_TEMACS"
    ;;

=== modified file 'lib-src/ChangeLog'
--- lib-src/ChangeLog	2013-03-13 07:27:34 +0000
+++ lib-src/ChangeLog	2013-03-13 07:31:15 +0000
@@ -1,5 +1,14 @@
 2013-03-13  Paul Eggert  <[email-masked]>
 
+	File synchronization fixes.
+	* Makefile.in (LIB_FDATASYNC): New macro.
+	(emacsclient${EXEEXT}): Use it.
+	* emacsclient.c (main): Use fdatasync, not fsync, since we don't
+	care about metadata.  Keep trying if interrupted.
+	* movemail.c (main, popmail): Don't worry about BSD_SYSTEM, since
+	fsync is available everywhere (or there is a substitute).  Don't
+	report an error if fsync returns EINVAL.
+
 	Static checking by Sun C 5.12.
 	* etags.c (analyse_regex): Omit unreachable code.

=== modified file 'lib-src/Makefile.in'
--- lib-src/Makefile.in	2013-01-01 09:11:05 +0000
+++ lib-src/Makefile.in	2013-03-13 05:42:28 +0000
@@ -161,6 +161,8 @@
 LIBS_MAIL=@LIBS_MAIL@
 ## empty or -lrt or -lposix4 if HAVE_CLOCK_GETTIME
 LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@
+## empty or -lrt or -lposix4 if HAVE_FDATASYNC
+LIB_FDATASYNC = @LIB_FDATASYNC@
 
 ## Extra libraries to use when linking movemail.
 LIBS_MOVE = $(LIBS_MAIL) $(KRB4LIB) $(DESLIB) $(KRB5LIB) $(CRYPTOLIB) \
@@ -334,7 +336,7 @@
 emacsclient${EXEEXT}: ${srcdir}/emacsclient.c $(config_h)
 	$(CC) ${ALL_CFLAGS} ${srcdir}/emacsclient.c \
 	   -DVERSION="\"${version}\"" \
-	   $(LOADLIBES) -o emacsclient
+	   $(LOADLIBES) $(LIB_FDATASYNC) -o emacsclient
 
 hexl${EXEEXT}: ${srcdir}/hexl.c $(config_h)
 	$(CC) ${ALL_CFLAGS} ${srcdir}/hexl.c $(LOADLIBES) -o hexl

=== modified file 'lib-src/emacsclient.c'
--- lib-src/emacsclient.c	2013-01-02 16:13:04 +0000
+++ lib-src/emacsclient.c	2013-03-13 05:23:47 +0000
@@ -1724,7 +1724,8 @@
       needlf = 2;
     }
   fflush (stdout);
-  fsync (1);
+  while (fdatasync (1) != 0 && errno == EINTR)
+    continue;
 
   /* Now, wait for an answer and print any messages.  */
   while (exit_status == EXIT_SUCCESS)
@@ -1825,7 +1826,8 @@
   if (needlf)
     printf ("\n");
   fflush (stdout);
-  fsync (1);
+  while (fdatasync (1) != 0 && errno == EINTR)
+    continue;
 
   if (rl < 0)
     exit_status = EXIT_FAILURE;

=== modified file 'lib-src/movemail.c'
--- lib-src/movemail.c	2013-03-13 01:05:40 +0000
+++ lib-src/movemail.c	2013-03-13 05:23:47 +0000
@@ -466,10 +466,8 @@
 	  }
       }
 
-#ifdef BSD_SYSTEM
-      if (fsync (outdesc) < 0)
+      if (fsync (outdesc) != 0 && errno != EINVAL)
 	pfatal_and_delete (outname);
-#endif
 
       /* Prevent symlink attacks truncating other users' mailboxes */
       if (setregid (-1, real_gid) < 0)
@@ -750,21 +748,14 @@
 	}
     }
 
-  /* On AFS, a call to write only modifies the file in the local
-   *     workstation's AFS cache.  The changes are not written to the server
-   *      until a call to fsync or close is made.  Users with AFS home
-   *      directories have lost mail when over quota because these checks were
-   *      not made in previous versions of movemail. */
-
-#ifdef BSD_SYSTEM
-  if (fsync (mbfi) < 0)
+  if (fsync (mbfi) != 0 && errno != EINVAL)
     {
       error ("Error in fsync: %s", strerror (errno), 0);
+      close (mbfi);
       return EXIT_FAILURE;
     }
-#endif
 
-  if (close (mbfi) == -1)
+  if (close (mbfi) != 0)
     {
       error ("Error in close: %s", strerror (errno), 0);
       return EXIT_FAILURE;

=== added file 'lib/fdatasync.c'
--- lib/fdatasync.c	1970-01-01 00:00:00 +0000
+++ lib/fdatasync.c	2013-03-13 05:23:47 +0000
@@ -0,0 +1,27 @@
+/* Emulate fdatasync on platforms that lack it.
+
+   Copyright (C) 2011-2013 Free Software Foundation, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+#include <unistd.h>
+
+int
+fdatasync (int fd)
+{
+  /* This does more work than strictly necessary, but is the best we
+     can do portably.  */
+  return fsync (fd);
+}

=== added file 'lib/fsync.c'
--- lib/fsync.c	1970-01-01 00:00:00 +0000
+++ lib/fsync.c	2013-03-13 05:23:47 +0000
@@ -0,0 +1,83 @@
+/* Emulate fsync on platforms that lack it, primarily Windows and
+   cross-compilers like MinGW.
+
+   This is derived from sqlite3 sources.
+   http://www.sqlite.org/cvstrac/rlog?f=sqlite/src/os_win.c
+   http://www.sqlite.org/copyright.html
+
+   Written by Richard W.M. Jones <rjones.at.redhat.com>
+
+   Copyright (C) 2008-2013 Free Software Foundation, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+#include <unistd.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+
+/* FlushFileBuffers */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+# include <errno.h>
+
+/* Get _get_osfhandle.  */
+# include "msvc-nothrow.h"
+
+int
+fsync (int fd)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (fd);
+  DWORD err;
+
+  if (h == INVALID_HANDLE_VALUE)
+    {
+      errno = EBADF;
+      return -1;
+    }
+
+  if (!FlushFileBuffers (h))
+    {
+      /* Translate some Windows errors into rough approximations of Unix
+       * errors.  MSDN is useless as usual - in this case it doesn't
+       * document the full range of errors.
+       */
+      err = GetLastError ();
+      switch (err)
+        {
+        case ERROR_ACCESS_DENIED:
+          /* For a read-only handle, fsync should succeed, even though we have
+             no way to sync the access-time changes.  */
+          return 0;
+
+          /* eg. Trying to fsync a tty. */
+        case ERROR_INVALID_HANDLE:
+          errno = EINVAL;
+          break;
+
+        default:
+          errno = EIO;
+        }
+      return -1;
+    }
+
+  return 0;
+}
+
+#else /* !Windows */
+
+# error "This platform lacks fsync function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
+
+#endif /* !Windows */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2013-02-11 23:37:18 +0000
+++ lib/gnulib.mk	2013-03-13 05:23:47 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdopendir filemode fstatat getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings memrchr mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=dup --avoid=errno --avoid=fchdir --avoid=fcntl --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat close-stream crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings memrchr mktime pselect pthread_sigmask putenv readlink readlinkat sig2str socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub unsetenv utimens warnings

 MOSTLYCLEANFILES += core *.stackdump
@@ -297,6 +297,15 @@
 
 ## end   gnulib module fcntl-h
 
+## begin gnulib module fdatasync
+
+
+EXTRA_DIST += fdatasync.c
+
+EXTRA_libgnu_a_SOURCES += fdatasync.c
+
+## end   gnulib module fdatasync
+
 ## begin gnulib module fdopendir

@@ -332,6 +341,15 @@
 
 ## end   gnulib module fstatat
 
+## begin gnulib module fsync
+
+
+EXTRA_DIST += fsync.c
+
+EXTRA_libgnu_a_SOURCES += fsync.c
+
+## end   gnulib module fsync
+
 ## begin gnulib module getgroups
 
 if gl_GNULIB_ENABLED_getgroups

=== added file 'm4/fdatasync.m4'
--- m4/fdatasync.m4	1970-01-01 00:00:00 +0000
+++ m4/fdatasync.m4	2013-03-13 05:23:47 +0000
@@ -0,0 +1,32 @@
+# fdatasync.m4 serial 4
+dnl Copyright (C) 2008-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_FDATASYNC],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Using AC_CHECK_FUNCS_ONCE would break our subsequent AC_SEARCH_LIBS
+  AC_CHECK_DECLS_ONCE([fdatasync])
+  LIB_FDATASYNC=
+  AC_SUBST([LIB_FDATASYNC])
+
+  if test $ac_cv_have_decl_fdatasync = no; then
+    HAVE_DECL_FDATASYNC=0
+    dnl Mac OS X 10.7 has fdatasync but does not declare it.
+    AC_CHECK_FUNCS([fdatasync])
+    if test $ac_cv_func_fdatasync = no; then
+      HAVE_FDATASYNC=0
+    fi
+  else
+    dnl Solaris <= 2.6 has fdatasync() in libposix4.
+    dnl Solaris 7..10 has it in librt.
+    gl_saved_libs=$LIBS
+    AC_SEARCH_LIBS([fdatasync], [rt posix4],
+                   [test "$ac_cv_search_fdatasync" = "none required" ||
+                    LIB_FDATASYNC=$ac_cv_search_fdatasync])
+    LIBS=$gl_saved_libs
+  fi
+])

=== added file 'm4/fsync.m4'
--- m4/fsync.m4	1970-01-01 00:00:00 +0000
+++ m4/fsync.m4	2013-03-13 05:23:47 +0000
@@ -0,0 +1,17 @@
+# fsync.m4 serial 2
+dnl Copyright (C) 2008-2013 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_FSYNC],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_CHECK_FUNCS_ONCE([fsync])
+  if test $ac_cv_func_fsync = no; then
+    HAVE_FSYNC=0
+  fi
+])
+
+# Prerequisites of lib/fsync.c.
+AC_DEFUN([gl_PREREQ_FSYNC], [:])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2013-03-01 15:16:43 +0000
+++ m4/gnulib-comp.m4	2013-03-13 05:23:47 +0000
@@ -63,10 +63,12 @@
   # Code from module extern-inline:
   # Code from module faccessat:
   # Code from module fcntl-h:
+  # Code from module fdatasync:
   # Code from module fdopendir:
   # Code from module filemode:
   # Code from module fpending:
   # Code from module fstatat:
+  # Code from module fsync:
   # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
@@ -187,6 +189,11 @@
   gl_MODULE_INDICATOR([faccessat])
   gl_UNISTD_MODULE_INDICATOR([faccessat])
   gl_FCNTL_H
+  gl_FUNC_FDATASYNC
+  if test $HAVE_FDATASYNC = 0; then
+    AC_LIBOBJ([fdatasync])
+  fi
+  gl_UNISTD_MODULE_INDICATOR([fdatasync])
   gl_FUNC_FDOPENDIR
   if test $HAVE_FDOPENDIR = 0 || test $REPLACE_FDOPENDIR = 1; then
     AC_LIBOBJ([fdopendir])
@@ -204,6 +211,12 @@
     AC_LIBOBJ([fstatat])
   fi
   gl_SYS_STAT_MODULE_INDICATOR([fstatat])
+  gl_FUNC_FSYNC
+  if test $HAVE_FSYNC = 0; then
+    AC_LIBOBJ([fsync])
+    gl_PREREQ_FSYNC
+  fi
+  gl_UNISTD_MODULE_INDICATOR([fsync])
   gl_GETLOADAVG
   if test $HAVE_GETLOADAVG = 0; then
     AC_LIBOBJ([getloadavg])
@@ -743,12 +756,14 @@
   lib/execinfo.in.h
   lib/faccessat.c
   lib/fcntl.in.h
+  lib/fdatasync.c
   lib/fdopendir.c
   lib/filemode.c
   lib/filemode.h
   lib/fpending.c
   lib/fpending.h
   lib/fstatat.c
+  lib/fsync.c
   lib/ftoastr.c
   lib/ftoastr.h
   lib/getgroups.c
@@ -842,10 +857,12 @@
   m4/faccessat.m4
   m4/fcntl-o.m4
   m4/fcntl_h.m4
+  m4/fdatasync.m4
   m4/fdopendir.m4
   m4/filemode.m4
   m4/fpending.m4
   m4/fstatat.m4
+  m4/fsync.m4
   m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2013-03-13 07:27:34 +0000
+++ src/ChangeLog	2013-03-13 07:31:15 +0000
@@ -1,5 +1,14 @@
 2013-03-13  Paul Eggert  <[email-masked]>
 
+	File synchronization fixes.
+	* Makefile.in (LIB_FDATASYNC): New macro.
+	(LIBES): Use it.
+	* conf_post.h (BSD_SYSTEM, BSD_SYSTEM_AHB): Remove; no longer needed.
+	* fileio.c (Fwrite_region, write_region_inhibit_fsync):
+	Don't worry about HAVE_FSYNC, since a substitute fsync is
+	available if the system lacks one.
+	(Fwrite_regin): Retry fsync if interrupted.
+
 	Static checking by Sun C 5.12.
 	* alloc.c (buffer_memory_full) [REL_ALLOC]:
 	* bytecode.c (exec_byte_code):

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2013-02-25 05:55:37 +0000
+++ src/Makefile.in	2013-03-13 05:42:28 +0000
@@ -139,6 +139,7 @@
 
 LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@
 LIB_EACCESS=@LIB_EACCESS@
+LIB_FDATASYNC=@LIB_FDATASYNC@
 LIB_TIMER_TIME=@LIB_TIMER_TIME@
 
 DBUS_CFLAGS = @DBUS_CFLAGS@
@@ -391,7 +392,7 @@
 LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
    $(LIBX_OTHER) $(LIBSOUND) \
    $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
-   $(LIB_EACCESS) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
+   $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
    $(LIB_EXECINFO) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \

=== modified file 'src/conf_post.h'
--- src/conf_post.h	2013-02-01 06:30:51 +0000
+++ src/conf_post.h	2013-03-13 05:52:50 +0000
@@ -44,19 +44,6 @@
 # define __has_attribute(a) 0 /* non-clang */
 #endif
 
-/* This silences a few compilation warnings on FreeBSD.  */
-#ifdef BSD_SYSTEM_AHB
-#undef BSD_SYSTEM_AHB
-#undef BSD_SYSTEM
-#if __FreeBSD__ == 1
-#define BSD_SYSTEM 199103
-#elif __FreeBSD__ == 2
-#define BSD_SYSTEM 199306
-#elif __FreeBSD__ >= 3
-#define BSD_SYSTEM 199506
-#endif
-#endif
-
 #ifdef DARWIN_OS
 #ifdef emacs
 #define malloc unexec_malloc

=== modified file 'src/fileio.c'
--- src/fileio.c	2013-03-13 07:27:34 +0000
+++ src/fileio.c	2013-03-13 07:31:15 +0000
@@ -4959,20 +4959,23 @@
 
   immediate_quit = 0;
 
-#ifdef HAVE_FSYNC
   /* fsync appears to change the modtime on BSD4.2.
      Disk full in NFS may be reported here.  */
   /* mib says that closing the file will try to write as fast as NFS can do
      it, and that means the fsync here is not crucial for autosave files.  */
-  if (!auto_saving && !write_region_inhibit_fsync && fsync (desc) < 0)
+  if (!auto_saving && !write_region_inhibit_fsync)
     {
-      /* If fsync fails with EINTR, don't treat that as serious.  Also
+      /* Transfer data and metadata to disk, retrying if interrupted.  Also,
 	 ignore EINVAL which happens when fsync is not supported on this
 	 file.  */
-      if (errno != EINTR && errno != EINVAL)
-	ok = 0, save_errno = errno;
+      while (fsync (desc) != 0)
+	if (errno != EINTR)
+	  {
+	    if (errno != EINVAL)
+	      ok = 0, save_errno = errno;
+	    break;
+	  }
     }
-#endif
 
   modtime = invalid_emacs_time ();
   if (visiting)
@@ -6046,13 +6049,11 @@
 file is usually more useful if it contains the deleted text.  */);
   Vauto_save_include_big_deletions = Qnil;
 
-#ifdef HAVE_FSYNC
   DEFVAR_BOOL ("write-region-inhibit-fsync", write_region_inhibit_fsync,
 	       doc: /* Non-nil means don't call fsync in `write-region'.
 This variable affects calls to `write-region' as well as save commands.
 A non-nil value may result in data loss!  */);
   write_region_inhibit_fsync = 0;
-#endif
 
   DEFVAR_BOOL ("delete-by-moving-to-trash", delete_by_moving_to_trash,
                doc: /* Specifies whether to use the system's trash can.

=== modified file 'src/filelock.c'
--- src/filelock.c	2013-03-05 22:35:41 +0000
+++ src/filelock.c	2013-03-13 05:23:47 +0000
@@ -437,6 +437,14 @@
 	  if (emacs_write (fd, lock_info_str, lock_info_len) != lock_info_len
 	      || (need_fchmod && fchmod (fd, world_readable) != 0))
 	    err = errno;
+	  else
+	    while (fsync (fd) != 0)
+	      if (errno != EINTR)
+		{
+		  if (errno != EINVAL)
+		    err = errno;
+		  break;
+		}
 	  if (emacs_close (fd) != 0)
 	    err = errno;
 	  if (!err && rename_lock_file (nonce, lfname, force) != 0)

=== modified file 'src/sysdep.c'
--- src/sysdep.c	2013-03-13 05:04:46 +0000
+++ src/sysdep.c	2013-03-13 05:23:47 +0000
@@ -1289,10 +1289,9 @@
   if (tty_out->terminal->reset_terminal_modes_hook)
     tty_out->terminal->reset_terminal_modes_hook (tty_out->terminal);
 
-#ifdef BSD_SYSTEM
   /* Avoid possible loss of output when changing terminal modes.  */
-  fsync (fileno (tty_out->output));
-#endif
+  while (fdatasync (fileno (tty_out->output)) != 0 && errno == EINTR)
+    continue;
 
 #ifndef DOS_NT
 #ifdef F_SETOWN