Inode.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/CharacterTypes.h>
  7. #include <AK/Endian.h>
  8. #include <Kernel/FileSystem/ISO9660FS/Inode.h>
  9. namespace Kernel {
  10. ErrorOr<size_t> ISO9660Inode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const
  11. {
  12. VERIFY(m_inode_lock.is_locked());
  13. u32 data_length = LittleEndian { m_record.data_length.little };
  14. u32 extent_location = LittleEndian { m_record.extent_location.little };
  15. if (static_cast<u64>(offset) >= data_length)
  16. return 0;
  17. auto block = TRY(KBuffer::try_create_with_size("ISO9660FS: Inode read buffer"sv, fs().m_logical_block_size));
  18. auto block_buffer = UserOrKernelBuffer::for_kernel_buffer(block->data());
  19. size_t total_bytes = min(size, data_length - offset);
  20. size_t nread = 0;
  21. size_t blocks_already_read = offset / fs().m_logical_block_size;
  22. size_t initial_offset = offset % fs().m_logical_block_size;
  23. auto current_block_index = BlockBasedFileSystem::BlockIndex { extent_location + blocks_already_read };
  24. while (nread != total_bytes) {
  25. size_t bytes_to_read = min(total_bytes - nread, fs().logical_block_size() - initial_offset);
  26. auto buffer_offset = buffer.offset(nread);
  27. dbgln_if(ISO9660_VERY_DEBUG, "ISO9660Inode::read_bytes: Reading {} bytes into buffer offset {}/{}, logical block index: {}", bytes_to_read, nread, total_bytes, current_block_index.value());
  28. TRY(const_cast<ISO9660FS&>(fs()).raw_read(current_block_index, block_buffer));
  29. TRY(buffer_offset.write(block->data() + initial_offset, bytes_to_read));
  30. nread += bytes_to_read;
  31. initial_offset = 0;
  32. current_block_index = BlockBasedFileSystem::BlockIndex { current_block_index.value() + 1 };
  33. }
  34. return nread;
  35. }
  36. InodeMetadata ISO9660Inode::metadata() const
  37. {
  38. return m_metadata;
  39. }
  40. ErrorOr<void> ISO9660Inode::traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> visitor) const
  41. {
  42. Array<u8, max_file_identifier_length> file_identifier_buffer;
  43. ErrorOr<void> result;
  44. return fs().visit_directory_record(m_record, [&](ISO::DirectoryRecordHeader const* record) {
  45. StringView filename = get_normalized_filename(*record, file_identifier_buffer);
  46. dbgln_if(ISO9660_VERY_DEBUG, "traverse_as_directory(): Found {}", filename);
  47. InodeIdentifier id { fsid(), get_inode_index(*record, filename) };
  48. auto entry = FileSystem::DirectoryEntryView(filename, id, static_cast<u8>(record->file_flags));
  49. result = visitor(entry);
  50. if (result.is_error())
  51. return RecursionDecision::Break;
  52. return RecursionDecision::Continue;
  53. });
  54. }
  55. ErrorOr<void> ISO9660Inode::replace_child(StringView, Inode&)
  56. {
  57. return EROFS;
  58. }
  59. ErrorOr<NonnullLockRefPtr<Inode>> ISO9660Inode::lookup(StringView name)
  60. {
  61. LockRefPtr<Inode> inode;
  62. Array<u8, max_file_identifier_length> file_identifier_buffer;
  63. TRY(fs().visit_directory_record(m_record, [&](ISO::DirectoryRecordHeader const* record) {
  64. StringView filename = get_normalized_filename(*record, file_identifier_buffer);
  65. if (filename == name) {
  66. auto maybe_inode = ISO9660Inode::try_create_from_directory_record(fs(), *record, filename);
  67. if (maybe_inode.is_error()) {
  68. // FIXME: The Inode API does not handle allocation failures very
  69. // well... we can't return a ErrorOr from here. It
  70. // would be nice if we could return a ErrorOr<void>(Or) from
  71. // any place where allocation may happen.
  72. dbgln("Could not allocate inode for lookup!");
  73. } else {
  74. inode = maybe_inode.release_value();
  75. }
  76. return RecursionDecision::Break;
  77. }
  78. return RecursionDecision::Continue;
  79. }));
  80. if (!inode)
  81. return ENOENT;
  82. return inode.release_nonnull();
  83. }
  84. ErrorOr<void> ISO9660Inode::flush_metadata()
  85. {
  86. return {};
  87. }
  88. ErrorOr<size_t> ISO9660Inode::write_bytes_locked(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*)
  89. {
  90. return EROFS;
  91. }
  92. ErrorOr<NonnullLockRefPtr<Inode>> ISO9660Inode::create_child(StringView, mode_t, dev_t, UserID, GroupID)
  93. {
  94. return EROFS;
  95. }
  96. ErrorOr<void> ISO9660Inode::add_child(Inode&, StringView, mode_t)
  97. {
  98. return EROFS;
  99. }
  100. ErrorOr<void> ISO9660Inode::remove_child(StringView)
  101. {
  102. return EROFS;
  103. }
  104. ErrorOr<void> ISO9660Inode::chmod(mode_t)
  105. {
  106. return EROFS;
  107. }
  108. ErrorOr<void> ISO9660Inode::chown(UserID, GroupID)
  109. {
  110. return EROFS;
  111. }
  112. ErrorOr<void> ISO9660Inode::truncate(u64)
  113. {
  114. return EROFS;
  115. }
  116. ErrorOr<void> ISO9660Inode::update_timestamps(Optional<Time>, Optional<Time>, Optional<Time>)
  117. {
  118. return EROFS;
  119. }
  120. ISO9660Inode::ISO9660Inode(ISO9660FS& fs, ISO::DirectoryRecordHeader const& record, StringView name)
  121. : Inode(fs, get_inode_index(record, name))
  122. , m_record(record)
  123. {
  124. dbgln_if(ISO9660_VERY_DEBUG, "Creating inode #{}", index());
  125. create_metadata();
  126. }
  127. ISO9660Inode::~ISO9660Inode() = default;
  128. ErrorOr<NonnullLockRefPtr<ISO9660Inode>> ISO9660Inode::try_create_from_directory_record(ISO9660FS& fs, ISO::DirectoryRecordHeader const& record, StringView name)
  129. {
  130. return adopt_nonnull_lock_ref_or_enomem(new (nothrow) ISO9660Inode(fs, record, name));
  131. }
  132. void ISO9660Inode::create_metadata()
  133. {
  134. u32 data_length = LittleEndian { m_record.data_length.little };
  135. bool is_directory = has_flag(m_record.file_flags, ISO::FileFlags::Directory);
  136. auto recorded_at = Time::from_timespec({ parse_numerical_date_time(m_record.recording_date_and_time), 0 });
  137. m_metadata = {
  138. .inode = identifier(),
  139. .size = data_length,
  140. .mode = static_cast<mode_t>((is_directory ? S_IFDIR : S_IFREG) | (is_directory ? 0555 : 0444)),
  141. .uid = 0,
  142. .gid = 0,
  143. .link_count = 1,
  144. .atime = recorded_at,
  145. .ctime = recorded_at,
  146. .mtime = recorded_at,
  147. .dtime = {},
  148. .block_count = 0,
  149. .block_size = 0,
  150. .major_device = 0,
  151. .minor_device = 0,
  152. };
  153. }
  154. time_t ISO9660Inode::parse_numerical_date_time(ISO::NumericalDateAndTime const& date)
  155. {
  156. i32 year_offset = date.years_since_1900 - 70;
  157. return (year_offset * 60 * 60 * 24 * 30 * 12)
  158. + (date.month * 60 * 60 * 24 * 30)
  159. + (date.day * 60 * 60 * 24)
  160. + (date.hour * 60 * 60)
  161. + (date.minute * 60)
  162. + date.second;
  163. }
  164. StringView ISO9660Inode::get_normalized_filename(ISO::DirectoryRecordHeader const& record, Bytes buffer)
  165. {
  166. auto const* file_identifier = reinterpret_cast<u8 const*>(&record + 1);
  167. auto filename = StringView { file_identifier, record.file_identifier_length };
  168. if (filename.length() == 1) {
  169. if (filename[0] == '\0') {
  170. filename = "."sv;
  171. }
  172. if (filename[0] == '\1') {
  173. filename = ".."sv;
  174. }
  175. }
  176. if (!has_flag(record.file_flags, ISO::FileFlags::Directory)) {
  177. // FIXME: We currently strip the file version from the filename,
  178. // but that may be used later down the line if the file actually
  179. // has multiple versions on the disk.
  180. Optional<size_t> semicolon = filename.find(';');
  181. if (semicolon.has_value()) {
  182. filename = filename.substring_view(0, semicolon.value());
  183. }
  184. if (filename[filename.length() - 1] == '.') {
  185. filename = filename.substring_view(0, filename.length() - 1);
  186. }
  187. }
  188. if (filename.length() > buffer.size()) {
  189. // FIXME: Rock Ridge allows filenames up to 255 characters, so we should
  190. // probably support that instead of truncating.
  191. filename = filename.substring_view(0, buffer.size());
  192. }
  193. for (size_t i = 0; i < filename.length(); i++) {
  194. buffer[i] = to_ascii_lowercase(filename[i]);
  195. }
  196. return { buffer.data(), filename.length() };
  197. }
  198. InodeIndex ISO9660Inode::get_inode_index(ISO::DirectoryRecordHeader const& record, StringView name)
  199. {
  200. if (name.is_null()) {
  201. // NOTE: This is the index of the root inode.
  202. return 1;
  203. }
  204. return { pair_int_hash(LittleEndian { record.extent_location.little }, string_hash(name.characters_without_null_termination(), name.length())) };
  205. }
  206. }