sysroot: Write symlinks before calling fsync(), then rename after
There might be a race here in that we create new symlink files *after* calling `syncfs`, and they are not guaranteed to end up on disk. Rework the code so that we create symlinks before, and then only rename them after (and `fsync()` the directory for good measure). Additional-fixes-by: Giuseppe Scrivano <gscrivan@redhat.com> Tested-by: Giuseppe Scrivano <gscrivan@redhat.com> This still needs verification that we're fixing a real bug; but I'm fairly confident this won't make the fsync situation worse. https://bugzilla.gnome.org/show_bug.cgi?id=755595
This commit is contained in:
1 parent
70c07a6338
commit
723705b803
3 files changed
+211
-110
No files matched your search
@@ -508,59 +508,3 @@ ot_util_ensure_directory_and_fsync (GFile *dir,
|
||||
(void) close (parentfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ot_gfile_atomic_symlink_swap:
|
||||
* @path: Replace the contents of this symbolic link
|
||||
* @target: New symbolic link target
|
||||
* @cancellable:
|
||||
* @error
|
||||
*
|
||||
* Create a new temporary symbolic link, then use the Unix rename()
|
||||
* function to atomically replace @path with the new symbolic link.
|
||||
* Do not use this function inside directories such as /tmp as it uses
|
||||
* a predicatable file name.
|
||||
*/
|
||||
gboolean
|
||||
ot_gfile_atomic_symlink_swap (GFile *path,
|
||||
const char *target,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
g_autoptr(GFile) parent = g_file_get_parent (path);
|
||||
g_autofree char *tmpname = g_strconcat (gs_file_get_basename_cached (path), ".tmp", NULL);
|
||||
g_autoptr(GFile) tmppath = g_file_get_child (parent, tmpname);
|
||||
int parent_dfd = -1;
|
||||
|
||||
if (!ot_gfile_ensure_unlinked (tmppath, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!g_file_make_symbolic_link (tmppath, target, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!gs_file_open_dir_fd (parent, &parent_dfd, cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* Ensure the link has hit disk */
|
||||
if (fsync (parent_dfd) != 0)
|
||||
{
|
||||
gs_set_error_from_errno (error, errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!gs_file_rename (tmppath, path, cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* And sync again for good measure */
|
||||
if (fsync (parent_dfd) != 0)
|
||||
{
|
||||
gs_set_error_from_errno (error, errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (parent_dfd != -1) (void) close (parent_dfd);
|
||||
return ret;
|
||||
}
|
||||
@@ -85,11 +85,6 @@ gboolean ot_gfile_ensure_unlinked (GFile *path,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_gfile_atomic_symlink_swap (GFile *path,
|
||||
const char *target,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_util_ensure_directory_and_fsync (GFile *dir,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
Reference in new issue
Block a user