Changes in v1.8.7 (4/22/2003) (brianp)
[reactos.git] / freeldr / freeldr / fs / iso.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 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 <disk.h>
23 #include <rtl.h>
24 #include <arch.h>
25 #include <mm.h>
26 #include <debug.h>
27 #include <cache.h>
28
29 #include "iso.h"
30
31
32 #define SECTORSIZE 2048
33
34 static U32 IsoRootSector; // Starting sector of the root directory
35 static U32 IsoRootLength; // Length of the root directory
36
37 U32 IsoDriveNumber = 0;
38
39
40 BOOL IsoOpenVolume(U32 DriveNumber)
41 {
42 PPVD Pvd = (PPVD)DISKREADBUFFER;
43
44 DbgPrint((DPRINT_FILESYSTEM, "IsoOpenVolume() DriveNumber = 0x%x VolumeStartSector = 16\n", DriveNumber));
45
46 // Store the drive number
47 IsoDriveNumber = DriveNumber;
48
49 IsoRootSector = 0;
50 IsoRootLength = 0;
51
52 if (!DiskReadLogicalSectors(DriveNumber, 16, 1, Pvd))
53 {
54 FileSystemError("Failed to read the PVD.");
55 return FALSE;
56 }
57
58 IsoRootSector = Pvd->RootDirRecord.ExtentLocationL;
59 IsoRootLength = Pvd->RootDirRecord.DataLengthL;
60
61 DbgPrint((DPRINT_FILESYSTEM, "IsoRootSector = %u IsoRootLegth = %u\n", IsoRootSector, IsoRootLength));
62
63 return TRUE;
64 }
65
66
67 static BOOL IsoSearchDirectoryBufferForFile(PVOID DirectoryBuffer, U32 DirectoryLength, PUCHAR FileName, PISO_FILE_INFO IsoFileInfoPointer)
68 {
69 PDIR_RECORD Record;
70 U32 Offset;
71 U32 i;
72 UCHAR Name[32];
73
74 DbgPrint((DPRINT_FILESYSTEM, "IsoSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x DirectoryLength = %d FileName = %s\n", DirectoryBuffer, DirectoryLength, FileName));
75
76 RtlZeroMemory(Name, 32 * sizeof(UCHAR));
77
78 Offset = 0;
79 Record = (PDIR_RECORD)DirectoryBuffer;
80 while (TRUE)
81 {
82 Offset = Offset + Record->RecordLength;
83 Record = (PDIR_RECORD)(DirectoryBuffer + Offset);
84
85 if (Record->RecordLength == 0)
86 {
87 Offset = ROUND_UP(Offset, SECTORSIZE);
88 Record = (PDIR_RECORD)(DirectoryBuffer + Offset);
89 }
90
91 if (Record->FileIdLength == 1 && Record->FileId[0] == 0)
92 {
93 DbgPrint((DPRINT_FILESYSTEM, "Name '.'\n"));
94 }
95 else if (Record->FileIdLength == 1 && Record->FileId[0] == 1)
96 {
97 DbgPrint((DPRINT_FILESYSTEM, "Name '..'\n"));
98 }
99 else
100 {
101 for (i = 0; i < Record->FileIdLength && Record->FileId[i] != ';'; i++)
102 Name[i] = Record->FileId[i];
103 Name[i] = 0;
104 DbgPrint((DPRINT_FILESYSTEM, "Name '%s'\n", Name));
105
106 if (strlen(FileName) == strlen(Name) && stricmp(FileName, Name) == 0)
107 {
108 IsoFileInfoPointer->FileStart = Record->ExtentLocationL;
109 IsoFileInfoPointer->FileSize = Record->DataLengthL;
110 IsoFileInfoPointer->FilePointer = 0;
111 IsoFileInfoPointer->Directory = (Record->FileFlags & 0x02)?TRUE:FALSE;
112
113 return TRUE;
114 }
115
116 }
117
118 if (Offset >= DirectoryLength)
119 return FALSE;
120
121 RtlZeroMemory(Name, 32 * sizeof(UCHAR));
122 }
123
124 return FALSE;
125 }
126
127
128 /*
129 * IsoBufferDirectory()
130 * This function allocates a buffer, reads the specified directory
131 * and returns a pointer to that buffer. The function returns NULL
132 * if allocation or read fails. The directory is specified by its
133 * starting sector and length.
134 */
135 static PVOID IsoBufferDirectory(U32 DirectoryStartSector, U32 DirectoryLength)
136 {
137 PVOID DirectoryBuffer;
138 PVOID Ptr;
139 U32 SectorCount;
140 U32 i;
141
142 DbgPrint((DPRINT_FILESYSTEM, "IsoBufferDirectory() DirectoryStartSector = %d DirectoryLength = %d\n", DirectoryStartSector, DirectoryLength));
143
144 SectorCount = ROUND_UP(DirectoryLength, SECTORSIZE) / SECTORSIZE;
145 DbgPrint((DPRINT_FILESYSTEM, "Trying to read (DirectoryCount) %d sectors.\n", SectorCount));
146
147 //
148 // Attempt to allocate memory for directory buffer
149 //
150 DbgPrint((DPRINT_FILESYSTEM, "Trying to allocate (DirectoryLength) %d bytes.\n", DirectoryLength));
151 DirectoryBuffer = MmAllocateMemory(DirectoryLength);
152
153 if (DirectoryBuffer == NULL)
154 {
155 return NULL;
156 }
157
158 //
159 // Now read directory contents into DirectoryBuffer
160 //
161 for (i = 0, Ptr = DirectoryBuffer; i < SectorCount; i++, Ptr += SECTORSIZE)
162 {
163 if (!DiskReadLogicalSectors(IsoDriveNumber, DirectoryStartSector + i, 1, (PVOID)DISKREADBUFFER))
164 {
165 MmFreeMemory(DirectoryBuffer);
166 return NULL;
167 }
168 RtlCopyMemory(Ptr, (PVOID)DISKREADBUFFER, SECTORSIZE);
169 }
170
171 return DirectoryBuffer;
172 }
173
174
175 /*
176 * IsoLookupFile()
177 * This function searches the file system for the
178 * specified filename and fills in an ISO_FILE_INFO structure
179 * with info describing the file, etc. returns true
180 * if the file exists or false otherwise
181 */
182 static BOOL IsoLookupFile(PUCHAR FileName, PISO_FILE_INFO IsoFileInfoPointer)
183 {
184 int i;
185 U32 NumberOfPathParts;
186 UCHAR PathPart[261];
187 PVOID DirectoryBuffer;
188 U32 DirectorySector;
189 U32 DirectoryLength;
190 ISO_FILE_INFO IsoFileInfo;
191
192 DbgPrint((DPRINT_FILESYSTEM, "IsoLookupFile() FileName = %s\n", FileName));
193
194 RtlZeroMemory(IsoFileInfoPointer, sizeof(ISO_FILE_INFO));
195
196 //
197 // Figure out how many sub-directories we are nested in
198 //
199 NumberOfPathParts = FsGetNumPathParts(FileName);
200
201 DirectorySector = IsoRootSector;
202 DirectoryLength = IsoRootLength;
203
204 //
205 // Loop once for each part
206 //
207 for (i=0; i<NumberOfPathParts; i++)
208 {
209 //
210 // Get first path part
211 //
212 FsGetFirstNameFromPath(PathPart, FileName);
213
214 //
215 // Advance to the next part of the path
216 //
217 for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++)
218 {
219 }
220 FileName++;
221
222 //
223 // Buffer the directory contents
224 //
225 DirectoryBuffer = IsoBufferDirectory(DirectorySector, DirectoryLength);
226 if (DirectoryBuffer == NULL)
227 {
228 return FALSE;
229 }
230
231 //
232 // Search for file name in directory
233 //
234 if (!IsoSearchDirectoryBufferForFile(DirectoryBuffer, DirectoryLength, PathPart, &IsoFileInfo))
235 {
236 MmFreeMemory(DirectoryBuffer);
237 return FALSE;
238 }
239
240 MmFreeMemory(DirectoryBuffer);
241
242 //
243 // If we have another sub-directory to go then
244 // grab the start sector and file size
245 //
246 if ((i+1) < NumberOfPathParts)
247 {
248 DirectorySector = IsoFileInfo.FileStart;
249 DirectoryLength = IsoFileInfo.FileSize;
250 }
251
252 }
253
254 RtlCopyMemory(IsoFileInfoPointer, &IsoFileInfo, sizeof(ISO_FILE_INFO));
255
256 return TRUE;
257 }
258
259
260 /*
261 * IsoOpenFile()
262 * Tries to open the file 'name' and returns true or false
263 * for success and failure respectively
264 */
265 FILE* IsoOpenFile(PUCHAR FileName)
266 {
267 ISO_FILE_INFO TempFileInfo;
268 PISO_FILE_INFO FileHandle;
269
270 DbgPrint((DPRINT_FILESYSTEM, "IsoOpenFile() FileName = %s\n", FileName));
271
272 if (!IsoLookupFile(FileName, &TempFileInfo))
273 {
274 return NULL;
275 }
276
277 FileHandle = MmAllocateMemory(sizeof(ISO_FILE_INFO));
278
279 if (FileHandle == NULL)
280 {
281 return NULL;
282 }
283
284 RtlCopyMemory(FileHandle, &TempFileInfo, sizeof(ISO_FILE_INFO));
285
286 return (FILE*)FileHandle;
287 }
288
289
290 /*
291 * IsoReadFile()
292 * Reads BytesToRead from open file and
293 * returns the number of bytes read in BytesRead
294 */
295 BOOL IsoReadFile(FILE *FileHandle, U32 BytesToRead, U32* BytesRead, PVOID Buffer)
296 {
297 PISO_FILE_INFO IsoFileInfo = (PISO_FILE_INFO)FileHandle;
298 U32 SectorNumber;
299 U32 OffsetInSector;
300 U32 LengthInSector;
301 U32 NumberOfSectors;
302 U32 i;
303
304 DbgPrint((DPRINT_FILESYSTEM, "IsoReadFile() BytesToRead = %d Buffer = 0x%x\n", BytesToRead, Buffer));
305
306 if (BytesRead != NULL)
307 {
308 *BytesRead = 0;
309 }
310
311 //
312 // If they are trying to read past the
313 // end of the file then return success
314 // with BytesRead == 0
315 //
316 if (IsoFileInfo->FilePointer >= IsoFileInfo->FileSize)
317 {
318 return TRUE;
319 }
320
321 //
322 // If they are trying to read more than there is to read
323 // then adjust the amount to read
324 //
325 if ((IsoFileInfo->FilePointer + BytesToRead) > IsoFileInfo->FileSize)
326 {
327 BytesToRead = (IsoFileInfo->FileSize - IsoFileInfo->FilePointer);
328 }
329
330 //
331 // Ok, now we have to perform at most 3 calculations
332 // I'll draw you a picture (using nifty ASCII art):
333 //
334 // CurrentFilePointer -+
335 // |
336 // +----------------+
337 // |
338 // +-----------+-----------+-----------+-----------+
339 // | Sector 1 | Sector 2 | Sector 3 | Sector 4 |
340 // +-----------+-----------+-----------+-----------+
341 // | |
342 // +---------------+--------------------+
343 // |
344 // BytesToRead -------+
345 //
346 // 1 - The first calculation (and read) will align
347 // the file pointer with the next sector
348 // boundary (if we are supposed to read that much)
349 // 2 - The next calculation (and read) will read
350 // in all the full sectors that the requested
351 // amount of data would cover (in this case
352 // sectors 2 & 3).
353 // 3 - The last calculation (and read) would read
354 // in the remainder of the data requested out of
355 // the last sector.
356 //
357
358
359 //
360 // Only do the first read if we
361 // aren't aligned on a cluster boundary
362 //
363 if (IsoFileInfo->FilePointer % SECTORSIZE)
364 {
365 //
366 // Do the math for our first read
367 //
368 SectorNumber = IsoFileInfo->FileStart + (IsoFileInfo->FilePointer / SECTORSIZE);
369 OffsetInSector = IsoFileInfo->FilePointer % SECTORSIZE;
370 LengthInSector = (BytesToRead > (SECTORSIZE - OffsetInSector)) ? (SECTORSIZE - OffsetInSector) : BytesToRead;
371
372 //
373 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
374 //
375 if (!DiskReadLogicalSectors(IsoDriveNumber, SectorNumber, 1, (PVOID)DISKREADBUFFER))
376 {
377 return FALSE;
378 }
379 RtlCopyMemory(Buffer, ((PVOID)DISKREADBUFFER + OffsetInSector), LengthInSector);
380 if (BytesRead != NULL)
381 {
382 *BytesRead += LengthInSector;
383 }
384 BytesToRead -= LengthInSector;
385 IsoFileInfo->FilePointer += LengthInSector;
386 Buffer += LengthInSector;
387 }
388
389 //
390 // Do the math for our second read (if any data left)
391 //
392 if (BytesToRead > 0)
393 {
394 //
395 // Determine how many full clusters we need to read
396 //
397 NumberOfSectors = (BytesToRead / SECTORSIZE);
398
399 for (i = 0; i < NumberOfSectors; i++)
400 {
401 SectorNumber = IsoFileInfo->FileStart + (IsoFileInfo->FilePointer / SECTORSIZE);
402
403 //
404 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
405 //
406 if (!DiskReadLogicalSectorsLBA(IsoDriveNumber, SectorNumber, 1, (PVOID)DISKREADBUFFER))
407 {
408 return FALSE;
409 }
410
411 RtlCopyMemory(Buffer, (PVOID)DISKREADBUFFER, SECTORSIZE);
412
413 if (BytesRead != NULL)
414 {
415 *BytesRead += SECTORSIZE;
416 }
417 BytesToRead -= SECTORSIZE;
418 IsoFileInfo->FilePointer += SECTORSIZE;
419 Buffer += SECTORSIZE;
420 }
421 }
422
423 //
424 // Do the math for our third read (if any data left)
425 //
426 if (BytesToRead > 0)
427 {
428 SectorNumber = IsoFileInfo->FileStart + (IsoFileInfo->FilePointer / SECTORSIZE);
429
430 //
431 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
432 //
433 if (!DiskReadLogicalSectorsLBA(IsoDriveNumber, SectorNumber, 1, (PVOID)DISKREADBUFFER))
434 {
435 return FALSE;
436 }
437 RtlCopyMemory(Buffer, (PVOID)DISKREADBUFFER, BytesToRead);
438 if (BytesRead != NULL)
439 {
440 *BytesRead += BytesToRead;
441 }
442 IsoFileInfo->FilePointer += BytesToRead;
443 BytesToRead -= BytesToRead;
444 Buffer += BytesToRead;
445 }
446
447 DbgPrint((DPRINT_FILESYSTEM, "IsoReadFile() done\n"));
448
449 return TRUE;
450 }
451
452
453 U32 IsoGetFileSize(FILE *FileHandle)
454 {
455 PISO_FILE_INFO IsoFileHandle = (PISO_FILE_INFO)FileHandle;
456
457 DbgPrint((DPRINT_FILESYSTEM, "IsoGetFileSize() FileSize = %d\n", IsoFileHandle->FileSize));
458
459 return IsoFileHandle->FileSize;
460 }
461
462 VOID IsoSetFilePointer(FILE *FileHandle, U32 NewFilePointer)
463 {
464 PISO_FILE_INFO IsoFileHandle = (PISO_FILE_INFO)FileHandle;
465
466 DbgPrint((DPRINT_FILESYSTEM, "IsoSetFilePointer() NewFilePointer = %d\n", NewFilePointer));
467
468 IsoFileHandle->FilePointer = NewFilePointer;
469 }
470
471 U32 IsoGetFilePointer(FILE *FileHandle)
472 {
473 PISO_FILE_INFO IsoFileHandle = (PISO_FILE_INFO)FileHandle;
474
475 DbgPrint((DPRINT_FILESYSTEM, "IsoGetFilePointer() FilePointer = %d\n", IsoFileHandle->FilePointer));
476
477 return IsoFileHandle->FilePointer;
478 }