iSulad/src/image/oci/oci_images_store.c
Grooooot e61a5e351e sync openeuler
Signed-off-by: Grooooot <isula@huawei.com>
2020-01-20 10:47:21 +08:00

830 lines
21 KiB
C

/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
* iSulad licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
* http://license.coscl.org.cn/MulanPSL
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v1 for more details.
* Author: lifeng
* Create: 2018-11-08
* Description: provide containers store definition
******************************************************************************/
#include <stdlib.h>
#include <pthread.h>
#include "oci_images_store.h"
#include "log.h"
#include "utils.h"
#include "isulad_config.h"
#include "constants.h"
#include "oci_common_operators.h"
pthread_rwlock_t g_image_memory_rwlock;
typedef struct image_memory_store_t {
map_t *map; // map id oci_image_t
pthread_rwlock_t rwlock;
} image_memory_store;
typedef struct image_name_id_t {
map_t *map; // map name id
pthread_rwlock_t rwlock;
} image_name_id;
static image_memory_store *g_images_store = NULL;
static image_name_id *g_images_ids = NULL;
/* image_name_id_add */
static bool image_name_id_add(const char *name, const char *id)
{
bool ret = false;
if (pthread_rwlock_wrlock(&g_images_ids->rwlock) != 0) {
ERROR("lock name index failed");
return false;
}
ret = map_replace(g_images_ids->map, (void *)name, (void *)id);
if (pthread_rwlock_unlock(&g_images_ids->rwlock) != 0) {
ERROR("unlock name index failed");
return false;
}
return ret;
}
/* image_name_id_remove */
static bool image_name_id_remove(const char *name)
{
bool ret = false;
if (pthread_rwlock_wrlock(&g_images_ids->rwlock) != 0) {
ERROR("lock name index failed");
return false;
}
ret = map_remove(g_images_ids->map, (void *)name);
if (pthread_rwlock_unlock(&g_images_ids->rwlock) != 0) {
ERROR("unlock name index failed");
return false;
}
return ret;
}
static int remove_all_names(oci_image_t *image)
{
int ret = 0;
size_t i = 0;
for (i = 0; i < image->info->repo_tags_len; i++) {
if (!image_name_id_remove(image->info->repo_tags[i])) {
ERROR("Failed to remove image name %s", image->info->repo_tags[i]);
ret = -1;
}
}
return ret;
}
static int register_all_names(imagetool_image *image_info)
{
int ret = 0;
size_t i = 0;
for (i = 0; i < image_info->repo_tags_len; i++) {
if (!image_name_id_add(image_info->repo_tags[i], image_info->id)) {
size_t j;
for (j = 0; j < i; j++) {
(void)image_name_id_remove(image_info->repo_tags[j]);
}
ERROR("Failed to register all names of image %s", image_info->id);
ret = -1;
goto out;
}
}
out:
return ret;
}
/* image name id free */
static void image_name_id_free(image_name_id *images_ids)
{
if (images_ids == NULL) {
return;
}
map_free(images_ids->map);
images_ids->map = NULL;
pthread_rwlock_destroy(&(images_ids->rwlock));
free(images_ids);
}
/* image name id new */
static image_name_id *image_name_id_new(void)
{
int ret;
image_name_id *tmp = NULL;
tmp = util_common_calloc_s(sizeof(image_name_id));
if (tmp == NULL) {
ERROR("Out of memory");
return NULL;
}
ret = pthread_rwlock_init(&(tmp->rwlock), NULL);
if (ret != 0) {
ERROR("Failed to init name image name id rwlock");
free(tmp);
return NULL;
}
tmp->map = map_new(MAP_STR_STR, MAP_DEFAULT_CMP_FUNC, MAP_DEFAULT_FREE_FUNC);
if (tmp->map == NULL) {
ERROR("Out of memory");
goto error_out;
}
return tmp;
error_out:
image_name_id_free(tmp);
return NULL;
}
/* get image id by name */
char *image_id_get_by_name(const char *name)
{
char *id = NULL;
if (name == NULL) {
return id;
}
if (pthread_rwlock_rdlock(&g_images_ids->rwlock) != 0) {
ERROR("lock name index failed");
return id;
}
id = map_search(g_images_ids->map, (void *)name);
if (pthread_rwlock_unlock(&g_images_ids->rwlock) != 0) {
ERROR("unlock name index failed");
}
return id;
}
/* image_name_id init */
int image_name_id_init(void)
{
g_images_ids = image_name_id_new();
if (g_images_ids == NULL) {
return -1;
}
return 0;
}
/* memory store map kvfree */
static void oci_image_memory_store_map_kvfree(void *key, void *value)
{
free(key);
oci_image_unref((oci_image_t *)value);
}
/* memory store free */
static void oci_images_memory_store_free(image_memory_store *store)
{
if (store == NULL) {
return;
}
map_free(store->map);
store->map = NULL;
pthread_rwlock_destroy(&(store->rwlock));
free(store);
}
/* memory store new */
static image_memory_store *oci_images_memory_store_new(void)
{
int ret;
image_memory_store *store = NULL;
store = util_common_calloc_s(sizeof(image_memory_store));
if (store == NULL) {
ERROR("Out of memory");
return NULL;
}
ret = pthread_rwlock_init(&(store->rwlock), NULL);
if (ret != 0) {
ERROR("Failed to init memory store rwlock");
free(store);
return NULL;
}
store->map = map_new(MAP_STR_PTR, MAP_DEFAULT_CMP_FUNC, oci_image_memory_store_map_kvfree);
if (store->map == NULL) {
ERROR("Out of memory");
goto error_out;
}
return store;
error_out:
oci_images_memory_store_free(store);
return NULL;
}
/* oci image store add or update */
static bool oci_image_store_add_or_update(const char *id, imagetool_image *image_info)
{
bool ret = false;
oci_image_t *image = NULL;
if (pthread_rwlock_wrlock(&g_images_store->rwlock)) {
ERROR("lock memory store failed");
free_imagetool_image(image_info);
return false;
}
/* Replace only but not allocate a new one if exist.If allocate a
* new object,locker will be invalid and cannot lock image anymore. */
image = map_search(g_images_store->map, (void *)id);
if (image == NULL) {
image = oci_image_new(image_info);
if (image == NULL) {
free_imagetool_image(image_info);
ERROR("oci image new failed");
goto out;
}
ret = map_replace(g_images_store->map, (void *)id, (void *)image);
if (!ret) {
oci_image_free(image);
ERROR("oci image new failed");
goto out;
}
} else {
free_imagetool_image(image->info);
image->info = image_info;
ret = true;
}
out:
if (pthread_rwlock_unlock(&g_images_store->rwlock)) {
ERROR("unlock memory store failed");
return false;
}
return ret;
}
/* oci images store remove */
static bool oci_images_store_remove(const char *id)
{
bool ret = false;
if (pthread_rwlock_wrlock(&g_images_store->rwlock) != 0) {
ERROR("lock memory store failed");
return false;
}
ret = map_remove(g_images_store->map, (void *)id);
if (pthread_rwlock_unlock(&g_images_store->rwlock) != 0) {
ERROR("unlock memory store failed");
return false;
}
return ret;
}
static int register_new_oci_image(imagetool_image *image_info)
{
int ret = 0;
if (!oci_image_store_add_or_update(image_info->id, image_info)) {
ret = -1;
goto out;
}
ret = register_all_names(image_info);
if (ret != 0) {
oci_images_store_remove(image_info->id);
ret = -1;
goto out;
}
out:
return ret;
}
static int update_old_image_by_id(const char *id)
{
int ret = 0;
imagetool_image *image_info;
image_info = oci_get_image_info_by_name(id);
if (image_info == NULL) {
WARN("Failed to status old oci image %s, may be remeved", id);
ret = 0;
goto out;
}
ret = register_new_oci_image(image_info);
out:
return ret;
}
static void remove_graph_root()
{
int ret = 0;
char *graph_root = NULL;
graph_root = conf_get_graph_rootpath();
if (graph_root == NULL) {
ERROR("Failed to get image graph root path");
return;
}
ret = util_recursive_rmdir(graph_root, 0);
if (ret != 0) {
ERROR("Failed to delete image graph root directory %s: %s", graph_root, strerror(errno));
}
free(graph_root);
}
/* WARNING:This function may free all memory of *all_images if failed
* or change value of *all_images if success. */
static int try_list_oci_images(const char *check_file, imagetool_images_list **all_images)
{
int ret = 0;
im_list_request *im_request = NULL;
int retry_count = 0;
int max_retry = 3;
bool list_images_ok = false;
bool need_check = false;
if (util_file_exists(check_file) && conf_get_image_layer_check_flag()) {
INFO("OCI image checked flag %s exist, need to check image integrity", check_file);
need_check = true;
}
im_request = util_common_calloc_s(sizeof(im_list_request));
if (im_request == NULL) {
ERROR("Out of memory");
ret = -1;
goto out;
}
im_request->check = need_check;
do {
ret = oci_get_all_images(im_request, all_images);
if (ret != 0 || *all_images == NULL) {
list_images_ok = false;
if (retry_count < max_retry) {
remove_graph_root(); /* remove graph root */
retry_count++;
free_imagetool_images_list(*all_images);
*all_images = NULL;
}
} else {
list_images_ok = true;
}
} while (!list_images_ok && retry_count < max_retry);
if (!list_images_ok) {
ERROR("Failed to load all oci images and retry %d times", retry_count);
ret = -1;
goto out;
}
out:
free_im_list_request(im_request);
return ret;
}
int load_all_oci_images()
{
int ret = 0;
int fd = -1;
size_t i = 0;
char *check_file = NULL;
imagetool_images_list *all_images = NULL;
imagetool_image *tmp = NULL;
check_file = conf_get_graph_check_flag_file();
if (check_file == NULL) {
ERROR("Failed to get oci image checked flag");
ret = -1;
goto out;
}
ret = try_list_oci_images(check_file, &all_images);
if (ret < 0) {
goto out;
}
ret = util_build_dir(check_file);
if (ret) {
ERROR("Failed to create directory for checked flag file: %s", check_file);
ret = -1;
goto out;
}
fd = util_open(check_file, O_RDWR | O_CREAT, DEFAULT_SECURE_FILE_MODE);
if (fd < 0) {
ERROR("Failed to create checked file: %s", check_file);
ret = -1;
goto out;
}
for (i = 0; i < all_images->images_len; i++) {
tmp = all_images->images[i];
all_images->images[i] = NULL;
ret = register_new_oci_image(tmp);
if (ret != 0) {
ERROR("Failed to register oci image");
ret = -1;
goto out;
}
}
out:
if (fd >= 0) {
close(fd);
}
free(check_file);
free_imagetool_images_list(all_images);
return ret;
}
/* oci images store list */
int oci_images_store_list(oci_image_t ***out, size_t *size)
{
int ret = -1;
size_t i;
oci_image_t **images = NULL;
map_itor *itor = NULL;
if (out == NULL || size == NULL) {
ERROR("Invalid input arguments");
return -1;
}
if (pthread_rwlock_rdlock(&g_images_store->rwlock) != 0) {
ERROR("lock memory store failed");
return -1;
}
*size = map_size(g_images_store->map);
if (*size == 0) {
ret = 0;
goto unlock;
}
images = util_smart_calloc_s(sizeof(oci_image_t *), (*size));
if (images == NULL) {
ERROR("Out of memory");
goto unlock;
}
itor = map_itor_new(g_images_store->map);
if (itor == NULL) {
ERROR("Out of memory");
goto unlock;
}
for (i = 0; map_itor_valid(itor) && i < *size; map_itor_next(itor), i++) {
images[i] = map_itor_value(itor);
oci_image_refinc(images[i]);
}
ret = 0;
unlock:
if (pthread_rwlock_unlock(&g_images_store->rwlock)) {
ERROR("unlock memory store failed");
}
map_itor_free(itor);
if (ret != 0) {
free(images);
*size = 0;
images = NULL;
}
*out = images;
return ret;
}
/* oci images store size */
size_t oci_images_store_size(void)
{
size_t count = 0;
if (pthread_rwlock_rdlock(&g_images_store->rwlock) != 0) {
ERROR("lock memory store failed");
return count;
}
count = map_size(g_images_store->map);
if (pthread_rwlock_unlock(&g_images_store->rwlock) != 0) {
ERROR("unlock memory store failed");
return count;
}
return count;
}
/* oci images store init */
int oci_images_store_init(void)
{
if (pthread_rwlock_init(&g_image_memory_rwlock, NULL) != 0) {
ERROR("Failed to init image memory rwlock");
return -1;
}
g_images_store = oci_images_memory_store_new();
if (g_images_store == NULL) {
pthread_rwlock_destroy(&g_image_memory_rwlock);
return -1;
}
return 0;
}
/* oci images store get by image id */
oci_image_t *oci_images_store_get_by_id(const char *id)
{
oci_image_t *image = NULL;
if (id == NULL) {
return NULL;
}
if (pthread_rwlock_rdlock(&g_images_store->rwlock) != 0) {
ERROR("lock memory store failed");
return image;
}
image = map_search(g_images_store->map, (void *)id);
oci_image_refinc(image);
if (pthread_rwlock_unlock(&g_images_store->rwlock) != 0) {
ERROR("unlock memory store failed");
return image;
}
return image;
}
/* oci images store get image by image name */
oci_image_t *oci_images_store_get_by_name(const char *name)
{
char *id = NULL;
if (name == NULL) {
ERROR("No image name supplied");
return NULL;
}
id = image_id_get_by_name(name);
if (id == NULL) {
INFO("Could not find entity for %s", name);
return NULL;
}
return oci_images_store_get_by_id(id);
}
/* oci images store get image by prefix */
oci_image_t *oci_images_store_get_by_prefix(const char *prefix)
{
oci_image_t *image = NULL;
map_itor *itor = NULL;
bool ret = false;
char *id = NULL;
if (prefix == NULL) {
return NULL;
}
if (pthread_rwlock_rdlock(&g_images_store->rwlock) != 0) {
ERROR("lock memory store failed");
return NULL;
}
itor = map_itor_new(g_images_store->map);
if (itor == NULL) {
ERROR("Out of memory");
ret = false;
goto unlock;
}
for (; map_itor_valid(itor); map_itor_next(itor)) {
id = map_itor_key(itor);
if (id == NULL) {
ERROR("Out of memory");
ret = false;
goto unlock;
}
if (strncmp(id, prefix, strlen(prefix)) == 0) {
if (image != NULL) {
ERROR("Multiple IDs found with provided prefix: %s", prefix);
ret = false;
goto unlock;
} else {
image = map_itor_value(itor);
}
}
}
ret = true;
oci_image_refinc(image);
unlock:
if (pthread_rwlock_unlock(&g_images_store->rwlock) != 0) {
ERROR("unlock memory store failed");
}
map_itor_free(itor);
if (!ret) {
image = NULL;
}
return image;
}
// oci_images_store_get_nolock looks for a image using the provided information, which could be
// one of the following inputs from the caller:
// - A full image ID, which will exact match a image in daemon's list
// - A image name, which will only exact match via the oci_images_store_get_by_name() function
// - A partial image ID prefix (e.g. short ID) of any length that is
// unique enough to only return a single container object
// If none of these searches succeed, an error is returned
oci_image_t *oci_images_store_get_nolock(const char *id_or_name)
{
oci_image_t *image = NULL;
if (id_or_name == NULL) {
ERROR("No container name or ID supplied");
return NULL;
}
// A full image ID, which will exact match a container in daemon's list
image = oci_images_store_get_by_id(id_or_name);
if (image != NULL) {
return image;
}
// A image name, which will only exact match via the oci_images_store_get_by_name() function
image = oci_images_store_get_by_name(id_or_name);
if (image != NULL) {
return image;
}
// A partial container ID prefix
image = oci_images_store_get_by_prefix(id_or_name);
if (image != NULL) {
return image;
}
return NULL;
}
/* Always do lock when get images from images store. */
oci_image_t *oci_images_store_get(const char *id_or_name)
{
oci_image_t *img = NULL;
if (id_or_name == NULL) {
ERROR("No container name or ID supplied");
return NULL;
}
if (pthread_rwlock_wrlock(&g_image_memory_rwlock) != 0) {
ERROR("lock image memory failed");
return NULL;
}
img = oci_images_store_get_nolock(id_or_name);
if (pthread_rwlock_unlock(&g_image_memory_rwlock) != 0) {
ERROR("unlock memory store failed");
}
return img;
}
int register_new_oci_image_into_memory(const char *name)
{
int ret = 0;
imagetool_image *image_info = NULL;
oci_image_t *old_image = NULL;
if (name == NULL) {
ERROR("Empty image name");
return -1;
}
if (pthread_rwlock_wrlock(&g_image_memory_rwlock) != 0) {
ERROR("lock image memory failed");
return -1;
}
image_info = oci_get_image_info_by_name(name);
if (image_info == NULL) {
ERROR("Failed to get oci image %s informations", name);
ret = -1;
goto out;
}
old_image = oci_images_store_get_nolock(name);
if (old_image == NULL) {
ret = register_new_oci_image(image_info);
if (ret != 0) {
ERROR("Failed to register oci image %s", name);
ret = -1;
}
goto out;
}
if (strcmp(old_image->info->id, image_info->id) != 0) {
ret = register_new_oci_image(image_info);
if (ret != 0) {
ERROR("Failed to register oci image %s", name);
ret = -1;
goto out;
}
ret = update_old_image_by_id(old_image->info->id);
if (ret != 0) {
ERROR("Failed to update old oci image %s", old_image->info->id);
ret = -1;
goto out;
}
} else {
/* we have the same image already, just free the image_info memory */
free_imagetool_image(image_info);
}
out:
oci_image_unref(old_image);
if (pthread_rwlock_unlock(&g_image_memory_rwlock) != 0) {
ERROR("unlock memory store failed");
ret = -1;
}
return ret;
}
int remove_oci_image_from_memory(const char *name_or_id)
{
int ret = 0;
oci_image_t *image = NULL;
imagetool_image *image_info = NULL;
if (name_or_id == NULL) {
ERROR("No container name or ID supplied");
return -1;
}
if (pthread_rwlock_wrlock(&g_image_memory_rwlock) != 0) {
ERROR("lock image memory failed");
return -1;
}
image = oci_images_store_get_nolock(name_or_id);
if (image == NULL) {
INFO("No such image exist %s", name_or_id);
ret = 0;
goto free_out;
}
ret = remove_all_names(image);
if (ret != 0) {
ERROR("Failed to remove all names of image %s from name-id store", image->info->id);
ret = -1;
goto free_out;
}
image_info = oci_get_image_info_by_name(image->info->id);
if (image_info == NULL) {
WARN("Failed to status old oci image %s, may be remeved", image->info->id);
if (oci_images_store_remove(image->info->id) != true) {
ERROR("Failed to remove image %s from store", image->info->id);
ret = -1;
goto free_out;
}
ret = 0;
goto free_out;
}
ret = register_new_oci_image(image_info);
free_out:
oci_image_unref(image);
if (pthread_rwlock_unlock(&g_image_memory_rwlock) != 0) {
ERROR("unlock memory store failed");
ret = -1;
}
return ret;
}
int oci_image_store_init()
{
int ret = 0;
ret = oci_images_store_init();
if (ret != 0) {
ERROR("Failed to init oci images store");
goto out;
}
ret = image_name_id_init();
if (ret != 0) {
ERROR("Failed to init oci name id store");
goto out;
}
ret = load_all_oci_images();
out:
return ret;
}