mirror of
https://github.com/ostreedev/ostree.git
synced 2025-03-19 22:50:35 +03:00
core: Make fsck verify from commit objects
Rather than verifying every object, traverse all commit objects we find. This is a better check, since primarily we care about being able to check out trees. In the new packfile regime, this ensures validity of packed data. It also means we aren't checking loose objects that we most likely don't care about.
This commit is contained in:
parent
d8173a5125
commit
56089abd43
@ -39,142 +39,9 @@ static GOptionEntry options[] = {
|
||||
|
||||
typedef struct {
|
||||
OstreeRepo *repo;
|
||||
guint n_loose_objects;
|
||||
guint n_pack_files;
|
||||
} OtFsckData;
|
||||
|
||||
static gboolean
|
||||
checksum_archived_file (OtFsckData *data,
|
||||
const char *exp_checksum,
|
||||
GFile *file,
|
||||
GChecksum **out_checksum,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GChecksum *ret_checksum = NULL;
|
||||
GVariant *archive_metadata = NULL;
|
||||
GVariant *xattrs = NULL;
|
||||
GFile *content_path = NULL;
|
||||
GInputStream *content_input = NULL;
|
||||
GFileInfo *file_info = NULL;
|
||||
char buf[8192];
|
||||
gsize bytes_read;
|
||||
guint32 mode;
|
||||
|
||||
if (!ostree_map_metadata_file (file, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META, &archive_metadata, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_parse_archived_file_meta (archive_metadata, &file_info, &xattrs, error))
|
||||
goto out;
|
||||
|
||||
content_path = ostree_repo_get_object_path (data->repo, exp_checksum, OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT);
|
||||
|
||||
if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR)
|
||||
{
|
||||
content_input = (GInputStream*)g_file_read (content_path, NULL, error);
|
||||
if (!content_input)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
|
||||
|
||||
mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
|
||||
if (S_ISREG (mode))
|
||||
{
|
||||
g_assert (content_input != NULL);
|
||||
do
|
||||
{
|
||||
if (!g_input_stream_read_all (content_input, buf, sizeof(buf), &bytes_read, NULL, error))
|
||||
goto out;
|
||||
g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
|
||||
}
|
||||
while (bytes_read > 0);
|
||||
}
|
||||
else if (S_ISLNK (mode))
|
||||
{
|
||||
const char *target = g_file_info_get_attribute_byte_string (file_info, "standard::symlink-target");
|
||||
g_checksum_update (ret_checksum, (guint8*) target, strlen (target));
|
||||
}
|
||||
else if (S_ISBLK (mode) || S_ISCHR (mode))
|
||||
{
|
||||
guint32 rdev = g_file_info_get_attribute_uint32 (file_info, "unix::rdev");
|
||||
guint32 rdev_be;
|
||||
|
||||
rdev_be = GUINT32_TO_BE (rdev);
|
||||
|
||||
g_checksum_update (ret_checksum, (guint8*)&rdev_be, 4);
|
||||
}
|
||||
|
||||
ostree_checksum_update_stat (ret_checksum,
|
||||
g_file_info_get_attribute_uint32 (file_info, "unix::uid"),
|
||||
g_file_info_get_attribute_uint32 (file_info, "unix::gid"),
|
||||
mode);
|
||||
if (xattrs)
|
||||
g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
|
||||
|
||||
ret = TRUE;
|
||||
ot_transfer_out_value (out_checksum, &ret_checksum);
|
||||
out:
|
||||
ot_clear_checksum (&ret_checksum);
|
||||
g_clear_object (&file_info);
|
||||
ot_clear_gvariant (&xattrs);
|
||||
ot_clear_gvariant (&archive_metadata);
|
||||
g_clear_object (&content_path);
|
||||
g_clear_object (&content_input);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fsck_loose_object (OtFsckData *data,
|
||||
const char *exp_checksum,
|
||||
OstreeObjectType objtype,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GFile *objf = NULL;
|
||||
GChecksum *real_checksum = NULL;
|
||||
|
||||
objf = ostree_repo_get_object_path (data->repo, exp_checksum, objtype);
|
||||
|
||||
if (objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META)
|
||||
{
|
||||
if (!g_str_has_suffix (ot_gfile_get_path_cached (objf), ".archive-meta"))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Invalid archive filename '%s'",
|
||||
ot_gfile_get_path_cached (objf));
|
||||
goto out;
|
||||
}
|
||||
if (!checksum_archived_file (data, exp_checksum, objf, &real_checksum, error))
|
||||
goto out;
|
||||
}
|
||||
else if (objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT)
|
||||
; /* Handled above */
|
||||
else
|
||||
{
|
||||
if (!ostree_checksum_file (objf, objtype, &real_checksum, NULL, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (real_checksum && strcmp (exp_checksum, g_checksum_get_string (real_checksum)) != 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"corrupted loose object '%s'; actual checksum: %s",
|
||||
ot_gfile_get_path_cached (objf), g_checksum_get_string (real_checksum));
|
||||
if (delete)
|
||||
(void) unlink (ot_gfile_get_path_cached (objf));
|
||||
goto out;
|
||||
}
|
||||
|
||||
data->n_loose_objects++;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
ot_clear_checksum (&real_checksum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fsck_pack_files (OtFsckData *data,
|
||||
GCancellable *cancellable,
|
||||
@ -274,6 +141,118 @@ fsck_pack_files (OtFsckData *data,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fsck_reachable_objects_from_commits (OtFsckData *data,
|
||||
GHashTable *commits,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GHashTable *reachable_objects = NULL;
|
||||
GHashTableIter hash_iter;
|
||||
gpointer key, value;
|
||||
GInputStream *input = NULL;
|
||||
GFileInfo *file_info = NULL;
|
||||
GVariant *xattrs = NULL;
|
||||
GVariant *metadata = NULL;
|
||||
GVariant *metadata_wrapped = NULL;
|
||||
GChecksum *computed_checksum = NULL;
|
||||
|
||||
reachable_objects = ostree_traverse_new_reachable ();
|
||||
|
||||
g_hash_table_iter_init (&hash_iter, commits);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
{
|
||||
GVariant *serialized_key = key;
|
||||
const char *checksum;
|
||||
OstreeObjectType objtype;
|
||||
|
||||
ostree_object_name_deserialize (serialized_key, &checksum, &objtype);
|
||||
|
||||
g_assert (objtype == OSTREE_OBJECT_TYPE_COMMIT);
|
||||
|
||||
if (!ostree_traverse_commit (data->repo, checksum, 0, reachable_objects,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
g_hash_table_iter_init (&hash_iter, reachable_objects);
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
{
|
||||
GVariant *serialized_key = key;
|
||||
const char *checksum;
|
||||
OstreeObjectType objtype;
|
||||
OstreeObjectType checksum_objtype;
|
||||
|
||||
ostree_object_name_deserialize (serialized_key, &checksum, &objtype);
|
||||
|
||||
g_clear_object (&input);
|
||||
g_clear_object (&file_info);
|
||||
ot_clear_gvariant (&xattrs);
|
||||
|
||||
checksum_objtype = objtype;
|
||||
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT
|
||||
|| objtype == OSTREE_OBJECT_TYPE_DIR_TREE
|
||||
|| objtype == OSTREE_OBJECT_TYPE_DIR_META)
|
||||
{
|
||||
ot_clear_gvariant (&metadata);
|
||||
if (!ostree_repo_load_variant (data->repo, objtype,
|
||||
checksum, &metadata, error))
|
||||
goto out;
|
||||
|
||||
ot_clear_gvariant (&metadata_wrapped);
|
||||
metadata_wrapped = ostree_wrap_metadata_variant (objtype, metadata);
|
||||
|
||||
input = g_memory_input_stream_new_from_data (g_variant_get_data (metadata_wrapped),
|
||||
g_variant_get_size (metadata_wrapped),
|
||||
NULL);
|
||||
}
|
||||
else if (objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_CONTENT)
|
||||
{
|
||||
/* Handled via ARCHIVED_FILE_META */
|
||||
continue;
|
||||
}
|
||||
else if (objtype == OSTREE_OBJECT_TYPE_RAW_FILE
|
||||
|| objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META)
|
||||
{
|
||||
if (!ostree_repo_load_file (data->repo, checksum, &input, &file_info,
|
||||
&xattrs, cancellable, error))
|
||||
goto out;
|
||||
checksum_objtype = OSTREE_OBJECT_TYPE_RAW_FILE; /* Override */
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
ot_clear_checksum (&computed_checksum);
|
||||
if (!ostree_checksum_file_from_input (file_info, xattrs, input,
|
||||
checksum_objtype, &computed_checksum,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (strcmp (checksum, g_checksum_get_string (computed_checksum)) != 0)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"corrupted object %s.%s; actual checksum: %s",
|
||||
checksum, ostree_object_type_to_string (objtype),
|
||||
g_checksum_get_string (computed_checksum));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
ot_clear_checksum (&computed_checksum);
|
||||
g_clear_object (&input);
|
||||
g_clear_object (&file_info);
|
||||
ot_clear_gvariant (&xattrs);
|
||||
ot_clear_gvariant (&metadata);
|
||||
ot_clear_gvariant (&metadata_wrapped);
|
||||
ot_clear_hashtable (&reachable_objects);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ostree_builtin_fsck (int argc, char **argv, GFile *repo_path, GError **error)
|
||||
@ -283,6 +262,7 @@ ostree_builtin_fsck (int argc, char **argv, GFile *repo_path, GError **error)
|
||||
gboolean ret = FALSE;
|
||||
OstreeRepo *repo = NULL;
|
||||
GHashTable *objects = NULL;
|
||||
GHashTable *commits = NULL;
|
||||
GCancellable *cancellable = NULL;
|
||||
GHashTableIter hash_iter;
|
||||
gpointer key, value;
|
||||
@ -300,44 +280,48 @@ ostree_builtin_fsck (int argc, char **argv, GFile *repo_path, GError **error)
|
||||
memset (&data, 0, sizeof (data));
|
||||
data.repo = repo;
|
||||
|
||||
g_print ("Enumerating objects...\n");
|
||||
|
||||
if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL,
|
||||
&objects, cancellable, error))
|
||||
goto out;
|
||||
|
||||
commits = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
|
||||
(GDestroyNotify)g_variant_unref, NULL);
|
||||
|
||||
g_hash_table_iter_init (&hash_iter, objects);
|
||||
|
||||
while (g_hash_table_iter_next (&hash_iter, &key, &value))
|
||||
{
|
||||
GVariant *serialized_key = key;
|
||||
GVariant *objdata = value;
|
||||
const char *checksum;
|
||||
OstreeObjectType objtype;
|
||||
gboolean is_loose;
|
||||
|
||||
ostree_object_name_deserialize (serialized_key, &checksum, &objtype);
|
||||
|
||||
g_variant_get_child (objdata, 0, "b", &is_loose);
|
||||
|
||||
if (is_loose)
|
||||
{
|
||||
if (!fsck_loose_object (&data, checksum, objtype, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
|
||||
g_hash_table_insert (commits, g_variant_ref (serialized_key), serialized_key);
|
||||
}
|
||||
|
||||
ot_clear_hashtable (&objects);
|
||||
|
||||
g_print ("Verifying content integrity of %u commit objects...\n",
|
||||
(guint)g_hash_table_size (commits));
|
||||
|
||||
if (!fsck_reachable_objects_from_commits (&data, commits, cancellable, error))
|
||||
goto out;
|
||||
|
||||
g_print ("Verifying structure of pack files...\n");
|
||||
|
||||
if (!fsck_pack_files (&data, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!quiet)
|
||||
g_print ("Loose Objects: %u\n", data.n_loose_objects);
|
||||
g_print ("Pack files: %u\n", data.n_pack_files);
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (context)
|
||||
g_option_context_free (context);
|
||||
g_clear_object (&repo);
|
||||
if (objects)
|
||||
g_hash_table_unref (objects);
|
||||
ot_clear_hashtable (&objects);
|
||||
ot_clear_hashtable (&commits);
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user