1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "gptdisk.hpp" |
4 | |
5 | #include "blockdev.h" |
6 | #include "layer-gpt.hpp" |
7 | #include "proto/blockdev.pb.h" |
8 | #include "uuid.hpp" |
9 | |
10 | #include <cassert> |
11 | #include <iostream> |
12 | #include <librpc/rpc.h> |
13 | #include <optional> |
14 | #include <pb_decode.h> |
15 | #include <string> |
16 | |
17 | static std::optional<GPT::Header> (const void *data) |
18 | { |
19 | const auto = static_cast<const GPT::Header *>(data); |
20 | |
21 | if (strncmp(a: (const char *) header, b: "EFI PART" , max_size: 8) != 0) |
22 | { |
23 | std::cout << " (invalid GPT header)" << std::endl; |
24 | return std::nullopt; |
25 | } |
26 | |
27 | std::cout << " (GPT signature is valid)" << std::endl; |
28 | std::cout << " Revision: " << header->revision << std::endl; |
29 | std::cout << " Header size: " << header->header_size << std::endl; |
30 | std::cout << " Header CRC32: " << header->header_crc32 << std::endl; |
31 | std::cout << " Current LBA: " << header->current_lba << std::endl; |
32 | std::cout << " Backup LBA: " << header->backup_lba << std::endl; |
33 | std::cout << " First usable LBA: " << header->first_usable_lba << std::endl; |
34 | std::cout << " Last usable LBA: " << header->last_usable_lba << std::endl; |
35 | std::cout << " Disk GUID: " << UUID{ header->disk_guid } << std::endl; |
36 | std::cout << " Partition table LBA: " << header->partition_table_lba << std::endl; |
37 | std::cout << " Partition count: " << header->partition_count << std::endl; |
38 | std::cout << " Partition entry size: " << header->partition_entry_size << std::endl; |
39 | std::cout << " Partition table CRC32: " << header->partition_table_crc32 << std::endl; |
40 | |
41 | return *header; |
42 | } |
43 | |
44 | GPTDisk::GPTDisk(const blockdev_handle handle, const std::string &disk_name) : device_handle(handle), disk_name(disk_name) |
45 | { |
46 | } |
47 | |
48 | bool GPTDisk::initialise_gpt() |
49 | { |
50 | if (ready) |
51 | throw std::runtime_error("GPTDisk already initialised" ); |
52 | |
53 | const auto = disk_read_header(); |
54 | if (!has_header) |
55 | return false; |
56 | |
57 | const auto has_partitions = disk_read_partitions(); |
58 | if (!has_partitions) |
59 | return false; |
60 | |
61 | ready = true; |
62 | return true; |
63 | } |
64 | |
65 | GPT::PartitionEntry GPTDisk::get_partition(size_t index) const |
66 | { |
67 | assert(ready); |
68 | return partitions.at(n: index); |
69 | } |
70 | |
71 | u32 GPTDisk::get_partition_count() const |
72 | { |
73 | assert(ready); |
74 | return partitions.size(); |
75 | } |
76 | |
77 | bool GPTDisk::() |
78 | { |
79 | const read_block::request read_request = { |
80 | .device = device_handle, |
81 | .n_boffset = 1, |
82 | .n_blocks = 1, |
83 | }; |
84 | |
85 | read_block::response read_resp; |
86 | const auto result = manager->read_block(request: &read_request, response: &read_resp); |
87 | if (result != RPC_RESULT_OK || !read_resp.result.success) |
88 | { |
89 | std::cout << " (failed to read block)" << std::endl; |
90 | if (read_resp.result.error) |
91 | std::cout << " " << read_resp.result.error << std::endl; |
92 | return {}; |
93 | } |
94 | |
95 | const auto = gpt_read_header(data: read_resp.data->bytes); |
96 | pb_release(mosrpc_blockdev_read_block_response_fields, dest_struct: &read_resp); |
97 | |
98 | if (header) |
99 | this->header = header.value(); |
100 | |
101 | return header.has_value(); |
102 | } |
103 | |
104 | bool GPTDisk::disk_read_partitions() |
105 | { |
106 | // continue reading the partition table |
107 | const read_block::request read_request{ |
108 | .device = device_handle, |
109 | .n_boffset = header.partition_table_lba, |
110 | .n_blocks = header.partition_count * header.partition_entry_size / 512, |
111 | }; |
112 | read_block::response read_resp; |
113 | |
114 | const auto result3 = manager->read_block(request: &read_request, response: &read_resp); |
115 | if (result3 != RPC_RESULT_OK || !read_resp.result.success) |
116 | { |
117 | std::cout << " (failed to read partition table)" << std::endl; |
118 | if (read_resp.result.error) |
119 | std::cout << " " << read_resp.result.error << std::endl; |
120 | return false; |
121 | } |
122 | |
123 | std::cout << " Partition table:" << std::endl; |
124 | for (size_t i = 0; i < header.partition_count; i++) |
125 | { |
126 | const auto ptr = (const char *) read_resp.data->bytes + i * header.partition_entry_size; |
127 | const auto entry = (const GPT::PartitionEntry *) ptr; |
128 | |
129 | if (entry->type_guid[0] == 0 && entry->type_guid[1] == 0) |
130 | continue; // skip empty entries |
131 | |
132 | std::cout << std::resetiosflags(mask: std::ios::hex); |
133 | std::cout << " Partition " << i << ":" << std::endl; |
134 | std::cout << " Type GUID: " << UUID{ entry->type_guid } << std::endl; |
135 | std::cout << " Partition GUID: " << UUID{ entry->partition_guid } << std::endl; |
136 | std::cout << " First LBA: " << entry->first_lba << std::endl; |
137 | std::cout << " Last LBA: " << entry->last_lba << std::endl; |
138 | std::cout << " Attributes: " << entry->attributes << std::endl; |
139 | // std::cout << " Name: " << name << std::endl; |
140 | |
141 | partitions.push_back(x: *entry); |
142 | } |
143 | |
144 | pb_release(mosrpc_blockdev_read_block_response_fields, dest_struct: &read_resp); |
145 | return true; |
146 | } |
147 | |
148 | size_t GPTDisk::read_partition_block(size_t partition_index, u64 blockoffset, u8 *buffer, u32 nblocks) |
149 | { |
150 | assert(ready); |
151 | const auto &partition = partitions.at(n: partition_index); |
152 | |
153 | const read_block::request read_request = { |
154 | .device = device_handle, |
155 | .n_boffset = partition.first_lba + blockoffset, |
156 | .n_blocks = nblocks, |
157 | }; |
158 | |
159 | read_block::response read_resp; |
160 | const auto result = manager->read_block(request: &read_request, response: &read_resp); |
161 | if (result != RPC_RESULT_OK || !read_resp.result.success) |
162 | { |
163 | std::cout << " (failed to read partition block)" << std::endl; |
164 | if (read_resp.result.error) |
165 | std::cout << " " << read_resp.result.error << std::endl; |
166 | return 0; |
167 | } |
168 | |
169 | const auto datasize = read_resp.data->size; |
170 | memcpy(dest: buffer, src: read_resp.data->bytes, size: datasize); |
171 | pb_release(mosrpc_blockdev_read_block_response_fields, dest_struct: &read_resp); |
172 | |
173 | return datasize; |
174 | } |
175 | |
176 | size_t GPTDisk::write_partition_block(size_t partition_index, u64 blockoffset, const u8 *buffer, u32 nblocks) |
177 | { |
178 | MOS_UNUSED(partition_index); |
179 | MOS_UNUSED(blockoffset); |
180 | MOS_UNUSED(buffer); |
181 | MOS_UNUSED(nblocks); |
182 | assert(ready); |
183 | return 0; |
184 | } |
185 | |