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