mirror of
https://github.com/linux-apfs/linux-apfs-oot.git
synced 2026-08-18 02:19:52 -07:00
253 lines
6.5 KiB
C
253 lines
6.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|||
|
|
/*
|
||
|
|
* linux/fs/apfs/btree.c
|
||
|
|
*
|
||
|
|
* Copyright (C) 2018 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <linux/buffer_head.h>
|
||
|
|
#include <linux/slab.h>
|
||
|
|
#include "apfs.h"
|
||
|
|
#include "btree.h"
|
||
|
|
#include "key.h"
|
||
|
|
#include "message.h"
|
||
|
|
#include "node.h"
|
||
|
|
#include "super.h"
|
||
|
|
|
||
|
|
/**
|
||
|
|
* apfs_child_from_query - Read the child id found by a successful nonleaf query
|
||
|
|
* @query: the query that found the record
|
||
|
|
* @child: Return parameter. The child id found.
|
||
|
|
*
|
||
|
|
* Reads the child id in the nonleaf node record into @child and performs a
|
||
|
|
* basic sanity check as a protection against crafted filesystems. Returns 0
|
||
|
|
* on success or -EFSCORRUPTED otherwise.
|
||
|
|
*/
|
||
|
|
static int apfs_child_from_query(struct apfs_query *query, u64 *child)
|
||
|
|
{
|
||
|
|
char *raw = query->node->object.bh->b_data;
|
||
|
|
|
||
|
|
if (query->len != 8) /* The data on a nonleaf node is the child id */
|
||
|
|
return -EFSCORRUPTED;
|
||
|
|
|
||
|
|
*child = le64_to_cpup((__le64 *)(raw + query->off));
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* apfs_omap_lookup_block - Find the block number of a b-tree node from its id
|
||
|
|
* @sb: filesystem superblock
|
||
|
|
* @tbl: Root of the object map to be searched
|
||
|
|
* @id: id of the node
|
||
|
|
* @block: on return, the found block number
|
||
|
|
*
|
||
|
|
* Returns 0 on success or a negative error code in case of failure.
|
||
|
|
*/
|
||
|
|
int apfs_omap_lookup_block(struct super_block *sb, struct apfs_node *tbl,
|
||
|
|
u64 id, u64 *block)
|
||
|
|
{
|
||
|
|
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||
|
|
struct apfs_query *query;
|
||
|
|
struct apfs_key key;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
query = apfs_alloc_query(tbl, NULL /* parent */);
|
||
|
|
if (!query)
|
||
|
|
return -ENOMEM;
|
||
|
|
|
||
|
|
apfs_init_omap_key(id, sbi->s_xid, &key);
|
||
|
|
query->key = &key;
|
||
|
|
query->flags |= APFS_QUERY_OMAP;
|
||
|
|
|
||
|
|
ret = apfs_btree_query(sb, &query);
|
||
|
|
if (ret)
|
||
|
|
goto fail;
|
||
|
|
|
||
|
|
ret = apfs_bno_from_query(query, block);
|
||
|
|
if (ret)
|
||
|
|
apfs_alert(sb, "bad object map leaf block: 0x%llx",
|
||
|
|
query->node->object.block_nr);
|
||
|
|
|
||
|
|
fail:
|
||
|
|
apfs_free_query(sb, query);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* apfs_alloc_query - Allocates a query structure
|
||
|
|
* @node: node to be searched
|
||
|
|
* @parent: query for the parent node
|
||
|
|
*
|
||
|
|
* Callers other than apfs_btree_query() should set @parent to NULL, and @node
|
||
|
|
* to the root of the b-tree. They should also initialize most of the query
|
||
|
|
* fields themselves; when @parent is not NULL the query will inherit them.
|
||
|
|
*
|
||
|
|
* Returns the allocated query, or NULL in case of failure.
|
||
|
|
*/
|
||
|
|
struct apfs_query *apfs_alloc_query(struct apfs_node *node,
|
||
|
|
struct apfs_query *parent)
|
||
|
|
{
|
||
|
|
struct apfs_query *query;
|
||
|
|
|
||
|
|
query = kmalloc(sizeof(*query), GFP_KERNEL);
|
||
|
|
if (!query)
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
/* To be released by free_query. */
|
||
|
|
apfs_node_get(node);
|
||
|
|
query->node = node;
|
||
|
|
query->key = parent ? parent->key : NULL;
|
||
|
|
query->flags = parent ?
|
||
|
|
parent->flags & ~(APFS_QUERY_DONE | APFS_QUERY_NEXT) : 0;
|
||
|
|
query->parent = parent;
|
||
|
|
/* Start the search with the last record and go backwards */
|
||
|
|
query->index = node->records;
|
||
|
|
query->depth = parent ? parent->depth + 1 : 0;
|
||
|
|
|
||
|
|
return query;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* apfs_free_query - Free a query structure
|
||
|
|
* @sb: filesystem superblock
|
||
|
|
* @query: query to free
|
||
|
|
*
|
||
|
|
* Also frees the ancestor queries, if they are kept.
|
||
|
|
*/
|
||
|
|
void apfs_free_query(struct super_block *sb, struct apfs_query *query)
|
||
|
|
{
|
||
|
|
while (query) {
|
||
|
|
struct apfs_query *parent = query->parent;
|
||
|
|
|
||
|
|
apfs_node_put(query->node);
|
||
|
|
kfree(query);
|
||
|
|
query = parent;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* apfs_btree_query - Execute a query on a b-tree
|
||
|
|
* @sb: filesystem superblock
|
||
|
|
* @query: the query to execute
|
||
|
|
*
|
||
|
|
* Searches the b-tree starting at @query->index in @query->node, looking for
|
||
|
|
* the record corresponding to @query->key.
|
||
|
|
*
|
||
|
|
* Returns 0 in case of success and sets the @query->len, @query->off and
|
||
|
|
* @query->index fields to the results of the query. @query->node will now
|
||
|
|
* point to the leaf node holding the record.
|
||
|
|
*
|
||
|
|
* In case of failure returns an appropriate error code.
|
||
|
|
*/
|
||
|
|
int apfs_btree_query(struct super_block *sb, struct apfs_query **query)
|
||
|
|
{
|
||
|
|
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||
|
|
struct apfs_node *node;
|
||
|
|
struct apfs_query *parent;
|
||
|
|
u64 child_id, child_blk;
|
||
|
|
int err;
|
||
|
|
|
||
|
|
next_node:
|
||
|
|
if ((*query)->depth >= 12) {
|
||
|
|
/*
|
||
|
|
* We need a maximum depth for the tree so we can't loop
|
||
|
|
* forever if the filesystem is damaged. 12 should be more
|
||
|
|
* than enough to map every block.
|
||
|
|
*/
|
||
|
|
apfs_alert(sb, "b-tree is corrupted");
|
||
|
|
return -EFSCORRUPTED;
|
||
|
|
}
|
||
|
|
|
||
|
|
err = apfs_node_query(sb, *query);
|
||
|
|
if (err == -EAGAIN) {
|
||
|
|
if (!(*query)->parent) /* We are at the root of the tree */
|
||
|
|
return -ENODATA;
|
||
|
|
|
||
|
|
/* Move back up one level and continue the query */
|
||
|
|
parent = (*query)->parent;
|
||
|
|
(*query)->parent = NULL; /* Don't free the parent */
|
||
|
|
apfs_free_query(sb, *query);
|
||
|
|
*query = parent;
|
||
|
|
goto next_node;
|
||
|
|
}
|
||
|
|
if (err)
|
||
|
|
return err;
|
||
|
|
if (apfs_node_is_leaf((*query)->node)) /* All done */
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
err = apfs_child_from_query(*query, &child_id);
|
||
|
|
if (err) {
|
||
|
|
apfs_alert(sb, "bad index block: 0x%llx",
|
||
|
|
(*query)->node->object.block_nr);
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* The omap maps a node id into a block number. The nodes
|
||
|
|
* of the omap itself do not need this translation.
|
||
|
|
*/
|
||
|
|
if ((*query)->flags & APFS_QUERY_OMAP) {
|
||
|
|
child_blk = child_id;
|
||
|
|
} else {
|
||
|
|
/*
|
||
|
|
* we are always performing lookup from omap root. Might
|
||
|
|
* need improvement in the future.
|
||
|
|
*/
|
||
|
|
err = apfs_omap_lookup_block(sb, sbi->s_omap_root,
|
||
|
|
child_id, &child_blk);
|
||
|
|
if (err)
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Now go a level deeper and search the child */
|
||
|
|
node = apfs_read_node(sb, child_blk);
|
||
|
|
if (IS_ERR(node))
|
||
|
|
return PTR_ERR(node);
|
||
|
|
|
||
|
|
if (node->object.oid != child_id)
|
||
|
|
apfs_debug(sb, "corrupt b-tree");
|
||
|
|
|
||
|
|
if ((*query)->flags & APFS_QUERY_MULTIPLE) {
|
||
|
|
/*
|
||
|
|
* We are looking for multiple entries, so we must remember
|
||
|
|
* the parent node and index to continue the search later.
|
||
|
|
*/
|
||
|
|
*query = apfs_alloc_query(node, *query);
|
||
|
|
apfs_node_put(node);
|
||
|
|
} else {
|
||
|
|
/* Reuse the same query structure to search the child */
|
||
|
|
apfs_node_put((*query)->node);
|
||
|
|
(*query)->node = node;
|
||
|
|
(*query)->index = node->records;
|
||
|
|
(*query)->depth++;
|
||
|
|
}
|
||
|
|
goto next_node;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* apfs_omap_read_node - Find and read a node from a b-tree
|
||
|
|
* @id: id for the seeked node
|
||
|
|
*
|
||
|
|
* Returns NULL is case of failure, otherwise a pointer to the resulting
|
||
|
|
* apfs_node structure.
|
||
|
|
*/
|
||
|
|
struct apfs_node *apfs_omap_read_node(struct super_block *sb, u64 id)
|
||
|
|
{
|
||
|
|
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||
|
|
struct apfs_node *result;
|
||
|
|
u64 block;
|
||
|
|
int err;
|
||
|
|
|
||
|
|
err = apfs_omap_lookup_block(sb, sbi->s_omap_root, id, &block);
|
||
|
|
if (err)
|
||
|
|
return ERR_PTR(err);
|
||
|
|
|
||
|
|
result = apfs_read_node(sb, block);
|
||
|
|
if (IS_ERR(result))
|
||
|
|
return result;
|
||
|
|
|
||
|
|
if (result->object.oid != id)
|
||
|
|
apfs_debug(sb, "corrupt b-tree");
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|