Teach fsck about partial commits
An OSTree user noticed that `ostree fsck` would produce `missing object` errors in the case of interrupted pulls. It's possible to do e.g. `ostree pull --subpath=/usr/share/rpm ...`, which gets you just that portion of the commit. The use case for this was being able to see what changes would appear in an update before actually downloading all of it. (I think this would be better covered by static deltas, but those aren't final yet, and `--subpath` predates it) Further, `.commitpartial` is used as a successor to the `transaction` symlink for more precise knowledge in the case where a pull was interrupted that we needed to resume scanning. So it makes sense for `ostree fsck` to be aware of it.
This commit is contained in:
1 parent
279308b5b9
commit
5becd5ccad
10 files changed
+153
-20
No files matched your search
@@ -21,6 +21,8 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "ostree-cmdprivate.h"
|
||||
#include "ostree-repo-private.h"
|
||||
#include "ostree-core-private.h"
|
||||
#include "ostree-sysroot.h"
|
||||
#include "ostree-bootloader-grub2.h"
|
||||
|
||||
|
||||
@@ -1126,10 +1126,12 @@ scan_one_metadata_object_c (OtPullData *pull_data,
|
||||
/* For commits, check whether we only had a partial fetch */
|
||||
if (!do_scan && objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
{
|
||||
g_autofree char *commitpartial_path = _ostree_get_commitpartial_path (tmp_checksum);
|
||||
struct stat stbuf;
|
||||
OstreeRepoCommitState commitstate;
|
||||
|
||||
if (fstatat (pull_data->repo->repo_dir_fd, commitpartial_path, &stbuf, 0) == 0)
|
||||
if (!ostree_repo_load_commit (pull_data->repo, tmp_checksum, NULL, &commitstate, error))
|
||||
goto out;
|
||||
|
||||
if (commitstate & OSTREE_REPO_COMMIT_STATE_PARTIAL)
|
||||
{
|
||||
do_scan = TRUE;
|
||||
pull_data->commitpartial_exists = TRUE;
|
||||
@@ -2178,14 +2180,8 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
const char *checksum = value;
|
||||
g_autofree char *commitpartial_path = _ostree_get_commitpartial_path (checksum);
|
||||
|
||||
if (unlinkat (pull_data->repo->repo_dir_fd, commitpartial_path, 0) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!ot_ensure_unlinked_at (pull_data->repo->repo_dir_fd, commitpartial_path, 0))
|
||||
goto out;
|
||||
}
|
||||
g_hash_table_iter_init (&hash_iter, commits_to_fetch);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
@@ -2193,14 +2189,8 @@ ostree_repo_pull_with_options (OstreeRepo *self,
|
||||
const char *commit = value;
|
||||
g_autofree char *commitpartial_path = _ostree_get_commitpartial_path (commit);
|
||||
|
||||
if (unlinkat (pull_data->repo->repo_dir_fd, commitpartial_path, 0) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!ot_ensure_unlinked_at (pull_data->repo->repo_dir_fd, commitpartial_path, 0))
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2651,6 +2651,58 @@ ostree_repo_load_variant (OstreeRepo *self,
|
||||
out_variant, NULL, NULL, NULL, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_load_commit:
|
||||
* @self: Repo
|
||||
* @checksum: Commit checksum
|
||||
* @out_commit: (out) (allow-none): Commit
|
||||
* @out_state: (out) (allow-none): Commit state
|
||||
* @error: Error
|
||||
*
|
||||
* A version of ostree_repo_load_variant() specialized to commits,
|
||||
* capable of returning extended state information. Currently
|
||||
* the only extended state is %OSTREE_REPO_COMMIT_STATE_PARTIAL, which
|
||||
* means that only a sub-path of the commit is available.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_load_commit (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
GVariant **out_variant,
|
||||
OstreeRepoCommitState *out_state,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
if (out_variant)
|
||||
{
|
||||
if (!load_metadata_internal (self, OSTREE_OBJECT_TYPE_COMMIT, checksum, TRUE,
|
||||
out_variant, NULL, NULL, NULL, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (out_state)
|
||||
{
|
||||
g_autofree char *commitpartial_path = _ostree_get_commitpartial_path (checksum);
|
||||
struct stat stbuf;
|
||||
|
||||
*out_state = 0;
|
||||
|
||||
if (fstatat (self->repo_dir_fd, commitpartial_path, &stbuf, 0) == 0)
|
||||
{
|
||||
*out_state |= OSTREE_REPO_COMMIT_STATE_PARTIAL;
|
||||
}
|
||||
else if (errno != ENOENT)
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_list_objects:
|
||||
* @self: Repo
|
||||
|
||||
@@ -280,6 +280,16 @@ gboolean ostree_repo_load_variant_if_exists (OstreeRepo *self,
|
||||
GVariant **out_variant,
|
||||
GError **error);
|
||||
|
||||
typedef enum {
|
||||
OSTREE_REPO_COMMIT_STATE_PARTIAL = (1 << 0),
|
||||
} OstreeRepoCommitState;
|
||||
|
||||
gboolean ostree_repo_load_commit (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
GVariant **out_commit,
|
||||
OstreeRepoCommitState *out_state,
|
||||
GError **error);
|
||||
|
||||
gboolean ostree_repo_load_file (OstreeRepo *self,
|
||||
const char *checksum,
|
||||
GInputStream **out_input,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "ot-fs-utils.h"
|
||||
#include "libgsystem.h"
|
||||
#include "libglnx.h"
|
||||
#include <sys/xattr.h>
|
||||
#include <gio/gunixinputstream.h>
|
||||
|
||||
@@ -188,3 +189,20 @@ ot_openat_read_stream (int dfd,
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_ensure_unlinked_at (int dfd,
|
||||
const char *path,
|
||||
GError **error)
|
||||
{
|
||||
if (unlinkat (dfd, path, 0) != 0)
|
||||
{
|
||||
if (G_UNLIKELY (errno != ENOENT))
|
||||
{
|
||||
glnx_set_error_from_errno (error);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -57,4 +57,8 @@ gboolean ot_openat_read_stream (int dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_ensure_unlinked_at (int dfd,
|
||||
const char *path,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ot-main.h"
|
||||
#include "ot-builtins.h"
|
||||
#include "ostree.h"
|
||||
#include "ostree-cmdprivate.h"
|
||||
#include "otutil.h"
|
||||
|
||||
static gboolean opt_quiet;
|
||||
@@ -242,6 +243,7 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
|
||||
GHashTableIter hash_iter;
|
||||
gpointer key, value;
|
||||
gboolean found_corruption = FALSE;
|
||||
guint n_partial = 0;
|
||||
gs_unref_hashtable GHashTable *objects = NULL;
|
||||
gs_unref_hashtable GHashTable *commits = NULL;
|
||||
|
||||
@@ -267,11 +269,22 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
|
||||
GVariant *serialized_key = key;
|
||||
const char *checksum;
|
||||
OstreeObjectType objtype;
|
||||
OstreeRepoCommitState commitstate = 0;
|
||||
|
||||
ostree_object_name_deserialize (serialized_key, &checksum, &objtype);
|
||||
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
g_hash_table_insert (commits, g_variant_ref (serialized_key), serialized_key);
|
||||
{
|
||||
if (!ostree_repo_load_commit (repo, checksum, NULL, &commitstate, error))
|
||||
goto out;
|
||||
|
||||
if (commitstate & OSTREE_REPO_COMMIT_STATE_PARTIAL)
|
||||
{
|
||||
n_partial++;
|
||||
}
|
||||
else
|
||||
g_hash_table_insert (commits, g_variant_ref (serialized_key), serialized_key);
|
||||
}
|
||||
}
|
||||
|
||||
g_clear_pointer (&objects, (GDestroyNotify) g_hash_table_unref);
|
||||
@@ -284,6 +297,11 @@ ostree_builtin_fsck (int argc, char **argv, GCancellable *cancellable, GError **
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (n_partial > 0)
|
||||
{
|
||||
g_print ("%u partial commits not verified\n", n_partial);
|
||||
}
|
||||
|
||||
if (found_corruption)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
|
||||
Reference in new issue
Block a user