Commit e4e618c6 authored by Philip Withnall's avatar Philip Withnall

gfileutils: Factor out fsync calculation

This introduces no functional changes, just makes the code a bit more
modular and reusable.
Signed-off-by: Philip Withnall's avatarPhilip Withnall <withnall@endlessm.com>

Helps: #1302
parent 00c17de7
...@@ -1050,13 +1050,48 @@ rename_file (const char *old_name, ...@@ -1050,13 +1050,48 @@ rename_file (const char *old_name,
return TRUE; return TRUE;
} }
static gboolean
fd_should_be_fsynced (int fd,
const gchar *test_file,
GFileSetContentsFlags flags)
{
#ifdef HAVE_FSYNC
struct stat statbuf;
#ifdef BTRFS_SUPER_MAGIC
{
struct statfs buf;
/* On Linux, on btrfs, skip the fsync since rename-over-existing is
* guaranteed to be atomic and this is the only case in which we
* would fsync() anyway.
*/
if (fstatfs (fd, &buf) == 0 && buf.f_type == BTRFS_SUPER_MAGIC)
return FALSE;
}
#endif /* BTRFS_SUPER_MAGIC */
errno = 0;
/* If the final destination exists and is > 0 bytes, 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.)
*/
return (g_lstat (test_file, &statbuf) == 0 && statbuf.st_size > 0);
#else /* if !HAVE_FSYNC */
return FALSE;
#endif /* !HAVE_FSYNC */
}
/* closes @fd once it’s finished (on success or error) */ /* closes @fd once it’s finished (on success or error) */
static gboolean static gboolean
write_to_file (const gchar *contents, write_to_file (const gchar *contents,
gssize length, gssize length,
int fd, int fd,
const gchar *dest_file, const gchar *dest_file,
const gchar *test_file, gboolean do_fsync,
GError **err) GError **err)
{ {
#ifdef HAVE_FALLOCATE #ifdef HAVE_FALLOCATE
...@@ -1094,46 +1129,19 @@ write_to_file (const gchar *contents, ...@@ -1094,46 +1129,19 @@ write_to_file (const gchar *contents,
length -= s; length -= s;
} }
#ifdef BTRFS_SUPER_MAGIC
{
struct statfs buf;
/* On Linux, on btrfs, skip the fsync since rename-over-existing is
* guaranteed to be atomic and this is the only case in which we
* would fsync() anyway.
*/
if (fstatfs (fd, &buf) == 0 && buf.f_type == BTRFS_SUPER_MAGIC)
goto no_fsync;
}
#endif
#ifdef HAVE_FSYNC #ifdef HAVE_FSYNC
{ errno = 0;
struct stat statbuf; if (do_fsync && fsync (fd) != 0)
{
errno = 0; int saved_errno = errno;
/* If the final destination exists and is > 0 bytes, we want to sync the set_file_error (err,
* newly written file to ensure the data is on disk when we rename over dest_file, _("Failed to write file “%s”: fsync() failed: %s"),
* the destination. Otherwise if we get a system crash we can lose both saved_errno);
* the new and the old file on some filesystems. (I.E. those that don't close (fd);
* guarantee the data is written to the disk before the metadata.)
*/
if (g_lstat (dest_file, &statbuf) == 0 && statbuf.st_size > 0 && fsync (fd) != 0)
{
int saved_errno = errno;
set_file_error (err,
dest_file, _("Failed to write file “%s”: fsync() failed: %s"),
saved_errno);
close (fd);
return FALSE;
}
}
#endif
#ifdef BTRFS_SUPER_MAGIC return FALSE;
no_fsync: }
#endif #endif
errno = 0; errno = 0;
...@@ -1159,6 +1167,7 @@ write_to_temp_file (const gchar *contents, ...@@ -1159,6 +1167,7 @@ write_to_temp_file (const gchar *contents,
{ {
gchar *tmp_name = NULL; gchar *tmp_name = NULL;
int fd; int fd;
gboolean do_fsync;
tmp_name = g_strdup_printf ("%s.XXXXXX", dest_file); tmp_name = g_strdup_printf ("%s.XXXXXX", dest_file);
...@@ -1175,7 +1184,8 @@ write_to_temp_file (const gchar *contents, ...@@ -1175,7 +1184,8 @@ write_to_temp_file (const gchar *contents,
return NULL; return NULL;
} }
if (!write_to_file (contents, length, steal_fd (&fd), tmp_name, dest_file, err)) do_fsync = fd_should_be_fsynced (fd, dest_file, flags);
if (!write_to_file (contents, length, steal_fd (&fd), tmp_name, do_fsync, err))
{ {
g_unlink (tmp_name); g_unlink (tmp_name);
g_free (tmp_name); g_free (tmp_name);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment