diff --git a/src/libsysprof/sysprof-podman.c b/src/libsysprof/sysprof-podman.c index 2832f79d..880bc6cc 100644 --- a/src/libsysprof/sysprof-podman.c +++ b/src/libsysprof/sysprof-podman.c @@ -74,6 +74,7 @@ struct _SysprofPodman { JsonParser *containers_parser; JsonParser *layers_parser; + JsonParser *images_parser; }; void @@ -81,6 +82,7 @@ sysprof_podman_free (SysprofPodman *self) { g_clear_object (&self->containers_parser); g_clear_object (&self->layers_parser); + g_clear_object (&self->images_parser); g_slice_free (SysprofPodman, self); } @@ -116,6 +118,22 @@ load_layers (SysprofPodman *self) json_parser_load_from_file (self->layers_parser, path, NULL); } +static void +load_images (SysprofPodman *self) +{ + g_autofree char *path = NULL; + + g_assert (self != NULL); + + path = g_build_filename (g_get_user_data_dir (), + "containers", + "storage", + "overlay-images", + "images.json", + NULL); + json_parser_load_from_file (self->images_parser, path, NULL); +} + SysprofPodman * sysprof_podman_snapshot_current_user (void) { @@ -124,17 +142,57 @@ sysprof_podman_snapshot_current_user (void) self = g_slice_new0 (SysprofPodman); self->containers_parser = json_parser_new (); self->layers_parser = json_parser_new (); + self->images_parser = json_parser_new (); load_containers (self); load_layers (self); + load_images (self); return self; } +static const char * +find_image_layer (JsonParser *parser, + const char *image) +{ + JsonNode *root; + JsonArray *ar; + guint n_items; + + g_assert (JSON_IS_PARSER (parser)); + g_assert (image != NULL); + + if (!(root = json_parser_get_root (parser)) || + !JSON_NODE_HOLDS_ARRAY (root) || + !(ar = json_node_get_array (root))) + return NULL; + + n_items = json_array_get_length (ar); + + for (guint i = 0; i < n_items; i++) + { + JsonObject *item = json_array_get_object_element (ar, i); + const char *id; + const char *layer; + + if (item == NULL || + !json_object_has_member (item, "id") || + !json_object_has_member (item, "layer") || + !(id = json_object_get_string_member (item, "id")) || + strcmp (id, image) != 0 || + !(layer = json_object_get_string_member (item, "layer"))) + continue; + + return layer; + } + + return NULL; +} + static const char * find_parent_layer (JsonParser *parser, const char *layer, - GPtrArray *seen) + GHashTable *seen) { JsonNode *root; JsonArray *ar; @@ -165,12 +223,8 @@ find_parent_layer (JsonParser *parser, !(parent = json_object_get_string_member (item, "parent"))) continue; - /* Avoid cycles by checking if we've seen this parent */ - for (guint j = 0; j < seen->len; j++) - { - if (strcmp (parent, g_ptr_array_index (seen, j)) == 0) - return NULL; - } + if (g_hash_table_contains (seen, parent)) + return NULL; return parent; } @@ -178,14 +232,35 @@ find_parent_layer (JsonParser *parser, return NULL; } +static char * +get_layer_dir (const char *layer) +{ + /* We don't use XDG data dir because this might be in a container + * or flatpak environment that doesn't match. And generally, it's + * always .local. + */ + return g_build_filename (g_get_home_dir (), + ".local", + "share", + "containers", + "storage", + "overlay", + layer, + "diff", + NULL); +} + gchar ** sysprof_podman_get_layers (SysprofPodman *self, const char *container) { const char *layer = NULL; - GPtrArray *layers; + const char *image = NULL; + GHashTable *layers; JsonNode *root; JsonArray *ar; + const char **keys; + char **ret; guint n_items; g_return_val_if_fail (self != NULL, NULL); @@ -214,18 +289,30 @@ sysprof_podman_get_layers (SysprofPodman *self, continue; layer = item_layer; + image = json_object_get_string_member (item, "image"); break; } - /* Now we need to try to locate the layer and all of the parents - * within the layers.json so that we populate from the most recent - * layer to those beneath it. - */ - layers = g_ptr_array_new (); - g_ptr_array_add (layers, g_strdup (layer)); - while ((layer = find_parent_layer (self->layers_parser, layer, layers))) - g_ptr_array_add (layers, g_strdup (layer)); - g_ptr_array_add (layers, NULL); + layers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - return (char **)g_ptr_array_free (layers, FALSE); + /* Add all our known layers starting from current layer */ + do + g_hash_table_add (layers, get_layer_dir (layer)); + while ((layer = find_parent_layer (self->layers_parser, layer, layers))); + + /* If an image was specified, add its layer */ + if ((layer = find_image_layer (self->images_parser, image))) + { + do + g_hash_table_add (layers, get_layer_dir (layer)); + while ((layer = find_parent_layer (self->layers_parser, layer, layers))); + } + + keys = (const char **)g_hash_table_get_keys_as_array (layers, NULL); + ret = g_strdupv ((char **)keys); + + g_hash_table_unref (layers); + g_free (keys); + + return ret; }