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