[FREELDR] Diverse enhancements.
[reactos.git] / boot / freeldr / freeldr / lib / fs / ext2.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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifndef _M_ARM
21 #include <freeldr.h>
22 #include <debug.h>
23
24 DBG_DEFAULT_CHANNEL(FILESYSTEM);
25
26 BOOLEAN Ext2OpenVolume(UCHAR DriveNumber, ULONGLONG VolumeStartSector, ULONGLONG PartitionSectorCount);
27 PEXT2_FILE_INFO Ext2OpenFile(PCSTR FileName);
28 BOOLEAN Ext2LookupFile(PCSTR FileName, PEXT2_FILE_INFO Ext2FileInfoPointer);
29 BOOLEAN Ext2SearchDirectoryBufferForFile(PVOID DirectoryBuffer, ULONG DirectorySize, PCHAR FileName, PEXT2_DIR_ENTRY DirectoryEntry);
30 BOOLEAN Ext2ReadVolumeSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer);
31
32 BOOLEAN Ext2ReadFileBig(PEXT2_FILE_INFO Ext2FileInfo, ULONGLONG BytesToRead, ULONGLONG* BytesRead, PVOID Buffer);
33 BOOLEAN Ext2ReadSuperBlock(VOID);
34 BOOLEAN Ext2ReadGroupDescriptors(VOID);
35 BOOLEAN Ext2ReadDirectory(ULONG Inode, PVOID* DirectoryBuffer, PEXT2_INODE InodePointer);
36 BOOLEAN Ext2ReadBlock(ULONG BlockNumber, PVOID Buffer);
37 BOOLEAN Ext2ReadPartialBlock(ULONG BlockNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer);
38 ULONG Ext2GetGroupDescBlockNumber(ULONG Group);
39 ULONG Ext2GetGroupDescOffsetInBlock(ULONG Group);
40 ULONG Ext2GetInodeGroupNumber(ULONG Inode);
41 ULONG Ext2GetInodeBlockNumber(ULONG Inode);
42 ULONG Ext2GetInodeOffsetInBlock(ULONG Inode);
43 BOOLEAN Ext2ReadInode(ULONG Inode, PEXT2_INODE InodeBuffer);
44 BOOLEAN Ext2ReadGroupDescriptor(ULONG Group, PEXT2_GROUP_DESC GroupBuffer);
45 ULONG* Ext2ReadBlockPointerList(PEXT2_INODE Inode);
46 ULONGLONG Ext2GetInodeFileSize(PEXT2_INODE Inode);
47 BOOLEAN Ext2CopyIndirectBlockPointers(ULONG* BlockList, ULONG* CurrentBlockInList, ULONG BlockCount, ULONG IndirectBlock);
48 BOOLEAN Ext2CopyDoubleIndirectBlockPointers(ULONG* BlockList, ULONG* CurrentBlockInList, ULONG BlockCount, ULONG DoubleIndirectBlock);
49 BOOLEAN Ext2CopyTripleIndirectBlockPointers(ULONG* BlockList, ULONG* CurrentBlockInList, ULONG BlockCount, ULONG TripleIndirectBlock);
50
51 GEOMETRY Ext2DiskGeometry; // Ext2 file system disk geometry
52
53 PEXT2_SUPER_BLOCK Ext2SuperBlock = NULL; // Ext2 file system super block
54 PEXT2_GROUP_DESC Ext2GroupDescriptors = NULL; // Ext2 file system group descriptors
55
56 UCHAR Ext2DriveNumber = 0; // Ext2 file system drive number
57 ULONGLONG Ext2VolumeStartSector = 0; // Ext2 file system starting sector
58 ULONG Ext2BlockSizeInBytes = 0; // Block size in bytes
59 ULONG Ext2BlockSizeInSectors = 0; // Block size in sectors
60 ULONG Ext2FragmentSizeInBytes = 0; // Fragment size in bytes
61 ULONG Ext2FragmentSizeInSectors = 0; // Fragment size in sectors
62 ULONG Ext2GroupCount = 0; // Number of groups in this file system
63 ULONG Ext2InodesPerBlock = 0; // Number of inodes in one block
64 ULONG Ext2GroupDescPerBlock = 0; // Number of group descriptors in one block
65
66 #define TAG_EXT_BLOCK_LIST 'LtxE'
67 #define TAG_EXT_FILE 'FtxE'
68 #define TAG_EXT_BUFFER 'BtxE'
69 #define TAG_EXT_SUPER_BLOCK 'StxE'
70 #define TAG_EXT_GROUP_DESC 'GtxE'
71
72 BOOLEAN DiskGetBootVolume(PUCHAR DriveNumber, PULONGLONG StartSector, PULONGLONG SectorCount, int *FsType)
73 {
74 *DriveNumber = 0;
75 *StartSector = 0;
76 *SectorCount = 0;
77 *FsType = 0;
78 return FALSE;
79 }
80
81 BOOLEAN Ext2OpenVolume(UCHAR DriveNumber, ULONGLONG VolumeStartSector, ULONGLONG PartitionSectorCount)
82 {
83
84 TRACE("Ext2OpenVolume() DriveNumber = 0x%x VolumeStartSector = %d\n", DriveNumber, VolumeStartSector);
85
86 // Store the drive number and start sector
87 Ext2DriveNumber = DriveNumber;
88 Ext2VolumeStartSector = VolumeStartSector;
89
90 if (!MachDiskGetDriveGeometry(DriveNumber, &Ext2DiskGeometry))
91 {
92 return FALSE;
93 }
94
95 //
96 // Initialize the disk cache for this drive
97 //
98 if (!CacheInitializeDrive(DriveNumber))
99 {
100 return FALSE;
101 }
102
103 // Read in the super block
104 if (!Ext2ReadSuperBlock())
105 {
106 return FALSE;
107 }
108
109 // Read in the group descriptors
110 if (!Ext2ReadGroupDescriptors())
111 {
112 return FALSE;
113 }
114
115 return TRUE;
116 }
117
118 /*
119 * Ext2OpenFile()
120 * Tries to open the file 'name' and returns true or false
121 * for success and failure respectively
122 */
123 PEXT2_FILE_INFO Ext2OpenFile(PCSTR FileName)
124 {
125 EXT2_FILE_INFO TempExt2FileInfo;
126 PEXT2_FILE_INFO FileHandle;
127 CHAR SymLinkPath[EXT2_NAME_LEN];
128 CHAR FullPath[EXT2_NAME_LEN * 2];
129 ULONG_PTR Index;
130
131 TRACE("Ext2OpenFile() FileName = %s\n", FileName);
132
133 RtlZeroMemory(SymLinkPath, sizeof(SymLinkPath));
134
135 // Lookup the file in the file system
136 if (!Ext2LookupFile(FileName, &TempExt2FileInfo))
137 {
138 return NULL;
139 }
140
141 // If we got a symbolic link then fix up the path
142 // and re-call this function
143 if ((TempExt2FileInfo.Inode.mode & EXT2_S_IFMT) == EXT2_S_IFLNK)
144 {
145 TRACE("File is a symbolic link\n");
146
147 // Now read in the symbolic link path
148 if (!Ext2ReadFileBig(&TempExt2FileInfo, TempExt2FileInfo.FileSize, NULL, SymLinkPath))
149 {
150 if (TempExt2FileInfo.FileBlockList != NULL)
151 {
152 FrLdrTempFree(TempExt2FileInfo.FileBlockList, TAG_EXT_BLOCK_LIST);
153 }
154
155 return NULL;
156 }
157
158 TRACE("Symbolic link path = %s\n", SymLinkPath);
159
160 // Get the full path
161 if (SymLinkPath[0] == '/' || SymLinkPath[0] == '\\')
162 {
163 // Symbolic link is an absolute path
164 // So copy it to FullPath, but skip over
165 // the '/' char at the beginning
166 strcpy(FullPath, &SymLinkPath[1]);
167 }
168 else
169 {
170 // Symbolic link is a relative path
171 // Copy the first part of the path
172 strcpy(FullPath, FileName);
173
174 // Remove the last part of the path
175 for (Index=strlen(FullPath); Index>0; )
176 {
177 Index--;
178 if (FullPath[Index] == '/' || FullPath[Index] == '\\')
179 {
180 break;
181 }
182 }
183 FullPath[Index] = '\0';
184
185 // Concatenate the symbolic link
186 strcat(FullPath, Index == 0 ? "" : "/");
187 strcat(FullPath, SymLinkPath);
188 }
189
190 TRACE("Full file path = %s\n", FullPath);
191
192 if (TempExt2FileInfo.FileBlockList != NULL)
193 {
194 FrLdrTempFree(TempExt2FileInfo.FileBlockList, TAG_EXT_BLOCK_LIST);
195 }
196
197 return Ext2OpenFile(FullPath);
198 }
199 else
200 {
201 FileHandle = FrLdrTempAlloc(sizeof(EXT2_FILE_INFO), TAG_EXT_FILE);
202
203 if (FileHandle == NULL)
204 {
205 if (TempExt2FileInfo.FileBlockList != NULL)
206 {
207 FrLdrTempFree(TempExt2FileInfo.FileBlockList, TAG_EXT_BLOCK_LIST);
208 }
209
210 return NULL;
211 }
212
213 RtlCopyMemory(FileHandle, &TempExt2FileInfo, sizeof(EXT2_FILE_INFO));
214
215 return FileHandle;
216 }
217 }
218
219 /*
220 * Ext2LookupFile()
221 * This function searches the file system for the
222 * specified filename and fills in a EXT2_FILE_INFO structure
223 * with info describing the file, etc. returns true
224 * if the file exists or false otherwise
225 */
226 BOOLEAN Ext2LookupFile(PCSTR FileName, PEXT2_FILE_INFO Ext2FileInfoPointer)
227 {
228 UINT32 i;
229 ULONG NumberOfPathParts;
230 CHAR PathPart[261];
231 PVOID DirectoryBuffer;
232 ULONG DirectoryInode = EXT2_ROOT_INO;
233 EXT2_INODE InodeData;
234 EXT2_DIR_ENTRY DirectoryEntry;
235
236 TRACE("Ext2LookupFile() FileName = %s\n", FileName);
237
238 RtlZeroMemory(Ext2FileInfoPointer, sizeof(EXT2_FILE_INFO));
239
240 //
241 // Figure out how many sub-directories we are nested in
242 //
243 NumberOfPathParts = FsGetNumPathParts(FileName);
244
245 //
246 // Loop once for each part
247 //
248 for (i=0; i<NumberOfPathParts; i++)
249 {
250 //
251 // Get first path part
252 //
253 FsGetFirstNameFromPath(PathPart, FileName);
254
255 //
256 // Advance to the next part of the path
257 //
258 for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++)
259 {
260 }
261 FileName++;
262
263 //
264 // Buffer the directory contents
265 //
266 if (!Ext2ReadDirectory(DirectoryInode, &DirectoryBuffer, &InodeData))
267 {
268 return FALSE;
269 }
270
271 //
272 // Search for file name in directory
273 //
274 if (!Ext2SearchDirectoryBufferForFile(DirectoryBuffer, (ULONG)Ext2GetInodeFileSize(&InodeData), PathPart, &DirectoryEntry))
275 {
276 FrLdrTempFree(DirectoryBuffer, TAG_EXT_BUFFER);
277 return FALSE;
278 }
279
280 FrLdrTempFree(DirectoryBuffer, TAG_EXT_BUFFER);
281
282 DirectoryInode = DirectoryEntry.inode;
283 }
284
285 if (!Ext2ReadInode(DirectoryInode, &InodeData))
286 {
287 return FALSE;
288 }
289
290 if (((InodeData.mode & EXT2_S_IFMT) != EXT2_S_IFREG) &&
291 ((InodeData.mode & EXT2_S_IFMT) != EXT2_S_IFLNK))
292 {
293 FileSystemError("Inode is not a regular file or symbolic link.");
294 return FALSE;
295 }
296
297 // Set the drive number
298 Ext2FileInfoPointer->DriveNumber = Ext2DriveNumber;
299
300 // If it's a regular file or a regular symbolic link
301 // then get the block pointer list otherwise it must
302 // be a fast symbolic link which doesn't have a block list
303 if (((InodeData.mode & EXT2_S_IFMT) == EXT2_S_IFREG) ||
304 ((InodeData.mode & EXT2_S_IFMT) == EXT2_S_IFLNK && InodeData.size > FAST_SYMLINK_MAX_NAME_SIZE))
305 {
306 Ext2FileInfoPointer->FileBlockList = Ext2ReadBlockPointerList(&InodeData);
307
308 if (Ext2FileInfoPointer->FileBlockList == NULL)
309 {
310 return FALSE;
311 }
312 }
313 else
314 {
315 Ext2FileInfoPointer->FileBlockList = NULL;
316 }
317
318 Ext2FileInfoPointer->FilePointer = 0;
319 Ext2FileInfoPointer->FileSize = Ext2GetInodeFileSize(&InodeData);
320 RtlCopyMemory(&Ext2FileInfoPointer->Inode, &InodeData, sizeof(EXT2_INODE));
321
322 return TRUE;
323 }
324
325 BOOLEAN Ext2SearchDirectoryBufferForFile(PVOID DirectoryBuffer, ULONG DirectorySize, PCHAR FileName, PEXT2_DIR_ENTRY DirectoryEntry)
326 {
327 ULONG CurrentOffset;
328 PEXT2_DIR_ENTRY CurrentDirectoryEntry;
329
330 TRACE("Ext2SearchDirectoryBufferForFile() DirectoryBuffer = 0x%x DirectorySize = %d FileName = %s\n", DirectoryBuffer, DirectorySize, FileName);
331
332 for (CurrentOffset=0; CurrentOffset<DirectorySize; )
333 {
334 CurrentDirectoryEntry = (PEXT2_DIR_ENTRY)((ULONG_PTR)DirectoryBuffer + CurrentOffset);
335
336 if (CurrentDirectoryEntry->direntlen == 0)
337 {
338 break;
339 }
340
341 if ((CurrentDirectoryEntry->direntlen + CurrentOffset) > DirectorySize)
342 {
343 FileSystemError("Directory entry extends past end of directory file.");
344 return FALSE;
345 }
346
347 TRACE("Dumping directory entry at offset %d:\n", CurrentOffset);
348 DbgDumpBuffer(DPRINT_FILESYSTEM, CurrentDirectoryEntry, CurrentDirectoryEntry->direntlen);
349
350 if ((_strnicmp(FileName, CurrentDirectoryEntry->name, CurrentDirectoryEntry->namelen) == 0) &&
351 (strlen(FileName) == CurrentDirectoryEntry->namelen))
352 {
353 RtlCopyMemory(DirectoryEntry, CurrentDirectoryEntry, sizeof(EXT2_DIR_ENTRY));
354
355 TRACE("EXT2 Directory Entry:\n");
356 TRACE("inode = %d\n", DirectoryEntry->inode);
357 TRACE("direntlen = %d\n", DirectoryEntry->direntlen);
358 TRACE("namelen = %d\n", DirectoryEntry->namelen);
359 TRACE("filetype = %d\n", DirectoryEntry->filetype);
360 TRACE("name = ");
361 for (CurrentOffset=0; CurrentOffset<DirectoryEntry->namelen; CurrentOffset++)
362 {
363 TRACE("%c", DirectoryEntry->name[CurrentOffset]);
364 }
365 TRACE("\n");
366
367 return TRUE;
368 }
369
370 CurrentOffset += CurrentDirectoryEntry->direntlen;
371 }
372
373 return FALSE;
374 }
375
376 /*
377 * Ext2ReadFileBig()
378 * Reads BytesToRead from open file and
379 * returns the number of bytes read in BytesRead
380 */
381 BOOLEAN Ext2ReadFileBig(PEXT2_FILE_INFO Ext2FileInfo, ULONGLONG BytesToRead, ULONGLONG* BytesRead, PVOID Buffer)
382 {
383 ULONG BlockNumber;
384 ULONG BlockNumberIndex;
385 ULONG OffsetInBlock;
386 ULONG LengthInBlock;
387 ULONG NumberOfBlocks;
388
389 TRACE("Ext2ReadFileBig() BytesToRead = %d Buffer = 0x%x\n", (ULONG)BytesToRead, Buffer);
390
391 if (BytesRead != NULL)
392 {
393 *BytesRead = 0;
394 }
395
396 // Make sure we have the block pointer list if we need it
397 if (Ext2FileInfo->FileBlockList == NULL)
398 {
399 // Block pointer list is NULL
400 // so this better be a fast symbolic link or else
401 if (((Ext2FileInfo->Inode.mode & EXT2_S_IFMT) != EXT2_S_IFLNK) ||
402 (Ext2FileInfo->FileSize > FAST_SYMLINK_MAX_NAME_SIZE))
403 {
404 FileSystemError("Block pointer list is NULL and file is not a fast symbolic link.");
405 return FALSE;
406 }
407 }
408
409 //
410 // If they are trying to read past the
411 // end of the file then return success
412 // with BytesRead == 0
413 //
414 if (Ext2FileInfo->FilePointer >= Ext2FileInfo->FileSize)
415 {
416 return TRUE;
417 }
418
419 //
420 // If they are trying to read more than there is to read
421 // then adjust the amount to read
422 //
423 if ((Ext2FileInfo->FilePointer + BytesToRead) > Ext2FileInfo->FileSize)
424 {
425 BytesToRead = (Ext2FileInfo->FileSize - Ext2FileInfo->FilePointer);
426 }
427
428 // Check if this is a fast symbolic link
429 // if so then the read is easy
430 if (((Ext2FileInfo->Inode.mode & EXT2_S_IFMT) == EXT2_S_IFLNK) &&
431 (Ext2FileInfo->FileSize <= FAST_SYMLINK_MAX_NAME_SIZE))
432 {
433 TRACE("Reading fast symbolic link data\n");
434
435 // Copy the data from the link
436 RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)Ext2FileInfo->FilePointer + Ext2FileInfo->Inode.symlink), (ULONG)BytesToRead);
437
438 if (BytesRead != NULL)
439 {
440 *BytesRead = BytesToRead;
441 }
442
443 return TRUE;
444 }
445
446 //
447 // Ok, now we have to perform at most 3 calculations
448 // I'll draw you a picture (using nifty ASCII art):
449 //
450 // CurrentFilePointer -+
451 // |
452 // +----------------+
453 // |
454 // +-----------+-----------+-----------+-----------+
455 // | Block 1 | Block 2 | Block 3 | Block 4 |
456 // +-----------+-----------+-----------+-----------+
457 // | |
458 // +---------------+--------------------+
459 // |
460 // BytesToRead -------+
461 //
462 // 1 - The first calculation (and read) will align
463 // the file pointer with the next block.
464 // boundary (if we are supposed to read that much)
465 // 2 - The next calculation (and read) will read
466 // in all the full blocks that the requested
467 // amount of data would cover (in this case
468 // blocks 2 & 3).
469 // 3 - The last calculation (and read) would read
470 // in the remainder of the data requested out of
471 // the last block.
472 //
473
474 //
475 // Only do the first read if we
476 // aren't aligned on a block boundary
477 //
478 if (Ext2FileInfo->FilePointer % Ext2BlockSizeInBytes)
479 {
480 //
481 // Do the math for our first read
482 //
483 BlockNumberIndex = (ULONG)(Ext2FileInfo->FilePointer / Ext2BlockSizeInBytes);
484 BlockNumber = Ext2FileInfo->FileBlockList[BlockNumberIndex];
485 OffsetInBlock = (Ext2FileInfo->FilePointer % Ext2BlockSizeInBytes);
486 LengthInBlock = (ULONG)((BytesToRead > (Ext2BlockSizeInBytes - OffsetInBlock)) ? (Ext2BlockSizeInBytes - OffsetInBlock) : BytesToRead);
487
488 //
489 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
490 //
491 if (!Ext2ReadPartialBlock(BlockNumber, OffsetInBlock, LengthInBlock, Buffer))
492 {
493 return FALSE;
494 }
495 if (BytesRead != NULL)
496 {
497 *BytesRead += LengthInBlock;
498 }
499 BytesToRead -= LengthInBlock;
500 Ext2FileInfo->FilePointer += LengthInBlock;
501 Buffer = (PVOID)((ULONG_PTR)Buffer + LengthInBlock);
502 }
503
504 //
505 // Do the math for our second read (if any data left)
506 //
507 if (BytesToRead > 0)
508 {
509 //
510 // Determine how many full clusters we need to read
511 //
512 NumberOfBlocks = (ULONG)(BytesToRead / Ext2BlockSizeInBytes);
513
514 while (NumberOfBlocks > 0)
515 {
516 BlockNumberIndex = (ULONG)(Ext2FileInfo->FilePointer / Ext2BlockSizeInBytes);
517 BlockNumber = Ext2FileInfo->FileBlockList[BlockNumberIndex];
518
519 //
520 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
521 //
522 if (!Ext2ReadBlock(BlockNumber, Buffer))
523 {
524 return FALSE;
525 }
526 if (BytesRead != NULL)
527 {
528 *BytesRead += Ext2BlockSizeInBytes;
529 }
530 BytesToRead -= Ext2BlockSizeInBytes;
531 Ext2FileInfo->FilePointer += Ext2BlockSizeInBytes;
532 Buffer = (PVOID)((ULONG_PTR)Buffer + Ext2BlockSizeInBytes);
533 NumberOfBlocks--;
534 }
535 }
536
537 //
538 // Do the math for our third read (if any data left)
539 //
540 if (BytesToRead > 0)
541 {
542 BlockNumberIndex = (ULONG)(Ext2FileInfo->FilePointer / Ext2BlockSizeInBytes);
543 BlockNumber = Ext2FileInfo->FileBlockList[BlockNumberIndex];
544
545 //
546 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
547 //
548 if (!Ext2ReadPartialBlock(BlockNumber, 0, (ULONG)BytesToRead, Buffer))
549 {
550 return FALSE;
551 }
552 if (BytesRead != NULL)
553 {
554 *BytesRead += BytesToRead;
555 }
556 Ext2FileInfo->FilePointer += BytesToRead;
557 BytesToRead -= BytesToRead;
558 Buffer = (PVOID)((ULONG_PTR)Buffer + (ULONG_PTR)BytesToRead);
559 }
560
561 return TRUE;
562 }
563
564 BOOLEAN Ext2ReadVolumeSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
565 {
566 //GEOMETRY DiskGeometry;
567 //BOOLEAN ReturnValue;
568 //if (!DiskGetDriveGeometry(DriveNumber, &DiskGeometry))
569 //{
570 // return FALSE;
571 //}
572 //ReturnValue = MachDiskReadLogicalSectors(DriveNumber, SectorNumber + Ext2VolumeStartSector, SectorCount, DiskReadBuffer);
573 //RtlCopyMemory(Buffer, DiskReadBuffer, SectorCount * DiskGeometry.BytesPerSector);
574 //return ReturnValue;
575
576 return CacheReadDiskSectors(DriveNumber, SectorNumber + Ext2VolumeStartSector, SectorCount, Buffer);
577 }
578
579 BOOLEAN Ext2ReadSuperBlock(VOID)
580 {
581
582 TRACE("Ext2ReadSuperBlock()\n");
583
584 //
585 // Free any memory previously allocated
586 //
587 if (Ext2SuperBlock != NULL)
588 {
589 FrLdrTempFree(Ext2SuperBlock, TAG_EXT_SUPER_BLOCK);
590
591 Ext2SuperBlock = NULL;
592 }
593
594 //
595 // Now allocate the memory to hold the super block
596 //
597 Ext2SuperBlock = (PEXT2_SUPER_BLOCK)FrLdrTempAlloc(1024, TAG_EXT_SUPER_BLOCK);
598
599 //
600 // Make sure we got the memory
601 //
602 if (Ext2SuperBlock == NULL)
603 {
604 FileSystemError("Out of memory.");
605 return FALSE;
606 }
607
608 // Now try to read the super block
609 // If this fails then abort
610 if (!MachDiskReadLogicalSectors(Ext2DriveNumber, Ext2VolumeStartSector, 8, DiskReadBuffer))
611 {
612 return FALSE;
613 }
614 RtlCopyMemory(Ext2SuperBlock, ((PUCHAR)DiskReadBuffer + 1024), 1024);
615
616 TRACE("Dumping super block:\n");
617 TRACE("total_inodes: %d\n", Ext2SuperBlock->total_inodes);
618 TRACE("total_blocks: %d\n", Ext2SuperBlock->total_blocks);
619 TRACE("reserved_blocks: %d\n", Ext2SuperBlock->reserved_blocks);
620 TRACE("free_blocks: %d\n", Ext2SuperBlock->free_blocks);
621 TRACE("free_inodes: %d\n", Ext2SuperBlock->free_inodes);
622 TRACE("first_data_block: %d\n", Ext2SuperBlock->first_data_block);
623 TRACE("log2_block_size: %d\n", Ext2SuperBlock->log2_block_size);
624 TRACE("log2_fragment_size: %d\n", Ext2SuperBlock->log2_fragment_size);
625 TRACE("blocks_per_group: %d\n", Ext2SuperBlock->blocks_per_group);
626 TRACE("fragments_per_group: %d\n", Ext2SuperBlock->fragments_per_group);
627 TRACE("inodes_per_group: %d\n", Ext2SuperBlock->inodes_per_group);
628 TRACE("mtime: %d\n", Ext2SuperBlock->mtime);
629 TRACE("utime: %d\n", Ext2SuperBlock->utime);
630 TRACE("mnt_count: %d\n", Ext2SuperBlock->mnt_count);
631 TRACE("max_mnt_count: %d\n", Ext2SuperBlock->max_mnt_count);
632 TRACE("magic: 0x%x\n", Ext2SuperBlock->magic);
633 TRACE("fs_state: %d\n", Ext2SuperBlock->fs_state);
634 TRACE("error_handling: %d\n", Ext2SuperBlock->error_handling);
635 TRACE("minor_revision_level: %d\n", Ext2SuperBlock->minor_revision_level);
636 TRACE("lastcheck: %d\n", Ext2SuperBlock->lastcheck);
637 TRACE("checkinterval: %d\n", Ext2SuperBlock->checkinterval);
638 TRACE("creator_os: %d\n", Ext2SuperBlock->creator_os);
639 TRACE("revision_level: %d\n", Ext2SuperBlock->revision_level);
640 TRACE("uid_reserved: %d\n", Ext2SuperBlock->uid_reserved);
641 TRACE("gid_reserved: %d\n", Ext2SuperBlock->gid_reserved);
642 TRACE("first_inode: %d\n", Ext2SuperBlock->first_inode);
643 TRACE("inode_size: %d\n", Ext2SuperBlock->inode_size);
644 TRACE("block_group_number: %d\n", Ext2SuperBlock->block_group_number);
645 TRACE("feature_compatibility: 0x%x\n", Ext2SuperBlock->feature_compatibility);
646 TRACE("feature_incompat: 0x%x\n", Ext2SuperBlock->feature_incompat);
647 TRACE("feature_ro_compat: 0x%x\n", Ext2SuperBlock->feature_ro_compat);
648 TRACE("unique_id = { 0x%x, 0x%x, 0x%x, 0x%x }\n",
649 Ext2SuperBlock->unique_id[0], Ext2SuperBlock->unique_id[1], Ext2SuperBlock->unique_id[2], Ext2SuperBlock->unique_id[3]);
650 TRACE("volume_name = '%.16s'\n", Ext2SuperBlock->volume_name);
651 TRACE("last_mounted_on = '%.64s'\n", Ext2SuperBlock->last_mounted_on);
652 TRACE("compression_info = 0x%x\n", Ext2SuperBlock->compression_info);
653
654 //
655 // Check the super block magic
656 //
657 if (Ext2SuperBlock->magic != EXT2_MAGIC)
658 {
659 FileSystemError("Invalid super block magic (0xef53)");
660 return FALSE;
661 }
662
663 //
664 // Check the revision level
665 //
666 if (Ext2SuperBlock->revision_level > EXT2_DYNAMIC_REVISION)
667 {
668 FileSystemError("FreeLoader does not understand the revision of this EXT2/EXT3 filesystem.\nPlease update FreeLoader.");
669 return FALSE;
670 }
671
672 //
673 // Check the feature set
674 // Don't need to check the compatible or read-only compatible features
675 // because we only mount the filesystem as read-only
676 //
677 if ((Ext2SuperBlock->revision_level >= EXT2_DYNAMIC_REVISION) &&
678 (/*((Ext2SuperBlock->s_feature_compat & ~EXT3_FEATURE_COMPAT_SUPP) != 0) ||*/
679 /*((Ext2SuperBlock->s_feature_ro_compat & ~EXT3_FEATURE_RO_COMPAT_SUPP) != 0) ||*/
680 ((Ext2SuperBlock->feature_incompat & ~EXT3_FEATURE_INCOMPAT_SUPP) != 0)))
681 {
682 FileSystemError("FreeLoader does not understand features of this EXT2/EXT3 filesystem.\nPlease update FreeLoader.");
683 return FALSE;
684 }
685
686 // Calculate the group count
687 Ext2GroupCount = (Ext2SuperBlock->total_blocks - Ext2SuperBlock->first_data_block + Ext2SuperBlock->blocks_per_group - 1) / Ext2SuperBlock->blocks_per_group;
688 TRACE("Ext2GroupCount: %d\n", Ext2GroupCount);
689
690 // Calculate the block size
691 Ext2BlockSizeInBytes = 1024 << Ext2SuperBlock->log2_block_size;
692 Ext2BlockSizeInSectors = Ext2BlockSizeInBytes / Ext2DiskGeometry.BytesPerSector;
693 TRACE("Ext2BlockSizeInBytes: %d\n", Ext2BlockSizeInBytes);
694 TRACE("Ext2BlockSizeInSectors: %d\n", Ext2BlockSizeInSectors);
695
696 // Calculate the fragment size
697 if (Ext2SuperBlock->log2_fragment_size >= 0)
698 {
699 Ext2FragmentSizeInBytes = 1024 << Ext2SuperBlock->log2_fragment_size;
700 }
701 else
702 {
703 Ext2FragmentSizeInBytes = 1024 >> -(Ext2SuperBlock->log2_fragment_size);
704 }
705 Ext2FragmentSizeInSectors = Ext2FragmentSizeInBytes / Ext2DiskGeometry.BytesPerSector;
706 TRACE("Ext2FragmentSizeInBytes: %d\n", Ext2FragmentSizeInBytes);
707 TRACE("Ext2FragmentSizeInSectors: %d\n", Ext2FragmentSizeInSectors);
708
709 // Verify that the fragment size and the block size are equal
710 if (Ext2BlockSizeInBytes != Ext2FragmentSizeInBytes)
711 {
712 FileSystemError("The fragment size must be equal to the block size.");
713 return FALSE;
714 }
715
716 // Calculate the number of inodes in one block
717 Ext2InodesPerBlock = Ext2BlockSizeInBytes / EXT2_INODE_SIZE(Ext2SuperBlock);
718 TRACE("Ext2InodesPerBlock: %d\n", Ext2InodesPerBlock);
719
720 // Calculate the number of group descriptors in one block
721 Ext2GroupDescPerBlock = EXT2_DESC_PER_BLOCK(Ext2SuperBlock);
722 TRACE("Ext2GroupDescPerBlock: %d\n", Ext2GroupDescPerBlock);
723
724 return TRUE;
725 }
726
727 BOOLEAN Ext2ReadGroupDescriptors(VOID)
728 {
729 ULONG GroupDescBlockCount;
730 ULONG BlockNumber;
731 PUCHAR CurrentGroupDescBlock;
732
733 TRACE("Ext2ReadGroupDescriptors()\n");
734
735 //
736 // Free any memory previously allocated
737 //
738 if (Ext2GroupDescriptors != NULL)
739 {
740 FrLdrTempFree(Ext2GroupDescriptors, TAG_EXT_GROUP_DESC);
741
742 Ext2GroupDescriptors = NULL;
743 }
744
745 //
746 // Now allocate the memory to hold the group descriptors
747 //
748 GroupDescBlockCount = ROUND_UP(Ext2GroupCount, Ext2GroupDescPerBlock) / Ext2GroupDescPerBlock;
749 Ext2GroupDescriptors = (PEXT2_GROUP_DESC)FrLdrTempAlloc(GroupDescBlockCount * Ext2BlockSizeInBytes, TAG_EXT_GROUP_DESC);
750
751 //
752 // Make sure we got the memory
753 //
754 if (Ext2GroupDescriptors == NULL)
755 {
756 FileSystemError("Out of memory.");
757 return FALSE;
758 }
759
760 // Now read the group descriptors
761 CurrentGroupDescBlock = (PUCHAR)Ext2GroupDescriptors;
762 BlockNumber = Ext2SuperBlock->first_data_block + 1;
763
764 while (GroupDescBlockCount--)
765 {
766 if (!Ext2ReadBlock(BlockNumber, CurrentGroupDescBlock))
767 {
768 return FALSE;
769 }
770
771 BlockNumber++;
772 CurrentGroupDescBlock += Ext2BlockSizeInBytes;
773 }
774
775 return TRUE;
776 }
777
778 BOOLEAN Ext2ReadDirectory(ULONG Inode, PVOID* DirectoryBuffer, PEXT2_INODE InodePointer)
779 {
780 EXT2_FILE_INFO DirectoryFileInfo;
781
782 TRACE("Ext2ReadDirectory() Inode = %d\n", Inode);
783
784 // Read the directory inode
785 if (!Ext2ReadInode(Inode, InodePointer))
786 {
787 return FALSE;
788 }
789
790 // Make sure it is a directory inode
791 if ((InodePointer->mode & EXT2_S_IFMT) != EXT2_S_IFDIR)
792 {
793 FileSystemError("Inode is not a directory.");
794 return FALSE;
795 }
796
797 // Fill in file info struct so we can call Ext2ReadFileBig()
798 RtlZeroMemory(&DirectoryFileInfo, sizeof(EXT2_FILE_INFO));
799 DirectoryFileInfo.DriveNumber = Ext2DriveNumber;
800 DirectoryFileInfo.FileBlockList = Ext2ReadBlockPointerList(InodePointer);
801 DirectoryFileInfo.FilePointer = 0;
802 DirectoryFileInfo.FileSize = Ext2GetInodeFileSize(InodePointer);
803
804 if (DirectoryFileInfo.FileBlockList == NULL)
805 {
806 return FALSE;
807 }
808
809 //
810 // Now allocate the memory to hold the group descriptors
811 //
812 ASSERT(DirectoryFileInfo.FileSize <= 0xFFFFFFFF);
813 *DirectoryBuffer = (PEXT2_DIR_ENTRY)FrLdrTempAlloc((ULONG)DirectoryFileInfo.FileSize, TAG_EXT_BUFFER);
814
815 //
816 // Make sure we got the memory
817 //
818 if (*DirectoryBuffer == NULL)
819 {
820 FrLdrTempFree(DirectoryFileInfo.FileBlockList, TAG_EXT_BLOCK_LIST);
821 FileSystemError("Out of memory.");
822 return FALSE;
823 }
824
825 // Now read the root directory data
826 if (!Ext2ReadFileBig(&DirectoryFileInfo, DirectoryFileInfo.FileSize, NULL, *DirectoryBuffer))
827 {
828 FrLdrTempFree(*DirectoryBuffer, TAG_EXT_BUFFER);
829 *DirectoryBuffer = NULL;
830 FrLdrTempFree(DirectoryFileInfo.FileBlockList, TAG_EXT_BLOCK_LIST);
831 return FALSE;
832 }
833
834 FrLdrTempFree(DirectoryFileInfo.FileBlockList, TAG_EXT_BLOCK_LIST);
835 return TRUE;
836 }
837
838 BOOLEAN Ext2ReadBlock(ULONG BlockNumber, PVOID Buffer)
839 {
840 CHAR ErrorString[80];
841
842 TRACE("Ext2ReadBlock() BlockNumber = %d Buffer = 0x%x\n", BlockNumber, Buffer);
843
844 // Make sure its a valid block
845 if (BlockNumber > Ext2SuperBlock->total_blocks)
846 {
847 sprintf(ErrorString, "Error reading block %d - block out of range.", (int) BlockNumber);
848 FileSystemError(ErrorString);
849 return FALSE;
850 }
851
852 // Check to see if this is a sparse block
853 if (BlockNumber == 0)
854 {
855 TRACE("Block is part of a sparse file. Zeroing input buffer.\n");
856
857 RtlZeroMemory(Buffer, Ext2BlockSizeInBytes);
858
859 return TRUE;
860 }
861
862 return Ext2ReadVolumeSectors(Ext2DriveNumber, (ULONGLONG)BlockNumber * Ext2BlockSizeInSectors, Ext2BlockSizeInSectors, Buffer);
863 }
864
865 /*
866 * Ext2ReadPartialBlock()
867 * Reads part of a block into memory
868 */
869 BOOLEAN Ext2ReadPartialBlock(ULONG BlockNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer)
870 {
871 PVOID TempBuffer;
872
873 TRACE("Ext2ReadPartialBlock() BlockNumber = %d StartingOffset = %d Length = %d Buffer = 0x%x\n", BlockNumber, StartingOffset, Length, Buffer);
874
875 TempBuffer = FrLdrTempAlloc(Ext2BlockSizeInBytes, TAG_EXT_BUFFER);
876
877 if (!Ext2ReadBlock(BlockNumber, TempBuffer))
878 {
879 return FALSE;
880 }
881
882 memcpy(Buffer, ((PUCHAR)TempBuffer + StartingOffset), Length);
883
884 FrLdrTempFree(TempBuffer, TAG_EXT_BUFFER);
885
886 return TRUE;
887 }
888
889 ULONG Ext2GetGroupDescBlockNumber(ULONG Group)
890 {
891 return (((Group * sizeof(EXT2_GROUP_DESC)) / Ext2GroupDescPerBlock) + Ext2SuperBlock->first_data_block + 1);
892 }
893
894 ULONG Ext2GetGroupDescOffsetInBlock(ULONG Group)
895 {
896 return ((Group * sizeof(EXT2_GROUP_DESC)) % Ext2GroupDescPerBlock);
897 }
898
899 ULONG Ext2GetInodeGroupNumber(ULONG Inode)
900 {
901 return ((Inode - 1) / Ext2SuperBlock->inodes_per_group);
902 }
903
904 ULONG Ext2GetInodeBlockNumber(ULONG Inode)
905 {
906 return (((Inode - 1) % Ext2SuperBlock->inodes_per_group) / Ext2InodesPerBlock);
907 }
908
909 ULONG Ext2GetInodeOffsetInBlock(ULONG Inode)
910 {
911 return (((Inode - 1) % Ext2SuperBlock->inodes_per_group) % Ext2InodesPerBlock);
912 }
913
914 BOOLEAN Ext2ReadInode(ULONG Inode, PEXT2_INODE InodeBuffer)
915 {
916 ULONG InodeGroupNumber;
917 ULONG InodeBlockNumber;
918 ULONG InodeOffsetInBlock;
919 CHAR ErrorString[80];
920 EXT2_GROUP_DESC GroupDescriptor;
921
922 TRACE("Ext2ReadInode() Inode = %d\n", Inode);
923
924 // Make sure its a valid inode
925 if ((Inode < 1) || (Inode > Ext2SuperBlock->total_inodes))
926 {
927 sprintf(ErrorString, "Error reading inode %ld - inode out of range.", Inode);
928 FileSystemError(ErrorString);
929 return FALSE;
930 }
931
932 // Get inode group & block number and offset in block
933 InodeGroupNumber = Ext2GetInodeGroupNumber(Inode);
934 InodeBlockNumber = Ext2GetInodeBlockNumber(Inode);
935 InodeOffsetInBlock = Ext2GetInodeOffsetInBlock(Inode);
936 TRACE("InodeGroupNumber = %d\n", InodeGroupNumber);
937 TRACE("InodeBlockNumber = %d\n", InodeBlockNumber);
938 TRACE("InodeOffsetInBlock = %d\n", InodeOffsetInBlock);
939
940 // Read the group descriptor
941 if (!Ext2ReadGroupDescriptor(InodeGroupNumber, &GroupDescriptor))
942 {
943 return FALSE;
944 }
945
946 // Add the start block of the inode table to the inode block number
947 InodeBlockNumber += GroupDescriptor.inode_table_id;
948 TRACE("InodeBlockNumber (after group desc correction) = %d\n", InodeBlockNumber);
949
950 // Read the block
951 if (!Ext2ReadPartialBlock(InodeBlockNumber,
952 (InodeOffsetInBlock * EXT2_INODE_SIZE(Ext2SuperBlock)),
953 sizeof(EXT2_INODE),
954 InodeBuffer))
955 {
956 return FALSE;
957 }
958
959 TRACE("Dumping inode information:\n");
960 TRACE("mode = 0x%x\n", InodeBuffer->mode);
961 TRACE("uid = %d\n", InodeBuffer->uid);
962 TRACE("size = %d\n", InodeBuffer->size);
963 TRACE("atime = %d\n", InodeBuffer->atime);
964 TRACE("ctime = %d\n", InodeBuffer->ctime);
965 TRACE("mtime = %d\n", InodeBuffer->mtime);
966 TRACE("dtime = %d\n", InodeBuffer->dtime);
967 TRACE("gid = %d\n", InodeBuffer->gid);
968 TRACE("nlinks = %d\n", InodeBuffer->nlinks);
969 TRACE("blockcnt = %d\n", InodeBuffer->blockcnt);
970 TRACE("flags = 0x%x\n", InodeBuffer->flags);
971 TRACE("osd1 = 0x%x\n", InodeBuffer->osd1);
972 TRACE("dir_blocks = { %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u }\n",
973 InodeBuffer->blocks.dir_blocks[0], InodeBuffer->blocks.dir_blocks[1], InodeBuffer->blocks.dir_blocks[ 2], InodeBuffer->blocks.dir_blocks[ 3],
974 InodeBuffer->blocks.dir_blocks[4], InodeBuffer->blocks.dir_blocks[5], InodeBuffer->blocks.dir_blocks[ 6], InodeBuffer->blocks.dir_blocks[ 7],
975 InodeBuffer->blocks.dir_blocks[8], InodeBuffer->blocks.dir_blocks[9], InodeBuffer->blocks.dir_blocks[10], InodeBuffer->blocks.dir_blocks[11]);
976 TRACE("indir_block = %u\n", InodeBuffer->blocks.indir_block);
977 TRACE("double_indir_block = %u\n", InodeBuffer->blocks.double_indir_block);
978 TRACE("tripple_indir_block = %u\n", InodeBuffer->blocks.tripple_indir_block);
979 TRACE("version = %d\n", InodeBuffer->version);
980 TRACE("acl = %d\n", InodeBuffer->acl);
981 TRACE("dir_acl = %d\n", InodeBuffer->dir_acl);
982 TRACE("fragment_addr = %d\n", InodeBuffer->fragment_addr);
983 TRACE("osd2 = { %d, %d, %d }\n",
984 InodeBuffer->osd2[0], InodeBuffer->osd2[1], InodeBuffer->osd2[2]);
985
986 return TRUE;
987 }
988
989 BOOLEAN Ext2ReadGroupDescriptor(ULONG Group, PEXT2_GROUP_DESC GroupBuffer)
990 {
991 TRACE("Ext2ReadGroupDescriptor()\n");
992
993 /*if (!Ext2ReadBlock(Ext2GetGroupDescBlockNumber(Group), (PVOID)FILESYSBUFFER))
994 {
995 return FALSE;
996 }
997
998 RtlCopyMemory(GroupBuffer, (PVOID)(FILESYSBUFFER + Ext2GetGroupDescOffsetInBlock(Group)), sizeof(EXT2_GROUP_DESC));*/
999
1000 RtlCopyMemory(GroupBuffer, &Ext2GroupDescriptors[Group], sizeof(EXT2_GROUP_DESC));
1001
1002 TRACE("Dumping group descriptor:\n");
1003 TRACE("block_id = %d\n", GroupBuffer->block_id);
1004 TRACE("inode_id = %d\n", GroupBuffer->inode_id);
1005 TRACE("inode_table_id = %d\n", GroupBuffer->inode_table_id);
1006 TRACE("free_blocks = %d\n", GroupBuffer->free_blocks);
1007 TRACE("free_inodes = %d\n", GroupBuffer->free_inodes);
1008 TRACE("used_dirs = %d\n", GroupBuffer->used_dirs);
1009
1010 return TRUE;
1011 }
1012
1013 ULONG* Ext2ReadBlockPointerList(PEXT2_INODE Inode)
1014 {
1015 ULONGLONG FileSize;
1016 ULONG BlockCount;
1017 ULONG* BlockList;
1018 ULONG CurrentBlockInList;
1019 ULONG CurrentBlock;
1020
1021 TRACE("Ext2ReadBlockPointerList()\n");
1022
1023 // Get the number of blocks this file occupies
1024 // I would just use Inode->i_blocks but it
1025 // doesn't seem to be the number of blocks
1026 // the file size corresponds to, but instead
1027 // it is much bigger.
1028 //BlockCount = Inode->i_blocks;
1029 FileSize = Ext2GetInodeFileSize(Inode);
1030 FileSize = ROUND_UP(FileSize, Ext2BlockSizeInBytes);
1031 BlockCount = (ULONG)(FileSize / Ext2BlockSizeInBytes);
1032
1033 // Allocate the memory for the block list
1034 BlockList = FrLdrTempAlloc(BlockCount * sizeof(ULONG), TAG_EXT_BLOCK_LIST);
1035 if (BlockList == NULL)
1036 {
1037 return NULL;
1038 }
1039
1040 RtlZeroMemory(BlockList, BlockCount * sizeof(ULONG));
1041
1042 // Copy the direct block pointers
1043 for (CurrentBlockInList = CurrentBlock = 0;
1044 CurrentBlockInList < BlockCount && CurrentBlock < INDIRECT_BLOCKS;
1045 CurrentBlock++, CurrentBlockInList++)
1046 {
1047 BlockList[CurrentBlockInList] = Inode->blocks.dir_blocks[CurrentBlock];
1048 }
1049
1050 // Copy the indirect block pointers
1051 if (CurrentBlockInList < BlockCount)
1052 {
1053 if (!Ext2CopyIndirectBlockPointers(BlockList, &CurrentBlockInList, BlockCount, Inode->blocks.indir_block))
1054 {
1055 FrLdrTempFree(BlockList, TAG_EXT_BLOCK_LIST);
1056 return NULL;
1057 }
1058 }
1059
1060 // Copy the double indirect block pointers
1061 if (CurrentBlockInList < BlockCount)
1062 {
1063 if (!Ext2CopyDoubleIndirectBlockPointers(BlockList, &CurrentBlockInList, BlockCount, Inode->blocks.double_indir_block))
1064 {
1065 FrLdrTempFree(BlockList, TAG_EXT_BLOCK_LIST);
1066 return NULL;
1067 }
1068 }
1069
1070 // Copy the triple indirect block pointers
1071 if (CurrentBlockInList < BlockCount)
1072 {
1073 if (!Ext2CopyTripleIndirectBlockPointers(BlockList, &CurrentBlockInList, BlockCount, Inode->blocks.tripple_indir_block))
1074 {
1075 FrLdrTempFree(BlockList, TAG_EXT_BLOCK_LIST);
1076 return NULL;
1077 }
1078 }
1079
1080 return BlockList;
1081 }
1082
1083 ULONGLONG Ext2GetInodeFileSize(PEXT2_INODE Inode)
1084 {
1085 if ((Inode->mode & EXT2_S_IFMT) == EXT2_S_IFDIR)
1086 {
1087 return (ULONGLONG)(Inode->size);
1088 }
1089 else
1090 {
1091 return ((ULONGLONG)(Inode->size) | ((ULONGLONG)(Inode->dir_acl) << 32));
1092 }
1093 }
1094
1095 BOOLEAN Ext2CopyIndirectBlockPointers(ULONG* BlockList, ULONG* CurrentBlockInList, ULONG BlockCount, ULONG IndirectBlock)
1096 {
1097 ULONG* BlockBuffer;
1098 ULONG CurrentBlock;
1099 ULONG BlockPointersPerBlock;
1100
1101 TRACE("Ext2CopyIndirectBlockPointers() BlockCount = %d\n", BlockCount);
1102
1103 BlockPointersPerBlock = Ext2BlockSizeInBytes / sizeof(ULONG);
1104
1105 BlockBuffer = FrLdrTempAlloc(Ext2BlockSizeInBytes, TAG_EXT_BUFFER);
1106 if (!BlockBuffer)
1107 {
1108 return FALSE;
1109 }
1110
1111 if (!Ext2ReadBlock(IndirectBlock, BlockBuffer))
1112 {
1113 return FALSE;
1114 }
1115
1116 for (CurrentBlock=0; (*CurrentBlockInList)<BlockCount && CurrentBlock<BlockPointersPerBlock; CurrentBlock++)
1117 {
1118 BlockList[(*CurrentBlockInList)] = BlockBuffer[CurrentBlock];
1119 (*CurrentBlockInList)++;
1120 }
1121
1122 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER);
1123
1124 return TRUE;
1125 }
1126
1127 BOOLEAN Ext2CopyDoubleIndirectBlockPointers(ULONG* BlockList, ULONG* CurrentBlockInList, ULONG BlockCount, ULONG DoubleIndirectBlock)
1128 {
1129 ULONG* BlockBuffer;
1130 ULONG CurrentBlock;
1131 ULONG BlockPointersPerBlock;
1132
1133 TRACE("Ext2CopyDoubleIndirectBlockPointers() BlockCount = %d\n", BlockCount);
1134
1135 BlockPointersPerBlock = Ext2BlockSizeInBytes / sizeof(ULONG);
1136
1137 BlockBuffer = (ULONG*)FrLdrTempAlloc(Ext2BlockSizeInBytes, TAG_EXT_BUFFER);
1138 if (BlockBuffer == NULL)
1139 {
1140 return FALSE;
1141 }
1142
1143 if (!Ext2ReadBlock(DoubleIndirectBlock, BlockBuffer))
1144 {
1145 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER);
1146 return FALSE;
1147 }
1148
1149 for (CurrentBlock=0; (*CurrentBlockInList)<BlockCount && CurrentBlock<BlockPointersPerBlock; CurrentBlock++)
1150 {
1151 if (!Ext2CopyIndirectBlockPointers(BlockList, CurrentBlockInList, BlockCount, BlockBuffer[CurrentBlock]))
1152 {
1153 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER);
1154 return FALSE;
1155 }
1156 }
1157
1158 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER);
1159 return TRUE;
1160 }
1161
1162 BOOLEAN Ext2CopyTripleIndirectBlockPointers(ULONG* BlockList, ULONG* CurrentBlockInList, ULONG BlockCount, ULONG TripleIndirectBlock)
1163 {
1164 ULONG* BlockBuffer;
1165 ULONG CurrentBlock;
1166 ULONG BlockPointersPerBlock;
1167
1168 TRACE("Ext2CopyTripleIndirectBlockPointers() BlockCount = %d\n", BlockCount);
1169
1170 BlockPointersPerBlock = Ext2BlockSizeInBytes / sizeof(ULONG);
1171
1172 BlockBuffer = (ULONG*)FrLdrTempAlloc(Ext2BlockSizeInBytes, TAG_EXT_BUFFER);
1173 if (BlockBuffer == NULL)
1174 {
1175 return FALSE;
1176 }
1177
1178 if (!Ext2ReadBlock(TripleIndirectBlock, BlockBuffer))
1179 {
1180 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER);
1181 return FALSE;
1182 }
1183
1184 for (CurrentBlock=0; (*CurrentBlockInList)<BlockCount && CurrentBlock<BlockPointersPerBlock; CurrentBlock++)
1185 {
1186 if (!Ext2CopyDoubleIndirectBlockPointers(BlockList, CurrentBlockInList, BlockCount, BlockBuffer[CurrentBlock]))
1187 {
1188 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER);
1189 return FALSE;
1190 }
1191 }
1192
1193 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER);
1194 return TRUE;
1195 }
1196
1197 ARC_STATUS Ext2Close(ULONG FileId)
1198 {
1199 PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1200
1201 FrLdrTempFree(FileHandle, TAG_EXT_FILE);
1202
1203 return ESUCCESS;
1204 }
1205
1206 ARC_STATUS Ext2GetFileInformation(ULONG FileId, FILEINFORMATION* Information)
1207 {
1208 PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1209
1210 RtlZeroMemory(Information, sizeof(*Information));
1211 Information->EndingAddress.QuadPart = FileHandle->FileSize;
1212 Information->CurrentAddress.QuadPart = FileHandle->FilePointer;
1213
1214 TRACE("Ext2GetFileInformation(%lu) -> FileSize = %llu, FilePointer = 0x%llx\n",
1215 FileId, Information->EndingAddress.QuadPart, Information->CurrentAddress.QuadPart);
1216
1217 return ESUCCESS;
1218 }
1219
1220 ARC_STATUS Ext2Open(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
1221 {
1222 PEXT2_FILE_INFO FileHandle;
1223 //ULONG DeviceId;
1224
1225 if (OpenMode != OpenReadOnly)
1226 return EACCES;
1227
1228 //DeviceId = FsGetDeviceId(*FileId);
1229
1230 TRACE("Ext2Open() FileName = %s\n", Path);
1231
1232 //
1233 // Call old open method
1234 //
1235 FileHandle = Ext2OpenFile(Path);
1236
1237 //
1238 // Check for error
1239 //
1240 if (!FileHandle)
1241 return ENOENT;
1242
1243 //
1244 // Success. Remember the handle
1245 //
1246 FsSetDeviceSpecific(*FileId, FileHandle);
1247 return ESUCCESS;
1248 }
1249
1250 ARC_STATUS Ext2Read(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
1251 {
1252 PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1253 ULONGLONG BytesReadBig;
1254 BOOLEAN Success;
1255
1256 //
1257 // Read data
1258 //
1259 Success = Ext2ReadFileBig(FileHandle, N, &BytesReadBig, Buffer);
1260 *Count = (ULONG)BytesReadBig;
1261
1262 //
1263 // Check for success
1264 //
1265 if (Success)
1266 return ESUCCESS;
1267 else
1268 return EIO;
1269 }
1270
1271 ARC_STATUS Ext2Seek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
1272 {
1273 PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1274
1275 TRACE("Ext2Seek() NewFilePointer = %lu\n", Position->LowPart);
1276
1277 if (SeekMode != SeekAbsolute)
1278 return EINVAL;
1279 if (Position->HighPart != 0)
1280 return EINVAL;
1281 if (Position->LowPart >= FileHandle->FileSize)
1282 return EINVAL;
1283
1284 FileHandle->FilePointer = Position->LowPart;
1285 return ESUCCESS;
1286 }
1287
1288 const DEVVTBL Ext2FuncTable =
1289 {
1290 Ext2Close,
1291 Ext2GetFileInformation,
1292 Ext2Open,
1293 Ext2Read,
1294 Ext2Seek,
1295 L"ext2fs",
1296 };
1297
1298 const DEVVTBL* Ext2Mount(ULONG DeviceId)
1299 {
1300 EXT2_SUPER_BLOCK SuperBlock;
1301 LARGE_INTEGER Position;
1302 ULONG Count;
1303 ARC_STATUS Status;
1304
1305 TRACE("Enter Ext2Mount(%lu)\n", DeviceId);
1306
1307 //
1308 // Read the SuperBlock
1309 //
1310 Position.HighPart = 0;
1311 Position.LowPart = 2 * 512;
1312 Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
1313 if (Status != ESUCCESS)
1314 return NULL;
1315 Status = ArcRead(DeviceId, &SuperBlock, sizeof(SuperBlock), &Count);
1316 if (Status != ESUCCESS || Count != sizeof(SuperBlock))
1317 return NULL;
1318
1319 //
1320 // Check if SuperBlock is valid. If yes, return Ext2 function table
1321 //
1322 if (SuperBlock.magic == EXT2_MAGIC)
1323 {
1324 //
1325 // Compatibility hack as long as FS is not using underlying device DeviceId
1326 //
1327 UCHAR DriveNumber;
1328 ULONGLONG StartSector;
1329 ULONGLONG SectorCount;
1330 int Type;
1331 if (!DiskGetBootVolume(&DriveNumber, &StartSector, &SectorCount, &Type))
1332 return NULL;
1333 Ext2OpenVolume(DriveNumber, StartSector, SectorCount);
1334
1335 /* Return success */
1336 TRACE("Ext2Mount(%lu) success\n", DeviceId);
1337 return &Ext2FuncTable;
1338 }
1339 else
1340 return NULL;
1341 }
1342
1343 #endif