/* * 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 #include #include #include #include #include #include #include "array.h" #include "block_allocator.h" #include "block_set.h" #include "debug.h" #include "transaction.h" static bool print_check_errors; /** * block_range_init_from_path * @range: range object to initialize. * @path: Block tree path. * * Helper function to initialize a block_range from a tree entry. */ static void block_range_init_from_path(struct block_range* range, struct block_tree_path* path) { if (!path->count) { block_range_clear(range); return; } range->start = block_tree_path_get_key(path); range->end = block_tree_path_get_data(path); assert(!range->end == !range->start); } /** * block_range_extend - Extend range if possible * @range: range object to initialize. * @add_range: Range to add * * Return: %true if @add_range was adjacent to @range, %false otherwise. */ static bool block_range_extend(struct block_range* range, struct block_range add_range) { pr_write("%" PRIu64 "-%" PRIu64 ", %" PRIu64 "-%" PRIu64 "\n", range->start, range->end - 1, add_range.start, add_range.end - 1); assert(!block_range_empty(add_range)); assert(!block_range_overlap(*range, add_range)); if (block_range_empty(*range)) { pr_write("failed, *range is empty\n"); return false; } if (add_range.end == range->start) { range->start = add_range.start; } else if (add_range.start == range->end) { range->end = add_range.end; } else { pr_write("failed\n"); return false; } pr_write("now %" PRIu64 "-%" PRIu64 "\n", range->start, range->end - 1); return true; } /** * block_range_shrink - Shrink range if possible * @range: range object to initialize. * @remove_range: Range to remove * * Return: %true if @remove_range was at either end of @range, %false otherwise. */ static bool block_range_shrink(struct block_range* range, struct block_range remove_range) { pr_write("%" PRIu64 "-%" PRIu64 ", %" PRIu64 "-%" PRIu64 "\n", range->start, range->end - 1, remove_range.start, remove_range.end - 1); assert(block_range_is_sub_range(*range, remove_range)); if (remove_range.start == range->start) { assert(!block_range_empty(*range)); range->start = remove_range.end; } else if (remove_range.end == range->end) { assert(!block_range_empty(*range)); range->end = remove_range.start; } else { pr_write("%s: failed\n", __func__); return false; } pr_write("now %" PRIu64 "-%" PRIu64 "\n", range->start, range->end - 1); return true; } /** * block_set_print_ranges - Print block-set range * @tr: Transaction object. * @range: Range to print. */ static void block_set_print_range(struct block_range range, int* split_line) { if (*split_line >= 8) { *split_line = 0; printf("\n"); } (*split_line)++; if (range.start == range.end - 1) { printf(" %3" PRIu64 " ", range.start); } else { printf(" %3" PRIu64 " - %3" PRIu64 "", range.start, range.end - 1); } (*split_line)++; } /** * block_set_print_ranges - Print block-set ranges * @tr: Transaction object. * @set: Block-set object. */ static void block_set_print_ranges(struct transaction* tr, struct block_set* set) { struct block_tree_path path; struct block_range range; int split_line = 0; printf("set:\n"); block_tree_walk(tr, &set->block_tree, 0, true, &path); block_range_init_from_path(&range, &path); while (!block_range_empty(range)) { block_set_print_range(range, &split_line); block_tree_path_next(&path); block_range_init_from_path(&range, &path); } printf("\n"); if (!block_range_empty(set->initial_range)) { printf("initial_range: "); split_line = 0; block_set_print_range(set->initial_range, &split_line); printf("\n"); } } /** * block_set_print - Print block-set ranges and the tree that stored them * @tr: Transaction object. * @set: Block-set object. */ void block_set_print(struct transaction* tr, struct block_set* set) { printf("block_tree:\n"); block_tree_print(tr, &set->block_tree); block_set_print_ranges(tr, set); } /** * block_set_check_ranges - Check that ranges in block-set are valid * @tr: Transaction object. * @set: Block-set object. * * Check that ranges in @set are valid, sorted, non-overlapping, non-adjacent * and only contain blocks that are valid for @tr. * * Return: %true if the ranges in @set are valid, %false otherwise. */ static bool block_set_check_ranges(struct transaction* tr, struct block_set* set) { struct block_tree_path path; data_block_t min = tr->fs->min_block_num; data_block_t max = tr->fs->dev->block_count; struct block_range range; block_tree_walk(tr, &set->block_tree, 0, true, &path); block_range_init_from_path(&range, &path); while (!block_range_empty(range)) { if (range.start < min) { pr_err("bad range start %" PRIu64 " < %" PRIu64 "\n", range.start, min); return false; } if (range.end <= range.start) { pr_err("bad range end %" PRIu64 " <= start %" PRIu64 "\n", range.end, range.start); return false; } if (range.end > max) { pr_err("bad range end %" PRIu64 " > max %" PRIu64 "\n", range.end, max); return false; } min = range.end + 1; block_tree_path_next(&path); block_range_init_from_path(&range, &path); } return true; } /** * block_set_check - Check block-set tree and ranges * @tr: Transaction object. * @set: Block-set object. * * Return: %true if the tree and ranges in @set are valid, %false otherwise. */ bool block_set_check(struct transaction* tr, struct block_set* set) { bool valid; valid = block_tree_check(tr, &set->block_tree); if (!valid) { return false; } valid = block_set_check_ranges(tr, set); if (!valid && print_check_errors) { printf("%s: invalid block_set:\n", __func__); block_set_print(tr, set); } return valid; } /** * block_set_find_next_block - find a block in or not in set * @tr: Transaction object. * @set: Block-set object. * @min_block: Block number to start search at. * @in_set: If %true look for a block in @set. If %false, look for a block * not in @set. * * Return: First block in or not in @set >= @min_block, or 0 if no match is * is found. */ data_block_t block_set_find_next_block(struct transaction* tr, struct block_set* set, data_block_t min_block, bool in_set) { struct block_tree_path path; struct block_range range; block_tree_walk(tr, &set->block_tree, min_block, true, &path); block_range_init_from_path(&range, &path); if (!block_range_empty(range) && range.end <= min_block) { block_tree_path_next(&path); block_range_init_from_path(&range, &path); } if (block_range_empty(range)) { range = set->initial_range; if (range.end <= min_block) { block_range_clear(&range); } } pr_read("set at %" PRIu64 ", %" PRIu64 " in_set %d, [%" PRIu64 "-%" PRIu64 "]\n", block_mac_to_block(tr, &set->block_tree.root), min_block, in_set, range.start, range.end - 1); /* The block tree walk should not find an empty range that is not 0, 0 */ assert(!block_range_empty(range) || !range.start); if (block_in_range(range, min_block) == in_set) { pr_read("%" PRIu64 " in_set %d, return min_block, %" PRIu64 "\n", min_block, in_set, min_block); return min_block; } else { if (!in_set) { assert(!block_range_empty(range)); pr_read("%" PRIu64 " in_set %d, return range.end, %" PRIu64 "\n", min_block, in_set, range.end); return range.end; } else { pr_read("%" PRIu64 " in_set %d, return range.start, %" PRIu64 "\n", min_block, in_set, range.start); return range.start; } } } /** * block_set_find_next_range - find a range in set * @tr: Transaction object. * @set: Block-set object. * @min_block: Block number to start search at. * * Return: First block range in @set >= @min_block, or and empty range if no * match is is found. If the matching range in @set starts before @min_block, * the returned range will start at @min_block, not at start of the range in * @set. */ struct block_range block_set_find_next_range(struct transaction* tr, struct block_set* set, data_block_t min_block) { struct block_range range; range.start = block_set_find_next_block(tr, set, min_block, true); if (range.start) { range.end = block_set_find_next_block(tr, set, range.start, false); } else { range.end = 0; } return range; } /** * block_set_block_in_set - Check if block is in set * @tr: Transaction object. * @set: Block-set object. * @block: Block number. * * Return: %true if @block is in @set, %false if @block is not on set/ */ bool block_set_block_in_set(struct transaction* tr, struct block_set* set, data_block_t block) { return block_set_find_next_block(tr, set, block, true) == block; } /** * block_set_range_in_set - Check if block range is in set * @tr: Transaction object. * @set: Block-set object. * @range: Block range to check. * * Return: %true if all of @range is in @set, %false if a block in @range is not * in @set. */ bool block_set_range_in_set(struct transaction* tr, struct block_set* set, struct block_range range) { return block_set_find_next_block(tr, set, range.start, true) == range.start && block_set_find_next_block(tr, set, range.start, false) >= range.end; } /** * block_set_range_in_set - Check if block range is not set * @tr: Transaction object. * @set: Block-set object. * @range: Block range to check. * * Return: %true if all of @range is not in @set, %false if a block in @range is * in @set. */ bool block_set_range_not_in_set(struct transaction* tr, struct block_set* set, struct block_range range) { data_block_t block = block_set_find_next_block(tr, set, range.start, true); return !block || block >= range.end; } /** * block_set_overlap - Check if block sets have any overlap * @tr: Transaction object. * @set_a: Block-set object. * @set_b: Block-set object. * * Return: %true a block is in both @set_s and @set_b, %false if no such block * exists. */ bool block_set_overlap(struct transaction* tr, struct block_set* set_a, struct block_set* set_b) { struct block_range range_a; struct block_range range_b = BLOCK_RANGE_INITIAL_VALUE(range_b); while (true) { range_a = block_set_find_next_range(tr, set_a, range_b.start); if (block_range_empty(range_a)) { return false; /* No overlap as there a no more ranges in set_a */ } if (block_in_range(range_b, range_a.start)) { return true; } /* range_a must start after range_b except first iteration */ assert(range_a.start >= range_b.start); range_b = block_set_find_next_range(tr, set_b, range_a.start); if (block_range_empty(range_b)) { return false; /* No overlap as there a no more ranges in set_b */ } if (block_in_range(range_a, range_b.start)) { return true; } assert(range_b.start > range_a.start); } } /** * block_set_add_range - Add a range to block set * @tr: Transaction object. * @set: Block-set object. * @range: Block range to add. * * Add @range to block set b+tree either by extending an existing range in the * tree, or by adding a new range to the tree. If an existing tree entry is * extended check if the next entry can be merged. */ void block_set_add_range(struct transaction* tr, struct block_set* set, struct block_range range) { struct block_range tree_range; struct block_range new_tree_range; bool extended; struct block_tree_path path; bool extend_left = false; bool merge; assert(!block_range_empty(range)); assert(block_range_empty(set->initial_range)); if (tr->failed) { pr_warn("transaction failed, ignore\n"); return; } pr_write("set %" PRIu64 ", add %" PRIu64 "-%" PRIu64 ", updating %d\n", block_mac_to_block(tr, &set->block_tree.root), range.start, range.end - 1, set->updating); assert(!set->updating); set->updating = true; full_assert(block_set_check(tr, set)); assert(block_set_range_not_in_set(tr, set, range)); block_tree_walk(tr, &set->block_tree, range.start - 1, true, &path); block_range_init_from_path(&tree_range, &path); if (!block_range_empty(tree_range) && tree_range.end < range.start) { block_tree_path_next(&path); block_range_init_from_path(&tree_range, &path); if (tree_range.start == range.end) { extend_left = true; } else { /* rewind */ block_tree_walk(tr, &set->block_tree, range.start - 1, true, &path); block_range_init_from_path(&tree_range, &path); } } if (tr->failed) { pr_warn("transaction failed, abort\n"); return; } assert(!tree_range.end || !block_range_overlap(tree_range, range)); new_tree_range = tree_range; extended = block_range_extend(&new_tree_range, range); if (!extended) { assert(!extend_left); /* TODO: use path for insert point */ block_tree_insert(tr, &set->block_tree, range.start, range.end); } else { block_tree_path_next(&path); if (tr->failed) { pr_warn("transaction failed, abort\n"); return; } merge = block_tree_path_get_key(&path) == new_tree_range.end; if (merge) { assert(block_tree_path_get_data(&path) > new_tree_range.end); new_tree_range.end = block_tree_path_get_data(&path); } /* TODO: use path? */ block_tree_update(tr, &set->block_tree, tree_range.start, tree_range.end, new_tree_range.start, new_tree_range.end); if (tr->failed) { pr_warn("transaction failed, abort\n"); return; } if (merge) { /* set has overlapping ranges at this point, TODO: check that set is * readable in this state */ block_tree_remove(tr, &set->block_tree, block_tree_path_get_key(&path), block_tree_path_get_data(&path)); } } if (tr->failed) { pr_warn("transaction failed, abort\n"); return; } assert(set->updating); set->updating = false; full_assert(block_set_check(tr, set)); pr_write("set %" PRIu64 ", add %" PRIu64 "-%" PRIu64 " done\n", block_mac_to_block(tr, &set->block_tree.root), range.start, range.end - 1); } /** * block_set_remove_range - Remove a range from block set * @tr: Transaction object. * @set: Block-set object. * @range: Block range to remove. * * Remove @range from block set b+tree either by shrinking an existing range * in the tree, or by splitting an existing range in the tree. */ void block_set_remove_range(struct transaction* tr, struct block_set* set, struct block_range range) { struct block_tree_path path; struct block_range tree_range; struct block_range new_tree_range; bool shrunk; assert(!block_range_empty(range)); assert(block_range_empty(set->initial_range)); if (tr->failed) { pr_warn("transaction failed, ignore\n"); return; } pr_write("set %" PRIu64 ", remove %" PRIu64 "-%" PRIu64 ", updating %d\n", block_mac_to_block(tr, &set->block_tree.root), range.start, range.end - 1, set->updating); assert(block_set_range_in_set(tr, set, range) || tr->failed); assert(!set->updating); set->updating = true; full_assert(block_set_check(tr, set)); block_tree_walk(tr, &set->block_tree, range.start, true, &path); block_range_init_from_path(&tree_range, &path); if (tr->failed) { pr_warn("transaction failed, abort\n"); return; } assert(path.count > 0); assert(block_range_is_sub_range(tree_range, range)); new_tree_range = tree_range; shrunk = block_range_shrink(&new_tree_range, range); if (!shrunk) { block_tree_insert(tr, &set->block_tree, range.end, tree_range.end); if (tr->failed) { pr_warn("transaction failed, abort\n"); return; } new_tree_range.end = range.start; } if (block_range_empty(new_tree_range)) { block_tree_remove(tr, &set->block_tree, tree_range.start, tree_range.end); } else { /* TODO: use path? */ block_tree_update(tr, &set->block_tree, tree_range.start, tree_range.end, new_tree_range.start, new_tree_range.end); } if (tr->failed) { pr_warn("transaction failed, abort\n"); return; } assert(set->updating); set->updating = false; full_assert(block_set_check(tr, set)); pr_write("set %" PRIu64 ", remove %" PRIu64 "-%" PRIu64 " done\n", block_mac_to_block(tr, &set->block_tree.root), range.start, range.end - 1); } /** * block_set_add_block - Add block to block set * @tr: Transaction object. * @set: Block-set object. * @block: Block to add. */ void block_set_add_block(struct transaction* tr, struct block_set* set, data_block_t block) { struct block_range range; block_range_init_single(&range, block); block_set_add_range(tr, set, range); } /** * block_set_remove_block - Remove block from block set * @tr: Transaction object. * @set: Block-set object. * @block: Block to remove. */ void block_set_remove_block(struct transaction* tr, struct block_set* set, data_block_t block) { struct block_range range; block_range_init_single(&range, block); block_set_remove_range(tr, set, range); } /** * block_set_init - Initialize block set * @fs: File system state object. * @set: Block-set object. * * Clear block set, get block_num_size and mac_size from @fs and intialize * tree. */ void block_set_init(struct fs* fs, struct block_set* set) { assert(fs); assert(fs->dev); memset(set, 0, sizeof(*set)); block_tree_init(&set->block_tree, fs->dev->block_size, fs->block_num_size, fs->block_num_size + fs->mac_size, fs->block_num_size); } /** * block_set_add_initial_range - Add a range to an empty block set without * updating tree * @set: Block-set object. * @range: Block range to add. * */ void block_set_add_initial_range(struct block_set* set, struct block_range range) { assert(block_range_empty(set->initial_range)); set->initial_range = range; } /** * block_set_copy - Initialize a writable copy of a copy-on-write block set * @tr: Transaction object. * @dest: New writable block-set object. * @src: Read-only block-set object. */ void block_set_copy(struct transaction* tr, struct block_set* dest, const struct block_set* src) { assert(src->block_tree.copy_on_write); assert(!src->block_tree.allow_copy_on_write); block_set_init(tr->fs, dest); dest->block_tree.copy_on_write = true; dest->block_tree.allow_copy_on_write = true; dest->block_tree.root = src->block_tree.root; if (!block_mac_valid(tr, &dest->block_tree.root)) { /* special case for newly created empty fs */ assert(!block_range_empty(src->initial_range)); block_set_add_range(tr, dest, src->initial_range); } else { assert(block_range_empty(src->initial_range)); } } /** * block_set_copy_ro_fs - Initialize a read-only copy of a block set * @fs: File-system that contains @src. * @dest: Output read-only copy of block-set object. * @src: Read-only block-set object. */ void block_set_copy_ro_fs(struct fs* fs, struct block_set* dest, const struct block_set* src) { block_set_init(fs, dest); dest->block_tree.root = src->block_tree.root; if (!block_range_empty(src->initial_range)) { block_set_add_initial_range(dest, src->initial_range); } } /** * block_set_copy_ro - Initialize a read-only copy of a block set * @tr: Transaction object. * @dest: Output read-only copy of block-set object. * @src: Read-only block-set object. */ void block_set_copy_ro(struct transaction* tr, struct block_set* dest, const struct block_set* src) { block_set_copy_ro_fs(tr->fs, dest, src); }