The Pedigree Project 0.1
Iso9660Filesystem.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 "Iso9660Filesystem.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/TargetInfo.h"
23#include "pedigree/kernel/machine/Disk.h"
24#include "pedigree/kernel/utilities/StaticString.h"
25#include "pedigree/kernel/utilities/utility.h"
26
27#include "Iso9660Directory.h"
28#include "Iso9660File.h"
29#include "iso9660.h"
30#include "modules/Module.h"
31#include "modules/system/vfs/File.h"
32#include "modules/system/vfs/VFS.h"
33
34namespace {
35constexpr size_t IsoSectorSize = 2048;
36constexpr size_t IsoSectorViewCapacity =
37 ((IsoSectorSize + TargetInfo::getPageSize() - 1) / TargetInfo::getPageSize()) + 1;
38} // namespace
39
40String WideToMultiByteStr(uint8_t* in, size_t inLen, size_t maxLen) {
42 ret.clear();
43
44 while (inLen > 0 && maxLen > 0 && (*in || in[1])) {
45 uint16_t c = (*in << 8) | in[1];
46 if (c > 0x7f) {
48 } else {
49 ret.append(static_cast<char>(c));
50 --maxLen;
51 }
52
53 in += 2;
54 --inLen;
55 }
56 ret.append('\0');
57
58 return String(ret);
59}
60
61Iso9660Filesystem::Iso9660Filesystem()
62 : m_pDisk(0),
63 m_PrimaryVolDesc(),
64 m_SuppVolDesc(),
65 m_VolDesc(),
66 m_RootDir(0),
67 m_JolietLevel(0),
68 m_BlockSize(0),
69 m_BlockNumber(0),
70 m_VolumeLabel(""),
71 m_pRoot(0) {}
72
73Iso9660Filesystem::~Iso9660Filesystem() {}
74
76 m_pDisk = pDisk;
77
79 m_BlockSize = IsoSectorSize;
80 m_BlockNumber = 0;
81
82 // Read volume descriptors until the primary one is found
83 // Sector 16 is the first sector on the disk
84 bool bFound = false;
85 for (size_t i = 16; i < 256; i++) {
86 const uint64_t diskLocation = i * m_BlockSize;
87 alignas(Iso9660VolumeDescriptorPrimary) uint8_t sector[IsoSectorSize];
88 if (!readSector(diskLocation, sector)) {
89 return false;
90 }
91 const BufferView buffer(sector, sizeof(sector));
92
93 // Get the descriptor for this entry
95 if (StringCompareN(reinterpret_cast<const char*>(vDesc->Ident), "CD001", 5) != 0) {
96 NOTICE("IDENT: " << reinterpret_cast<const char*>(vDesc->Ident));
97 return false;
98 }
99
100 const uint8_t descriptorType = vDesc->Type;
101
102 // Is this a primary descriptor?
103 if (descriptorType == PRIM_VOL_DESC) {
104 MemoryCopy(&m_PrimaryVolDesc, buffer.data(), sizeof(Iso9660VolumeDescriptorPrimary));
105 bFound = true;
106 } else if (descriptorType == SUPP_VOL_DESC) {
107 MemoryCopy(&m_SuppVolDesc, buffer.data(), sizeof(Iso9660VolumeDescriptorPrimary));
108 bFound = true;
109
110 // Figure out the Joliet level
111 if (m_SuppVolDesc.Unused3_EscSequences[0] == 0x25 &&
112 m_SuppVolDesc.Unused3_EscSequences[1] == 0x2F) {
113 if (m_SuppVolDesc.Unused3_EscSequences[2] == 0x40)
114 m_JolietLevel = 1;
115 else if (m_SuppVolDesc.Unused3_EscSequences[2] == 0x43)
116 m_JolietLevel = 2;
117 else if (m_SuppVolDesc.Unused3_EscSequences[2] == 0x45)
118 m_JolietLevel = 3;
119 else
120 NOTICE("No Joliet level found");
121 } else
122 NOTICE("Not handling Joliet level");
123 }
124 if (descriptorType == TERM_VOL_DESC)
125 break;
126 }
127
128 if (!bFound) {
129 WARNING(
130 "ISO9660: Neither a primary or supplementary volume descriptor "
131 "was found");
132 return false;
133 }
134
135 if (m_JolietLevel) {
136 // In this case, the supplementary descriptor is actually the main one
137 m_PrimaryVolDesc = m_SuppVolDesc;
138 }
139
140 // Grab the volume label, properly trimmed
141 char volLabel[33] = {};
142 MemoryCopy(volLabel, m_PrimaryVolDesc.VolIdent, 32);
143 if (m_JolietLevel) {
144 String volLabelString = WideToMultiByteStr(reinterpret_cast<uint8_t*>(volLabel), 16, 32);
146 str.append(volLabelString);
147
148 size_t i;
149 for (i = str.length() - 1; static_cast<int32_t>(i) >= 0; i--)
150 if (str[i] != ' ')
151 break;
152 str = str.left(i + 1);
153 m_VolumeLabel = String(str);
154 } else {
155 size_t labelLength = 32;
156 while (labelLength && volLabel[labelLength - 1] == ' ')
157 --labelLength;
158 volLabel[labelLength] = 0;
159 m_VolumeLabel = String(volLabel);
160 }
161
162 if (m_VolumeLabel.length() == 0) {
163 // no volume label found, do a default
165 str += "no-volume-label@";
166 str.append(reinterpret_cast<uintptr_t>(this), 16);
167 m_VolumeLabel.assign(str, str.length(), true);
168 }
169
170 m_RootDir = reinterpret_cast<Iso9660DirRecord*>(m_PrimaryVolDesc.RootDirRecord);
171 m_pRoot = fileFromDirRecord(*m_RootDir, 1, 0, true);
172
173 m_bReadOnly = true;
174
175 return true;
176}
177
178Filesystem* Iso9660Filesystem::probe(Disk* pDisk) {
180 if (!pFs->initialise(pDisk)) {
181 delete pFs;
182 return 0;
183 } else {
184 return pFs;
185 }
186}
187
189 return m_pRoot;
190}
191
195
196bool Iso9660Filesystem::readSector(uint64_t location, void* destination) {
197 if (!m_pDisk || !destination) {
198 return false;
199 }
200
201 BufferView storage[IsoSectorViewCapacity];
202 BufferViewSequence views(storage, IsoSectorViewCapacity);
203 if (!m_pDisk->readViews(location, IsoSectorSize, views)) {
204 return false;
205 }
206
207 const bool copied = views.copyTo(destination, IsoSectorSize);
208 m_pDisk->unpinViews(location, views);
209 return copied;
210}
211
212uintptr_t Iso9660Filesystem::readBlock(File* pFile, uint64_t location) {
213 // Sanity check.
214 if (pFile->isDirectory())
215 return 0;
216
217 Iso9660File* file = reinterpret_cast<Iso9660File*>(pFile);
218 Iso9660DirRecord rec = file->getDirRecord();
219
220 // Attempting to read past the EOF?
221 if (location >= pFile->getSize())
222 return 0; // Impossible
223
224 const size_t viewLength =
226 const uint64_t diskLocation =
227 (static_cast<uint64_t>(LITTLE_TO_HOST32(rec.ExtentLocation_LE)) * m_BlockSize) + location;
228 const BufferView buffer = m_pDisk->read(diskLocation);
229 if (!buffer || buffer.size() < viewLength) {
230 if (buffer) {
231 m_pDisk->unpin(diskLocation);
232 }
233 return 0;
234 }
235
236 return buffer.address();
237}
238
239bool Iso9660Filesystem::createFile(File* parent, const String& filename, uint32_t mask) {
240 return false;
241}
242
243bool Iso9660Filesystem::createDirectory(File* parent, const String& filename, uint32_t mask) {
244 return false;
245}
246
247bool Iso9660Filesystem::createSymlink(File* parent, const String& filename, const String& value) {
248 return false;
249}
250
252 return false;
253}
254
255String Iso9660Filesystem::parseName(Iso9660DirRecord& dirRecord) {
256 if (m_JolietLevel)
257 return parseJolietName(dirRecord);
258
260 ret.clear();
261
262 // ISO9660 maximum name length is 31 bytes
263 uint8_t* fileIdent = reinterpret_cast<uint8_t*>(adjust_pointer(&dirRecord, sizeof(dirRecord)));
264 size_t len = (dirRecord.FileIdentLen < 31) ? dirRecord.FileIdentLen : 31;
265 size_t i;
266 for (i = 0; i < len; i++) {
267 if (fileIdent[i] == ';')
268 break;
269 else
270 ret.append(toLower(static_cast<char>(fileIdent[i])));
271 }
272
273 if (i && (fileIdent[i - 1] == '.'))
274 ret.stripLast();
275 ret.append('\0');
276
277 return String(ret);
278}
279
280String Iso9660Filesystem::parseJolietName(Iso9660DirRecord& name) {
281 uint8_t* fileIdent = reinterpret_cast<uint8_t*>(adjust_pointer(&name, sizeof(name)));
282 String s = WideToMultiByteStr(fileIdent, name.FileIdentLen >> 1, 64);
284 str.append(s);
285 size_t len = str.length();
286
287 if ((len > 2) && (str[len - 2] == ';') && (str[len - 1] == '1'))
288 len -= 2;
289
290 while (len >= 2 && (str[len - 1] == '.'))
291 len--;
292 str = str.left(len);
293
294 return String(str);
295}
296
297File* Iso9660Filesystem::fileFromDirRecord(Iso9660DirRecord& dir, size_t inodeNum, File* parent,
298 bool bDirectory) {
299 String fileName = parseName(dir);
300 Time::Timestamp t = timeToUnix(dir.Time);
301 if (bDirectory) {
302 Iso9660Directory* ret = new Iso9660Directory(fileName, inodeNum, this, parent, dir, t, t, t);
303 return ret;
304 } else {
305 Iso9660File* ret = new Iso9660File(fileName, t, t, t, inodeNum, this,
306 LITTLE_TO_HOST32(dir.DataLen_LE), dir, parent);
307 return ret;
308 }
309}
310
311#ifndef ISO9660_STANDALONE
312static bool initIso9660() {
313 VFS::instance().addProbeCallback(&Iso9660Filesystem::probe);
314 return true;
315}
316
317static void destroyIso9660() {
318 if (!VFS::instance().removeProbeCallback(&Iso9660Filesystem::probe)) {
319 FATAL("ISO9660 probe callback was not registered during unload");
320 }
321}
322
323MODULE_INFO("iso9660", &initIso9660, &destroyIso9660, "vfs");
324#endif
Definition Disk.h:35
void unpinViews(uint64_t location, BufferViewSequence &views)
Definition Disk.cc:330
virtual BufferView read(uint64_t location)
Definition Disk.cc:163
virtual void unpin(uint64_t location)=0
MUST_USE_RESULT bool readViews(uint64_t location, size_t length, BufferViewSequence &views)
Definition Disk.cc:303
Definition File.h:75
virtual bool isDirectory()
Definition File.cc:804
bool m_bReadOnly
Definition Filesystem.h:186
virtual const String & getVolumeLabel() const
virtual bool createFile(File *parent, const String &filename, uint32_t mask)
virtual bool createSymlink(File *parent, const String &filename, const String &value)
bool readSector(uint64_t location, void *destination)
virtual bool removeNode(File *parent, const String &filename, File *file)
virtual File * getRoot() const
virtual bool initialise(Disk *pDisk)
Iso9660VolumeDescriptorPrimary m_PrimaryVolDesc
virtual bool createDirectory(File *parent, const String &filename, uint32_t mask)
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
void addProbeCallback(Filesystem::ProbeCallback callback)
Definition VFS.cc:1050
static VFS & instance()
Definition VFS.cc:311