Re: fsync in glib/gio
- From: Alexander Larsson <alexl redhat com>
- To: Sven Neumann <sven gimp org>
- Cc: gtk-devel-list gnome org
- Subject: Re: fsync in glib/gio
- Date: Fri, 13 Mar 2009 20:57:19 +0100
On Fri, 2009-03-13 at 18:45 +0100, Alexander Larsson wrote:
> One compromise we could make it to only fsync in the case we're actually
> overwriting an existing file. This would mean that we don't risk loosing
> both the old and the new version of the file, you only lose "new" files.
> This case is far less common so the performance aspects are not as bad,
> and its also get rids of the worst failure mode.
Attached is a patch that does this. It adds a
G_FILE_CREATE_SYNC_ON_CLOSE flag, and turns it on by default when
handling an overwrite. The same thing happens in g_file_set_contents().
I think we need to do this, at the minimum, because even if there are
some ext4 patches to fix up this issue they can be disabled, and from
what I understand XFS and btrfs have similar issues.
The question is, do we want this or the full patch?
Index: gio/glocalfileoutputstream.c
===================================================================
--- gio/glocalfileoutputstream.c (revision 7974)
+++ gio/glocalfileoutputstream.c (working copy)
@@ -69,6 +69,7 @@ struct _GLocalFileOutputStreamPrivate {
char *original_filename;
char *backup_filename;
char *etag;
+ gboolean sync_on_close;
int fd;
};
@@ -81,7 +82,7 @@ static gboolean g_local_file_output_st
GCancellable *cancellable,
GError **error);
static GFileInfo *g_local_file_output_stream_query_info (GFileOutputStream *stream,
- char *attributes,
+ const char *attributes,
GCancellable *cancellable,
GError **error);
static char * g_local_file_output_stream_get_etag (GFileOutputStream *stream);
@@ -190,6 +191,22 @@ g_local_file_output_stream_close (GOutpu
file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
+#ifdef HAVE_FSYNC
+ if (file->priv->sync_on_close)
+ {
+ if (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
@@ -459,7 +476,7 @@ g_local_file_output_stream_truncate (GFi
static GFileInfo *
g_local_file_output_stream_query_info (GFileOutputStream *stream,
- char *attributes,
+ const char *attributes,
GCancellable *cancellable,
GError **error)
{
@@ -517,6 +534,8 @@ _g_local_file_output_stream_create (con
stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
stream->priv->fd = fd;
+ if (flags & G_FILE_CREATE_SYNC_ON_CLOSE)
+ stream->priv->sync_on_close = TRUE;
return G_FILE_OUTPUT_STREAM (stream);
}
@@ -562,6 +581,8 @@ _g_local_file_output_stream_append (con
stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
stream->priv->fd = fd;
+ if (flags & G_FILE_CREATE_SYNC_ON_CLOSE)
+ stream->priv->sync_on_close = TRUE;
return G_FILE_OUTPUT_STREAM (stream);
}
@@ -992,6 +1013,7 @@ _g_local_file_output_stream_replace (con
if (fd == -1 && errno == EEXIST)
{
+ flags |= G_FILE_CREATE_SYNC_ON_CLOSE;
/* The file already exists */
fd = handle_overwrite_open (filename, etag, create_backup, &temp_file,
flags, cancellable, error);
@@ -1022,6 +1044,8 @@ _g_local_file_output_stream_replace (con
stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
stream->priv->fd = fd;
+ if (flags & G_FILE_CREATE_SYNC_ON_CLOSE)
+ stream->priv->sync_on_close = TRUE;
stream->priv->tmp_filename = temp_file;
if (create_backup)
stream->priv->backup_filename = create_backup_filename (filename);
Index: gio/gioenums.h
===================================================================
--- gio/gioenums.h (revision 7974)
+++ gio/gioenums.h (working copy)
@@ -164,13 +164,17 @@ typedef enum {
* You can think of it as "unlink destination" before
* writing to it, although the implementation may not
* be exactly like that. Since 2.20
+ * @G_FILE_CREATE_SYNC_ON_CLOSE: If possible, try to ensure
+ * that all data is on disk before returning. For local
+ * files this means calling fsync() before close.
*
* Flags used when an operation may create a file.
*/
typedef enum {
G_FILE_CREATE_NONE = 0,
G_FILE_CREATE_PRIVATE = (1 << 0),
- G_FILE_CREATE_REPLACE_DESTINATION = (1 << 1)
+ G_FILE_CREATE_REPLACE_DESTINATION = (1 << 1),
+ G_FILE_CREATE_SYNC_ON_CLOSE = (1 << 2)
} GFileCreateFlags;
Index: configure.in
===================================================================
--- configure.in (revision 7974)
+++ configure.in (working copy)
@@ -563,6 +563,7 @@ AC_CHECK_FUNCS(mmap)
AC_CHECK_FUNCS(posix_memalign)
AC_CHECK_FUNCS(memalign)
AC_CHECK_FUNCS(valloc)
+AC_CHECK_FUNCS(fsync)
AC_CHECK_FUNCS(atexit on_exit)
Index: glib/gfileutils.c
===================================================================
--- glib/gfileutils.c (revision 7974)
+++ glib/gfileutils.c (working copy)
@@ -869,6 +869,7 @@ static gchar *
write_to_temp_file (const gchar *contents,
gssize length,
const gchar *template,
+ gboolean sync_on_close,
GError **err)
{
gchar *tmp_name;
@@ -942,11 +943,47 @@ write_to_temp_file (const gchar *conten
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 (sync_on_close && 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,
@@ -1027,7 +1064,9 @@ g_file_set_contents (const gchar *filen
if (length == -1)
length = strlen (contents);
- tmp_filename = write_to_temp_file (contents, length, filename, error);
+ tmp_filename = write_to_temp_file (contents, length, filename,
+ g_file_test (filename, G_FILE_TEST_EXISTS),
+ error);
if (!tmp_filename)
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]