summaryrefslogtreecommitdiff
path: root/Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp')
-rw-r--r--Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp134
1 files changed, 134 insertions, 0 deletions
diff --git a/Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp b/Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp
new file mode 100644
index 0000000000..a901122814
--- /dev/null
+++ b/Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Types.h>
+#include <Kernel/FileSystem/ISO9660FS/Definitions.h>
+#include <Kernel/FileSystem/ISO9660FS/DirectoryIterator.h>
+#include <Kernel/KBuffer.h>
+
+namespace Kernel {
+
+ISO9660DirectoryIterator::ISO9660DirectoryIterator(ISO9660FS& fs, ISO::DirectoryRecordHeader const& header)
+ : m_fs(fs)
+ , m_current_header(&header)
+{
+ // FIXME: Panic or alternative method?
+ (void)read_directory_contents();
+ get_header();
+}
+
+ErrorOr<bool> ISO9660DirectoryIterator::next()
+{
+ if (done())
+ return false;
+ dbgln_if(ISO9660_VERY_DEBUG, "next(): Called");
+
+ if (has_flag(m_current_header->file_flags, ISO::FileFlags::Directory)) {
+ dbgln_if(ISO9660_VERY_DEBUG, "next(): Recursing");
+ {
+ TRY(m_directory_stack.try_append(move(m_current_directory)));
+ }
+
+ dbgln_if(ISO9660_VERY_DEBUG, "next(): Pushed into directory stack");
+
+ TRY(read_directory_contents());
+
+ dbgln_if(ISO9660_VERY_DEBUG, "next(): Read directory contents");
+
+ m_current_directory.offset = 0;
+ get_header();
+ if (m_current_header->length == 0) {
+ // We have found an empty directory, let's continue with the
+ // next one.
+ if (!go_up())
+ return false;
+ } else {
+ // We cannot skip here, as this is the first record in this
+ // extent.
+ return true;
+ }
+ }
+
+ return skip();
+}
+
+bool ISO9660DirectoryIterator::skip()
+{
+ VERIFY(m_current_directory.entry);
+
+ if (m_current_directory.offset >= m_current_directory.entry->length) {
+ dbgln_if(ISO9660_VERY_DEBUG, "skip(): Was at last item already");
+ return false;
+ }
+
+ m_current_directory.offset += m_current_header->length;
+ get_header();
+ if (m_current_header->length == 0) {
+ // According to ECMA 119, if a logical block contains directory
+ // records, then the leftover bytes in the logical block are
+ // all zeros. So if our directory header has a length of 0,
+ // we're probably looking at padding.
+ //
+ // Of course, this doesn't mean we're done; it only means that there
+ // are no more directory entries in *this* logical block. If we
+ // have at least one more logical block of data length to go, we
+ // need to snap to the next logical block, because directory records
+ // cannot span multiple logical blocks.
+ u32 remaining_bytes = m_current_directory.entry->length - m_current_directory.offset;
+ if (remaining_bytes > m_fs.logical_block_size()) {
+ m_current_directory.offset += remaining_bytes % m_fs.logical_block_size();
+ get_header();
+
+ dbgln_if(ISO9660_VERY_DEBUG, "skip(): Snapped to next logical block (succeeded)");
+ return true;
+ }
+
+ dbgln_if(ISO9660_VERY_DEBUG, "skip(): Was at the last logical block, at padding now (offset {}, data length {})", m_current_directory.entry->length, m_current_directory.offset);
+ return false;
+ }
+
+ dbgln_if(ISO9660_VERY_DEBUG, "skip(): Skipped to next item");
+ return true;
+}
+
+bool ISO9660DirectoryIterator::go_up()
+{
+ if (m_directory_stack.is_empty()) {
+ dbgln_if(ISO9660_VERY_DEBUG, "go_up(): Empty directory stack");
+ return false;
+ }
+
+ m_current_directory = m_directory_stack.take_last();
+ get_header();
+
+ dbgln_if(ISO9660_VERY_DEBUG, "go_up(): Went up a directory");
+ return true;
+}
+
+bool ISO9660DirectoryIterator::done() const
+{
+ VERIFY(m_current_directory.entry);
+ auto result = m_directory_stack.is_empty() && m_current_directory.offset >= m_current_directory.entry->length;
+ dbgln_if(ISO9660_VERY_DEBUG, "done(): {}", result);
+ return result;
+}
+
+ErrorOr<void> ISO9660DirectoryIterator::read_directory_contents()
+{
+ m_current_directory.entry = TRY(m_fs.directory_entry_for_record({}, m_current_header));
+ return {};
+}
+
+void ISO9660DirectoryIterator::get_header()
+{
+ VERIFY(m_current_directory.entry);
+ if (!m_current_directory.entry->blocks)
+ return;
+
+ m_current_header = reinterpret_cast<ISO::DirectoryRecordHeader const*>(m_current_directory.entry->blocks->data() + m_current_directory.offset);
+}
+
+}