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