ISO9660FileSystem.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /*
  2. * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/EnumBits.h>
  8. #include <AK/Error.h>
  9. #include <AK/HashMap.h>
  10. #include <AK/NonnullRefPtr.h>
  11. #include <AK/RecursionDecision.h>
  12. #include <AK/StringView.h>
  13. #include <AK/Types.h>
  14. #include <Kernel/FileSystem/BlockBasedFileSystem.h>
  15. #include <Kernel/FileSystem/Inode.h>
  16. #include <Kernel/KBuffer.h>
  17. namespace Kernel {
  18. namespace ISO {
  19. // The implemented spec here is ECMA 119, available at:
  20. // https://www.ecma-international.org/wp-content/uploads/ECMA-119_4th_edition_june_2019.pdf
  21. template<typename T>
  22. struct [[gnu::packed]] LittleAndBigEndian {
  23. T little;
  24. T big;
  25. };
  26. // 8.4.26.1 Date and Time Format
  27. struct [[gnu::packed]] AsciiDateAndTime {
  28. // All of these fields are ASCII digits. :^)
  29. u8 year[4];
  30. u8 month[2];
  31. u8 day[2];
  32. u8 hour[2];
  33. u8 minute[2];
  34. u8 second[2];
  35. u8 hundredths_of_second[2];
  36. // From OSDev wiki:
  37. // Time zone offset from GMT in 15 minute intervals, starting at
  38. // interval -48 (west) and running up to interval 52 (east). So value 0
  39. // indicates interval -48 which equals GMT-12 hours, and value 100
  40. // indicates interval 52 which equals GMT+13 hours.
  41. u8 timezone_offset;
  42. };
  43. static_assert(sizeof(AsciiDateAndTime) == 17);
  44. // 9.1.5 Recording Date and Time (BP 19 to 25)
  45. struct [[gnu::packed]] NumericalDateAndTime {
  46. u8 years_since_1900;
  47. u8 month;
  48. u8 day;
  49. u8 hour;
  50. u8 minute;
  51. u8 second;
  52. // Same format as AsciiDateAndTime.
  53. u8 timezone_offset;
  54. };
  55. static_assert(sizeof(NumericalDateAndTime) == 7);
  56. // --- Path Table ---
  57. // 9.4 Format of a Path Table Record
  58. struct [[gnu::packed]] PathTableRecord {
  59. u8 directory_identifier_length;
  60. u8 extended_attribute_record_length;
  61. u32 extent_location;
  62. u16 parent_directory_number;
  63. u8 directory_identifier[];
  64. };
  65. static_assert(sizeof(PathTableRecord) == 8);
  66. // --- Extended Attribute Record ---
  67. // 9.5.3 Permissions
  68. enum class ExtendedPermissions : u16 {
  69. SystemGroupReadable = 1 << 0,
  70. SystemGroupExecutable = 1 << 2,
  71. UserReadable = 1 << 4,
  72. UserExecutable = 1 << 6,
  73. GroupReadable = 1 << 8,
  74. GroupExecutable = 1 << 10,
  75. OtherReadable = 1 << 12,
  76. OtherExecutable = 1 << 14,
  77. };
  78. AK_ENUM_BITWISE_OPERATORS(ExtendedPermissions);
  79. // 9.5.8 Record Format
  80. enum class RecordFormat : u8 {
  81. NotSpecified = 0,
  82. FixedLengthRecords = 1,
  83. LittleEndianVariableRecords = 2,
  84. BigEndianVariableRecords = 3,
  85. // 4-127 are reserved for future standardization.
  86. // 128-255 are reserved for system use.
  87. };
  88. // 9.5.9 Record Attributes
  89. enum class RecordAttributes : u8 {
  90. // This value means the record is stored like: \n123456\r.
  91. LfCrDelimited = 0,
  92. FortranVerticalSpacing = 1,
  93. ContainsControlInformation = 2,
  94. // 3-255 are reserved for future standardization.
  95. };
  96. // 9.5 Format of an Extended Attribute Record
  97. struct [[gnu::packed]] ExtendedAttributeRecord {
  98. LittleAndBigEndian<u16> owner_identification;
  99. LittleAndBigEndian<u16> group_identification;
  100. ExtendedPermissions permissions;
  101. AsciiDateAndTime file_creation_date_and_time;
  102. AsciiDateAndTime file_modification_date_and_time;
  103. AsciiDateAndTime file_expiration_date_and_time;
  104. AsciiDateAndTime file_effective_date_and_time;
  105. RecordFormat record_format;
  106. u8 record_attributes;
  107. LittleAndBigEndian<u16> record_length;
  108. u8 system_identifier[32];
  109. u8 system_use[64];
  110. u8 extended_attribute_record_version;
  111. u8 escape_sequence_length;
  112. u8 reserved[64];
  113. LittleAndBigEndian<u16> application_use_length;
  114. // NOTE: Application use is immediately followed by escape sequences (no
  115. // padding).
  116. u8 application_use_and_escape_sequences[];
  117. };
  118. static_assert(sizeof(ExtendedAttributeRecord) == 250);
  119. // --- Files and Directories ---
  120. // 9.1.6 File Flags
  121. enum class FileFlags : u8 {
  122. Hidden = 1 << 0, // The "existence" flag
  123. Directory = 1 << 1,
  124. AssociatedFile = 1 << 2,
  125. Record = 1 << 3,
  126. Protection = 1 << 4,
  127. // 5 and 6 are reserved.
  128. MultiExtent = 1 << 7,
  129. };
  130. AK_ENUM_BITWISE_OPERATORS(FileFlags);
  131. struct [[gnu::packed]] DirectoryRecordHeader {
  132. u8 length;
  133. u8 extended_attribute_record_length;
  134. LittleAndBigEndian<u32> extent_location;
  135. LittleAndBigEndian<u32> data_length;
  136. NumericalDateAndTime recording_date_and_time;
  137. FileFlags file_flags;
  138. u8 file_unit_size;
  139. u8 interleave_gap_size;
  140. LittleAndBigEndian<u16> volume_sequence_number;
  141. u8 file_identifier_length;
  142. // NOTE: The file identifier itself is of variable length, so it and the
  143. // fields following it are not included in this struct. Instead, they are:
  144. //
  145. // 34 to (33+file_identifier_length) - file identifier
  146. // 1 byte of padding, if file_identifier_length is even
  147. //
  148. // The remaining bytes are system use (ISO9660 extensions).
  149. };
  150. static_assert(sizeof(DirectoryRecordHeader) == 33);
  151. // --- Volume Descriptors ---
  152. enum class VolumeDescriptorType : u8 {
  153. BootRecord = 0,
  154. PrimaryVolumeDescriptor = 1,
  155. SupplementaryOrEnhancedVolumeDescriptor = 2,
  156. VolumePartitionDescriptor = 3,
  157. // 4-254 are reserved.
  158. VolumeDescriptorSetTerminator = 255,
  159. };
  160. // 8.1 Format of a Volume Descriptor
  161. struct [[gnu::packed]] VolumeDescriptorHeader {
  162. VolumeDescriptorType type;
  163. // NOTE: Contains exactly "CD001".
  164. u8 identifier[5];
  165. u8 version;
  166. };
  167. static_assert(sizeof(VolumeDescriptorHeader) == 7);
  168. // 8.2 Boot Record
  169. struct [[gnu::packed]] BootRecord {
  170. VolumeDescriptorHeader header;
  171. u8 boot_system_identifier[32];
  172. u8 boot_identifier[32];
  173. u8 boot_system_use[1977];
  174. };
  175. static_assert(sizeof(BootRecord) == 2048);
  176. // 8.3 Volume Descriptor Set Terminator
  177. struct [[gnu::packed]] VolumeDescriptorSetTerminator {
  178. VolumeDescriptorHeader header;
  179. u8 zeros[2041];
  180. };
  181. static_assert(sizeof(VolumeDescriptorSetTerminator) == 2048);
  182. // 8.4 Primary Volume Descriptor
  183. struct [[gnu::packed]] PrimaryVolumeDescriptor {
  184. VolumeDescriptorHeader header;
  185. u8 unused1;
  186. u8 system_identifier[32];
  187. u8 volume_identifier[32];
  188. u64 unused2;
  189. LittleAndBigEndian<u32> volume_space_size;
  190. u8 unused3[32];
  191. LittleAndBigEndian<u16> volume_set_size;
  192. LittleAndBigEndian<u16> volume_sequence_number;
  193. LittleAndBigEndian<u16> logical_block_size;
  194. LittleAndBigEndian<u32> path_table_size;
  195. u32 l_path_table_occurrence_location;
  196. u32 l_path_table_optional_occurrence_location;
  197. u32 m_path_table_occurrence_location;
  198. u32 m_path_table_optional_occurrence_location;
  199. DirectoryRecordHeader root_directory_record_header;
  200. u8 root_directory_identifier; // Exactly 0x00.
  201. u8 volume_set_identifier[128];
  202. u8 publisher_identifier[128];
  203. u8 data_preparer_identifier[128];
  204. u8 application_identifier[128];
  205. u8 copyright_file_identifier[37];
  206. u8 abstract_file_identifier[37];
  207. u8 bibliographic_file_identifier[37];
  208. AsciiDateAndTime volume_creation_date_and_time;
  209. AsciiDateAndTime volume_modification_date_and_time;
  210. AsciiDateAndTime volume_expiration_date_and_time;
  211. AsciiDateAndTime volume_effective_date_and_time;
  212. u8 file_structure_version; // Always 0x01.
  213. u8 unused4;
  214. u8 application_use[512];
  215. u8 reserved[653];
  216. };
  217. static_assert(sizeof(PrimaryVolumeDescriptor) == 2048);
  218. // 8.6 Volume Partition Descriptor
  219. struct [[gnu::packed]] VolumePartitionDescriptor {
  220. VolumeDescriptorHeader header;
  221. u8 unused;
  222. u8 system_identifier[32];
  223. u8 volume_partition_identifier[32];
  224. LittleAndBigEndian<u32> volume_partition_location;
  225. LittleAndBigEndian<u32> volume_partition_size;
  226. u8 system_use[1960];
  227. };
  228. static_assert(sizeof(VolumePartitionDescriptor) == 2048);
  229. }
  230. class ISO9660Inode;
  231. class ISO9660DirectoryIterator;
  232. class ISO9660FS final : public BlockBasedFileSystem {
  233. friend ISO9660Inode;
  234. friend ISO9660DirectoryIterator;
  235. public:
  236. struct DirectoryEntry : public RefCounted<DirectoryEntry> {
  237. u32 extent { 0 };
  238. u32 length { 0 };
  239. // NOTE: This can never be empty if we read the directory successfully.
  240. // We need it as an OwnPtr to default-construct this struct.
  241. OwnPtr<KBuffer> blocks;
  242. static ErrorOr<NonnullRefPtr<DirectoryEntry>> try_create(u32 extent, u32 length, OwnPtr<KBuffer> blocks)
  243. {
  244. return adopt_nonnull_ref_or_enomem(new (nothrow) DirectoryEntry(extent, length, move(blocks)));
  245. }
  246. private:
  247. DirectoryEntry(u32 extent, u32 length, OwnPtr<KBuffer> blocks)
  248. : extent(extent)
  249. , length(length)
  250. , blocks(move(blocks))
  251. {
  252. }
  253. };
  254. static ErrorOr<NonnullRefPtr<ISO9660FS>> try_create(OpenFileDescription&);
  255. virtual ~ISO9660FS() override;
  256. virtual ErrorOr<void> initialize() override;
  257. virtual StringView class_name() const override { return "ISO9660FS"sv; }
  258. virtual Inode& root_inode() override;
  259. virtual unsigned total_block_count() const override;
  260. virtual unsigned total_inode_count() const override;
  261. virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override;
  262. ErrorOr<NonnullRefPtr<DirectoryEntry>> directory_entry_for_record(Badge<ISO9660DirectoryIterator>, ISO::DirectoryRecordHeader const* record);
  263. private:
  264. ISO9660FS(OpenFileDescription&);
  265. ErrorOr<void> parse_volume_set();
  266. ErrorOr<void> create_root_inode();
  267. ErrorOr<void> calculate_inode_count() const;
  268. u32 calculate_directory_entry_cache_key(ISO::DirectoryRecordHeader const&);
  269. ErrorOr<void> visit_directory_record(ISO::DirectoryRecordHeader const& record, Function<ErrorOr<RecursionDecision>(ISO::DirectoryRecordHeader const*)> const& visitor) const;
  270. OwnPtr<ISO::PrimaryVolumeDescriptor> m_primary_volume;
  271. RefPtr<ISO9660Inode> m_root_inode;
  272. mutable u32 m_cached_inode_count { 0 };
  273. HashMap<u32, NonnullRefPtr<DirectoryEntry>> m_directory_entry_cache;
  274. };
  275. class ISO9660Inode final : public Inode {
  276. friend ISO9660FS;
  277. public:
  278. virtual ~ISO9660Inode() override;
  279. ISO9660FS& fs() { return static_cast<ISO9660FS&>(Inode::fs()); }
  280. ISO9660FS const& fs() const { return static_cast<ISO9660FS const&>(Inode::fs()); }
  281. // ^Inode
  282. virtual ErrorOr<size_t> read_bytes(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override;
  283. virtual InodeMetadata metadata() const override;
  284. virtual ErrorOr<void> traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)>) const override;
  285. virtual ErrorOr<NonnullRefPtr<Inode>> lookup(StringView name) override;
  286. virtual ErrorOr<void> flush_metadata() override;
  287. virtual ErrorOr<size_t> write_bytes(off_t, size_t, const UserOrKernelBuffer& buffer, OpenFileDescription*) override;
  288. virtual ErrorOr<NonnullRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
  289. virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
  290. virtual ErrorOr<void> remove_child(StringView name) override;
  291. virtual ErrorOr<void> chmod(mode_t) override;
  292. virtual ErrorOr<void> chown(UserID, GroupID) override;
  293. virtual ErrorOr<void> truncate(u64) override;
  294. virtual ErrorOr<void> set_atime(time_t) override;
  295. virtual ErrorOr<void> set_ctime(time_t) override;
  296. virtual ErrorOr<void> set_mtime(time_t) override;
  297. virtual void one_ref_left() override;
  298. private:
  299. // HACK: The base ISO 9660 standard says the maximum filename length is 37
  300. // bytes large; however, we can read filenames longer than that right now
  301. // without any problems, so let's allow it anyway.
  302. static constexpr size_t max_file_identifier_length = 256 - sizeof(ISO::DirectoryRecordHeader);
  303. ISO9660Inode(ISO9660FS&, ISO::DirectoryRecordHeader const& record, StringView name);
  304. static ErrorOr<NonnullRefPtr<ISO9660Inode>> try_create_from_directory_record(ISO9660FS&, ISO::DirectoryRecordHeader const& record, StringView name);
  305. static InodeIndex get_inode_index(ISO::DirectoryRecordHeader const& record, StringView name);
  306. static StringView get_normalized_filename(ISO::DirectoryRecordHeader const& record, Bytes buffer);
  307. void create_metadata();
  308. time_t parse_numerical_date_time(ISO::NumericalDateAndTime const&);
  309. InodeMetadata m_metadata;
  310. ISO::DirectoryRecordHeader m_record;
  311. };
  312. }
  313. using Kernel::ISO::has_any_flag;
  314. using Kernel::ISO::has_flag;