FileSystem.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /*
  2. * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Endian.h>
  7. #include <Kernel/FileSystem/ISO9660FS/DirectoryIterator.h>
  8. #include <Kernel/FileSystem/ISO9660FS/FileSystem.h>
  9. #include <Kernel/FileSystem/ISO9660FS/Inode.h>
  10. namespace Kernel {
  11. // NOTE: According to the spec, logical blocks 0 to 15 are system use.
  12. constexpr u32 first_data_area_block = 16;
  13. constexpr u32 logical_sector_size = 2048;
  14. constexpr u32 max_cached_directory_entries = 128;
  15. ErrorOr<NonnullRefPtr<FileSystem>> ISO9660FS::try_create(OpenFileDescription& description)
  16. {
  17. return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ISO9660FS(description)));
  18. }
  19. ISO9660FS::ISO9660FS(OpenFileDescription& description)
  20. : BlockBasedFileSystem(description)
  21. {
  22. set_block_size(logical_sector_size);
  23. m_logical_block_size = logical_sector_size;
  24. }
  25. ISO9660FS::~ISO9660FS() = default;
  26. bool ISO9660FS::is_initialized_while_locked()
  27. {
  28. VERIFY(m_lock.is_locked());
  29. return !m_root_inode.is_null();
  30. }
  31. ErrorOr<void> ISO9660FS::initialize_while_locked()
  32. {
  33. VERIFY(m_lock.is_locked());
  34. VERIFY(!is_initialized_while_locked());
  35. TRY(BlockBasedFileSystem::initialize_while_locked());
  36. TRY(parse_volume_set());
  37. TRY(create_root_inode());
  38. return {};
  39. }
  40. Inode& ISO9660FS::root_inode()
  41. {
  42. VERIFY(!m_root_inode.is_null());
  43. return *m_root_inode;
  44. }
  45. unsigned ISO9660FS::total_block_count() const
  46. {
  47. return LittleEndian { m_primary_volume->volume_space_size.little };
  48. }
  49. unsigned ISO9660FS::total_inode_count() const
  50. {
  51. if (!m_cached_inode_count) {
  52. auto result = calculate_inode_count();
  53. if (result.is_error()) {
  54. // FIXME: This should be able to return a ErrorOr<void>.
  55. return 0;
  56. }
  57. }
  58. return m_cached_inode_count;
  59. }
  60. u8 ISO9660FS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const
  61. {
  62. if (has_flag(static_cast<ISO::FileFlags>(entry.file_type), ISO::FileFlags::Directory)) {
  63. return DT_DIR;
  64. }
  65. return DT_REG;
  66. }
  67. ErrorOr<void> ISO9660FS::prepare_to_clear_last_mount()
  68. {
  69. // FIXME: Do proper cleaning here.
  70. BlockBasedFileSystem::remove_disk_cache_before_last_unmount();
  71. return {};
  72. }
  73. ErrorOr<void> ISO9660FS::parse_volume_set()
  74. {
  75. VERIFY(!m_primary_volume);
  76. auto block = TRY(KBuffer::try_create_with_size("ISO9660FS: Temporary volume descriptor storage"sv, m_logical_block_size, Memory::Region::Access::Read | Memory::Region::Access::Write));
  77. auto block_buffer = UserOrKernelBuffer::for_kernel_buffer(block->data());
  78. auto current_block_index = first_data_area_block;
  79. while (true) {
  80. auto result = raw_read(BlockIndex { current_block_index }, block_buffer);
  81. if (result.is_error()) {
  82. dbgln_if(ISO9660_DEBUG, "Failed to read volume descriptor from ISO file: {}", result.error());
  83. return result;
  84. }
  85. auto const* header = reinterpret_cast<ISO::VolumeDescriptorHeader const*>(block->data());
  86. if (StringView { header->identifier, 5 } != "CD001"sv) {
  87. dbgln_if(ISO9660_DEBUG, "Header magic at volume descriptor {} is not valid", current_block_index - first_data_area_block);
  88. return EIO;
  89. }
  90. switch (header->type) {
  91. case ISO::VolumeDescriptorType::PrimaryVolumeDescriptor: {
  92. auto const* primary_volume = reinterpret_cast<ISO::PrimaryVolumeDescriptor const*>(header);
  93. m_primary_volume = adopt_own_if_nonnull(new ISO::PrimaryVolumeDescriptor(*primary_volume));
  94. break;
  95. }
  96. case ISO::VolumeDescriptorType::BootRecord:
  97. case ISO::VolumeDescriptorType::SupplementaryOrEnhancedVolumeDescriptor:
  98. case ISO::VolumeDescriptorType::VolumePartitionDescriptor: {
  99. break;
  100. }
  101. case ISO::VolumeDescriptorType::VolumeDescriptorSetTerminator: {
  102. goto all_headers_read;
  103. }
  104. default:
  105. dbgln_if(ISO9660_DEBUG, "Unexpected volume descriptor type {} in volume set", static_cast<u8>(header->type));
  106. return EIO;
  107. }
  108. current_block_index++;
  109. }
  110. all_headers_read:
  111. if (!m_primary_volume) {
  112. dbgln_if(ISO9660_DEBUG, "Could not find primary volume");
  113. return EIO;
  114. }
  115. m_logical_block_size = LittleEndian { m_primary_volume->logical_block_size.little };
  116. return {};
  117. }
  118. ErrorOr<void> ISO9660FS::create_root_inode()
  119. {
  120. if (!m_primary_volume) {
  121. dbgln_if(ISO9660_DEBUG, "Primary volume doesn't exist, can't create root inode");
  122. return EIO;
  123. }
  124. m_root_inode = TRY(ISO9660Inode::try_create_from_directory_record(*this, m_primary_volume->root_directory_record_header, {}));
  125. return {};
  126. }
  127. ErrorOr<void> ISO9660FS::calculate_inode_count() const
  128. {
  129. if (!m_primary_volume) {
  130. dbgln_if(ISO9660_DEBUG, "Primary volume doesn't exist, can't calculate inode count");
  131. return EIO;
  132. }
  133. size_t inode_count = 1;
  134. TRY(visit_directory_record(m_primary_volume->root_directory_record_header, [&](ISO::DirectoryRecordHeader const* header) {
  135. if (header == nullptr) {
  136. return RecursionDecision::Continue;
  137. }
  138. inode_count += 1;
  139. if (has_flag(header->file_flags, ISO::FileFlags::Directory)) {
  140. if (header->file_identifier_length == 1) {
  141. auto file_identifier = reinterpret_cast<u8 const*>(header + 1);
  142. if (file_identifier[0] == '\0' || file_identifier[0] == '\1') {
  143. return RecursionDecision::Continue;
  144. }
  145. }
  146. return RecursionDecision::Recurse;
  147. }
  148. return RecursionDecision::Continue;
  149. }));
  150. m_cached_inode_count = inode_count;
  151. return {};
  152. }
  153. ErrorOr<void> ISO9660FS::visit_directory_record(ISO::DirectoryRecordHeader const& record, Function<ErrorOr<RecursionDecision>(ISO::DirectoryRecordHeader const*)> const& visitor) const
  154. {
  155. if (!has_flag(record.file_flags, ISO::FileFlags::Directory)) {
  156. return {};
  157. }
  158. ISO9660DirectoryIterator iterator { const_cast<ISO9660FS&>(*this), record };
  159. while (!iterator.done()) {
  160. auto decision = TRY(visitor(*iterator));
  161. switch (decision) {
  162. case RecursionDecision::Recurse: {
  163. auto has_moved = TRY(iterator.next());
  164. if (!has_moved) {
  165. // If next() hasn't moved then we have read through all the
  166. // directories, and can exit.
  167. return {};
  168. }
  169. continue;
  170. }
  171. case RecursionDecision::Continue: {
  172. while (!iterator.done()) {
  173. if (iterator.skip())
  174. break;
  175. if (!iterator.go_up())
  176. return {};
  177. }
  178. continue;
  179. }
  180. case RecursionDecision::Break:
  181. return {};
  182. }
  183. }
  184. return {};
  185. }
  186. ErrorOr<NonnullLockRefPtr<ISO9660FSDirectoryEntry>> ISO9660FS::directory_entry_for_record(Badge<ISO9660DirectoryIterator>, ISO::DirectoryRecordHeader const* record)
  187. {
  188. u32 extent_location = LittleEndian { record->extent_location.little };
  189. u32 data_length = LittleEndian { record->data_length.little };
  190. auto key = calculate_directory_entry_cache_key(*record);
  191. auto it = m_directory_entry_cache.find(key);
  192. if (it != m_directory_entry_cache.end()) {
  193. dbgln_if(ISO9660_DEBUG, "Cache hit for dirent @ {}", extent_location);
  194. return it->value;
  195. }
  196. dbgln_if(ISO9660_DEBUG, "Cache miss for dirent @ {} :^(", extent_location);
  197. if (m_directory_entry_cache.size() == max_cached_directory_entries) {
  198. // FIXME: A smarter algorithm would probably be nicer.
  199. m_directory_entry_cache.remove(m_directory_entry_cache.begin());
  200. }
  201. if (!(data_length % logical_block_size() == 0)) {
  202. dbgln_if(ISO9660_DEBUG, "Found a directory with non-logical block size aligned data length!");
  203. return EIO;
  204. }
  205. auto blocks = TRY(KBuffer::try_create_with_size("ISO9660FS: Directory traversal buffer"sv, data_length, Memory::Region::Access::Read | Memory::Region::Access::Write));
  206. auto blocks_buffer = UserOrKernelBuffer::for_kernel_buffer(blocks->data());
  207. TRY(raw_read_blocks(BlockBasedFileSystem::BlockIndex { extent_location }, data_length / logical_block_size(), blocks_buffer));
  208. auto entry = TRY(ISO9660FSDirectoryEntry::try_create(extent_location, data_length, move(blocks)));
  209. m_directory_entry_cache.set(key, entry);
  210. dbgln_if(ISO9660_DEBUG, "Cached dirent @ {}", extent_location);
  211. return entry;
  212. }
  213. u32 ISO9660FS::calculate_directory_entry_cache_key(ISO::DirectoryRecordHeader const& record)
  214. {
  215. return LittleEndian { record.extent_location.little };
  216. }
  217. }