2021-11-16 18:36:02 -03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2021-03-31 17:16:24 -03:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2018 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <linux/buffer_head.h>
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
|
#include "apfs.h"
|
|
|
|
|
|
2023-12-30 00:13:08 -03:00
|
|
|
struct apfs_node *apfs_query_root(const struct apfs_query *query)
|
|
|
|
|
{
|
|
|
|
|
while (query->parent)
|
|
|
|
|
query = query->parent;
|
|
|
|
|
ASSERT(apfs_node_is_root(query->node));
|
|
|
|
|
return query->node;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-13 23:12:19 -03:00
|
|
|
static u64 apfs_catalog_base_oid(struct apfs_query *query)
|
|
|
|
|
{
|
|
|
|
|
struct apfs_query *root_query = NULL;
|
|
|
|
|
|
|
|
|
|
root_query = query;
|
|
|
|
|
while (root_query->parent)
|
|
|
|
|
root_query = root_query->parent;
|
|
|
|
|
|
|
|
|
|
return root_query->node->object.oid;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/**
|
|
|
|
|
* 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)
|
|
|
|
|
{
|
2023-01-13 23:12:19 -03:00
|
|
|
struct super_block *sb = query->node->object.sb;
|
2022-02-25 14:50:27 -03:00
|
|
|
char *raw = query->node->object.data;
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2023-01-13 23:12:19 -03:00
|
|
|
if (query->flags & APFS_QUERY_CAT && apfs_is_sealed(sb)) {
|
|
|
|
|
struct apfs_btn_index_node_val *index_val = NULL;
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2023-03-24 22:07:47 -03:00
|
|
|
if (query->len != sizeof(*index_val)) {
|
|
|
|
|
apfs_err(sb, "bad sealed index value length (%d)", query->len);
|
2023-01-13 23:12:19 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2023-01-13 23:12:19 -03:00
|
|
|
index_val = (struct apfs_btn_index_node_val *)(raw + query->off);
|
|
|
|
|
*child = le64_to_cpu(index_val->binv_child_oid) + apfs_catalog_base_oid(query);
|
|
|
|
|
} else {
|
2024-12-04 20:32:26 -03:00
|
|
|
if (query->len != 8) { /* The value on a nonleaf node is the child id */
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(sb, "bad index value length (%d)", query->len);
|
2023-01-13 23:12:19 -03:00
|
|
|
return -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2023-01-13 23:12:19 -03:00
|
|
|
*child = le64_to_cpup((__le64 *)(raw + query->off));
|
|
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-03 21:24:45 -03:00
|
|
|
/**
|
2022-10-12 21:42:15 -03:00
|
|
|
* apfs_omap_cache_lookup - Look for an oid in an omap's cache
|
|
|
|
|
* @omap: the object map
|
2021-11-03 21:24:45 -03:00
|
|
|
* @oid: object id to look up
|
|
|
|
|
* @bno: on return, the block number for the oid
|
|
|
|
|
*
|
2022-10-12 21:42:15 -03:00
|
|
|
* Returns 0 on success, or -1 if this mapping is not cached.
|
2021-11-03 21:24:45 -03:00
|
|
|
*/
|
2022-10-12 21:42:15 -03:00
|
|
|
static int apfs_omap_cache_lookup(struct apfs_omap *omap, u64 oid, u64 *bno)
|
2021-11-03 21:24:45 -03:00
|
|
|
{
|
2022-10-12 21:42:15 -03:00
|
|
|
struct apfs_omap_cache *cache = &omap->omap_cache;
|
2021-11-03 21:24:45 -03:00
|
|
|
struct apfs_omap_rec *record = NULL;
|
|
|
|
|
int slot;
|
|
|
|
|
int ret = -1;
|
|
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
if (cache->disabled)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2021-11-03 21:24:45 -03:00
|
|
|
/* Uninitialized cache records use OID 0, so check this just in case */
|
|
|
|
|
if (!oid)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
slot = oid & APFS_OMAP_CACHE_SLOT_MASK;
|
|
|
|
|
record = &cache->recs[slot];
|
|
|
|
|
|
|
|
|
|
spin_lock(&cache->lock);
|
|
|
|
|
if (record->oid == oid) {
|
|
|
|
|
*bno = record->bno;
|
|
|
|
|
ret = 0;
|
|
|
|
|
}
|
|
|
|
|
spin_unlock(&cache->lock);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-10-12 21:42:15 -03:00
|
|
|
* apfs_omap_cache_save - Save a record in an omap's cache
|
|
|
|
|
* @omap: the object map
|
2021-11-03 21:24:45 -03:00
|
|
|
* @oid: object id of the record
|
|
|
|
|
* @bno: block number for the oid
|
|
|
|
|
*/
|
2022-10-12 21:42:15 -03:00
|
|
|
static void apfs_omap_cache_save(struct apfs_omap *omap, u64 oid, u64 bno)
|
2021-11-03 21:24:45 -03:00
|
|
|
{
|
2022-10-12 21:42:15 -03:00
|
|
|
struct apfs_omap_cache *cache = &omap->omap_cache;
|
2021-11-03 21:24:45 -03:00
|
|
|
struct apfs_omap_rec *record = NULL;
|
|
|
|
|
int slot;
|
|
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
if (cache->disabled)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-11-03 21:24:45 -03:00
|
|
|
slot = oid & APFS_OMAP_CACHE_SLOT_MASK;
|
|
|
|
|
record = &cache->recs[slot];
|
|
|
|
|
|
|
|
|
|
spin_lock(&cache->lock);
|
|
|
|
|
record->oid = oid;
|
|
|
|
|
record->bno = bno;
|
|
|
|
|
spin_unlock(&cache->lock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-10-12 21:42:15 -03:00
|
|
|
* apfs_omap_cache_delete - Try to delete a record from an omap's cache
|
|
|
|
|
* @omap: the object map
|
2021-11-03 21:24:45 -03:00
|
|
|
* @oid: object id of the record
|
|
|
|
|
*/
|
2022-10-12 21:42:15 -03:00
|
|
|
static void apfs_omap_cache_delete(struct apfs_omap *omap, u64 oid)
|
2021-11-03 21:24:45 -03:00
|
|
|
{
|
2022-10-12 21:42:15 -03:00
|
|
|
struct apfs_omap_cache *cache = &omap->omap_cache;
|
2021-11-03 21:24:45 -03:00
|
|
|
struct apfs_omap_rec *record = NULL;
|
|
|
|
|
int slot;
|
|
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
if (cache->disabled)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-11-03 21:24:45 -03:00
|
|
|
slot = oid & APFS_OMAP_CACHE_SLOT_MASK;
|
|
|
|
|
record = &cache->recs[slot];
|
|
|
|
|
|
|
|
|
|
spin_lock(&cache->lock);
|
|
|
|
|
if (record->oid == oid) {
|
|
|
|
|
record->oid = 0;
|
|
|
|
|
record->bno = 0;
|
|
|
|
|
}
|
|
|
|
|
spin_unlock(&cache->lock);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-05 20:45:12 -03:00
|
|
|
/**
|
|
|
|
|
* apfs_mounted_xid - Returns the mounted xid for this superblock
|
|
|
|
|
* @sb: superblock structure
|
|
|
|
|
*
|
|
|
|
|
* This function is needed instead of APFS_NXI(@sb)->nx_xid in situations where
|
|
|
|
|
* we might be working with a snapshot. Snapshots are read-only and should
|
|
|
|
|
* mostly ignore xids, so this only appears to matter for omap lookups.
|
|
|
|
|
*/
|
|
|
|
|
static inline u64 apfs_mounted_xid(struct super_block *sb)
|
|
|
|
|
{
|
|
|
|
|
struct apfs_sb_info *sbi = APFS_SB(sb);
|
|
|
|
|
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
|
|
|
|
|
|
|
|
|
return sbi->s_snap_xid ? sbi->s_snap_xid : nxi->nx_xid;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
/**
|
|
|
|
|
* apfs_xid_in_snapshot - Check if an xid is part of a snapshot
|
|
|
|
|
* @omap: the object map
|
|
|
|
|
* @xid: the xid to check
|
|
|
|
|
*/
|
|
|
|
|
static inline bool apfs_xid_in_snapshot(struct apfs_omap *omap, u64 xid)
|
|
|
|
|
{
|
|
|
|
|
return xid <= omap->omap_latest_snap;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/**
|
2023-12-07 21:22:32 -03:00
|
|
|
* apfs_omap_lookup_block_with_xid - Find bno of a virtual object from oid/xid
|
2021-03-31 17:16:24 -03:00
|
|
|
* @sb: filesystem superblock
|
2022-10-12 21:42:15 -03:00
|
|
|
* @omap: object map to be searched
|
2021-03-31 17:16:24 -03:00
|
|
|
* @id: id of the node
|
2023-12-07 21:22:32 -03:00
|
|
|
* @xid: transaction id
|
2021-03-31 17:16:24 -03:00
|
|
|
* @block: on return, the found block number
|
|
|
|
|
* @write: get write access to the object?
|
|
|
|
|
*
|
2023-12-07 21:22:32 -03:00
|
|
|
* Searches @omap for the most recent matching object with a transaction id
|
|
|
|
|
* below @xid. Returns 0 on success or a negative error code in case of failure.
|
2021-03-31 17:16:24 -03:00
|
|
|
*/
|
2023-12-07 21:22:32 -03:00
|
|
|
static int apfs_omap_lookup_block_with_xid(struct super_block *sb, struct apfs_omap *omap, u64 id, u64 xid, u64 *block, bool write)
|
2021-03-31 17:16:24 -03:00
|
|
|
{
|
2021-04-01 22:45:25 -03:00
|
|
|
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
2021-03-31 17:16:24 -03:00
|
|
|
struct apfs_query *query;
|
2022-09-30 17:40:33 -03:00
|
|
|
struct apfs_omap_map map = {0};
|
2021-03-31 17:16:24 -03:00
|
|
|
int ret = 0;
|
|
|
|
|
|
2021-11-03 21:24:45 -03:00
|
|
|
if (!write) {
|
2022-10-12 21:42:15 -03:00
|
|
|
if (!apfs_omap_cache_lookup(omap, id, block))
|
2021-11-03 21:24:45 -03:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
query = apfs_alloc_query(omap->omap_root, NULL /* parent */);
|
2021-03-31 17:16:24 -03:00
|
|
|
if (!query)
|
|
|
|
|
return -ENOMEM;
|
2023-12-20 20:14:49 -03:00
|
|
|
apfs_init_omap_key(id, xid, &query->key);
|
2021-03-31 17:16:24 -03:00
|
|
|
query->flags |= APFS_QUERY_OMAP;
|
|
|
|
|
|
|
|
|
|
ret = apfs_btree_query(sb, &query);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (ret) {
|
|
|
|
|
if (ret != -ENODATA)
|
2023-12-07 21:22:32 -03:00
|
|
|
apfs_err(sb, "query failed for oid 0x%llx, xid 0x%llx", id, xid);
|
2021-03-31 17:16:24 -03:00
|
|
|
goto fail;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2022-09-30 17:40:33 -03:00
|
|
|
ret = apfs_omap_map_from_query(query, &map);
|
2021-03-31 17:16:24 -03:00
|
|
|
if (ret) {
|
|
|
|
|
apfs_alert(sb, "bad object map leaf block: 0x%llx",
|
|
|
|
|
query->node->object.block_nr);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
2022-09-30 17:40:33 -03:00
|
|
|
*block = map.bno;
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
if (write) {
|
|
|
|
|
struct apfs_omap_key key;
|
|
|
|
|
struct apfs_omap_val val;
|
|
|
|
|
struct buffer_head *new_bh;
|
2022-10-12 21:42:15 -03:00
|
|
|
bool preserve;
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
preserve = apfs_xid_in_snapshot(omap, map.xid);
|
|
|
|
|
|
|
|
|
|
new_bh = apfs_read_object_block(sb, *block, write, preserve);
|
2021-03-31 17:16:24 -03:00
|
|
|
if (IS_ERR(new_bh)) {
|
2023-12-07 21:22:32 -03:00
|
|
|
apfs_err(sb, "CoW failed for oid 0x%llx, xid 0x%llx", id, xid);
|
2021-03-31 17:16:24 -03:00
|
|
|
ret = PTR_ERR(new_bh);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key.ok_oid = cpu_to_le64(id);
|
2022-09-30 17:40:33 -03:00
|
|
|
key.ok_xid = cpu_to_le64(nxi->nx_xid);
|
|
|
|
|
val.ov_flags = cpu_to_le32(map.flags);
|
2021-03-31 17:16:24 -03:00
|
|
|
val.ov_size = cpu_to_le32(sb->s_blocksize);
|
|
|
|
|
val.ov_paddr = cpu_to_le64(new_bh->b_blocknr);
|
2022-09-30 17:40:33 -03:00
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
if (preserve)
|
2022-09-30 17:40:33 -03:00
|
|
|
ret = apfs_btree_insert(query, &key, sizeof(key), &val, sizeof(val));
|
|
|
|
|
else
|
|
|
|
|
ret = apfs_btree_replace(query, &key, sizeof(key), &val, sizeof(val));
|
2023-03-24 22:07:47 -03:00
|
|
|
if (ret)
|
2023-12-07 21:22:32 -03:00
|
|
|
apfs_err(sb, "CoW omap update failed (oid 0x%llx, xid 0x%llx)", id, xid);
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
*block = new_bh->b_blocknr;
|
|
|
|
|
brelse(new_bh);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
apfs_omap_cache_save(omap, id, *block);
|
2021-11-03 21:24:45 -03:00
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
fail:
|
2022-03-01 20:54:30 -03:00
|
|
|
apfs_free_query(query);
|
2021-03-31 17:16:24 -03:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-07 21:22:32 -03:00
|
|
|
/**
|
|
|
|
|
* apfs_omap_lookup_block - Find the block number of a b-tree node from its id
|
|
|
|
|
* @sb: filesystem superblock
|
|
|
|
|
* @omap: object map to be searched
|
|
|
|
|
* @id: id of the node
|
|
|
|
|
* @block: on return, the found block number
|
|
|
|
|
* @write: get write access to the object?
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success or a negative error code in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap, u64 id, u64 *block, bool write)
|
|
|
|
|
{
|
|
|
|
|
return apfs_omap_lookup_block_with_xid(sb, omap, id, apfs_mounted_xid(sb), block, write);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_omap_lookup_newest_block - Find newest bno for a virtual object's oid
|
|
|
|
|
* @sb: filesystem superblock
|
|
|
|
|
* @omap: object map to be searched
|
|
|
|
|
* @id: id of the object
|
|
|
|
|
* @block: on return, the found block number
|
|
|
|
|
* @write: get write access to the object?
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success or a negative error code in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
int apfs_omap_lookup_newest_block(struct super_block *sb, struct apfs_omap *omap, u64 id, u64 *block, bool write)
|
|
|
|
|
{
|
|
|
|
|
return apfs_omap_lookup_block_with_xid(sb, omap, id, -1, block, write);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/**
|
|
|
|
|
* apfs_create_omap_rec - Create a record in the volume's omap tree
|
|
|
|
|
* @sb: filesystem superblock
|
|
|
|
|
* @oid: object id
|
|
|
|
|
* @bno: block number
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success or a negative error code in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
int apfs_create_omap_rec(struct super_block *sb, u64 oid, u64 bno)
|
|
|
|
|
{
|
|
|
|
|
struct apfs_sb_info *sbi = APFS_SB(sb);
|
2021-04-01 22:45:25 -03:00
|
|
|
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
2022-10-12 21:42:15 -03:00
|
|
|
struct apfs_omap *omap = sbi->s_omap;
|
2021-03-31 17:16:24 -03:00
|
|
|
struct apfs_query *query;
|
|
|
|
|
struct apfs_omap_key raw_key;
|
|
|
|
|
struct apfs_omap_val raw_val;
|
|
|
|
|
int ret;
|
|
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
query = apfs_alloc_query(omap->omap_root, NULL /* parent */);
|
2021-03-31 17:16:24 -03:00
|
|
|
if (!query)
|
|
|
|
|
return -ENOMEM;
|
2023-12-20 20:14:49 -03:00
|
|
|
apfs_init_omap_key(oid, nxi->nx_xid, &query->key);
|
2021-03-31 17:16:24 -03:00
|
|
|
query->flags |= APFS_QUERY_OMAP;
|
|
|
|
|
|
|
|
|
|
ret = apfs_btree_query(sb, &query);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (ret && ret != -ENODATA) {
|
|
|
|
|
apfs_err(sb, "query failed for oid 0x%llx, bno 0x%llx", oid, bno);
|
2021-03-31 17:16:24 -03:00
|
|
|
goto fail;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
raw_key.ok_oid = cpu_to_le64(oid);
|
2021-04-01 22:45:25 -03:00
|
|
|
raw_key.ok_xid = cpu_to_le64(nxi->nx_xid);
|
2021-03-31 17:16:24 -03:00
|
|
|
raw_val.ov_flags = 0;
|
|
|
|
|
raw_val.ov_size = cpu_to_le32(sb->s_blocksize);
|
|
|
|
|
raw_val.ov_paddr = cpu_to_le64(bno);
|
|
|
|
|
|
|
|
|
|
ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key),
|
|
|
|
|
&raw_val, sizeof(raw_val));
|
2023-03-24 22:07:47 -03:00
|
|
|
if (ret) {
|
|
|
|
|
apfs_err(sb, "insertion failed for oid 0x%llx, bno 0x%llx", oid, bno);
|
2021-11-03 21:24:45 -03:00
|
|
|
goto fail;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-11-03 21:24:45 -03:00
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
apfs_omap_cache_save(omap, oid, bno);
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
fail:
|
2022-03-01 20:54:30 -03:00
|
|
|
apfs_free_query(query);
|
2021-03-31 17:16:24 -03:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_delete_omap_rec - Delete an existing record from the volume's omap tree
|
|
|
|
|
* @sb: filesystem superblock
|
|
|
|
|
* @oid: object id for the record
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success or a negative error code in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
int apfs_delete_omap_rec(struct super_block *sb, u64 oid)
|
|
|
|
|
{
|
|
|
|
|
struct apfs_sb_info *sbi = APFS_SB(sb);
|
2021-04-01 22:45:25 -03:00
|
|
|
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
2022-10-12 21:42:15 -03:00
|
|
|
struct apfs_omap *omap = sbi->s_omap;
|
2021-03-31 17:16:24 -03:00
|
|
|
struct apfs_query *query;
|
|
|
|
|
int ret;
|
|
|
|
|
|
2022-10-12 21:42:15 -03:00
|
|
|
query = apfs_alloc_query(omap->omap_root, NULL /* parent */);
|
2021-03-31 17:16:24 -03:00
|
|
|
if (!query)
|
|
|
|
|
return -ENOMEM;
|
2023-12-20 20:14:49 -03:00
|
|
|
apfs_init_omap_key(oid, nxi->nx_xid, &query->key);
|
2021-03-31 17:16:24 -03:00
|
|
|
query->flags |= APFS_QUERY_OMAP;
|
|
|
|
|
|
|
|
|
|
ret = apfs_btree_query(sb, &query);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (ret == -ENODATA) {
|
|
|
|
|
apfs_err(sb, "nonexistent record (oid 0x%llx)", oid);
|
2021-03-31 17:16:24 -03:00
|
|
|
ret = -EFSCORRUPTED;
|
2023-03-24 22:07:47 -03:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
if (ret) {
|
|
|
|
|
apfs_err(sb, "query failed (oid 0x%llx)", oid);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
ret = apfs_btree_remove(query);
|
|
|
|
|
if (ret) {
|
|
|
|
|
apfs_err(sb, "removal failed (oid 0x%llx)", oid);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
apfs_omap_cache_delete(omap, oid);
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2023-03-24 22:07:47 -03:00
|
|
|
fail:
|
2022-03-01 20:54:30 -03:00
|
|
|
apfs_free_query(query);
|
2021-03-31 17:16:24 -03:00
|
|
|
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;
|
|
|
|
|
|
2023-12-20 20:14:49 -03:00
|
|
|
query = kzalloc(sizeof(*query), GFP_KERNEL);
|
2021-03-31 17:16:24 -03:00
|
|
|
if (!query)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* To be released by free_query. */
|
|
|
|
|
query->node = node;
|
2023-12-20 20:14:49 -03:00
|
|
|
|
|
|
|
|
if (parent) {
|
|
|
|
|
query->key = parent->key;
|
|
|
|
|
query->flags = parent->flags & ~(APFS_QUERY_DONE | APFS_QUERY_NEXT);
|
|
|
|
|
query->parent = parent;
|
|
|
|
|
query->depth = parent->depth + 1;
|
|
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2024-03-08 16:25:41 -03:00
|
|
|
/*
|
|
|
|
|
* We start the search with the last record and go backwards, but
|
|
|
|
|
* some queries later use the PREV flag later to list them in order.
|
|
|
|
|
*/
|
|
|
|
|
if (query->flags & APFS_QUERY_PREV)
|
|
|
|
|
query->index = -1;
|
|
|
|
|
else
|
|
|
|
|
query->index = node->records;
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
return query;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_free_query - Free a query structure
|
2022-03-01 20:54:30 -03:00
|
|
|
* @query: query to free
|
2021-03-31 17:16:24 -03:00
|
|
|
*
|
|
|
|
|
* Also frees the ancestor queries, if they are kept.
|
|
|
|
|
*/
|
2022-03-01 20:54:30 -03:00
|
|
|
void apfs_free_query(struct apfs_query *query)
|
2021-03-31 17:16:24 -03:00
|
|
|
{
|
|
|
|
|
while (query) {
|
|
|
|
|
struct apfs_query *parent = query->parent;
|
|
|
|
|
|
2022-02-25 21:09:26 -03:00
|
|
|
/* The caller decides whether to free the root node */
|
|
|
|
|
if (query->depth != 0)
|
|
|
|
|
apfs_node_free(query->node);
|
2021-05-11 22:24:23 -03:00
|
|
|
kfree(query);
|
2021-03-31 17:16:24 -03:00
|
|
|
query = parent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-19 19:48:25 -03:00
|
|
|
/**
|
|
|
|
|
* apfs_query_set_before_first - Set the query to point before the first record
|
|
|
|
|
* @sb: superblock structure
|
|
|
|
|
* @query: the query to set
|
|
|
|
|
*
|
|
|
|
|
* Queries set in this way are used to insert a record before the first one.
|
|
|
|
|
* Only the leaf gets set to the -1 entry; queries for other levels must be set
|
|
|
|
|
* to 0, since the first entry in each index node will need to be modified.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success, or a negative error code in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
static int apfs_query_set_before_first(struct super_block *sb, struct apfs_query **query)
|
|
|
|
|
{
|
2022-01-10 23:34:58 -03:00
|
|
|
struct apfs_node *node = NULL;
|
|
|
|
|
struct apfs_query *parent = NULL;
|
2021-04-19 19:48:25 -03:00
|
|
|
u64 child_id;
|
|
|
|
|
u32 storage = apfs_query_storage(*query);
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
while ((*query)->depth < 12) {
|
|
|
|
|
if (apfs_node_is_leaf((*query)->node)) {
|
|
|
|
|
(*query)->index = -1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
apfs_node_query_first(*query);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now go a level deeper */
|
|
|
|
|
node = apfs_read_node(sb, child_id, storage, false /* write */);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (IS_ERR(node)) {
|
|
|
|
|
apfs_err(sb, "failed to read child 0x%llx of node 0x%llx", child_id, (*query)->node->object.oid);
|
2021-04-19 19:48:25 -03:00
|
|
|
return PTR_ERR(node);
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-04-19 19:48:25 -03:00
|
|
|
|
2022-01-10 23:34:58 -03:00
|
|
|
parent = *query;
|
|
|
|
|
*query = apfs_alloc_query(node, parent);
|
|
|
|
|
if (!*query) {
|
2022-02-25 21:09:26 -03:00
|
|
|
apfs_node_free(node);
|
2022-01-10 23:34:58 -03:00
|
|
|
*query = parent;
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
2022-02-25 21:09:26 -03:00
|
|
|
node = NULL;
|
2021-04-19 19:48:25 -03:00
|
|
|
}
|
|
|
|
|
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(sb, "btree is too high");
|
2021-04-19 19:48:25 -03:00
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/**
|
|
|
|
|
* 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_node *node;
|
|
|
|
|
struct apfs_query *parent;
|
|
|
|
|
u64 child_id;
|
|
|
|
|
u32 storage = apfs_query_storage(*query);
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(sb, "btree is too high");
|
2024-03-18 18:18:49 -03:00
|
|
|
err = -EFSCORRUPTED;
|
|
|
|
|
goto fail;
|
2021-03-31 17:16:24 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = apfs_node_query(sb, *query);
|
2021-04-19 19:48:25 -03:00
|
|
|
if (err == -ENODATA && !(*query)->parent && (*query)->index == -1) {
|
|
|
|
|
/*
|
|
|
|
|
* We may be trying to insert a record before all others: don't
|
|
|
|
|
* let the query give up at the root node.
|
|
|
|
|
*/
|
|
|
|
|
err = apfs_query_set_before_first(sb, query);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "failed to set before the first record");
|
2024-03-18 18:18:49 -03:00
|
|
|
goto fail;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2024-03-18 18:18:49 -03:00
|
|
|
err = -ENODATA;
|
|
|
|
|
goto fail;
|
2021-04-19 19:48:25 -03:00
|
|
|
} else if (err == -EAGAIN) {
|
2024-03-18 18:18:49 -03:00
|
|
|
if (!(*query)->parent) {
|
|
|
|
|
/* We are at the root of the tree */
|
|
|
|
|
err = -ENODATA;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
/* Move back up one level and continue the query */
|
|
|
|
|
parent = (*query)->parent;
|
|
|
|
|
(*query)->parent = NULL; /* Don't free the parent */
|
2022-03-01 20:54:30 -03:00
|
|
|
apfs_free_query(*query);
|
2021-03-31 17:16:24 -03:00
|
|
|
*query = parent;
|
|
|
|
|
goto next_node;
|
2021-04-19 19:48:25 -03:00
|
|
|
} else if (err) {
|
2024-03-18 18:18:49 -03:00
|
|
|
goto fail;
|
2021-04-19 19:48:25 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
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);
|
2024-03-18 18:18:49 -03:00
|
|
|
goto fail;
|
2021-03-31 17:16:24 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now go a level deeper and search the child */
|
|
|
|
|
node = apfs_read_node(sb, child_id, storage, false /* write */);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (IS_ERR(node)) {
|
|
|
|
|
apfs_err(sb, "failed to read node 0x%llx", child_id);
|
2024-03-18 18:18:49 -03:00
|
|
|
err = PTR_ERR(node);
|
|
|
|
|
goto fail;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
if (node->object.oid != child_id)
|
|
|
|
|
apfs_debug(sb, "corrupt b-tree");
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Remember the parent node and index in case the search needs
|
2021-05-11 22:24:23 -03:00
|
|
|
* to be continued later.
|
2021-03-31 17:16:24 -03:00
|
|
|
*/
|
2022-01-10 23:34:58 -03:00
|
|
|
parent = *query;
|
|
|
|
|
*query = apfs_alloc_query(node, parent);
|
|
|
|
|
if (!*query) {
|
2022-02-25 21:09:26 -03:00
|
|
|
apfs_node_free(node);
|
2022-01-10 23:34:58 -03:00
|
|
|
*query = parent;
|
2024-03-18 18:18:49 -03:00
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto fail;
|
2022-01-10 23:34:58 -03:00
|
|
|
}
|
2022-02-25 21:09:26 -03:00
|
|
|
node = NULL;
|
2021-03-31 17:16:24 -03:00
|
|
|
goto next_node;
|
2024-03-18 18:18:49 -03:00
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
/* Don't leave stale record info here or some callers will use it */
|
|
|
|
|
(*query)->key_len = (*query)->len = 0;
|
|
|
|
|
return err;
|
2021-03-31 17:16:24 -03:00
|
|
|
}
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
static int __apfs_btree_replace(struct apfs_query *query, void *key, int key_len, void *val, int val_len);
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/**
|
|
|
|
|
* apfs_query_join_transaction - Add the found node to the current transaction
|
|
|
|
|
* @query: query that found the node
|
|
|
|
|
*/
|
|
|
|
|
int apfs_query_join_transaction(struct apfs_query *query)
|
|
|
|
|
{
|
|
|
|
|
struct apfs_node *node = query->node;
|
|
|
|
|
struct super_block *sb = node->object.sb;
|
|
|
|
|
u64 oid = node->object.oid;
|
|
|
|
|
u32 storage = apfs_query_storage(query);
|
2024-02-22 20:28:20 -03:00
|
|
|
struct apfs_obj_phys *raw = NULL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Ephemeral objects are checkpoint data, and all of their xids get
|
|
|
|
|
* updated on commit. There is no real need to do it here as well, but
|
|
|
|
|
* it's better for consistency with the other object types.
|
|
|
|
|
*/
|
|
|
|
|
if (storage == APFS_OBJ_EPHEMERAL) {
|
|
|
|
|
ASSERT(node->object.ephemeral);
|
|
|
|
|
raw = (void *)node->object.data;
|
|
|
|
|
raw->o_xid = cpu_to_le64(APFS_NXI(sb)->nx_xid);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2022-02-25 14:50:27 -03:00
|
|
|
if (buffer_trans(node->object.o_bh)) /* Already in the transaction */
|
2021-03-31 17:16:24 -03:00
|
|
|
return 0;
|
2022-02-25 21:09:26 -03:00
|
|
|
/* Root nodes should join the transaction before the query is created */
|
|
|
|
|
ASSERT(!apfs_node_is_root(node));
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
node = apfs_read_node(sb, oid, storage, true /* write */);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (IS_ERR(node)) {
|
|
|
|
|
apfs_err(sb, "Cow failed for node 0x%llx", oid);
|
2021-03-31 17:16:24 -03:00
|
|
|
return PTR_ERR(node);
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2022-02-25 21:09:26 -03:00
|
|
|
apfs_node_free(query->node);
|
2021-03-31 17:16:24 -03:00
|
|
|
query->node = node;
|
|
|
|
|
|
|
|
|
|
if (storage == APFS_OBJ_PHYSICAL && query->parent) {
|
|
|
|
|
__le64 bno = cpu_to_le64(node->object.block_nr);
|
|
|
|
|
|
|
|
|
|
/* The parent node needs to report the new location */
|
2023-12-29 23:51:19 -03:00
|
|
|
return __apfs_btree_replace(query->parent, NULL /* key */, 0 /* key_len */, &bno, sizeof(bno));
|
2021-03-31 17:16:24 -03:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_btree_change_rec_count - Update the b-tree info before a record change
|
|
|
|
|
* @query: query used to insert/remove/replace the leaf record
|
|
|
|
|
* @change: change in the record count
|
|
|
|
|
* @key_len: length of the new leaf record key (0 if removed or unchanged)
|
|
|
|
|
* @val_len: length of the new leaf record value (0 if removed or unchanged)
|
|
|
|
|
*
|
|
|
|
|
* Don't call this function if @query->parent was reset to NULL, or if the same
|
|
|
|
|
* is true of any of its ancestor queries.
|
|
|
|
|
*/
|
|
|
|
|
static void apfs_btree_change_rec_count(struct apfs_query *query, int change,
|
|
|
|
|
int key_len, int val_len)
|
|
|
|
|
{
|
|
|
|
|
struct super_block *sb;
|
|
|
|
|
struct apfs_node *root;
|
|
|
|
|
struct apfs_btree_node_phys *root_raw;
|
|
|
|
|
struct apfs_btree_info *info;
|
|
|
|
|
|
|
|
|
|
if (change == -1)
|
|
|
|
|
ASSERT(!key_len && !val_len);
|
|
|
|
|
ASSERT(apfs_node_is_leaf(query->node));
|
|
|
|
|
|
2023-12-30 00:13:08 -03:00
|
|
|
root = apfs_query_root(query);
|
2021-03-31 17:16:24 -03:00
|
|
|
ASSERT(apfs_node_is_root(root));
|
|
|
|
|
|
|
|
|
|
sb = root->object.sb;
|
2022-02-25 14:50:27 -03:00
|
|
|
root_raw = (void *)root->object.data;
|
2021-03-31 17:16:24 -03:00
|
|
|
info = (void *)root_raw + sb->s_blocksize - sizeof(*info);
|
|
|
|
|
|
2021-04-01 22:45:25 -03:00
|
|
|
apfs_assert_in_transaction(sb, &root_raw->btn_o);
|
2021-03-31 17:16:24 -03:00
|
|
|
if (key_len > le32_to_cpu(info->bt_longest_key))
|
|
|
|
|
info->bt_longest_key = cpu_to_le32(key_len);
|
|
|
|
|
if (val_len > le32_to_cpu(info->bt_longest_val))
|
|
|
|
|
info->bt_longest_val = cpu_to_le32(val_len);
|
|
|
|
|
le64_add_cpu(&info->bt_key_count, change);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_btree_change_node_count - Change the node count for a b-tree
|
|
|
|
|
* @query: query used to remove/create the node
|
|
|
|
|
* @change: change in the node count
|
|
|
|
|
*
|
|
|
|
|
* Also changes the node count in the volume superblock. Don't call this
|
|
|
|
|
* function if @query->parent was reset to NULL, or if the same is true of
|
|
|
|
|
* any of its ancestor queries.
|
|
|
|
|
*/
|
|
|
|
|
void apfs_btree_change_node_count(struct apfs_query *query, int change)
|
|
|
|
|
{
|
|
|
|
|
struct super_block *sb;
|
|
|
|
|
struct apfs_node *root;
|
|
|
|
|
struct apfs_btree_node_phys *root_raw;
|
|
|
|
|
struct apfs_btree_info *info;
|
|
|
|
|
|
2023-12-30 00:13:08 -03:00
|
|
|
root = apfs_query_root(query);
|
2021-03-31 17:16:24 -03:00
|
|
|
ASSERT(apfs_node_is_root(root));
|
|
|
|
|
|
|
|
|
|
sb = root->object.sb;
|
2022-02-25 14:50:27 -03:00
|
|
|
root_raw = (void *)root->object.data;
|
2021-03-31 17:16:24 -03:00
|
|
|
info = (void *)root_raw + sb->s_blocksize - sizeof(*info);
|
|
|
|
|
|
2021-04-01 22:45:25 -03:00
|
|
|
apfs_assert_in_transaction(sb, &root_raw->btn_o);
|
2021-03-31 17:16:24 -03:00
|
|
|
le64_add_cpu(&info->bt_node_count, change);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-26 21:32:18 -03:00
|
|
|
/**
|
|
|
|
|
* apfs_query_refresh - Recreate a catalog query invalidated by node splits
|
2023-12-29 23:51:19 -03:00
|
|
|
* @old_query: query chain to refresh
|
|
|
|
|
* @root: root node of the query chain
|
|
|
|
|
* @nodata: is the query expected to find nothing?
|
2021-07-26 21:32:18 -03:00
|
|
|
*
|
|
|
|
|
* On success, @old_query is left pointing to the same leaf record, but with
|
|
|
|
|
* valid ancestor queries as well. Returns a negative error code in case of
|
|
|
|
|
* failure, or 0 on success.
|
|
|
|
|
*/
|
2023-12-29 23:51:19 -03:00
|
|
|
static int apfs_query_refresh(struct apfs_query *old_query, struct apfs_node *root, bool nodata)
|
2021-07-26 21:32:18 -03:00
|
|
|
{
|
2023-12-29 23:51:19 -03:00
|
|
|
struct super_block *sb = NULL;
|
|
|
|
|
struct apfs_query *new_query = NULL;
|
2021-07-26 21:32:18 -03:00
|
|
|
int err = 0;
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
sb = root->object.sb;
|
|
|
|
|
|
|
|
|
|
if (!apfs_node_is_leaf(old_query->node)) {
|
|
|
|
|
apfs_alert(sb, "attempting refresh of non-leaf query");
|
2021-07-26 21:32:18 -03:00
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
}
|
2023-12-29 23:51:19 -03:00
|
|
|
if (apfs_node_is_root(old_query->node)) {
|
|
|
|
|
apfs_alert(sb, "attempting refresh of root query");
|
2021-07-26 21:32:18 -03:00
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
new_query = apfs_alloc_query(root, NULL /* parent */);
|
2021-07-26 21:32:18 -03:00
|
|
|
if (!new_query)
|
|
|
|
|
return -ENOMEM;
|
2023-12-29 23:51:19 -03:00
|
|
|
new_query->key = old_query->key;
|
|
|
|
|
new_query->flags = old_query->flags & ~(APFS_QUERY_DONE | APFS_QUERY_NEXT);
|
2021-07-26 21:32:18 -03:00
|
|
|
|
|
|
|
|
err = apfs_btree_query(sb, &new_query);
|
2023-12-29 23:51:19 -03:00
|
|
|
if (!nodata && err == -ENODATA) {
|
|
|
|
|
apfs_err(sb, "record should exist");
|
|
|
|
|
err = -EFSCORRUPTED;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
if (err && err != -ENODATA) {
|
2023-03-24 22:07:47 -03:00
|
|
|
apfs_err(sb, "failed to rerun");
|
2021-07-26 21:32:18 -03:00
|
|
|
goto fail;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2023-12-29 23:51:19 -03:00
|
|
|
err = 0;
|
2021-07-26 21:32:18 -03:00
|
|
|
|
|
|
|
|
/* Replace the parent of the original query with the new valid one */
|
2022-03-01 20:54:30 -03:00
|
|
|
apfs_free_query(old_query->parent);
|
2021-07-26 21:32:18 -03:00
|
|
|
old_query->parent = new_query->parent;
|
|
|
|
|
new_query->parent = NULL;
|
|
|
|
|
|
2024-01-06 01:34:22 -03:00
|
|
|
/*
|
|
|
|
|
* The records may have moved around so update this too. TODO: rework
|
|
|
|
|
* the query struct so this stuff is not needed.
|
|
|
|
|
*/
|
|
|
|
|
ASSERT(old_query->node->object.oid == new_query->node->object.oid);
|
2023-12-29 23:51:19 -03:00
|
|
|
old_query->index = new_query->index;
|
|
|
|
|
old_query->key_off = new_query->key_off;
|
|
|
|
|
old_query->key_len = new_query->key_len;
|
|
|
|
|
old_query->off = new_query->off;
|
|
|
|
|
old_query->len = new_query->len;
|
|
|
|
|
old_query->depth = new_query->depth;
|
|
|
|
|
|
2021-07-26 21:32:18 -03:00
|
|
|
fail:
|
2022-03-01 20:54:30 -03:00
|
|
|
apfs_free_query(new_query);
|
2021-07-26 21:32:18 -03:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-28 00:22:18 -03:00
|
|
|
/**
|
2023-12-29 23:51:19 -03:00
|
|
|
* __apfs_btree_insert - Insert a new record into a b-tree (at any level)
|
|
|
|
|
* @query: query run to search for the record
|
|
|
|
|
* @key: on-disk record key
|
|
|
|
|
* @key_len: length of @key
|
|
|
|
|
* @val: on-disk record value (NULL for ghost records)
|
|
|
|
|
* @val_len: length of @val (0 for ghost records)
|
2021-07-28 00:22:18 -03:00
|
|
|
*
|
2023-12-29 23:51:19 -03:00
|
|
|
* The new record is placed right after the one found by @query. On success,
|
|
|
|
|
* returns 0 and sets @query to the new record; returns a negative error code
|
|
|
|
|
* in case of failure, which may be -EAGAIN if a split happened and the caller
|
|
|
|
|
* must retry.
|
2021-07-28 00:22:18 -03:00
|
|
|
*/
|
2023-12-29 23:51:19 -03:00
|
|
|
int __apfs_btree_insert(struct apfs_query *query, void *key, int key_len, void *val, int val_len)
|
2021-07-28 00:22:18 -03:00
|
|
|
{
|
2023-12-29 23:51:19 -03:00
|
|
|
struct apfs_node *node = query->node;
|
|
|
|
|
struct super_block *sb = node->object.sb;
|
|
|
|
|
struct apfs_btree_node_phys *node_raw;
|
|
|
|
|
int needed_room;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
|
|
|
|
|
err = apfs_query_join_transaction(query);
|
|
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "query join failed");
|
|
|
|
|
return err;
|
2021-07-28 00:22:18 -03:00
|
|
|
}
|
2023-12-29 23:51:19 -03:00
|
|
|
|
|
|
|
|
node = query->node;
|
|
|
|
|
node_raw = (void *)node->object.data;
|
|
|
|
|
apfs_assert_in_transaction(node->object.sb, &node_raw->btn_o);
|
|
|
|
|
|
|
|
|
|
needed_room = key_len + val_len;
|
|
|
|
|
if (!apfs_node_has_room(node, needed_room, false /* replace */)) {
|
|
|
|
|
if (node->records == 1) {
|
|
|
|
|
/* The new record just won't fit in the node */
|
|
|
|
|
err = apfs_create_single_rec_node(query, key, key_len, val, val_len);
|
|
|
|
|
if (err && err != -EAGAIN)
|
|
|
|
|
apfs_err(sb, "failed to create single-record node");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
err = apfs_node_split(query);
|
|
|
|
|
if (err && err != -EAGAIN) {
|
|
|
|
|
apfs_err(sb, "node split failed");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
|
|
|
|
|
if (query->parent && query->index == -1) {
|
|
|
|
|
/* We are about to insert a record before all others */
|
|
|
|
|
err = __apfs_btree_replace(query->parent, key, key_len, NULL /* val */, 0 /* val_len */);
|
|
|
|
|
if (err) {
|
|
|
|
|
if (err != -EAGAIN)
|
|
|
|
|
apfs_err(sb, "parent update failed");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
|
|
|
|
|
err = apfs_node_insert(query, key, key_len, val, val_len);
|
|
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "node record insertion failed");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2021-07-28 00:22:18 -03:00
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/**
|
2023-12-29 23:51:19 -03:00
|
|
|
* apfs_btree_insert - Insert a new record into a b-tree leaf
|
2021-03-31 17:16:24 -03:00
|
|
|
* @query: query run to search for the record
|
|
|
|
|
* @key: on-disk record key
|
|
|
|
|
* @key_len: length of @key
|
|
|
|
|
* @val: on-disk record value (NULL for ghost records)
|
|
|
|
|
* @val_len: length of @val (0 for ghost records)
|
|
|
|
|
*
|
|
|
|
|
* The new record is placed right after the one found by @query. On success,
|
|
|
|
|
* returns 0 and sets @query to the new record; returns a negative error code
|
|
|
|
|
* in case of failure.
|
|
|
|
|
*/
|
2023-12-29 23:51:19 -03:00
|
|
|
int apfs_btree_insert(struct apfs_query *query, void *key, int key_len, void *val, int val_len)
|
2021-03-31 17:16:24 -03:00
|
|
|
{
|
2023-12-29 23:51:19 -03:00
|
|
|
struct super_block *sb = NULL;
|
|
|
|
|
struct apfs_node *root = NULL, *leaf = NULL;
|
2021-03-31 17:16:24 -03:00
|
|
|
int err;
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
root = apfs_query_root(query);
|
|
|
|
|
ASSERT(apfs_node_is_root(root));
|
|
|
|
|
leaf = query->node;
|
|
|
|
|
ASSERT(apfs_node_is_leaf(leaf));
|
|
|
|
|
sb = root->object.sb;
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
while (true) {
|
|
|
|
|
err = __apfs_btree_insert(query, key, key_len, val, val_len);
|
|
|
|
|
if (err != -EAGAIN) {
|
|
|
|
|
if (err)
|
2021-07-26 21:32:18 -03:00
|
|
|
return err;
|
2023-12-29 23:51:19 -03:00
|
|
|
break;
|
2021-07-26 21:32:18 -03:00
|
|
|
}
|
2023-12-29 23:51:19 -03:00
|
|
|
err = apfs_query_refresh(query, root, true /* nodata */);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (err) {
|
2023-12-29 23:51:19 -03:00
|
|
|
apfs_err(sb, "query refresh failed");
|
2021-03-31 17:16:24 -03:00
|
|
|
return err;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-04-19 19:48:25 -03:00
|
|
|
}
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
apfs_btree_change_rec_count(query, 1 /* change */, key_len, val_len);
|
|
|
|
|
return 0;
|
2021-03-31 17:16:24 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-12-29 23:51:19 -03:00
|
|
|
* __apfs_btree_remove - Remove a record from a b-tree (at any level)
|
2021-03-31 17:16:24 -03:00
|
|
|
* @query: exact query that found the record
|
|
|
|
|
*
|
2023-12-29 23:51:19 -03:00
|
|
|
* Returns 0 on success, or a negative error code in case of failure, which may
|
|
|
|
|
* be -EAGAIN if a split happened and the caller must retry.
|
2021-03-31 17:16:24 -03:00
|
|
|
*/
|
2023-12-29 23:51:19 -03:00
|
|
|
static int __apfs_btree_remove(struct apfs_query *query)
|
2021-03-31 17:16:24 -03:00
|
|
|
{
|
|
|
|
|
struct apfs_node *node = query->node;
|
2023-03-24 22:07:47 -03:00
|
|
|
struct super_block *sb = node->object.sb;
|
2021-03-31 17:16:24 -03:00
|
|
|
struct apfs_btree_node_phys *node_raw;
|
|
|
|
|
int later_entries = node->records - query->index - 1;
|
|
|
|
|
int err;
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
apfs_assert_query_is_valid(query);
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
err = apfs_query_join_transaction(query);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "query join failed");
|
2021-03-31 17:16:24 -03:00
|
|
|
return err;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
|
|
|
|
|
node = query->node;
|
2022-02-25 14:50:27 -03:00
|
|
|
node_raw = (void *)query->node->object.data;
|
2021-04-09 20:18:10 -03:00
|
|
|
apfs_assert_in_transaction(node->object.sb, &node_raw->btn_o);
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
if (query->parent && node->records == 1) {
|
|
|
|
|
/* Just get rid of the node */
|
|
|
|
|
err = __apfs_btree_remove(query->parent);
|
|
|
|
|
if (err == -EAGAIN)
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "parent index removal failed");
|
|
|
|
|
return err;
|
2021-07-29 00:00:07 -03:00
|
|
|
}
|
2023-12-29 23:51:19 -03:00
|
|
|
apfs_btree_change_node_count(query, -1 /* change */);
|
|
|
|
|
err = apfs_delete_node(node, query->flags & APFS_QUERY_TREE_MASK);
|
|
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "node deletion failed");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2021-07-29 00:00:07 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/* The first key in a node must match the parent record's */
|
|
|
|
|
if (query->parent && query->index == 0) {
|
|
|
|
|
int first_key_len, first_key_off;
|
|
|
|
|
void *key;
|
|
|
|
|
|
|
|
|
|
first_key_len = apfs_node_locate_key(node, 1, &first_key_off);
|
|
|
|
|
if (!first_key_len)
|
|
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
key = (void *)node_raw + first_key_off;
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
err = __apfs_btree_replace(query->parent, key, first_key_len, NULL /* val */, 0 /* val_len */);
|
|
|
|
|
if (err == -EAGAIN)
|
|
|
|
|
return -EAGAIN;
|
2023-03-24 22:07:47 -03:00
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "parent update failed");
|
2021-03-31 17:16:24 -03:00
|
|
|
return err;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
}
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
/* Remove the entry from the table of contents */
|
|
|
|
|
if (apfs_node_has_fixed_kv_size(node)) {
|
|
|
|
|
struct apfs_kvoff *toc_entry;
|
|
|
|
|
|
|
|
|
|
toc_entry = (struct apfs_kvoff *)node_raw->btn_data +
|
|
|
|
|
query->index;
|
|
|
|
|
memmove(toc_entry, toc_entry + 1,
|
|
|
|
|
later_entries * sizeof(*toc_entry));
|
|
|
|
|
} else {
|
|
|
|
|
struct apfs_kvloc *toc_entry;
|
|
|
|
|
|
|
|
|
|
toc_entry = (struct apfs_kvloc *)node_raw->btn_data +
|
|
|
|
|
query->index;
|
|
|
|
|
memmove(toc_entry, toc_entry + 1,
|
|
|
|
|
later_entries * sizeof(*toc_entry));
|
|
|
|
|
}
|
2021-04-14 18:30:53 -03:00
|
|
|
|
|
|
|
|
apfs_node_free_range(node, query->key_off, query->key_len);
|
|
|
|
|
apfs_node_free_range(node, query->off, query->len);
|
|
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
--node->records;
|
2023-12-29 23:51:19 -03:00
|
|
|
if (node->records == 0) {
|
|
|
|
|
/* All descendants are gone, root is the whole tree */
|
|
|
|
|
node_raw->btn_level = 0;
|
|
|
|
|
node->flags |= APFS_BTNODE_LEAF;
|
|
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
apfs_update_node(node);
|
2021-04-14 18:30:53 -03:00
|
|
|
|
2021-03-31 17:16:24 -03:00
|
|
|
--query->index;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-12-29 23:51:19 -03:00
|
|
|
* apfs_btree_remove - Remove a record from a b-tree leaf
|
|
|
|
|
* @query: exact query that found the record
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success, or a negative error code in case of failure.
|
|
|
|
|
*/
|
|
|
|
|
int apfs_btree_remove(struct apfs_query *query)
|
|
|
|
|
{
|
|
|
|
|
struct super_block *sb = NULL;
|
|
|
|
|
struct apfs_node *root = NULL, *leaf = NULL;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
root = apfs_query_root(query);
|
|
|
|
|
ASSERT(apfs_node_is_root(root));
|
|
|
|
|
leaf = query->node;
|
|
|
|
|
ASSERT(apfs_node_is_leaf(leaf));
|
|
|
|
|
sb = root->object.sb;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
err = __apfs_btree_remove(query);
|
|
|
|
|
if (err != -EAGAIN) {
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
err = apfs_query_refresh(query, root, false /* nodata */);
|
|
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "query refresh failed");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
apfs_btree_change_rec_count(query, -1 /* change */, 0 /* key_len */, 0 /* val_len */);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* __apfs_btree_replace - Replace a record in a b-tree (at any level)
|
|
|
|
|
* @query: exact query that found the record
|
|
|
|
|
* @key: new on-disk record key (NULL if unchanged)
|
|
|
|
|
* @key_len: length of @key
|
|
|
|
|
* @val: new on-disk record value (NULL if unchanged)
|
|
|
|
|
* @val_len: length of @val
|
|
|
|
|
*
|
|
|
|
|
* It's important that the order of the records is not changed by the new @key.
|
|
|
|
|
* This function is not needed to replace an old value with a new one of the
|
|
|
|
|
* same length: it can just be overwritten in place.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success, and @query is left pointing to the same record; returns
|
|
|
|
|
* a negative error code in case of failure, which may be -EAGAIN if a split
|
|
|
|
|
* happened and the caller must retry.
|
|
|
|
|
*/
|
|
|
|
|
static int __apfs_btree_replace(struct apfs_query *query, void *key, int key_len, void *val, int val_len)
|
|
|
|
|
{
|
|
|
|
|
struct apfs_node *node = query->node;
|
|
|
|
|
struct super_block *sb = node->object.sb;
|
|
|
|
|
struct apfs_btree_node_phys *node_raw;
|
|
|
|
|
int needed_room;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
ASSERT(key || val);
|
|
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
|
|
|
|
|
err = apfs_query_join_transaction(query);
|
|
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "query join failed");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node = query->node;
|
|
|
|
|
node_raw = (void *)node->object.data;
|
|
|
|
|
apfs_assert_in_transaction(sb, &node_raw->btn_o);
|
|
|
|
|
|
|
|
|
|
needed_room = key_len + val_len;
|
|
|
|
|
/* We can reuse the space of the replaced key/value */
|
|
|
|
|
if (key)
|
|
|
|
|
needed_room -= query->key_len;
|
|
|
|
|
if (val)
|
|
|
|
|
needed_room -= query->len;
|
|
|
|
|
|
|
|
|
|
if (!apfs_node_has_room(node, needed_room, true /* replace */)) {
|
|
|
|
|
if (node->records == 1) {
|
|
|
|
|
apfs_alert(sb, "no room in empty node?");
|
|
|
|
|
return -EFSCORRUPTED;
|
|
|
|
|
}
|
|
|
|
|
err = apfs_node_split(query);
|
|
|
|
|
if (err && err != -EAGAIN) {
|
|
|
|
|
apfs_err(sb, "node split failed");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
|
|
|
|
|
/* The first key in a node must match the parent record's */
|
|
|
|
|
if (key && query->parent && query->index == 0) {
|
|
|
|
|
err = __apfs_btree_replace(query->parent, key, key_len, NULL /* val */, 0 /* val_len */);
|
|
|
|
|
if (err) {
|
|
|
|
|
if (err != -EAGAIN)
|
|
|
|
|
apfs_err(sb, "parent update failed");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
|
|
|
|
|
err = apfs_node_replace(query, key, key_len, val, val_len);
|
|
|
|
|
if (err) {
|
|
|
|
|
apfs_err(sb, "node record replacement failed");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_btree_replace - Replace a record in a b-tree leaf
|
2021-03-31 17:16:24 -03:00
|
|
|
* @query: exact query that found the record
|
|
|
|
|
* @key: new on-disk record key (NULL if unchanged)
|
|
|
|
|
* @key_len: length of @key
|
|
|
|
|
* @val: new on-disk record value (NULL if unchanged)
|
|
|
|
|
* @val_len: length of @val
|
|
|
|
|
*
|
|
|
|
|
* It's important that the order of the records is not changed by the new @key.
|
|
|
|
|
* This function is not needed to replace an old value with a new one of the
|
|
|
|
|
* same length: it can just be overwritten in place.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success, and @query is left pointing to the same record; returns
|
|
|
|
|
* a negative error code in case of failure.
|
|
|
|
|
*/
|
2023-12-29 23:51:19 -03:00
|
|
|
int apfs_btree_replace(struct apfs_query *query, void *key, int key_len, void *val, int val_len)
|
2021-03-31 17:16:24 -03:00
|
|
|
{
|
2023-12-29 23:51:19 -03:00
|
|
|
struct super_block *sb = NULL;
|
|
|
|
|
struct apfs_node *root = NULL, *leaf = NULL;
|
2021-03-31 17:16:24 -03:00
|
|
|
int err;
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
root = apfs_query_root(query);
|
|
|
|
|
ASSERT(apfs_node_is_root(root));
|
|
|
|
|
leaf = query->node;
|
|
|
|
|
ASSERT(apfs_node_is_leaf(leaf));
|
|
|
|
|
sb = root->object.sb;
|
2021-03-31 17:16:24 -03:00
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
while (true) {
|
|
|
|
|
err = __apfs_btree_replace(query, key, key_len, val, val_len);
|
|
|
|
|
if (err != -EAGAIN) {
|
2021-07-28 00:22:18 -03:00
|
|
|
if (err)
|
|
|
|
|
return err;
|
2023-12-29 23:51:19 -03:00
|
|
|
break;
|
2021-07-28 00:22:18 -03:00
|
|
|
}
|
2023-12-29 23:51:19 -03:00
|
|
|
err = apfs_query_refresh(query, root, false /* nodata */);
|
2023-03-24 22:07:47 -03:00
|
|
|
if (err) {
|
2023-12-29 23:51:19 -03:00
|
|
|
apfs_err(sb, "query refresh failed");
|
2021-03-31 17:16:24 -03:00
|
|
|
return err;
|
2023-03-24 22:07:47 -03:00
|
|
|
}
|
2021-03-31 17:16:24 -03:00
|
|
|
}
|
|
|
|
|
|
2023-12-29 23:51:19 -03:00
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
apfs_btree_change_rec_count(query, 0 /* change */, key_len, val_len);
|
2023-12-29 22:54:46 -03:00
|
|
|
return 0;
|
2021-03-31 17:16:24 -03:00
|
|
|
}
|
2024-03-08 16:25:41 -03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* apfs_query_direct_forward - Set a query to start listing records forwards
|
|
|
|
|
* @query: a successfully executed query
|
|
|
|
|
*
|
|
|
|
|
* Multiple queries list records backwards, but queries marked with this
|
|
|
|
|
* function after execution will go in the opposite direction.
|
|
|
|
|
*/
|
|
|
|
|
void apfs_query_direct_forward(struct apfs_query *query)
|
|
|
|
|
{
|
|
|
|
|
if (query->flags & APFS_QUERY_PREV)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
apfs_assert_query_is_valid(query);
|
|
|
|
|
ASSERT(apfs_node_is_leaf(query->node));
|
|
|
|
|
|
|
|
|
|
while (query) {
|
|
|
|
|
query->flags |= APFS_QUERY_PREV;
|
|
|
|
|
query = query->parent;
|
|
|
|
|
}
|
|
|
|
|
}
|