glib r7991 - in trunk: . gio glib



Author: alexl
Date: Mon Mar 16 16:03:13 2009
New Revision: 7991
URL: http://svn.gnome.org/viewvc/glib?rev=7991&view=rev

Log:
2009-03-16  Alexander Larsson  <alexl redhat com>

	Bug 575555 â Use fsync() when replacing files to avoid data loss on crash

        * configure.in:
	Look for fsync().

        * glib/gfileutils.c:
        (write_to_temp_file):
	fsync temp file if destination file exists

2009-03-16  Alexander Larsson  <alexl redhat com>

	Bug 575555 â Use fsync() when replacing files to avoid data loss on crash

        * glocalfileoutputstream.c:
        (g_local_file_output_stream_close):
        (_g_local_file_output_stream_replace):
	fsync temp file before closing if replacing target file

	


Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/gio/ChangeLog
   trunk/gio/glocalfileoutputstream.c
   trunk/glib/gfileutils.c

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Mon Mar 16 16:03:13 2009
@@ -563,6 +563,7 @@
 AC_CHECK_FUNCS(posix_memalign)
 AC_CHECK_FUNCS(memalign)
 AC_CHECK_FUNCS(valloc)
+AC_CHECK_FUNCS(fsync)
 
 AC_CHECK_FUNCS(atexit on_exit)
 

Modified: trunk/gio/glocalfileoutputstream.c
==============================================================================
--- trunk/gio/glocalfileoutputstream.c	(original)
+++ trunk/gio/glocalfileoutputstream.c	Mon Mar 16 16:03:13 2009
@@ -69,6 +69,7 @@
   char *original_filename;
   char *backup_filename;
   char *etag;
+  gboolean sync_on_close;
   int fd;
 };
 
@@ -190,6 +191,20 @@
 
   file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
 
+#ifdef HAVE_FSYNC
+  if (file->priv->sync_on_close &&
+      fsync (file->priv->fd) != 0)
+    {
+      int errsv = errno;
+      
+      g_set_error (error, G_IO_ERROR,
+		   g_io_error_from_errno (errno),
+		   _("Error writing to file: %s"),
+		   g_strerror (errsv));
+      goto err_out;
+    }
+#endif
+ 
 #ifdef G_OS_WIN32
 
   /* Must close before renaming on Windows, so just do the close first
@@ -976,6 +991,7 @@
   int mode;
   int fd;
   char *temp_file;
+  gboolean sync_on_close;
 
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return NULL;
@@ -986,6 +1002,7 @@
     mode = 0600;
   else
     mode = 0666;
+  sync_on_close = FALSE;
 
   /* If the file doesn't exist, create it */
   fd = g_open (filename, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode);
@@ -997,6 +1014,14 @@
 				  flags, cancellable, error);
       if (fd == -1)
 	return NULL;
+
+      /* If the final destination exists, we want to sync the newly written
+       * file to ensure the data is on disk when we rename over the destination.
+       * otherwise if we get a system crash we can lose both the new and the
+       * old file on some filesystems. (I.E. those that don't guarantee the
+       * data is written to the disk before the metadata.)
+       */
+      sync_on_close = TRUE;
     }
   else if (fd == -1)
     {
@@ -1022,6 +1047,7 @@
  
   stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
   stream->priv->fd = fd;
+  stream->priv->sync_on_close = sync_on_close;
   stream->priv->tmp_filename = temp_file;
   if (create_backup)
     stream->priv->backup_filename = create_backup_filename (filename);

Modified: trunk/glib/gfileutils.c
==============================================================================
--- trunk/glib/gfileutils.c	(original)
+++ trunk/glib/gfileutils.c	Mon Mar 16 16:03:13 2009
@@ -868,7 +868,7 @@
 static gchar *
 write_to_temp_file (const gchar  *contents,
 		    gssize        length,
-		    const gchar  *template,
+		    const gchar  *dest_file,
 		    GError      **err)
 {
   gchar *tmp_name;
@@ -880,7 +880,7 @@
 
   retval = NULL;
   
-  tmp_name = g_strdup_printf ("%s.XXXXXX", template);
+  tmp_name = g_strdup_printf ("%s.XXXXXX", dest_file);
 
   errno = 0;
   fd = create_temp_file (tmp_name, 0666);
@@ -942,11 +942,54 @@
 	  goto out;
 	}
     }
-   
+
+  errno = 0;
+  if (fflush (file) != 0)
+    { 
+      save_errno = errno;
+      
+      g_set_error (err,
+		   G_FILE_ERROR,
+		   g_file_error_from_errno (save_errno),
+		   _("Failed to write file '%s': fflush() failed: %s"),
+		   display_name, 
+		   g_strerror (save_errno));
+
+      g_unlink (tmp_name);
+      
+      goto out;
+    }
+  
+#ifdef HAVE_FSYNC
+  errno = 0;
+  /* If the final destination exists, we want to sync the newly written
+   * file to ensure the data is on disk when we rename over the destination.
+   * otherwise if we get a system crash we can lose both the new and the
+   * old file on some filesystems. (I.E. those that don't guarantee the
+   * data is written to the disk before the metadata.)
+   */
+  if (g_file_test (dest_file, G_FILE_TEST_EXISTS) &&
+      fsync (fileno (file)) != 0)
+    { 
+      save_errno = errno;
+      
+      g_set_error (err,
+		   G_FILE_ERROR,
+		   g_file_error_from_errno (save_errno),
+		   _("Failed to write file '%s': fsync() failed: %s"),
+		   display_name, 
+		   g_strerror (save_errno));
+
+      g_unlink (tmp_name);
+      
+      goto out;
+    }
+#endif
+  
   errno = 0;
   if (fclose (file) == EOF)
     { 
-      save_errno = 0;
+      save_errno = errno;
       
       g_set_error (err,
 		   G_FILE_ERROR,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]