GUIDPartitionTable.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*
  2. * Copyright (c) 2020-2022, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Debug.h>
  7. #include <LibPartition/GUIDPartitionTable.h>
  8. #ifndef KERNEL
  9. # include <LibCore/DeprecatedFile.h>
  10. #endif
  11. namespace Partition {
  12. #define GPT_SIGNATURE2 0x54524150
  13. #define GPT_SIGNATURE 0x20494645
  14. struct [[gnu::packed]] GPTPartitionEntry {
  15. u8 partition_guid[16];
  16. u8 unique_guid[16];
  17. u64 first_lba;
  18. u64 last_lba;
  19. u64 attributes;
  20. char partition_name[72];
  21. };
  22. struct [[gnu::packed]] GUIDPartitionHeader {
  23. u32 sig[2];
  24. u32 revision;
  25. u32 header_size;
  26. u32 crc32_header;
  27. u32 reserved;
  28. u64 current_lba;
  29. u64 backup_lba;
  30. u64 first_usable_lba;
  31. u64 last_usable_lba;
  32. u64 disk_guid1[2];
  33. u64 partition_array_start_lba;
  34. u32 entries_count;
  35. u32 partition_entry_size;
  36. u32 crc32_entries_array;
  37. };
  38. #ifdef KERNEL
  39. ErrorOr<NonnullOwnPtr<GUIDPartitionTable>> GUIDPartitionTable::try_to_initialize(Kernel::StorageDevice& device)
  40. {
  41. auto table = TRY(adopt_nonnull_own_or_enomem(new (nothrow) GUIDPartitionTable(device)));
  42. #else
  43. ErrorOr<NonnullOwnPtr<GUIDPartitionTable>> GUIDPartitionTable::try_to_initialize(NonnullRefPtr<Core::DeprecatedFile> device_file)
  44. {
  45. auto table = TRY(adopt_nonnull_own_or_enomem(new (nothrow) GUIDPartitionTable(move(device_file))));
  46. #endif
  47. if (!table->is_valid())
  48. return Error::from_errno(EINVAL);
  49. return table;
  50. }
  51. #ifdef KERNEL
  52. GUIDPartitionTable::GUIDPartitionTable(Kernel::StorageDevice& device)
  53. : MBRPartitionTable(device)
  54. #else
  55. GUIDPartitionTable::GUIDPartitionTable(NonnullRefPtr<Core::DeprecatedFile> device_file)
  56. : MBRPartitionTable(move(device_file))
  57. #endif
  58. {
  59. // FIXME: Handle OOM failure here.
  60. m_cached_header = ByteBuffer::create_zeroed(m_block_size).release_value_but_fixme_should_propagate_errors();
  61. VERIFY(partitions_count() == 0);
  62. if (!initialize())
  63. m_valid = false;
  64. }
  65. GUIDPartitionHeader const& GUIDPartitionTable::header() const
  66. {
  67. return *(GUIDPartitionHeader const*)m_cached_header.data();
  68. }
  69. bool GUIDPartitionTable::initialize()
  70. {
  71. VERIFY(m_cached_header.data() != nullptr);
  72. auto first_gpt_block = (m_block_size == 512) ? 1 : 0;
  73. #ifdef KERNEL
  74. auto buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_header.data());
  75. if (!m_device->read_block(first_gpt_block, buffer))
  76. return false;
  77. #else
  78. m_device_file->seek(first_gpt_block * m_block_size);
  79. if (m_device_file->read(m_cached_header.data(), m_cached_header.size()) != (int)m_block_size)
  80. return false;
  81. #endif
  82. dbgln_if(GPT_DEBUG, "GUIDPartitionTable: signature - {:#08x} {:#08x}", header().sig[1], header().sig[0]);
  83. if (header().sig[0] != GPT_SIGNATURE && header().sig[1] != GPT_SIGNATURE2) {
  84. dbgln("GUIDPartitionTable: bad signature {:#08x} {:#08x}", header().sig[1], header().sig[0]);
  85. return false;
  86. }
  87. auto entries_buffer_result = ByteBuffer::create_zeroed(m_block_size);
  88. if (entries_buffer_result.is_error()) {
  89. dbgln("GUIDPartitionTable: not enough memory for entries buffer");
  90. return false;
  91. }
  92. auto entries_buffer = entries_buffer_result.release_value();
  93. #ifdef KERNEL
  94. auto raw_entries_buffer = UserOrKernelBuffer::for_kernel_buffer(entries_buffer.data());
  95. #endif
  96. size_t raw_byte_index = header().partition_array_start_lba * m_block_size;
  97. for (size_t entry_index = 0; entry_index < header().entries_count; entry_index++) {
  98. #ifdef KERNEL
  99. if (!m_device->read_block((raw_byte_index / m_block_size), raw_entries_buffer))
  100. return false;
  101. #else
  102. m_device_file->seek(raw_byte_index);
  103. if (m_device_file->read(entries_buffer.data(), entries_buffer.size()) != (int)m_block_size)
  104. return false;
  105. #endif
  106. auto* entries = (GPTPartitionEntry const*)entries_buffer.data();
  107. auto& entry = entries[entry_index % (m_block_size / (size_t)header().partition_entry_size)];
  108. Array<u8, 16> partition_type {};
  109. partition_type.span().overwrite(0, entry.partition_guid, partition_type.size());
  110. if (is_unused_entry(partition_type)) {
  111. raw_byte_index += header().partition_entry_size;
  112. continue;
  113. }
  114. Array<u8, 16> unique_guid {};
  115. unique_guid.span().overwrite(0, entry.unique_guid, unique_guid.size());
  116. dbgln("Detected GPT partition (entry={}), offset={}, limit={}", entry_index, entry.first_lba, entry.last_lba);
  117. m_partitions.append({ entry.first_lba, entry.last_lba, partition_type, unique_guid, entry.attributes });
  118. raw_byte_index += header().partition_entry_size;
  119. }
  120. return true;
  121. }
  122. bool GUIDPartitionTable::is_unused_entry(Array<u8, 16> partition_type) const
  123. {
  124. return all_of(partition_type, [](auto const octet) { return octet == 0; });
  125. }
  126. }