vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / partitioning_systems / gpt / Header.cpp
blob252ed8788a6cfa8f82fa8b25fcffc0377f90b2e3
1 /*
2 * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 */
9 #include "Header.h"
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <string.h>
15 #include <KernelExport.h>
17 #ifdef _KERNEL_MODE
18 # include <util/kernel_cpp.h>
19 #else
20 # include <new>
21 #endif
23 #ifndef _BOOT_MODE
24 #include "PartitionMap.h"
25 #include "PartitionMapWriter.h"
26 #endif
28 #if !defined(_BOOT_MODE) && !defined(_USER_MODE)
29 #include "uuid.h"
30 #endif
32 #include "crc32.h"
33 #include "utility.h"
36 #define TRACE_EFI_GPT
37 #ifdef TRACE_EFI_GPT
38 # ifndef _KERNEL_MODE
39 # define dprintf printf
40 # endif
41 # define TRACE(x) dprintf x
42 #else
43 # define TRACE(x) ;
44 #endif
47 namespace EFI {
50 Header::Header(int fd, uint64 lastBlock, uint32 blockSize)
52 fBlockSize(blockSize),
53 fStatus(B_NO_INIT),
54 fEntries(NULL),
55 fDirty(false)
57 // TODO: check the correctness of the protective MBR and warn if invalid
59 // Read and check the partition table header
61 fStatus = _Read(fd, (uint64)EFI_HEADER_LOCATION * blockSize,
62 &fHeader, sizeof(efi_table_header));
63 if (fStatus == B_OK) {
64 if (!_IsHeaderValid(fHeader, EFI_HEADER_LOCATION))
65 fStatus = B_BAD_DATA;
68 if (fStatus == B_OK && lastBlock != fHeader.AlternateBlock()) {
69 dprintf("gpt: alternate header not in last block (%" B_PRIu64 " vs. %"
70 B_PRIu64 ")\n", fHeader.AlternateBlock(), lastBlock);
71 lastBlock = fHeader.AlternateBlock();
74 // Read backup header, too
75 status_t status = _Read(fd, lastBlock * blockSize, &fBackupHeader,
76 sizeof(efi_table_header));
77 if (status == B_OK) {
78 if (!_IsHeaderValid(fBackupHeader, lastBlock))
79 status = B_BAD_DATA;
82 // If both headers are invalid, bail out -- this is probably not a GPT disk
83 if (status != B_OK && fStatus != B_OK)
84 return;
86 if (fStatus != B_OK) {
87 // Recreate primary header from the backup
88 fHeader = fBackupHeader;
89 fHeader.SetAbsoluteBlock(EFI_HEADER_LOCATION);
90 fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK);
91 fHeader.SetAlternateBlock(lastBlock);
92 fDirty = true;
93 } else if (status != B_OK) {
94 // Recreate backup header from primary
95 _SetBackupHeaderFromPrimary(lastBlock);
98 // allocate, read, and check partition entry array
100 fEntries = new (std::nothrow) uint8[_EntryArraySize()];
101 if (fEntries == NULL) {
102 // TODO: if there cannot be allocated enough (ie. the boot loader's
103 // heap is limited), try a smaller size before failing
104 fStatus = B_NO_MEMORY;
105 return;
108 fStatus = _Read(fd, fHeader.EntriesBlock() * blockSize,
109 fEntries, _EntryArraySize());
110 if (fStatus != B_OK || !_ValidateEntriesCRC()) {
111 // Read backup entries instead
112 fStatus = _Read(fd, fBackupHeader.EntriesBlock() * blockSize,
113 fEntries, _EntryArraySize());
114 if (fStatus != B_OK)
115 return;
117 if (!_ValidateEntriesCRC()) {
118 fStatus = B_BAD_DATA;
119 return;
123 // TODO: check overlapping or out of range partitions
125 #ifdef TRACE_EFI_GPT
126 _Dump(fHeader);
127 _Dump(fBackupHeader);
128 _DumpPartitions();
129 #endif
131 fStatus = B_OK;
135 #if !defined(_BOOT_MODE) && !defined(_USER_MODE)
136 Header::Header(uint64 lastBlock, uint32 blockSize)
138 fBlockSize(blockSize),
139 fStatus(B_NO_INIT),
140 fEntries(NULL),
141 fDirty(true)
143 TRACE(("EFI::Header: Initialize GPT, block size %" B_PRIu32 "\n",
144 blockSize));
146 // Initialize to an empty header
147 memcpy(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header));
148 fHeader.SetRevision(EFI_TABLE_REVISION);
149 fHeader.SetHeaderSize(sizeof(fHeader));
150 fHeader.SetHeaderCRC(0);
151 fHeader.SetAbsoluteBlock(EFI_HEADER_LOCATION);
152 fHeader.SetAlternateBlock(lastBlock);
153 uuid_t uuid;
154 uuid_generate_random(uuid);
155 memcpy((uint8*)&fHeader.disk_guid, uuid, sizeof(guid_t));
156 fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK);
157 fHeader.SetEntryCount(EFI_PARTITION_ENTRY_COUNT);
158 fHeader.SetEntrySize(EFI_PARTITION_ENTRY_SIZE);
159 fHeader.SetEntriesCRC(0);
161 size_t arraySize = _EntryArraySize();
162 fEntries = new (std::nothrow) uint8[arraySize];
163 if (fEntries == NULL) {
164 fStatus = B_NO_MEMORY;
165 return;
168 memset(fEntries, 0, arraySize);
169 // TODO: initialize the entry guids
171 uint32 entryBlocks = (arraySize + fBlockSize - 1) / fBlockSize;
172 fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK + entryBlocks);
173 fHeader.SetLastUsableBlock(lastBlock - 1 - entryBlocks);
175 _SetBackupHeaderFromPrimary(lastBlock);
177 #ifdef TRACE_EFI_GPT
178 _Dump(fHeader);
179 _DumpPartitions();
180 #endif
182 fStatus = B_OK;
184 #endif // !_BOOT_MODE && !_USER_MODE
187 Header::~Header()
189 delete[] fEntries;
193 status_t
194 Header::InitCheck() const
196 return fStatus;
200 bool
201 Header::IsDirty() const
203 return fDirty;
207 #ifndef _BOOT_MODE
208 status_t
209 Header::WriteEntry(int fd, uint32 entryIndex)
211 off_t entryOffset = entryIndex * fHeader.EntrySize();
213 status_t status = _Write(fd,
214 fHeader.EntriesBlock() * fBlockSize + entryOffset,
215 fEntries + entryOffset, fHeader.EntrySize());
216 if (status != B_OK)
217 return status;
219 // Update header, too -- the entries CRC changed
220 status = _WriteHeader(fd);
222 // Write backup
223 status_t backupStatus = _Write(fd,
224 fBackupHeader.EntriesBlock() * fBlockSize + entryOffset,
225 fEntries + entryOffset, fHeader.EntrySize());
227 if (status == B_OK && backupStatus == B_OK)
228 fDirty = false;
229 return status == B_OK ? backupStatus : status;
233 status_t
234 Header::Write(int fd)
236 // Try to write the protective MBR
237 PartitionMap partitionMap;
238 PrimaryPartition *partition = NULL;
239 uint32 index = 0;
240 while ((partition = partitionMap.PrimaryPartitionAt(index)) != NULL) {
241 if (index == 0) {
242 uint64 deviceSize = fHeader.AlternateBlock() * fBlockSize;
243 partition->SetTo(fBlockSize, deviceSize, 0xEE, false, fBlockSize);
244 } else
245 partition->Unset();
246 ++index;
248 PartitionMapWriter writer(fd, fBlockSize);
249 writer.WriteMBR(&partitionMap, true);
250 // We also write the bootcode, so we can boot GPT disks from BIOS
252 status_t status = _Write(fd, fHeader.EntriesBlock() * fBlockSize, fEntries,
253 _EntryArraySize());
254 if (status != B_OK)
255 return status;
257 // First write the header, so that we have at least one completely correct
258 // data set
259 status = _WriteHeader(fd);
262 // Write backup entries
263 status_t backupStatus = _Write(fd,
264 fBackupHeader.EntriesBlock() * fBlockSize, fEntries, _EntryArraySize());
266 if (status == B_OK && backupStatus == B_OK)
267 fDirty = false;
268 return status == B_OK ? backupStatus : status;
272 status_t
273 Header::_WriteHeader(int fd)
275 _UpdateCRC();
277 status_t status = _Write(fd, fHeader.AbsoluteBlock() * fBlockSize,
278 &fHeader, sizeof(efi_table_header));
279 if (status != B_OK)
280 return status;
282 return _Write(fd, fBackupHeader.AbsoluteBlock() * fBlockSize,
283 &fBackupHeader, sizeof(efi_table_header));
287 status_t
288 Header::_Write(int fd, off_t offset, const void* data, size_t size) const
290 ssize_t bytesWritten = write_pos(fd, offset, data, size);
291 if (bytesWritten < 0)
292 return bytesWritten;
293 if (bytesWritten != (ssize_t)size)
294 return B_IO_ERROR;
296 return B_OK;
300 void
301 Header::_UpdateCRC()
303 _UpdateCRC(fHeader);
304 _UpdateCRC(fBackupHeader);
308 void
309 Header::_UpdateCRC(efi_table_header& header)
311 header.SetEntriesCRC(crc32(fEntries, _EntryArraySize()));
312 header.SetHeaderCRC(0);
313 header.SetHeaderCRC(crc32((uint8*)&header, sizeof(efi_table_header)));
315 #endif // !_BOOT_MODE
318 status_t
319 Header::_Read(int fd, off_t offset, void* data, size_t size) const
321 ssize_t bytesRead = read_pos(fd, offset, data, size);
322 if (bytesRead < 0)
323 return bytesRead;
324 if (bytesRead != (ssize_t)size)
325 return B_IO_ERROR;
327 return B_OK;
331 bool
332 Header::_IsHeaderValid(efi_table_header& header, uint64 block)
334 return !memcmp(header.header, EFI_PARTITION_HEADER, sizeof(header.header))
335 && _ValidateHeaderCRC(header)
336 && header.AbsoluteBlock() == block;
340 bool
341 Header::_ValidateHeaderCRC(efi_table_header& header)
343 uint32 originalCRC = header.HeaderCRC();
344 header.SetHeaderCRC(0);
346 bool matches = originalCRC == crc32((const uint8*)&header,
347 sizeof(efi_table_header));
349 header.SetHeaderCRC(originalCRC);
350 return matches;
354 bool
355 Header::_ValidateEntriesCRC() const
357 return fHeader.EntriesCRC() == crc32(fEntries, _EntryArraySize());
361 void
362 Header::_SetBackupHeaderFromPrimary(uint64 lastBlock)
364 fBackupHeader = fHeader;
365 fBackupHeader.SetAbsoluteBlock(lastBlock);
366 fBackupHeader.SetEntriesBlock(
367 lastBlock - _EntryArraySize() / fBlockSize);
368 fBackupHeader.SetAlternateBlock(1);
372 #ifdef TRACE_EFI_GPT
373 const char *
374 Header::_PrintGUID(const guid_t &id)
376 static char guid[48];
377 snprintf(guid, sizeof(guid),
378 "%08" B_PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
379 B_LENDIAN_TO_HOST_INT32(id.data1), B_LENDIAN_TO_HOST_INT16(id.data2),
380 B_LENDIAN_TO_HOST_INT16(id.data3), id.data4[0], id.data4[1],
381 id.data4[2], id.data4[3], id.data4[4], id.data4[5], id.data4[6],
382 id.data4[7]);
383 return guid;
387 void
388 Header::_Dump(const efi_table_header& header)
390 dprintf("EFI header: %.8s\n", header.header);
391 dprintf("EFI revision: %" B_PRIx32 "\n", header.Revision());
392 dprintf("header size: %" B_PRId32 "\n", header.HeaderSize());
393 dprintf("header CRC: %" B_PRIx32 "\n", header.HeaderCRC());
394 dprintf("absolute block: %" B_PRIu64 "\n", header.AbsoluteBlock());
395 dprintf("alternate block: %" B_PRIu64 "\n", header.AlternateBlock());
396 dprintf("first usable block: %" B_PRIu64 "\n", header.FirstUsableBlock());
397 dprintf("last usable block: %" B_PRIu64 "\n", header.LastUsableBlock());
398 dprintf("disk GUID: %s\n", _PrintGUID(header.disk_guid));
399 dprintf("entries block: %" B_PRIu64 "\n", header.EntriesBlock());
400 dprintf("entry size: %" B_PRIu32 "\n", header.EntrySize());
401 dprintf("entry count: %" B_PRIu32 "\n", header.EntryCount());
402 dprintf("entries CRC: %" B_PRIx32 "\n", header.EntriesCRC());
406 void
407 Header::_DumpPartitions()
409 for (uint32 i = 0; i < EntryCount(); i++) {
410 const efi_partition_entry &entry = EntryAt(i);
412 if (entry.partition_type == kEmptyGUID)
413 continue;
415 dprintf("[%3" B_PRIu32 "] partition type: %s\n", i,
416 _PrintGUID(entry.partition_type));
417 dprintf(" unique id: %s\n", _PrintGUID(entry.unique_guid));
418 dprintf(" start block: %" B_PRIu64 "\n", entry.StartBlock());
419 dprintf(" end block: %" B_PRIu64 "\n", entry.EndBlock());
420 dprintf(" size: %g MB\n", (entry.EndBlock() - entry.StartBlock())
421 * 512 / 1024.0 / 1024.0);
422 dprintf(" attributes: %" B_PRIx64 "\n", entry.Attributes());
424 char name[64];
425 to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, name, sizeof(name));
426 dprintf(" name: %s\n", name);
429 #endif // TRACE_EFI_GPT
432 } // namespace EFI