1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
/*
* Copyright (C) 2022 Ernesto A. Fernández <ernesto@corellium.com>
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <apfs/raw.h>
#include <apfs/types.h>
#include "apfsck.h"
#include "htable.h"
#include "key.h"
#include "snapshot.h"
#include "super.h"
/**
* free_snap - Free a snapshot structure after some final checks
* @entry: the entry to free
*/
static void free_snap(struct htable_entry *entry)
{
struct snapshot *snap = (struct snapshot *)entry;
if (!snap->sn_name_seen || !snap->sn_meta_seen)
report("Snapshot", "missing metadata entry.");
if (!snap->sn_omap_seen)
report("Snapshot", "missing omap entry.");
free(entry);
}
/**
* free_snap_table - Free the snapshot hash table and all its entries
* @table: table to free
*/
void free_snap_table(struct htable_entry **table)
{
free_htable(table, free_snap);
}
/**
* get_snapshot - Find or create a snapshot structure in the snapshot hash table
* @xid: transaction id for the snapshot
*
* Returns the snapshot structure, after creating it if necessary.
*/
struct snapshot *get_snapshot(u64 xid)
{
struct htable_entry *entry;
entry = get_htable_entry(xid, sizeof(struct snapshot), vsb->v_snap_table);
return (struct snapshot *)entry;
}
/**
* parse_snap_name_record - Parse and check a snapshot name record value
* @key: pointer to the raw key
* @val: pointer to the raw value
* @len: length of the raw value
*
* Internal consistency of @key must be checked before calling this function.
*/
static void parse_snap_name_record(struct apfs_snap_name_key *key, struct apfs_snap_name_val *val, int len)
{
struct snapshot *snap = NULL;
if (len != sizeof(*val))
report("Snapshot name record", "wrong length for value.");
snap = get_snapshot(le64_to_cpu(val->snap_xid));
if (snap->sn_name_seen)
report("Snapshot tree", "snap with two name records.");
snap->sn_name_seen = true;
if (!snap->sn_meta_seen || !snap->sn_meta_name)
report("Snapshot tree", "missing a metadata record.");
if (strcmp((char *)key->name, snap->sn_meta_name) != 0)
report("Snapshot tree", "inconsistent names for snapshot.");
}
/**
* check_snapshot - Check a whole snapshot
* @xid: transaction for the snapshot
* @vol_bno: block number for the snapshot volume superblock
* @extentref_bno: block number for the snapshot's extentref tree
*/
static void check_snapshot(u64 xid, u64 vol_bno, u64 extentref_bno)
{
struct volume_superblock *latest_vsb = vsb;
u64 latest_xid = sb->s_xid;
vsb = NULL;
sb->s_xid = xid;
vsb = alloc_volume_super(true);
/* The list of extref trees is shared by all transactions */
vsb->v_snap_extrefs = latest_vsb->v_snap_extrefs;
vsb->v_snap_count = latest_vsb->v_snap_count;
vsb->v_raw = read_object(vol_bno, NULL, &vsb->v_obj);
read_volume_super(latest_vsb->v_index, vsb, &vsb->v_obj);
if (vsb->v_extref_oid != 0)
report("Snapshot volume superblock", "has extentref tree.");
vsb->v_extref_oid = extentref_bno;
if (vsb->v_omap_oid != 0)
report("Snapshot volume superblock", "has object map.");
vsb->v_omap = latest_vsb->v_omap;
vsb->v_omap_table = latest_vsb->v_omap_table;
omap_htable_clear_seen_for_snap(latest_vsb->v_omap_table);
vsb->v_snap_max_xid = latest_vsb->v_snap_max_xid;
if (vsb->v_snap_meta_oid != 0)
report("Snapshot volume superblock", "has snapshot tree.");
check_volume_super();
/* Go back to the latest transaction */
sb->s_xid = latest_xid;
latest_vsb->v_snap_extrefs = vsb->v_snap_extrefs;
latest_vsb->v_block_count += vsb->v_block_count;
vsb = latest_vsb; /* TODO: don't leak */
}
/**
* parse_snap_metadata_record - Parse and check a snapshot metadata record value
* @key: pointer to the raw key
* @val: pointer to the raw value
* @len: length of the raw value
*
* Internal consistency of @key must be checked before calling this function.
*/
static void parse_snap_metadata_record(struct apfs_snap_metadata_key *key, struct apfs_snap_metadata_val *val, int len)
{
struct snapshot *snap = NULL;
u64 snap_xid;
int namelen;
if (len < sizeof(*val) + 1)
report("Snapshot metadata record", "value is too small.");
if (*((char *)val + len - 1) != 0)
report("Snapshot metadata record", "name lacks NULL-termination.");
namelen = le16_to_cpu(val->name_len);
if (strlen((char *)val->name) + 1 != namelen)
report("Snapshot metadata record", "wrong name length.");
if (len != sizeof(*val) + namelen)
report("Snapshot metadata record", "size of value doesn't match name length.");
snap_xid = cat_cnid(&key->hdr);
snap = get_snapshot(snap_xid);
if (snap->sn_meta_seen)
report("Snapshot tree", "snap with two metadata records.");
snap->sn_meta_seen = true;
snap->sn_meta_name = calloc(1, namelen);
if (!snap->sn_meta_name)
system_error();
strcpy(snap->sn_meta_name, (char *)val->name);
if (le32_to_cpu(val->extentref_tree_type) != (APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_BTREE))
report("Snapshot metadata", "wrong type for extentref tree.");
if (val->flags)
report_unknown("Snapshot flags");
check_snapshot(snap_xid, le64_to_cpu(val->sblock_oid), le64_to_cpu(val->extentref_tree_oid));
++vsb->v_snap_count;
}
/**
* parse_snap_record - Parse and check a snapshot tree record value
* @key: pointer to the raw key
* @val: pointer to the raw value
* @len: length of the raw value
*
* Internal consistency of @key must be checked before calling this function.
*/
void parse_snap_record(void *key, void *val, int len)
{
switch (cat_type(key)) {
case APFS_TYPE_SNAP_METADATA:
return parse_snap_metadata_record(key, val, len);
case APFS_TYPE_SNAP_NAME:
return parse_snap_name_record(key, val, len);
default:
report(NULL, "Bug!");
}
}
/**
* parse_omap_snap_record - Parse and check an omap snapshot tree record value
* @key: pointer to the raw key
* @val: pointer to the raw value
* @len: length of the raw value
*
* Internal consistency of @key must be checked before calling this function.
*/
void parse_omap_snap_record(__le64 *key, struct apfs_omap_snapshot *val, int len)
{
struct snapshot *snapshot = NULL;
u64 snap_xid;
/*
* These keys and values must be aligned to eight bytes.
* TODO: add the same check to the free queue?
*/
if ((u64)key & 7 || (u64)val & 7)
report("Omap snapshot record", "bad alignment for key or value.");
if (len != sizeof(*val))
report("Omap snapshot record", "value is too small.");
snap_xid = le64_to_cpu(*key);
if (snap_xid == 0)
report("Omap snapshot record", "xid is zero.");
if (snap_xid >= sb->s_xid)
report("Omap snapshot record", "xid is in the future.");
if (snap_xid >= vsb->v_snap_max_xid)
vsb->v_snap_max_xid = snap_xid;
snapshot = get_snapshot(snap_xid);
snapshot->sn_omap_seen = true;
if (val->oms_flags)
report_unknown("Deleted or reverted snapshot");
if (val->oms_pad)
report("Omap snapshot record", "padding should be zeroes.");
if (val->oms_oid)
report("Omap snapshot record", "oid should be zero.");
}
|