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