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