/****************************************************************************** * 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 #include #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; }