dd1231558d79833f00c9bb1bb9dbae0dbb26ec76
[reactos.git] / reactos / boot / freeldr / freeldr / fs / iso.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 * Copyright (C) 2009 Hervé Poussineau <hpoussin@reactos.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #ifndef _M_ARM
22 #include <freeldr.h>
23 #include <debug.h>
24
25 #define SECTORSIZE 2048
26
27 static BOOLEAN IsoSearchDirectoryBufferForFile(PVOID DirectoryBuffer, ULONG DirectoryLength, PCHAR FileName, PISO_FILE_INFO IsoFileInfoPointer)
28 {
29 PDIR_RECORD Record;
30 ULONG Offset;
31 ULONG i;
32 CHAR Name[32];
33
34 DPRINTM(DPRINT_FILESYSTEM, "IsoSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x DirectoryLength = %d FileName = %s\n", DirectoryBuffer, DirectoryLength, FileName);
35
36 RtlZeroMemory(Name, 32 * sizeof(UCHAR));
37
38 Offset = 0;
39 Record = (PDIR_RECORD)DirectoryBuffer;
40 while (TRUE)
41 {
42 Offset = Offset + Record->RecordLength;
43 Record = (PDIR_RECORD)((ULONG_PTR)DirectoryBuffer + Offset);
44
45 if (Record->RecordLength == 0)
46 {
47 Offset = ROUND_UP(Offset, SECTORSIZE);
48 Record = (PDIR_RECORD)((ULONG_PTR)DirectoryBuffer + Offset);
49 }
50
51 if (Offset >= DirectoryLength)
52 return FALSE;
53
54 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
55 {
56 DPRINTM(DPRINT_FILESYSTEM, "Name '.'\n");
57 }
58 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
59 {
60 DPRINTM(DPRINT_FILESYSTEM, "Name '..'\n");
61 }
62 else
63 {
64 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
65 Name[i] = Record->FileId[i];
66 Name[i] = 0;
67 DPRINTM(DPRINT_FILESYSTEM, "Name '%s'\n", Name);
68
69 if (strlen(FileName) == strlen(Name) && _stricmp(FileName, Name) == 0)
70 {
71 IsoFileInfoPointer->FileStart = Record->ExtentLocationL;
72 IsoFileInfoPointer->FileSize = Record->DataLengthL;
73 IsoFileInfoPointer->FilePointer = 0;
74 IsoFileInfoPointer->Directory = (Record->FileFlags & 0x02)?TRUE:FALSE;
75
76 return TRUE;
77 }
78
79 }
80
81 RtlZeroMemory(Name, 32 * sizeof(UCHAR));
82 }
83
84 return FALSE;
85 }
86
87
88 /*
89 * IsoBufferDirectory()
90 * This function allocates a buffer, reads the specified directory
91 * and returns a pointer to that buffer into pDirectoryBuffer. The
92 * function returns an ARC error code. The directory is specified
93 * by its starting sector and length.
94 */
95 static LONG IsoBufferDirectory(ULONG DeviceId, ULONG DirectoryStartSector, ULONG DirectoryLength,
96 PVOID* pDirectoryBuffer)
97 {
98 PVOID DirectoryBuffer;
99 ULONG SectorCount;
100 LARGE_INTEGER Position;
101 ULONG Count;
102 ULONG ret;
103
104 DPRINTM(DPRINT_FILESYSTEM, "IsoBufferDirectory() DirectoryStartSector = %d DirectoryLength = %d\n", DirectoryStartSector, DirectoryLength);
105
106 SectorCount = ROUND_UP(DirectoryLength, SECTORSIZE) / SECTORSIZE;
107 DPRINTM(DPRINT_FILESYSTEM, "Trying to read (DirectoryCount) %d sectors.\n", SectorCount);
108
109 //
110 // Attempt to allocate memory for directory buffer
111 //
112 DPRINTM(DPRINT_FILESYSTEM, "Trying to allocate (DirectoryLength) %d bytes.\n", DirectoryLength);
113 DirectoryBuffer = MmHeapAlloc(DirectoryLength);
114 if (!DirectoryBuffer)
115 return ENOMEM;
116
117 //
118 // Now read directory contents into DirectoryBuffer
119 //
120 Position.HighPart = 0;
121 Position.LowPart = DirectoryStartSector * SECTORSIZE;
122 ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
123 if (ret != ESUCCESS)
124 {
125 MmHeapFree(DirectoryBuffer);
126 return ret;
127 }
128 ret = ArcRead(DeviceId, DirectoryBuffer, SectorCount * SECTORSIZE, &Count);
129 if (ret != ESUCCESS || Count != SectorCount * SECTORSIZE)
130 {
131 MmHeapFree(DirectoryBuffer);
132 return EIO;
133 }
134
135 *pDirectoryBuffer = DirectoryBuffer;
136 return ESUCCESS;
137 }
138
139
140 /*
141 * IsoLookupFile()
142 * This function searches the file system for the
143 * specified filename and fills in an ISO_FILE_INFO structure
144 * with info describing the file, etc. returns ARC error code
145 */
146 static LONG IsoLookupFile(PCSTR FileName, ULONG DeviceId, PISO_FILE_INFO IsoFileInfoPointer)
147 {
148 UCHAR Buffer[SECTORSIZE];
149 PPVD Pvd = (PPVD)Buffer;
150 UINT32 i;
151 ULONG NumberOfPathParts;
152 CHAR PathPart[261];
153 PVOID DirectoryBuffer;
154 ULONG DirectorySector;
155 ULONG DirectoryLength;
156 ISO_FILE_INFO IsoFileInfo;
157 LARGE_INTEGER Position;
158 ULONG Count;
159 LONG ret;
160
161 DPRINTM(DPRINT_FILESYSTEM, "IsoLookupFile() FileName = %s\n", FileName);
162
163 RtlZeroMemory(IsoFileInfoPointer, sizeof(ISO_FILE_INFO));
164
165 //
166 // Read The Primary Volume Descriptor
167 //
168 Position.HighPart = 0;
169 Position.LowPart = 16 * SECTORSIZE;
170 ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
171 if (ret != ESUCCESS)
172 return ret;
173 ret = ArcRead(DeviceId, Pvd, SECTORSIZE, &Count);
174 if (ret != ESUCCESS || Count < sizeof(PVD))
175 return EIO;
176
177 DirectorySector = Pvd->RootDirRecord.ExtentLocationL;
178 DirectoryLength = Pvd->RootDirRecord.DataLengthL;
179
180 //
181 // Figure out how many sub-directories we are nested in
182 //
183 NumberOfPathParts = FsGetNumPathParts(FileName);
184
185 //
186 // Loop once for each part
187 //
188 for (i=0; i<NumberOfPathParts; i++)
189 {
190 //
191 // Get first path part
192 //
193 FsGetFirstNameFromPath(PathPart, FileName);
194
195 //
196 // Advance to the next part of the path
197 //
198 for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++)
199 {
200 }
201 FileName++;
202
203 //
204 // Buffer the directory contents
205 //
206 ret = IsoBufferDirectory(DeviceId, DirectorySector, DirectoryLength, &DirectoryBuffer);
207 if (ret != ESUCCESS)
208 return ret;
209
210 //
211 // Search for file name in directory
212 //
213 if (!IsoSearchDirectoryBufferForFile(DirectoryBuffer, DirectoryLength, PathPart, &IsoFileInfo))
214 {
215 MmHeapFree(DirectoryBuffer);
216 return ENOENT;
217 }
218
219 MmHeapFree(DirectoryBuffer);
220
221 //
222 // If we have another sub-directory to go then
223 // grab the start sector and file size
224 //
225 if ((i+1) < NumberOfPathParts)
226 {
227 DirectorySector = IsoFileInfo.FileStart;
228 DirectoryLength = IsoFileInfo.FileSize;
229 }
230
231 }
232
233 RtlCopyMemory(IsoFileInfoPointer, &IsoFileInfo, sizeof(ISO_FILE_INFO));
234
235 return ESUCCESS;
236 }
237
238 LONG IsoClose(ULONG FileId)
239 {
240 PISO_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
241
242 MmHeapFree(FileHandle);
243
244 return ESUCCESS;
245 }
246
247 LONG IsoGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
248 {
249 PISO_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
250
251 DPRINTM(DPRINT_FILESYSTEM, "IsoGetFileInformation() FileSize = %d\n", FileHandle->FileSize);
252 DPRINTM(DPRINT_FILESYSTEM, "IsoGetFileInformation() FilePointer = %d\n", FileHandle->FilePointer);
253
254 RtlZeroMemory(Information, sizeof(FILEINFORMATION));
255 Information->EndingAddress.LowPart = FileHandle->FileSize;
256 Information->CurrentAddress.LowPart = FileHandle->FilePointer;
257
258 return ESUCCESS;
259 }
260
261 LONG IsoOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
262 {
263 ISO_FILE_INFO TempFileInfo;
264 PISO_FILE_INFO FileHandle;
265 ULONG DeviceId;
266 LONG ret;
267
268 if (OpenMode != OpenReadOnly)
269 return EACCES;
270
271 DeviceId = FsGetDeviceId(*FileId);
272
273 DPRINTM(DPRINT_FILESYSTEM, "IsoOpen() FileName = %s\n", Path);
274
275 RtlZeroMemory(&TempFileInfo, sizeof(TempFileInfo));
276 ret = IsoLookupFile(Path, DeviceId, &TempFileInfo);
277 if (ret != ESUCCESS)
278 return ENOENT;
279
280 FileHandle = MmHeapAlloc(sizeof(ISO_FILE_INFO));
281 if (!FileHandle)
282 return ENOMEM;
283
284 RtlCopyMemory(FileHandle, &TempFileInfo, sizeof(ISO_FILE_INFO));
285
286 FsSetDeviceSpecific(*FileId, FileHandle);
287 return ESUCCESS;
288 }
289
290 LONG IsoRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
291 {
292 PISO_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
293 UCHAR SectorBuffer[SECTORSIZE];
294 LARGE_INTEGER Position;
295 ULONG DeviceId;
296 ULONG FilePointer;
297 ULONG SectorNumber;
298 ULONG OffsetInSector;
299 ULONG LengthInSector;
300 ULONG NumberOfSectors;
301 ULONG BytesRead;
302 LONG ret;
303
304 DPRINTM(DPRINT_FILESYSTEM, "IsoRead() Buffer = %p, N = %lu\n", Buffer, N);
305
306 DeviceId = FsGetDeviceId(FileId);
307 *Count = 0;
308
309 //
310 // If they are trying to read past the
311 // end of the file then return success
312 // with Count == 0
313 //
314 FilePointer = FileHandle->FilePointer;
315 if (FilePointer >= FileHandle->FileSize)
316 {
317 return ESUCCESS;
318 }
319
320 //
321 // If they are trying to read more than there is to read
322 // then adjust the amount to read
323 //
324 if (FilePointer + N > FileHandle->FileSize)
325 {
326 N = FileHandle->FileSize - FilePointer;
327 }
328
329 //
330 // Ok, now we have to perform at most 3 calculations
331 // I'll draw you a picture (using nifty ASCII art):
332 //
333 // CurrentFilePointer -+
334 // |
335 // +----------------+
336 // |
337 // +-----------+-----------+-----------+-----------+
338 // | Sector 1 | Sector 2 | Sector 3 | Sector 4 |
339 // +-----------+-----------+-----------+-----------+
340 // | |
341 // +---------------+--------------------+
342 // |
343 // N -----------------+
344 //
345 // 1 - The first calculation (and read) will align
346 // the file pointer with the next sector
347 // boundary (if we are supposed to read that much)
348 // 2 - The next calculation (and read) will read
349 // in all the full sectors that the requested
350 // amount of data would cover (in this case
351 // sectors 2 & 3).
352 // 3 - The last calculation (and read) would read
353 // in the remainder of the data requested out of
354 // the last sector.
355 //
356
357
358 //
359 // Only do the first read if we
360 // aren't aligned on a cluster boundary
361 //
362 if (FilePointer % SECTORSIZE)
363 {
364 //
365 // Do the math for our first read
366 //
367 SectorNumber = FileHandle->FileStart + (FilePointer / SECTORSIZE);
368 OffsetInSector = FilePointer % SECTORSIZE;
369 LengthInSector = (N > (SECTORSIZE - OffsetInSector)) ? (SECTORSIZE - OffsetInSector) : N;
370
371 //
372 // Now do the read and update Count, N, FilePointer, & Buffer
373 //
374 Position.HighPart = 0;
375 Position.LowPart = SectorNumber * SECTORSIZE;
376 ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
377 if (ret != ESUCCESS)
378 {
379 return ret;
380 }
381 ret = ArcRead(DeviceId, SectorBuffer, SECTORSIZE, &BytesRead);
382 if (ret != ESUCCESS || BytesRead != SECTORSIZE)
383 {
384 return EIO;
385 }
386 RtlCopyMemory(Buffer, SectorBuffer + OffsetInSector, LengthInSector);
387 *Count += LengthInSector;
388 N -= LengthInSector;
389 FilePointer += LengthInSector;
390 Buffer = (PVOID)((ULONG_PTR)Buffer + LengthInSector);
391 }
392
393 //
394 // Do the math for our second read (if any data left)
395 //
396 if (N > 0)
397 {
398 //
399 // Determine how many full clusters we need to read
400 //
401 NumberOfSectors = (N / SECTORSIZE);
402
403 SectorNumber = FileHandle->FileStart + (FilePointer / SECTORSIZE);
404
405 //
406 // Now do the read and update Count, N, FilePointer, & Buffer
407 //
408 Position.HighPart = 0;
409 Position.LowPart = SectorNumber * SECTORSIZE;
410 ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
411 if (ret != ESUCCESS)
412 {
413 return ret;
414 }
415 ret = ArcRead(DeviceId, Buffer, NumberOfSectors * SECTORSIZE, &BytesRead);
416 if (ret != ESUCCESS || BytesRead != NumberOfSectors * SECTORSIZE)
417 {
418 return EIO;
419 }
420
421 *Count += NumberOfSectors * SECTORSIZE;
422 N -= NumberOfSectors * SECTORSIZE;
423 FilePointer += NumberOfSectors * SECTORSIZE;
424 Buffer = (PVOID)((ULONG_PTR)Buffer + NumberOfSectors * SECTORSIZE);
425 }
426
427 //
428 // Do the math for our third read (if any data left)
429 //
430 if (N > 0)
431 {
432 SectorNumber = FileHandle->FileStart + (FilePointer / SECTORSIZE);
433
434 //
435 // Now do the read and update Count, N, FilePointer, & Buffer
436 //
437 Position.HighPart = 0;
438 Position.LowPart = SectorNumber * SECTORSIZE;
439 ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
440 if (ret != ESUCCESS)
441 {
442 return ret;
443 }
444 ret = ArcRead(DeviceId, SectorBuffer, SECTORSIZE, &BytesRead);
445 if (ret != ESUCCESS || BytesRead != SECTORSIZE)
446 {
447 return EIO;
448 }
449 RtlCopyMemory(Buffer, SectorBuffer, N);
450 *Count += N;
451 FilePointer += N;
452 }
453
454 DPRINTM(DPRINT_FILESYSTEM, "IsoRead() done\n");
455
456 return ESUCCESS;
457 }
458
459 LONG IsoSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
460 {
461 PISO_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
462
463 DPRINTM(DPRINT_FILESYSTEM, "IsoSeek() NewFilePointer = %lu\n", Position->LowPart);
464
465 if (SeekMode != SeekAbsolute)
466 return EINVAL;
467 if (Position->HighPart != 0)
468 return EINVAL;
469 if (Position->LowPart >= FileHandle->FileSize)
470 return EINVAL;
471
472 FileHandle->FilePointer = Position->LowPart;
473 return ESUCCESS;
474 }
475
476 const DEVVTBL Iso9660FuncTable =
477 {
478 IsoClose,
479 IsoGetFileInformation,
480 IsoOpen,
481 IsoRead,
482 IsoSeek,
483 L"cdfs",
484 };
485
486 const DEVVTBL* IsoMount(ULONG DeviceId)
487 {
488 UCHAR Buffer[SECTORSIZE];
489 PPVD Pvd = (PPVD)Buffer;
490 LARGE_INTEGER Position;
491 ULONG Count;
492 LONG ret;
493
494 //
495 // Read The Primary Volume Descriptor
496 //
497 Position.HighPart = 0;
498 Position.LowPart = 16 * SECTORSIZE;
499 ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
500 if (ret != ESUCCESS)
501 return NULL;
502 ret = ArcRead(DeviceId, Pvd, SECTORSIZE, &Count);
503 if (ret != ESUCCESS || Count < sizeof(PVD))
504 return NULL;
505
506 //
507 // Check if PVD is valid. If yes, return ISO9660 function table
508 //
509 if (Pvd->VdType == 1 && RtlEqualMemory(Pvd->StandardId, "CD001", 5))
510 return &Iso9660FuncTable;
511 else
512 return NULL;
513 }
514
515 #endif
516