| /* |
| * Copyright (C) 2015-2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <assert.h> |
| #include <inttypes.h> |
| |
| #include "block_allocator.h" |
| #include "block_map.h" |
| #include "block_tree.h" |
| #include "debug.h" |
| #include "transaction.h" |
| |
| static bool print_block_map; |
| |
| /** |
| * block_map_init - Initialize in-memory block map structute |
| * @tr: Transaction object. |
| * @block_map: Block map object. |
| * @root: Block mac of root tree. |
| * @block_size: Block size of device. |
| */ |
| |
| void block_map_init(const struct transaction* tr, |
| struct block_map* block_map, |
| const struct block_mac* root, |
| size_t block_size) { |
| size_t block_num_size = tr->fs->block_num_size; |
| size_t block_mac_size = block_num_size + tr->fs->mac_size; |
| memset(block_map, 0, sizeof(*block_map)); |
| block_tree_init(&block_map->tree, block_size, block_num_size, |
| block_mac_size, block_mac_size); |
| block_map->tree.copy_on_write = 1; |
| block_map->tree.allow_copy_on_write = 1; |
| block_map->tree.root = *root; |
| } |
| |
| /** |
| * block_map_get - Lookup a block |
| * @tr: Transaction object. |
| * @block_map: Block map object. |
| * @index: Index of block to get. |
| * @block_mac: Pointer to return block_mac in. |
| * |
| * Return: %true if a block_mac exists at @index, %false if not. When returning |
| * %true, @block_mac will be filled in. Otherwise, @block_mac is not touched. |
| */ |
| bool block_map_get(struct transaction* tr, |
| struct block_map* block_map, |
| data_block_t index, |
| struct block_mac* block_mac) { |
| struct block_tree_path path; |
| |
| index++; /* 0 is not a valid block tree key */ |
| |
| block_tree_walk(tr, &block_map->tree, index, false, &path); |
| if (block_tree_path_get_key(&path) != index) { |
| if (print_block_map) { |
| printf("%s: %" PRIu64 " not found (next key %" PRIu64 ")\n", |
| __func__, index, block_tree_path_get_key(&path)); |
| } |
| return false; |
| } |
| *block_mac = block_tree_path_get_data_block_mac(&path); |
| |
| return true; |
| } |
| |
| /** |
| * block_map_set - Store a block_mac |
| * @tr: Transaction object. |
| * @block_map: Block map object. |
| * @index: Index of block to set. |
| * @block_mac: block_mac to store, or %NULL to remove the block_mac at @index. |
| */ |
| void block_map_set(struct transaction* tr, |
| struct block_map* block_map, |
| data_block_t index, |
| const struct block_mac* block_mac) { |
| struct block_tree_path path; |
| |
| index++; /* 0 is not a valid block tree key */ |
| |
| if (tr->failed) { |
| pr_warn("transaction failed, ignore\n"); |
| return; |
| } |
| |
| block_tree_walk(tr, &block_map->tree, index, false, &path); |
| if (tr->failed) { |
| pr_warn("transaction failed, abort\n"); |
| return; |
| } |
| if (block_tree_path_get_key(&path) == index) { |
| if (print_block_map) { |
| printf("%s: block_map at %" PRIu64 |
| ": remove existing entry at %" PRIu64 "\n", |
| __func__, block_mac_to_block(tr, &block_map->tree.root), |
| index); |
| } |
| block_tree_remove(tr, &block_map->tree, index, |
| block_tree_path_get_data(&path)); |
| if (tr->failed) { |
| pr_warn("transaction failed, abort\n"); |
| return; |
| } |
| } |
| if (block_mac && block_mac_valid(tr, block_mac)) { |
| if (print_block_map) { |
| printf("%s: block_map at %" PRIu64 ": [%" PRIu64 "] = %" PRIu64 |
| "\n", |
| __func__, block_mac_to_block(tr, &block_map->tree.root), |
| index, block_mac_to_block(tr, block_mac)); |
| } |
| block_tree_insert(tr, &block_map->tree, index, |
| block_mac_to_block(tr, block_mac)); |
| /* TODO: insert mac */ |
| } |
| } |
| |
| /** |
| * block_map_put_dirty - Release a block stored in a block_map, and update mac |
| * @tr: Transaction object. |
| * @block_map: Block map object. |
| * @index: Index of block to update. |
| * @data: block cache entry. |
| * @data_ref: reference to @data. |
| */ |
| void block_map_put_dirty(struct transaction* tr, |
| struct block_map* block_map, |
| data_block_t index, |
| void* data, |
| struct obj_ref* data_ref) { |
| struct block_tree_path path; |
| |
| index++; /* 0 is not a valid block tree key */ |
| |
| block_tree_walk(tr, &block_map->tree, index, false, &path); |
| if (tr->failed) { |
| pr_warn("transaction failed\n"); |
| block_put_dirty_discard(data, data_ref); |
| return; |
| } |
| |
| if (print_block_map) { |
| printf("%s: %" PRIu64 " (found key %" PRIu64 ")\n", __func__, index, |
| block_tree_path_get_key(&path)); |
| } |
| |
| assert(block_tree_path_get_key(&path) == index); |
| block_tree_path_put_dirty(tr, &path, path.count, data, data_ref); |
| } |
| |
| /** |
| * block_map_truncate - Free blocks |
| * @tr: Transaction object. |
| * @block_map: Block map object. |
| * @index: Index to start freeing at. |
| * |
| * Remove and free all blocks starting at @index. |
| */ |
| void block_map_truncate(struct transaction* tr, |
| struct block_map* block_map, |
| data_block_t index) { |
| struct block_tree_path path; |
| data_block_t key; |
| data_block_t data; |
| data_block_t curr_index; |
| |
| curr_index = index + 1; /* 0 is not a valid block tree key */ |
| |
| while (true) { |
| block_tree_walk(tr, &block_map->tree, curr_index, false, &path); |
| if (tr->failed) { |
| pr_warn("transaction failed, abort\n"); |
| return; |
| } |
| key = block_tree_path_get_key(&path); |
| if (!key) { |
| break; |
| } |
| assert(key >= curr_index); |
| data = block_tree_path_get_data(&path); |
| if (!data) { |
| /* block_tree_walk returned an empty insert spot for curr_index */ |
| assert(key != curr_index); |
| curr_index = key; |
| continue; |
| } |
| assert(data); |
| block_tree_remove(tr, &block_map->tree, key, data); |
| if (tr->failed) { |
| pr_warn("transaction failed, abort\n"); |
| return; |
| } |
| block_discard_dirty_by_block(tr->fs->dev, data); |
| block_free(tr, data); |
| if (tr->failed) { |
| pr_warn("transaction failed, abort\n"); |
| return; |
| } |
| } |
| |
| /* Only a root leaf node should remain if truncating to 0 */ |
| assert(index || path.count == 1); |
| } |
| |
| /** |
| * block_map_check - Check block map for errors |
| * @tr: Transaction object. |
| * @block_map: Block map object. |
| * @max_index: Block map index at and after which no data is expected (i.e. |
| * maximum exclusive) |
| * |
| * Return: %false if an error was detected, %true otherwise. |
| */ |
| bool block_map_check(struct transaction* tr, |
| struct block_map* block_map, |
| data_block_t max_index) { |
| struct block_tree_path path; |
| data_block_t prev_index = 0; |
| data_block_t index = 1; |
| data_block_t min = tr->fs->min_block_num; |
| data_block_t max = tr->fs->dev->block_count; |
| data_block_t data; |
| |
| if (!block_tree_check(tr, &block_map->tree)) { |
| return false; |
| } |
| |
| max_index++; /* 0 is not a valid block tree key */ |
| |
| block_tree_walk(tr, &block_map->tree, index, false, &path); |
| index = block_tree_path_get_key(&path); |
| while (index) { |
| if (index <= prev_index) { |
| pr_err("block map indices are not sequential, %" PRIu64 |
| " is after %" PRIu64 "\n", |
| index, prev_index); |
| return false; |
| } |
| if (index >= max_index) { |
| pr_err("block map index %" PRIu64 |
| " is past (exclusive) block map max index %" PRIu64 "\n", |
| index, max_index); |
| return false; |
| } |
| data = block_tree_path_get_data(&path); |
| if (data < min) { |
| pr_err("block map data %" PRIu64 " < minimum block %" PRIu64 "\n", |
| index, min); |
| return false; |
| } |
| if (data >= max) { |
| pr_err("block map data %" PRIu64 " >= block count %" PRIu64 "\n", |
| index, max); |
| return false; |
| } |
| prev_index = index; |
| block_tree_path_next(&path); |
| index = block_tree_path_get_key(&path); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * block_map_free - Free blocks |
| * @tr: Transaction object. |
| * @block_map: Block map object. |
| * |
| * Free block_map and all blocks stored in it. |
| */ |
| void block_map_free(struct transaction* tr, struct block_map* block_map) { |
| data_block_t root_block; |
| |
| if (!block_mac_valid(tr, &block_map->tree.root)) { |
| return; |
| } |
| if (print_block_map) { |
| printf("%s: root %" PRIu64 "\n", __func__, |
| block_mac_to_block(tr, &block_map->tree.root)); |
| block_tree_print(tr, &block_map->tree); |
| } |
| |
| block_map_truncate(tr, block_map, 0); |
| if (tr->failed) { |
| pr_warn("transaction failed\n"); |
| return; |
| } |
| |
| root_block = block_mac_to_block(tr, &block_map->tree.root); |
| |
| block_discard_dirty_by_block(tr->fs->dev, root_block); |
| block_free(tr, root_block); |
| } |