Serenity Operating System
1/*
2 * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Types.h>
8#include <Kernel/FileSystem/ISO9660FS/Definitions.h>
9#include <Kernel/FileSystem/ISO9660FS/DirectoryIterator.h>
10#include <Kernel/KBuffer.h>
11
12namespace Kernel {
13
14ISO9660DirectoryIterator::ISO9660DirectoryIterator(ISO9660FS& fs, ISO::DirectoryRecordHeader const& header)
15 : m_fs(fs)
16 , m_current_header(&header)
17{
18 // FIXME: Panic or alternative method?
19 (void)read_directory_contents();
20 get_header();
21}
22
23ErrorOr<bool> ISO9660DirectoryIterator::next()
24{
25 if (done())
26 return false;
27 dbgln_if(ISO9660_VERY_DEBUG, "next(): Called");
28
29 if (has_flag(m_current_header->file_flags, ISO::FileFlags::Directory)) {
30 dbgln_if(ISO9660_VERY_DEBUG, "next(): Recursing");
31 {
32 TRY(m_directory_stack.try_append(move(m_current_directory)));
33 }
34
35 dbgln_if(ISO9660_VERY_DEBUG, "next(): Pushed into directory stack");
36
37 TRY(read_directory_contents());
38
39 dbgln_if(ISO9660_VERY_DEBUG, "next(): Read directory contents");
40
41 m_current_directory.offset = 0;
42 get_header();
43 if (m_current_header->length == 0) {
44 // We have found an empty directory, let's continue with the
45 // next one.
46 if (!go_up())
47 return false;
48 } else {
49 // We cannot skip here, as this is the first record in this
50 // extent.
51 return true;
52 }
53 }
54
55 return skip();
56}
57
58bool ISO9660DirectoryIterator::skip()
59{
60 VERIFY(m_current_directory.entry);
61
62 if (m_current_directory.offset >= m_current_directory.entry->length) {
63 dbgln_if(ISO9660_VERY_DEBUG, "skip(): Was at last item already");
64 return false;
65 }
66
67 m_current_directory.offset += m_current_header->length;
68 get_header();
69 if (m_current_header->length == 0) {
70 // According to ECMA 119, if a logical block contains directory
71 // records, then the leftover bytes in the logical block are
72 // all zeros. So if our directory header has a length of 0,
73 // we're probably looking at padding.
74 //
75 // Of course, this doesn't mean we're done; it only means that there
76 // are no more directory entries in *this* logical block. If we
77 // have at least one more logical block of data length to go, we
78 // need to snap to the next logical block, because directory records
79 // cannot span multiple logical blocks.
80 u32 remaining_bytes = m_current_directory.entry->length - m_current_directory.offset;
81 if (remaining_bytes > m_fs.logical_block_size()) {
82 m_current_directory.offset += remaining_bytes % m_fs.logical_block_size();
83 get_header();
84
85 dbgln_if(ISO9660_VERY_DEBUG, "skip(): Snapped to next logical block (succeeded)");
86 return true;
87 }
88
89 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);
90 return false;
91 }
92
93 dbgln_if(ISO9660_VERY_DEBUG, "skip(): Skipped to next item");
94 return true;
95}
96
97bool ISO9660DirectoryIterator::go_up()
98{
99 if (m_directory_stack.is_empty()) {
100 dbgln_if(ISO9660_VERY_DEBUG, "go_up(): Empty directory stack");
101 return false;
102 }
103
104 m_current_directory = m_directory_stack.take_last();
105 get_header();
106
107 dbgln_if(ISO9660_VERY_DEBUG, "go_up(): Went up a directory");
108 return true;
109}
110
111bool ISO9660DirectoryIterator::done() const
112{
113 VERIFY(m_current_directory.entry);
114 auto result = m_directory_stack.is_empty() && m_current_directory.offset >= m_current_directory.entry->length;
115 dbgln_if(ISO9660_VERY_DEBUG, "done(): {}", result);
116 return result;
117}
118
119ErrorOr<void> ISO9660DirectoryIterator::read_directory_contents()
120{
121 m_current_directory.entry = TRY(m_fs.directory_entry_for_record({}, m_current_header));
122 return {};
123}
124
125void ISO9660DirectoryIterator::get_header()
126{
127 VERIFY(m_current_directory.entry);
128 if (!m_current_directory.entry->blocks)
129 return;
130
131 m_current_header = reinterpret_cast<ISO::DirectoryRecordHeader const*>(m_current_directory.entry->blocks->data() + m_current_directory.offset);
132}
133
134}