1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <inttypes.h>
6 #include <stdarg.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9
10 #include <fbl/array.h>
11 #include <fbl/unique_fd.h>
12 #include <fbl/vector.h>
13 #include <fvm/fvm.h>
14 #include <fvm/fvm-check.h>
15 #include <zircon/status.h>
16
17 #include <utility>
18
19 namespace fvm {
20
21 Checker::Checker() = default;
22
Checker(fbl::unique_fd fd,uint32_t block_size,bool silent)23 Checker::Checker(fbl::unique_fd fd, uint32_t block_size, bool silent) :
24 fd_(std::move(fd)), block_size_(block_size), logger_(silent) {}
25
26 Checker::~Checker() = default;
27
Validate() const28 bool Checker::Validate() const {
29 if (!ValidateOptions()) {
30 return false;
31 }
32
33 FvmInfo info;
34 if (!LoadFVM(&info)) {
35 return false;
36 }
37
38 return CheckFVM(info);
39 }
40
ValidateOptions() const41 bool Checker::ValidateOptions() const {
42 if (!fd_) {
43 logger_.Error("FVM checker missing a device\n");
44 return false;
45 }
46 if (block_size_ == 0) {
47 logger_.Error("Invalid block size\n");
48 return false;
49 }
50 return true;
51 }
52
LoadFVM(FvmInfo * out) const53 bool Checker::LoadFVM(FvmInfo* out) const {
54 const off_t device_size = lseek(fd_.get(), 0, SEEK_END);
55 if (device_size < 0) {
56 logger_.Error("Unable to get file length\n");
57 return false;
58 }
59 if (device_size % block_size_ != 0) {
60 logger_.Error("File size is not divisible by block size\n");
61 return false;
62 }
63 const size_t block_count = device_size / block_size_;
64
65 fbl::unique_ptr<uint8_t[]> header(new uint8_t[FVM_BLOCK_SIZE]);
66 if (pread(fd_.get(), header.get(), FVM_BLOCK_SIZE, 0) != static_cast<ssize_t>(FVM_BLOCK_SIZE)) {
67 logger_.Error("Could not read header\n");
68 return false;
69 }
70 const fvm::fvm_t* superblock = reinterpret_cast<fvm::fvm_t*>(header.get());
71 const size_t slice_size = superblock->slice_size;
72 if (slice_size % block_size_ != 0) {
73 logger_.Error("Slice size not divisible by block size\n");
74 return false;
75 } else if (slice_size == 0) {
76 logger_.Error("Slice size cannot be zero\n");
77 return false;
78 }
79 const size_t metadata_size = fvm::MetadataSize(device_size, slice_size);
80 fbl::unique_ptr<uint8_t[]> metadata(new uint8_t[metadata_size * 2]);
81 if (pread(fd_.get(), metadata.get(), metadata_size * 2, 0) !=
82 static_cast<ssize_t>(metadata_size * 2)) {
83 logger_.Error("Could not read metadata\n");
84 return false;
85 }
86
87 const void* metadata1 = metadata.get();
88 const void* metadata2 = reinterpret_cast<const void*>(metadata.get() + metadata_size);
89
90 const void* valid_metadata;
91 zx_status_t status = fvm_validate_header(metadata1, metadata2, metadata_size,
92 &valid_metadata);
93 if (status != ZX_OK) {
94 logger_.Error("Invalid FVM metadata\n");
95 return false;
96 }
97
98 const void* invalid_metadata = (metadata1 == valid_metadata) ? metadata2 : metadata1;
99 const size_t valid_metadata_offset = (metadata1 == valid_metadata) ? 0 : metadata_size;
100
101 FvmInfo info = {
102 fbl::Array<uint8_t>(metadata.release(), metadata_size * 2),
103 valid_metadata_offset,
104 static_cast<const uint8_t*>(valid_metadata),
105 static_cast<const uint8_t*>(invalid_metadata),
106 block_size_,
107 block_count,
108 static_cast<size_t>(device_size),
109 slice_size,
110 };
111
112 *out = std::move(info);
113 return true;
114 }
115
LoadPartitions(const size_t slice_count,const fvm::slice_entry_t * slice_table,const fvm::vpart_entry_t * vpart_table,fbl::Vector<Slice> * out_slices,fbl::Array<Partition> * out_partitions) const116 bool Checker::LoadPartitions(const size_t slice_count, const fvm::slice_entry_t* slice_table,
117 const fvm::vpart_entry_t* vpart_table, fbl::Vector<Slice>* out_slices,
118 fbl::Array<Partition>* out_partitions) const {
119 fbl::Vector<Slice> slices;
120 fbl::Array<Partition> partitions(new Partition[FVM_MAX_ENTRIES], FVM_MAX_ENTRIES);
121
122 bool valid = true;
123
124 // Initialize all allocated partitions.
125 for (size_t i = 1; i < FVM_MAX_ENTRIES; i++) {
126 const uint32_t slices = vpart_table[i].slices;
127 if (slices != 0) {
128 partitions[i].entry = &vpart_table[i];
129 }
130 }
131
132 // Initialize all slices, ensure they are used for allocated partitions.
133 for (size_t i = 1; i <= slice_count; i++) {
134 if (slice_table[i].Vpart() != FVM_SLICE_ENTRY_FREE) {
135 const uint64_t vpart = slice_table[i].Vpart();
136 if (vpart >= FVM_MAX_ENTRIES) {
137 logger_.Error("Invalid vslice entry; claims vpart which is out of range.\n");
138 valid = false;
139 } else if (!partitions[vpart].Allocated()) {
140 logger_.Error("Invalid slice entry; claims that it is allocated to invalid ");
141 logger_.Error("partition %zu\n", vpart);
142 valid = false;
143 }
144
145 Slice slice = { vpart, slice_table[i].Vslice(), i };
146
147 slices.push_back(slice);
148 partitions[vpart].slices.push_back(std::move(slice));
149 }
150 }
151
152 // Validate that all allocated partitions are correct about the number of slices used.
153 for (size_t i = 1; i < FVM_MAX_ENTRIES; i++) {
154 if (partitions[i].Allocated()) {
155 const size_t claimed = partitions[i].entry->slices;
156 const size_t actual = partitions[i].slices.size();
157 if (claimed != actual) {
158 logger_.Error("Disagreement about allocated slice count: ");
159 logger_.Error("Partition %zu claims %zu slices, has %zu\n", i, claimed, actual);
160 valid = false;
161 }
162 }
163 }
164
165 *out_slices = std::move(slices);
166 *out_partitions = std::move(partitions);
167 return valid;
168 }
169
DumpSlices(const fbl::Vector<Slice> & slices) const170 void Checker::DumpSlices(const fbl::Vector<Slice>& slices) const {
171 logger_.Log("[ Slice Info ]\n");
172 Slice* run_start = nullptr;
173 size_t run_length = 0;
174
175 // Prints whatever information we can from the current contiguous range of
176 // virtual / physical slices, then reset the "run" information.
177 //
178 // A run is a contiguous set of virtual / physical slices, all allocated to the same
179 // virtual partition. Noncontiguity in either the virtual or physical range
180 // "breaks" the run, since these cases provide new information.
181 auto start_run = [&run_start, &run_length](Slice* slice) {
182 run_start = slice;
183 run_length = 1;
184 };
185 auto end_run = [this, &run_start, &run_length]() {
186 if (run_length == 1) {
187 logger_.Log("Physical Slice %zu allocated\n", run_start->physical_slice);
188 logger_.Log(" Allocated as virtual slice %zu\n", run_start->virtual_slice);
189 logger_.Log(" Allocated to partition %zu\n", run_start->virtual_partition);
190 } else if (run_length > 1) {
191 logger_.Log("Physical Slices [%zu, %zu] allocated\n",
192 run_start->physical_slice, run_start->physical_slice + run_length - 1);
193 logger_.Log(" Allocated as virtual slices [%zu, %zu]\n",
194 run_start->virtual_slice, run_start->virtual_slice + run_length - 1);
195 logger_.Log(" Allocated to partition %zu\n", run_start->virtual_partition);
196 }
197 run_start = nullptr;
198 run_length = 0;
199 };
200
201 if (!slices.is_empty()) {
202 start_run(&slices[0]);
203 }
204 for (size_t i = 1; i < slices.size(); i++) {
205 const auto& slice = slices[i];
206 const size_t expected_pslice = run_start->physical_slice + run_length;
207 const size_t expected_vslice = run_start->virtual_slice + run_length;
208 if (slice.physical_slice == expected_pslice &&
209 slice.virtual_slice == expected_vslice &&
210 slice.virtual_partition == run_start->virtual_partition) {
211 run_length++;
212 } else {
213 end_run();
214 start_run(&slices[i]);
215 }
216 }
217 end_run();
218 }
219
CheckFVM(const FvmInfo & info) const220 bool Checker::CheckFVM(const FvmInfo& info) const {
221 auto superblock = reinterpret_cast<const fvm::fvm_t*>(info.valid_metadata);
222 auto invalid_superblock = reinterpret_cast<const fvm::fvm_t*>(info.invalid_metadata);
223 logger_.Log("[ FVM Info ]\n");
224 logger_.Log("Version: %" PRIu64 "\n", superblock->version);
225 logger_.Log("Generation number: %" PRIu64 "\n", superblock->generation);
226 logger_.Log("Generation number: %" PRIu64 " (invalid copy)\n", invalid_superblock->generation);
227 logger_.Log("\n");
228
229 const size_t slice_count = fvm::UsableSlicesCount(info.device_size, info.slice_size);
230 logger_.Log("[ Size Info ]\n");
231 logger_.Log("%-15s %10zu\n", "Device Length:", info.device_size);
232 logger_.Log("%-15s %10zu\n", "Block size:", info.block_size);
233 logger_.Log("%-15s %10zu\n", "Slice size:", info.slice_size);
234 logger_.Log("%-15s %10zu\n", "Slice count:", slice_count);
235 logger_.Log("\n");
236
237 const size_t metadata_size = fvm::MetadataSize(info.device_size, info.slice_size);
238 const size_t metadata_count = 2;
239 const size_t metadata_end = metadata_size * metadata_count;
240 logger_.Log("[ Metadata ]\n");
241 logger_.Log("%-25s 0x%016zx\n", "Valid metadata start:", info.valid_metadata_offset);
242 logger_.Log("%-25s 0x%016x\n", "Metadata start:", 0);
243 logger_.Log("%-25s %16zu (for each copy)\n", "Metadata size:", metadata_size);
244 logger_.Log("%-25s %16zu\n", "Metadata count:", metadata_count);
245 logger_.Log("%-25s 0x%016zx\n", "Metadata end:", metadata_end);
246 logger_.Log("\n");
247
248 logger_.Log("[ All Subsequent Offsets Relative to Valid Metadata Start ]\n");
249 logger_.Log("\n");
250
251 const size_t vpart_table_start = fvm::kVPartTableOffset;
252 const size_t vpart_entry_size = sizeof(fvm::vpart_entry_t);
253 const size_t vpart_table_size = fvm::kVPartTableLength;
254 const size_t vpart_table_end = vpart_table_start + vpart_table_size;
255 logger_.Log("[ Virtual Partition Table ]\n");
256 logger_.Log("%-25s 0x%016zx\n", "VPartition Entry Start:", vpart_table_start);
257 logger_.Log("%-25s %16zu\n", "VPartition entry size:", vpart_entry_size);
258 logger_.Log("%-25s %16zu\n", "VPartition table size:", vpart_table_size);
259 logger_.Log("%-25s 0x%016zx\n", "VPartition table end:", vpart_table_end);
260 logger_.Log("\n");
261
262 const size_t slice_table_start = fvm::kAllocTableOffset;
263 const size_t slice_entry_size = sizeof(fvm::slice_entry_t);
264 const size_t slice_table_size = slice_entry_size * slice_count;
265 const size_t slice_table_end = slice_table_start + slice_table_size;
266 logger_.Log("[ Slice Allocation Table ]\n");
267 logger_.Log("%-25s 0x%016zx\n", "Slice table start:", slice_table_start);
268 logger_.Log("%-25s %16zu\n", "Slice entry size:", slice_entry_size);
269 logger_.Log("%-25s %16zu\n", "Slice table size:", slice_table_size);
270 logger_.Log("%-25s 0x%016zx\n", "Slice table end:", slice_table_end);
271 logger_.Log("\n");
272
273 const fvm::slice_entry_t* slice_table = reinterpret_cast<const fvm::slice_entry_t*>(
274 info.valid_metadata + slice_table_start);
275 const fvm::vpart_entry_t* vpart_table = reinterpret_cast<const fvm::vpart_entry_t*>(
276 info.valid_metadata + vpart_table_start);
277
278 fbl::Vector<Slice> slices;
279 fbl::Array<Partition> partitions;
280 bool valid = true;
281 if (!LoadPartitions(slice_count, slice_table, vpart_table, &slices, &partitions)) {
282 valid = false;
283 logger_.Log("Partitions invalid; displaying info anyway...\n");
284 }
285
286 logger_.Log("[ Partition Info ]\n");
287 for (size_t i = 1; i < FVM_MAX_ENTRIES; i++) {
288 const uint32_t slices = vpart_table[i].slices;
289 if (slices != 0) {
290 char guid_string[GPT_GUID_STRLEN];
291 uint8_to_guid_string(guid_string, vpart_table[i].type);
292 logger_.Log("Partition %zu allocated\n", i);
293 logger_.Log(" Has %u slices allocated\n", slices);
294 logger_.Log(" Type: %s\n", gpt_guid_to_type(guid_string));
295 logger_.Log(" Name: %.*s\n", FVM_NAME_LEN, vpart_table[i].name);
296 }
297 }
298 logger_.Log("\n");
299
300 DumpSlices(slices);
301 return valid;
302 }
303
304 } // namespace fvm
305