Added ISO-9660 support.
[reactos.git] / freeldr / freeldr / fs / iso.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2002 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <freeldr.h>
21 #include <fs.h>
22 #include "iso.h"
23 #include <disk.h>
24 #include <rtl.h>
25 #include <ui.h>
26 #include <arch.h>
27 #include <mm.h>
28 #include <debug.h>
29 #include <cache.h>
30
31
32 #define SECTORSIZE 2048
33
34 static ULONG IsoRootSector; // Starting sector of the root directory
35 static ULONG IsoRootLength; // Length of the root directory
36
37 ULONG IsoDriveNumber = 0;
38
39
40 BOOL IsoOpenVolume(ULONG DriveNumber)
41 {
42 PPVD Pvd;
43
44 // DbgPrint((DPRINT_FILESYSTEM, "IsoOpenVolume() DriveNumber = 0x%x VolumeStartSector = 16\n", DriveNumber));
45 printf("IsoOpenVolume() DriveNumber = 0x%x\n", DriveNumber);
46
47 // Store the drive number
48 IsoDriveNumber = DriveNumber;
49
50 IsoRootSector = 0;
51 IsoRootLength = 0;
52
53 Pvd = AllocateMemory(SECTORSIZE);
54
55 if (!BiosInt13ReadExtended(DriveNumber, 16, 1, Pvd))
56 {
57 FileSystemError("Failed to read the PVD.");
58 FreeMemory(Pvd);
59 return FALSE;
60 }
61
62 IsoRootSector = Pvd->RootDirRecord.ExtentLocationL;
63 IsoRootLength = Pvd->RootDirRecord.DataLengthL;
64
65 FreeMemory(Pvd);
66
67 // DbgPrint((DPRINT_FILESYSTEM, "IsoRootSector = %u IsoRootLegth = %u\n", IsoRootSector, IsoRootLength));
68 printf("IsoRootSector = %u IsoRootLegth = %u\n", IsoRootSector, IsoRootLength);
69
70 return TRUE;
71 }
72
73
74 static BOOL IsoSearchDirectoryBufferForFile(PVOID DirectoryBuffer, UINT32 DirectoryLength, PUCHAR FileName, PISO_FILE_INFO IsoFileInfoPointer)
75 {
76 PDIR_RECORD Record;
77 ULONG Offset;
78 ULONG i;
79 UCHAR Name[32];
80
81 // DbgPrint((DPRINT_FILESYSTEM, "IsoSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x DirectoryLength = %d FileName = %s\n", DirectoryBuffer, DirectoryLength, FileName));
82 printf("IsoSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x DirectoryLength = %d FileName = %s\n", DirectoryBuffer, DirectoryLength, FileName);
83
84 memset(Name, 0, 32 * sizeof(UCHAR));
85
86 Offset = 0;
87 Record = (PDIR_RECORD)DirectoryBuffer;
88 while (TRUE)
89 {
90 Offset = Offset + Record->RecordLength;
91 Record = (PDIR_RECORD)(DirectoryBuffer + Offset);
92
93 if (Record->RecordLength == 0)
94 {
95 Offset = ROUND_UP(Offset, SECTORSIZE);
96 Record = (PDIR_RECORD)(DirectoryBuffer + Offset);
97 }
98
99 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
100 {
101 // DbgPrint((DPRINT_FILESYSTEM, "Name '.'\n"));
102 printf("Name '.'\n");
103 }
104 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
105 {
106 // DbgPrint((DPRINT_FILESYSTEM, "Name '..'\n"));
107 printf("Name '..'\n");
108 }
109 else
110 {
111 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
112 Name[i] = Record->FileId[i];
113 Name[i] = 0;
114 // DbgPrint((DPRINT_FILESYSTEM, "Name '%s'\n", Name));
115 printf("Name '%s'\n", Name);
116
117 if (strlen(FileName) == strlen(Name) && stricmp(FileName, Name) == 0)
118 {
119 IsoFileInfoPointer->FileStart = Record->ExtentLocationL;
120 IsoFileInfoPointer->FileSize = Record->DataLengthL;
121 IsoFileInfoPointer->FilePointer = 0;
122 IsoFileInfoPointer->Directory = (Record->FileFlags & 0x02)?TRUE:FALSE;
123
124 return TRUE;
125 }
126
127 }
128
129 if (Offset >= DirectoryLength)
130 return FALSE;
131
132 memset(Name, 0, 32 * sizeof(UCHAR));
133 }
134
135 return FALSE;
136 }
137
138
139
140 static PVOID IsoBufferDirectory(UINT32 DirectoryStartSector, UINT32 DirectoryLength)
141 {
142 PVOID DirectoryBuffer;
143 UINT32 SectorCount;
144
145 // DbgPrint((DPRINT_FILESYSTEM, "IsoBufferDirectory() DirectoryStartSector = %d DirectoryLength = %d\n", DirectoryStartSector, DirectoryLength));
146 printf("IsoBufferDirectory() DirectoryStartSector = %d DirectoryLength = %d\n", DirectoryStartSector, DirectoryLength);
147
148 //
149 // Attempt to allocate memory for directory buffer
150 //
151 // DbgPrint((DPRINT_FILESYSTEM, "Trying to allocate (DirectoryLength) %d bytes.\n", DirectoryLength));
152 printf("Trying to allocate (DirectoryLength) %d bytes.\n", DirectoryLength);
153 DirectoryBuffer = AllocateMemory(DirectoryLength);
154
155 if (DirectoryBuffer == NULL)
156 {
157 return NULL;
158 }
159
160 SectorCount = ROUND_UP(DirectoryLength, SECTORSIZE) / SECTORSIZE;
161 // DbgPrint((DPRINT_FILESYSTEM, "Trying to read (DirectoryCount) %d sectors.\n", SectorCount));
162 printf("Trying to read (DirectoryCount) %d sectors.\n", SectorCount);
163
164 //
165 // Now read directory contents into DirectoryBuffer
166 //
167 if (!BiosInt13ReadExtended(IsoDriveNumber, DirectoryStartSector, SectorCount, DirectoryBuffer))
168 {
169 FreeMemory(DirectoryBuffer);
170 return NULL;
171 }
172
173 return DirectoryBuffer;
174 }
175
176
177 /*
178 * IsoGetNumPathParts()
179 * This function parses a path in the form of dir1\dir2\file1.ext
180 * and returns the number of parts it has (i.e. 3 - dir1,dir2,file1.ext)
181 */
182 static ULONG IsoGetNumPathParts(PUCHAR Path)
183 {
184 ULONG i;
185 ULONG num;
186
187 for (i=0,num=0; i<(int)strlen(Path); i++)
188 {
189 if (Path[i] == '\\')
190 {
191 num++;
192 }
193 }
194 num++;
195
196 // DbgPrint((DPRINT_FILESYSTEM, "IsoGetNumPathParts() Path = %s NumPathParts = %d\n", Path, num));
197 printf("IsoGetNumPathParts() Path = %s NumPathParts = %d\n", Path, num);
198
199 return num;
200 }
201
202
203 /*
204 * IsoGetFirstNameFromPath()
205 * This function parses a path in the form of dir1\dir2\file1.ext
206 * and puts the first name of the path (e.g. "dir1") in buffer
207 * compatible with the MSDOS directory structure
208 */
209 static VOID IsoGetFirstNameFromPath(PUCHAR Buffer, PUCHAR Path)
210 {
211 ULONG i;
212
213 // Copy all the characters up to the end of the
214 // string or until we hit a '\' character
215 // and put them in Buffer
216 for (i=0; i<(int)strlen(Path); i++)
217 {
218 if (Path[i] == '\\')
219 {
220 break;
221 }
222 else
223 {
224 Buffer[i] = Path[i];
225 }
226 }
227
228 Buffer[i] = 0;
229
230 // DbgPrint((DPRINT_FILESYSTEM, "IsoGetFirstNameFromPath() Path = %s FirstName = %s\n", Path, Buffer));
231 printf("IsoGetFirstNameFromPath() Path = %s FirstName = %s\n", Path, Buffer);
232 }
233
234
235 /*
236 * IsoLookupFile()
237 * This function searches the file system for the
238 * specified filename and fills in an ISO_FILE_INFO structure
239 * with info describing the file, etc. returns true
240 * if the file exists or false otherwise
241 */
242 static BOOL IsoLookupFile(PUCHAR FileName, PISO_FILE_INFO IsoFileInfoPointer)
243 {
244 int i;
245 ULONG NumberOfPathParts;
246 UCHAR PathPart[261];
247 PVOID DirectoryBuffer;
248 UINT32 DirectorySector;
249 UINT32 DirectoryLength;
250 ISO_FILE_INFO IsoFileInfo;
251
252 // DbgPrint((DPRINT_FILESYSTEM, "IsoLookupFile() FileName = %s\n", FileName));
253 printf("IsoLookupFile() FileName = %s\n", FileName);
254
255 memset(IsoFileInfoPointer, 0, sizeof(ISO_FILE_INFO));
256
257 //
258 // Check and see if the first character is '\' and remove it if so
259 //
260 while (*FileName == '\\')
261 {
262 FileName++;
263 }
264
265 //
266 // Figure out how many sub-directories we are nested in
267 //
268 NumberOfPathParts = IsoGetNumPathParts(FileName);
269
270 DirectorySector = IsoRootSector;
271 DirectoryLength = IsoRootLength;
272
273 //
274 // Loop once for each part
275 //
276 for (i=0; i<NumberOfPathParts; i++)
277 {
278 //
279 // Get first path part
280 //
281 IsoGetFirstNameFromPath(PathPart, FileName);
282
283 //
284 // Advance to the next part of the path
285 //
286 for (; (*FileName != '\\') && (*FileName != '\0'); FileName++)
287 {
288 }
289 FileName++;
290
291 //
292 // Buffer the directory contents
293 //
294 DirectoryBuffer = IsoBufferDirectory(DirectorySector, DirectoryLength);
295 if (DirectoryBuffer == NULL)
296 {
297 return FALSE;
298 }
299
300 //
301 // Search for file name in directory
302 //
303 if (!IsoSearchDirectoryBufferForFile(DirectoryBuffer, DirectoryLength, PathPart, &IsoFileInfo))
304 {
305 FreeMemory(DirectoryBuffer);
306 return FALSE;
307 }
308
309 FreeMemory(DirectoryBuffer);
310
311 //
312 // If we have another sub-directory to go then
313 // grab the start sector and file size
314 //
315 if ((i+1) < NumberOfPathParts)
316 {
317 DirectorySector = IsoFileInfo.FileStart;
318 DirectoryLength = IsoFileInfo.FileSize;
319 }
320
321 }
322
323 memcpy(IsoFileInfoPointer, &IsoFileInfo, sizeof(ISO_FILE_INFO));
324
325 return TRUE;
326 }
327
328
329 /*
330 * IsoOpenFile()
331 * Tries to open the file 'name' and returns true or false
332 * for success and failure respectively
333 */
334 FILE* IsoOpenFile(PUCHAR FileName)
335 {
336 ISO_FILE_INFO TempFileInfo;
337 PISO_FILE_INFO FileHandle;
338
339 // DbgPrint((DPRINT_FILESYSTEM, "IsoOpenFile() FileName = %s\n", FileName));
340 printf("IsoOpenFile() FileName = %s\n", FileName);
341
342 if (!IsoLookupFile(FileName, &TempFileInfo))
343 {
344 return NULL;
345 }
346
347 FileHandle = AllocateMemory(sizeof(ISO_FILE_INFO));
348
349 if (FileHandle == NULL)
350 {
351 return NULL;
352 }
353
354 memcpy(FileHandle, &TempFileInfo, sizeof(ISO_FILE_INFO));
355
356 return (FILE*)FileHandle;
357 }
358
359
360 /*
361 * IsoReadPartialSector()
362 * Reads part of a cluster into memory
363 */
364 static BOOL IsoReadPartialSector(ULONG SectorNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer)
365 {
366 PUCHAR SectorBuffer;
367
368 // DbgPrint((DPRINT_FILESYSTEM, "IsoReadPartialSector() SectorNumber = %d StartingOffset = %d Length = %d Buffer = 0x%x\n", SectorNumber, StartingOffset, Length, Buffer));
369 printf("IsoReadPartialSector() SectorNumber = %d StartingOffset = %d Length = %d Buffer = 0x%x\n", SectorNumber, StartingOffset, Length, Buffer);
370
371 SectorBuffer = AllocateMemory(SECTORSIZE);
372 if (SectorBuffer == NULL)
373 {
374 return FALSE;
375 }
376
377 if (!BiosInt13ReadExtended(IsoDriveNumber, SectorNumber, 1, SectorBuffer))
378 {
379 FreeMemory(SectorBuffer);
380 return FALSE;
381 }
382
383 memcpy(Buffer, ((PVOID)SectorBuffer + StartingOffset), Length);
384
385 FreeMemory(SectorBuffer);
386
387 return TRUE;
388 }
389
390
391 /*
392 * IsoReadFile()
393 * Reads BytesToRead from open file and
394 * returns the number of bytes read in BytesRead
395 */
396 BOOL IsoReadFile(FILE *FileHandle, ULONG BytesToRead, PULONG BytesRead, PVOID Buffer)
397 {
398 PISO_FILE_INFO IsoFileInfo = (PISO_FILE_INFO)FileHandle;
399 UINT32 SectorNumber;
400 UINT32 OffsetInSector;
401 UINT32 LengthInSector;
402 UINT32 NumberOfSectors;
403
404 // DbgPrint((DPRINT_FILESYSTEM, "IsoReadFile() BytesToRead = %d Buffer = 0x%x\n", BytesToRead, Buffer));
405 printf("IsoReadFile() BytesToRead = %d Buffer = 0x%x\n", BytesToRead, Buffer);
406
407 if (BytesRead != NULL)
408 {
409 *BytesRead = 0;
410 }
411
412 //
413 // If they are trying to read past the
414 // end of the file then return success
415 // with BytesRead == 0
416 //
417 if (IsoFileInfo->FilePointer >= IsoFileInfo->FileSize)
418 {
419 return TRUE;
420 }
421
422 //
423 // If they are trying to read more than there is to read
424 // then adjust the amount to read
425 //
426 if ((IsoFileInfo->FilePointer + BytesToRead) > IsoFileInfo->FileSize)
427 {
428 BytesToRead = (IsoFileInfo->FileSize - IsoFileInfo->FilePointer);
429 }
430
431 //
432 // Ok, now we have to perform at most 3 calculations
433 // I'll draw you a picture (using nifty ASCII art):
434 //
435 // CurrentFilePointer -+
436 // |
437 // +----------------+
438 // |
439 // +-----------+-----------+-----------+-----------+
440 // | Sector 1 | Sector 2 | Sector 3 | Sector 4 |
441 // +-----------+-----------+-----------+-----------+
442 // | |
443 // +---------------+--------------------+
444 // |
445 // BytesToRead -------+
446 //
447 // 1 - The first calculation (and read) will align
448 // the file pointer with the next sector
449 // boundary (if we are supposed to read that much)
450 // 2 - The next calculation (and read) will read
451 // in all the full sectors that the requested
452 // amount of data would cover (in this case
453 // sectors 2 & 3).
454 // 3 - The last calculation (and read) would read
455 // in the remainder of the data requested out of
456 // the last sector.
457 //
458
459
460 //
461 // Only do the first read if we
462 // aren't aligned on a cluster boundary
463 //
464 if (IsoFileInfo->FilePointer % SECTORSIZE)
465 {
466 //
467 // Do the math for our first read
468 //
469 SectorNumber = IsoFileInfo->FileStart + (IsoFileInfo->FilePointer / SECTORSIZE);
470 OffsetInSector = IsoFileInfo->FilePointer % SECTORSIZE;
471 LengthInSector = (BytesToRead > (SECTORSIZE - OffsetInSector)) ? (SECTORSIZE - OffsetInSector) : BytesToRead;
472
473 //
474 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
475 //
476 if (!IsoReadPartialSector(SectorNumber, OffsetInSector, LengthInSector, Buffer))
477 {
478 return FALSE;
479 }
480 if (BytesRead != NULL)
481 {
482 *BytesRead += LengthInSector;
483 }
484 BytesToRead -= LengthInSector;
485 IsoFileInfo->FilePointer += LengthInSector;
486 Buffer += LengthInSector;
487 }
488
489 //
490 // Do the math for our second read (if any data left)
491 //
492 if (BytesToRead > 0)
493 {
494 //
495 // Determine how many full clusters we need to read
496 //
497 NumberOfSectors = (BytesToRead / SECTORSIZE);
498
499 if (NumberOfSectors > 0)
500 {
501 SectorNumber = IsoFileInfo->FileStart + (IsoFileInfo->FilePointer / SECTORSIZE);
502
503 //
504 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
505 //
506 if (!BiosInt13ReadExtended(IsoDriveNumber, SectorNumber, NumberOfSectors, Buffer))
507 {
508 return FALSE;
509 }
510 if (BytesRead != NULL)
511 {
512 *BytesRead += (NumberOfSectors * SECTORSIZE);
513 }
514 BytesToRead -= (NumberOfSectors * SECTORSIZE);
515 IsoFileInfo->FilePointer += (NumberOfSectors * SECTORSIZE);
516 Buffer += (NumberOfSectors * SECTORSIZE);
517 }
518 }
519
520 //
521 // Do the math for our third read (if any data left)
522 //
523 if (BytesToRead > 0)
524 {
525 SectorNumber = IsoFileInfo->FileStart + (IsoFileInfo->FilePointer / SECTORSIZE);
526
527 //
528 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
529 //
530 if (!IsoReadPartialSector(SectorNumber, 0, BytesToRead, Buffer))
531 {
532 return FALSE;
533 }
534 if (BytesRead != NULL)
535 {
536 *BytesRead += BytesToRead;
537 }
538 BytesToRead -= BytesToRead;
539 IsoFileInfo->FilePointer += BytesToRead;
540 Buffer += BytesToRead;
541 }
542
543 printf("IsoReadFile() done\n");
544
545 return TRUE;
546 }
547
548
549 ULONG IsoGetFileSize(FILE *FileHandle)
550 {
551 PISO_FILE_INFO IsoFileHandle = (PISO_FILE_INFO)FileHandle;
552
553 // DbgPrint((DPRINT_FILESYSTEM, "IsoGetFileSize() FileSize = %d\n", IsoFileHandle->FileSize));
554 printf("IsoGetFileSize() FileSize = %d\n", IsoFileHandle->FileSize);
555
556 return IsoFileHandle->FileSize;
557 }
558
559 VOID IsoSetFilePointer(FILE *FileHandle, ULONG NewFilePointer)
560 {
561 PISO_FILE_INFO IsoFileHandle = (PISO_FILE_INFO)FileHandle;
562
563 // DbgPrint((DPRINT_FILESYSTEM, "IsoSetFilePointer() NewFilePointer = %d\n", NewFilePointer));
564 printf("IsoSetFilePointer() NewFilePointer = %d\n", NewFilePointer);
565
566 IsoFileHandle->FilePointer = NewFilePointer;
567 }
568
569 ULONG IsoGetFilePointer(FILE *FileHandle)
570 {
571 PISO_FILE_INFO IsoFileHandle = (PISO_FILE_INFO)FileHandle;
572
573 // DbgPrint((DPRINT_FILESYSTEM, "IsoGetFilePointer() FilePointer = %d\n", IsoFileHandle->FilePointer));
574 printf("IsoGetFilePointer() FilePointer = %d\n", IsoFileHandle->FilePointer);
575
576 return IsoFileHandle->FilePointer;
577 }