The Pedigree Project 0.1
Iso9660Directory.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "Iso9660Directory.h"
21#include "pedigree/kernel/utilities/BufferView.h"
22
23namespace {
24constexpr uint64_t IsoSectorSize = 2048;
25}
26
27Iso9660Directory::~Iso9660Directory() = default;
28
29Directory::LookupStatus Iso9660Directory::resolveChild(const StringView& name, File*& child) {
30 child = nullptr;
31 if (!m_pFs) {
32 return LookupStatus::IoError;
33 }
34
35 ResolveContext context = {this, name, nullptr, 0, false};
36 uint64_t cookie = 0;
37 const ReadStatus status = scanDirectory(cookie, resolveEntry, &context);
38 if (context.child) {
39 child = context.child;
40 return LookupStatus::Found;
41 }
42 if (status == ReadStatus::IoError) {
43 return LookupStatus::IoError;
44 }
45 return LookupStatus::NotFound;
46}
47
48Directory::LookupStatus Iso9660Directory::resolveChildAt(uint64_t cookie, const StringView& name,
49 File*& child) {
50 child = nullptr;
51 if (!m_pFs) {
52 return LookupStatus::IoError;
53 }
54
55 const uint64_t expectedCookie = cookie;
56 ResolveContext context = {this, name, nullptr, expectedCookie, true};
57 scanDirectory(cookie, resolveEntry, &context);
58 if (context.child) {
59 child = context.child;
60 return LookupStatus::Found;
61 }
62 // A cookie is only a fast path; retain ordinary lookup semantics if a
63 // caller resumes with a stale location.
64 return resolveChild(name, child);
65}
66
67Directory::ReadStatus Iso9660Directory::readDirectory(uint64_t& cookie,
68 DirectoryEntryEmitter emitter,
69 void* context) {
70 if (!emitter) {
71 return ReadStatus::IoError;
72 }
73
74 ReadContext readContext = {emitter, context};
75 return scanDirectory(cookie, emitEntry, &readContext);
76}
77
78Directory::ReadStatus Iso9660Directory::scanDirectory(uint64_t& cookie, ScannedEntryEmitter emitter,
79 void* context) {
80 if (!m_pFs || !emitter) {
81 return ReadStatus::IoError;
82 }
83
84 const uint64_t directorySize = LITTLE_TO_HOST32(m_Dir.DataLen_LE);
85 const uint64_t directoryLocation = LITTLE_TO_HOST32(m_Dir.ExtentLocation_LE);
86 const uint64_t sectorCount = (directorySize + IsoSectorSize - 1) / IsoSectorSize;
87 if (cookie > directorySize) {
88 return ReadStatus::IoError;
89 }
90
91 uint64_t scanCookie = cookie;
92 while (scanCookie < directorySize) {
93 const uint64_t sector = scanCookie / IsoSectorSize;
94 if (sector >= sectorCount) {
95 return ReadStatus::IoError;
96 }
97
98 const uint64_t sectorStart = sector * IsoSectorSize;
99 const size_t sectorLimit = static_cast<size_t>(min(IsoSectorSize, directorySize - sectorStart));
100 size_t offset = static_cast<size_t>(scanCookie - sectorStart);
101
102 alignas(Iso9660DirRecord) uint8_t sectorBytes[IsoSectorSize];
103 const uint64_t diskLocation = (directoryLocation + sector) * IsoSectorSize;
104 if (!m_pFs->readSector(diskLocation, sectorBytes)) {
105 return ReadStatus::IoError;
106 }
107 const BufferView buffer(sectorBytes, sizeof(sectorBytes));
108
109 while (offset < sectorLimit) {
110 const uint64_t currentCookie = sectorStart + offset;
111 cookie = currentCookie;
112
113 // A zero-length record pads the remainder of this logical sector.
114 if (!sectorBytes[offset]) {
115 scanCookie = min(sectorStart + IsoSectorSize, directorySize);
116 cookie = scanCookie;
117 break;
118 }
119
120 const size_t bytesRemaining = sectorLimit - offset;
121 if (bytesRemaining < sizeof(Iso9660DirRecord)) {
122 return ReadStatus::IoError;
123 }
124
125 Iso9660DirRecord* record = buffer.as<Iso9660DirRecord>(offset);
126 const size_t recordLength = record->RecLen;
127 if (recordLength < sizeof(*record) || recordLength > bytesRemaining ||
128 !record->FileIdentLen || record->FileIdentLen > (recordLength - sizeof(*record))) {
129 return ReadStatus::IoError;
130 }
131
132 const uint64_t nextCookie = currentCookie + recordLength;
133 uint8_t* fileIdentifier = buffer.as<uint8_t>(offset + sizeof(*record));
134 offset += recordLength;
135 scanCookie = nextCookie;
136
137 // Path walking and the common enumeration wrapper provide . and ...
138 const bool dotEntry =
139 record->FileIdentLen == 1 && (fileIdentifier[0] == 0 || fileIdentifier[0] == 1);
140 if (dotEntry || (record->FileFlags & (1 << 0))) {
141 cookie = nextCookie;
142 continue;
143 }
144
145 String fileName = m_pFs->parseName(*record);
146 ScannedEntry entry = {&fileName, record, currentCookie, nextCookie};
147 if (!emitter(context, entry)) {
148 cookie = currentCookie;
149 return ReadStatus::Stopped;
150 }
151 cookie = nextCookie;
152 }
153 }
154
155 cookie = directorySize;
156 return ReadStatus::Complete;
157}
158
159bool Iso9660Directory::resolveEntry(void* opaque, const ScannedEntry& entry) {
160 ResolveContext* context = reinterpret_cast<ResolveContext*>(opaque);
161 if (context->checkCookie && entry.currentCookie != context->expectedCookie) {
162 return false;
163 }
164 if (!(*entry.name == context->name)) {
165 return !context->checkCookie;
166 }
167
168 const bool directory = (entry.record->FileFlags & (1 << 1)) != 0;
169 context->child =
170 context->directory->m_pFs->fileFromDirRecord(*entry.record, 0, context->directory, directory);
171 return false;
172}
173
174bool Iso9660Directory::emitEntry(void* opaque, const ScannedEntry& scanned) {
175 ReadContext* context = reinterpret_cast<ReadContext*>(opaque);
176 const EntryType type =
177 (scanned.record->FileFlags & (1 << 1)) ? EntryType::Directory : EntryType::Regular;
178 DirectoryEntryView entry = {scanned.name->view(), 0, type, scanned.currentCookie,
179 scanned.nextCookie};
180 return context->emitter(context->context, entry);
181}
Definition File.h:75
LookupStatus resolveChild(const StringView &name, File *&child) override
LookupStatus resolveChildAt(uint64_t cookie, const StringView &name, File *&child) override
ReadStatus readDirectory(uint64_t &cookie, DirectoryEntryEmitter emitter, void *context) override
bool readSector(uint64_t location, void *destination)