xref: /reactos/boot/freeldr/freeldr/lib/fs/ext2.c (revision 84ccccab)
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(FILEINFORMATION));
1211     Information->EndingAddress.QuadPart = FileHandle->FileSize;
1212     Information->CurrentAddress.QuadPart = FileHandle->FilePointer;
1213 
1214     TRACE("Ext2GetFileInformation() FileSize = %d\n",
1215         Information->EndingAddress.LowPart);
1216     TRACE("Ext2GetFileInformation() FilePointer = %d\n",
1217         Information->CurrentAddress.LowPart);
1218 
1219     return ESUCCESS;
1220 }
1221 
1222 ARC_STATUS Ext2Open(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
1223 {
1224     PEXT2_FILE_INFO FileHandle;
1225     //ULONG DeviceId;
1226 
1227     if (OpenMode != OpenReadOnly)
1228         return EACCES;
1229 
1230     //DeviceId = FsGetDeviceId(*FileId);
1231 
1232     TRACE("Ext2Open() FileName = %s\n", Path);
1233 
1234     //
1235     // Call old open method
1236     //
1237     FileHandle = Ext2OpenFile(Path);
1238 
1239     //
1240     // Check for error
1241     //
1242     if (!FileHandle)
1243         return ENOENT;
1244 
1245     //
1246     // Success. Remember the handle
1247     //
1248     FsSetDeviceSpecific(*FileId, FileHandle);
1249     return ESUCCESS;
1250 }
1251 
1252 ARC_STATUS Ext2Read(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
1253 {
1254     PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1255     ULONGLONG BytesReadBig;
1256     BOOLEAN Success;
1257 
1258     //
1259     // Read data
1260     //
1261     Success = Ext2ReadFileBig(FileHandle, N, &BytesReadBig, Buffer);
1262     *Count = (ULONG)BytesReadBig;
1263 
1264     //
1265     // Check for success
1266     //
1267     if (Success)
1268         return ESUCCESS;
1269     else
1270         return EIO;
1271 }
1272 
1273 ARC_STATUS Ext2Seek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
1274 {
1275     PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1276 
1277     TRACE("Ext2Seek() NewFilePointer = %lu\n", Position->LowPart);
1278 
1279     if (SeekMode != SeekAbsolute)
1280         return EINVAL;
1281     if (Position->HighPart != 0)
1282         return EINVAL;
1283     if (Position->LowPart >= FileHandle->FileSize)
1284         return EINVAL;
1285 
1286     FileHandle->FilePointer = Position->LowPart;
1287     return ESUCCESS;
1288 }
1289 
1290 const DEVVTBL Ext2FuncTable =
1291 {
1292     Ext2Close,
1293     Ext2GetFileInformation,
1294     Ext2Open,
1295     Ext2Read,
1296     Ext2Seek,
1297     L"ext2fs",
1298 };
1299 
1300 const DEVVTBL* Ext2Mount(ULONG DeviceId)
1301 {
1302     EXT2_SUPER_BLOCK SuperBlock;
1303     LARGE_INTEGER Position;
1304     ULONG Count;
1305     ARC_STATUS Status;
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         return &Ext2FuncTable;
1335     }
1336     else
1337         return NULL;
1338 }
1339 
1340 #endif
1341 
1342