core: Use linkat() rather than link() in threaded checkouts

This seems to work around a likely Linux kernel VFS bug, where I
randomly see ENOENT on link() when we *definitely* called mkdir() at
an earlier point in time.
This commit is contained in:
Colin Walters 2012-10-15 10:44:20 -04:00
parent d6956bfc19
commit 1642310486

View File

@ -3196,6 +3196,7 @@ checkout_file_hardlink (OstreeRepo *self,
OstreeRepoCheckoutOverwriteMode overwrite_mode, OstreeRepoCheckoutOverwriteMode overwrite_mode,
GFile *source, GFile *source,
GFile *destination, GFile *destination,
int dirfd,
gboolean *out_was_supported, gboolean *out_was_supported,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
@ -3204,7 +3205,11 @@ checkout_file_hardlink (OstreeRepo *self,
gboolean ret_was_supported = FALSE; gboolean ret_was_supported = FALSE;
ot_lobj GFile *dir = NULL; ot_lobj GFile *dir = NULL;
if (link (ot_gfile_get_path_cached (source), ot_gfile_get_path_cached (destination)) != -1) if (dirfd != -1 &&
linkat (-1, ot_gfile_get_path_cached (source),
dirfd, ot_gfile_get_basename_cached (destination), 0) != -1)
ret_was_supported = TRUE;
else if (link (ot_gfile_get_path_cached (source), ot_gfile_get_path_cached (destination)) != -1)
ret_was_supported = TRUE; ret_was_supported = TRUE;
else if (errno == EMLINK || errno == EXDEV || errno == EPERM) else if (errno == EMLINK || errno == EXDEV || errno == EPERM)
{ {
@ -3318,6 +3323,7 @@ typedef struct {
OstreeRepoCheckoutMode mode; OstreeRepoCheckoutMode mode;
OstreeRepoCheckoutOverwriteMode overwrite_mode; OstreeRepoCheckoutOverwriteMode overwrite_mode;
GFile *destination; GFile *destination;
int dirfd;
OstreeRepoFile *source; OstreeRepoFile *source;
GFileInfo *source_info; GFileInfo *source_info;
GCancellable *cancellable; GCancellable *cancellable;
@ -3447,7 +3453,7 @@ checkout_file_thread (GSimpleAsyncResult *result,
/* If we found one, try hardlinking */ /* If we found one, try hardlinking */
if (!checkout_file_hardlink (checkout_data->repo, checkout_data->mode, if (!checkout_file_hardlink (checkout_data->repo, checkout_data->mode,
checkout_data->overwrite_mode, loose_path, checkout_data->overwrite_mode, loose_path,
checkout_data->destination, checkout_data->destination, checkout_data->dirfd,
&hardlink_supported, cancellable, error)) &hardlink_supported, cancellable, error))
{ {
g_prefix_error (error, "Hardlinking loose object %s to %s: ", checksum, g_prefix_error (error, "Hardlinking loose object %s to %s: ", checksum,
@ -3487,6 +3493,7 @@ checkout_one_file_async (OstreeRepo *self,
OstreeRepoFile *source, OstreeRepoFile *source,
GFileInfo *source_info, GFileInfo *source_info,
GFile *destination, GFile *destination,
int dirfd,
GCancellable *cancellable, GCancellable *cancellable,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data) gpointer user_data)
@ -3498,6 +3505,7 @@ checkout_one_file_async (OstreeRepo *self,
checkout_data->mode = mode; checkout_data->mode = mode;
checkout_data->overwrite_mode = overwrite_mode; checkout_data->overwrite_mode = overwrite_mode;
checkout_data->destination = g_object_ref (destination); checkout_data->destination = g_object_ref (destination);
checkout_data->dirfd = dirfd;
checkout_data->source = g_object_ref (source); checkout_data->source = g_object_ref (source);
checkout_data->source_info = g_object_ref (source_info); checkout_data->source_info = g_object_ref (source_info);
checkout_data->cancellable = cancellable ? g_object_ref (cancellable) : NULL; checkout_data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
@ -3542,6 +3550,8 @@ typedef struct {
gboolean caught_error; gboolean caught_error;
GError *error; GError *error;
DIR *dir_handle;
guint pending_ops; guint pending_ops;
GMainLoop *loop; GMainLoop *loop;
GSimpleAsyncResult *result; GSimpleAsyncResult *result;
@ -3557,6 +3567,8 @@ checkout_tree_async_data_free (gpointer data)
g_clear_object (&checkout_data->source); g_clear_object (&checkout_data->source);
g_clear_object (&checkout_data->source_info); g_clear_object (&checkout_data->source_info);
g_clear_object (&checkout_data->cancellable); g_clear_object (&checkout_data->cancellable);
if (checkout_data->dir_handle)
(void) closedir (checkout_data->dir_handle);
g_free (checkout_data); g_free (checkout_data);
} }
@ -3667,8 +3679,8 @@ on_got_next_files (GObject *src,
checkout_one_file_async (data->repo, data->mode, checkout_one_file_async (data->repo, data->mode,
data->overwrite_mode, data->overwrite_mode,
(OstreeRepoFile*)src_child, file_info, (OstreeRepoFile*)src_child, file_info,
dest_path, data->cancellable, dest_path, dirfd(data->dir_handle),
on_one_file_checked_out, data->cancellable, on_one_file_checked_out,
data); data);
} }
data->pending_ops++; data->pending_ops++;
@ -3726,6 +3738,13 @@ ostree_repo_checkout_tree_async (OstreeRepo *self,
cancellable, error)) cancellable, error))
goto out; goto out;
checkout_data->dir_handle = opendir (ot_gfile_get_path_cached (checkout_data->destination));
if (!checkout_data->dir_handle)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
g_clear_pointer (&xattrs, (GDestroyNotify) g_variant_unref); g_clear_pointer (&xattrs, (GDestroyNotify) g_variant_unref);
dir_enum = g_file_enumerate_children ((GFile*)checkout_data->source, dir_enum = g_file_enumerate_children ((GFile*)checkout_data->source,