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