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