summaryrefslogtreecommitdiff
path: root/Kernel/FileSystem/ISO9660FS/DirectoryIterator.cpp
blob: b127d85c7a062d65dab5e8b53e07e1279cba96e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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/Library/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);
}

}