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 & FilePointer 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 } 537 538 return TRUE; 539 } 540 541 BOOLEAN Ext2ReadVolumeSectors(PEXT2_VOLUME_INFO Volume, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer) 542 { 543 #if 0 544 return CacheReadDiskSectors(DriveNumber, SectorNumber + Ext2VolumeStartSector, SectorCount, Buffer); 545 #endif 546 547 LARGE_INTEGER Position; 548 ULONG Count; 549 ARC_STATUS Status; 550 551 /* Seek to right position */ 552 Position.QuadPart = (ULONGLONG)SectorNumber * 512; 553 Status = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute); 554 if (Status != ESUCCESS) 555 { 556 TRACE("Ext2ReadVolumeSectors() Failed to seek\n"); 557 return FALSE; 558 } 559 560 /* Read data */ 561 Status = ArcRead(Volume->DeviceId, Buffer, SectorCount * 512, &Count); 562 if (Status != ESUCCESS || Count != SectorCount * 512) 563 { 564 TRACE("Ext2ReadVolumeSectors() Failed to read\n"); 565 return FALSE; 566 } 567 568 /* Return success */ 569 return TRUE; 570 } 571 572 BOOLEAN Ext2ReadSuperBlock(PEXT2_VOLUME_INFO Volume) 573 { 574 PEXT2_SUPER_BLOCK SuperBlock = Volume->SuperBlock; 575 LARGE_INTEGER Position; 576 ULONG Count; 577 ARC_STATUS Status; 578 579 TRACE("Ext2ReadSuperBlock()\n"); 580 581 #if 0 582 /* Free any memory previously allocated */ 583 if (SuperBlock != NULL) 584 { 585 FrLdrTempFree(SuperBlock, TAG_EXT_SUPER_BLOCK); 586 SuperBlock = NULL; 587 } 588 #endif 589 590 /* Allocate the memory to hold the super block if needed */ 591 if (SuperBlock == NULL) 592 { 593 SuperBlock = (PEXT2_SUPER_BLOCK)FrLdrTempAlloc(1024, TAG_EXT_SUPER_BLOCK); 594 if (SuperBlock == NULL) 595 { 596 FileSystemError("Out of memory."); 597 return FALSE; 598 } 599 } 600 Volume->SuperBlock = SuperBlock; 601 602 /* Reset its contents */ 603 RtlZeroMemory(SuperBlock, 1024); 604 605 /* Read the SuperBlock */ 606 Position.QuadPart = 2 * 512; 607 Status = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute); 608 if (Status != ESUCCESS) 609 return FALSE; 610 Status = ArcRead(Volume->DeviceId, SuperBlock, 2 * 512, &Count); 611 if (Status != ESUCCESS || Count != 2 * 512) 612 return FALSE; 613 614 TRACE("Dumping super block:\n"); 615 TRACE("total_inodes: %d\n", SuperBlock->total_inodes); 616 TRACE("total_blocks: %d\n", SuperBlock->total_blocks); 617 TRACE("reserved_blocks: %d\n", SuperBlock->reserved_blocks); 618 TRACE("free_blocks: %d\n", SuperBlock->free_blocks); 619 TRACE("free_inodes: %d\n", SuperBlock->free_inodes); 620 TRACE("first_data_block: %d\n", SuperBlock->first_data_block); 621 TRACE("log2_block_size: %d\n", SuperBlock->log2_block_size); 622 TRACE("log2_fragment_size: %d\n", SuperBlock->log2_fragment_size); 623 TRACE("blocks_per_group: %d\n", SuperBlock->blocks_per_group); 624 TRACE("fragments_per_group: %d\n", SuperBlock->fragments_per_group); 625 TRACE("inodes_per_group: %d\n", SuperBlock->inodes_per_group); 626 TRACE("mtime: %d\n", SuperBlock->mtime); 627 TRACE("utime: %d\n", SuperBlock->utime); 628 TRACE("mnt_count: %d\n", SuperBlock->mnt_count); 629 TRACE("max_mnt_count: %d\n", SuperBlock->max_mnt_count); 630 TRACE("magic: 0x%x\n", SuperBlock->magic); 631 TRACE("fs_state: %d\n", SuperBlock->fs_state); 632 TRACE("error_handling: %d\n", SuperBlock->error_handling); 633 TRACE("minor_revision_level: %d\n", SuperBlock->minor_revision_level); 634 TRACE("lastcheck: %d\n", SuperBlock->lastcheck); 635 TRACE("checkinterval: %d\n", SuperBlock->checkinterval); 636 TRACE("creator_os: %d\n", SuperBlock->creator_os); 637 TRACE("revision_level: %d\n", SuperBlock->revision_level); 638 TRACE("uid_reserved: %d\n", SuperBlock->uid_reserved); 639 TRACE("gid_reserved: %d\n", SuperBlock->gid_reserved); 640 TRACE("first_inode: %d\n", SuperBlock->first_inode); 641 TRACE("inode_size: %d\n", SuperBlock->inode_size); 642 TRACE("block_group_number: %d\n", SuperBlock->block_group_number); 643 TRACE("feature_compatibility: 0x%x\n", SuperBlock->feature_compatibility); 644 TRACE("feature_incompat: 0x%x\n", SuperBlock->feature_incompat); 645 TRACE("feature_ro_compat: 0x%x\n", SuperBlock->feature_ro_compat); 646 TRACE("unique_id = { 0x%x, 0x%x, 0x%x, 0x%x }\n", 647 SuperBlock->unique_id[0], SuperBlock->unique_id[1], 648 SuperBlock->unique_id[2], SuperBlock->unique_id[3]); 649 TRACE("volume_name = '%.16s'\n", SuperBlock->volume_name); 650 TRACE("last_mounted_on = '%.64s'\n", SuperBlock->last_mounted_on); 651 TRACE("compression_info = 0x%x\n", SuperBlock->compression_info); 652 653 // 654 // Check the super block magic 655 // 656 if (SuperBlock->magic != EXT2_MAGIC) 657 { 658 FileSystemError("Invalid super block magic (0xef53)"); 659 return FALSE; 660 } 661 662 // 663 // Check the revision level 664 // 665 if (SuperBlock->revision_level > EXT2_DYNAMIC_REVISION) 666 { 667 FileSystemError("FreeLoader does not understand the revision of this EXT2/EXT3 filesystem.\nPlease update FreeLoader."); 668 return FALSE; 669 } 670 671 // 672 // Check the feature set 673 // Don't need to check the compatible or read-only compatible features 674 // because we only mount the filesystem as read-only 675 // 676 if ((SuperBlock->revision_level >= EXT2_DYNAMIC_REVISION) && 677 (/*((SuperBlock->s_feature_compat & ~EXT3_FEATURE_COMPAT_SUPP) != 0) ||*/ 678 /*((SuperBlock->s_feature_ro_compat & ~EXT3_FEATURE_RO_COMPAT_SUPP) != 0) ||*/ 679 ((SuperBlock->feature_incompat & ~EXT3_FEATURE_INCOMPAT_SUPP) != 0))) 680 { 681 FileSystemError("FreeLoader does not understand features of this EXT2/EXT3 filesystem.\nPlease update FreeLoader."); 682 return FALSE; 683 } 684 685 // Calculate the group count 686 Volume->GroupCount = (SuperBlock->total_blocks - SuperBlock->first_data_block + SuperBlock->blocks_per_group - 1) / SuperBlock->blocks_per_group; 687 TRACE("Ext2GroupCount: %d\n", Volume->GroupCount); 688 689 // Calculate the block size 690 Volume->BlockSizeInBytes = 1024 << SuperBlock->log2_block_size; 691 Volume->BlockSizeInSectors = Volume->BlockSizeInBytes / Volume->BytesPerSector; 692 TRACE("Ext2BlockSizeInBytes: %d\n", Volume->BlockSizeInBytes); 693 TRACE("Ext2BlockSizeInSectors: %d\n", Volume->BlockSizeInSectors); 694 695 // Calculate the fragment size 696 if (SuperBlock->log2_fragment_size >= 0) 697 { 698 Volume->FragmentSizeInBytes = 1024 << SuperBlock->log2_fragment_size; 699 } 700 else 701 { 702 Volume->FragmentSizeInBytes = 1024 >> -(SuperBlock->log2_fragment_size); 703 } 704 Volume->FragmentSizeInSectors = Volume->FragmentSizeInBytes / Volume->BytesPerSector; 705 TRACE("Ext2FragmentSizeInBytes: %d\n", Volume->FragmentSizeInBytes); 706 TRACE("Ext2FragmentSizeInSectors: %d\n", Volume->FragmentSizeInSectors); 707 708 // Verify that the fragment size and the block size are equal 709 if (Volume->BlockSizeInBytes != Volume->FragmentSizeInBytes) 710 { 711 FileSystemError("The fragment size must be equal to the block size."); 712 return FALSE; 713 } 714 715 // Calculate the number of inodes in one block 716 Volume->InodesPerBlock = Volume->BlockSizeInBytes / EXT2_INODE_SIZE(SuperBlock); 717 TRACE("Ext2InodesPerBlock: %d\n", Volume->InodesPerBlock); 718 719 // Calculate the number of group descriptors in one block 720 Volume->GroupDescPerBlock = EXT2_DESC_PER_BLOCK(SuperBlock); 721 TRACE("Ext2GroupDescPerBlock: %d\n", Volume->GroupDescPerBlock); 722 723 return TRUE; 724 } 725 726 BOOLEAN Ext2ReadGroupDescriptors(PEXT2_VOLUME_INFO Volume) 727 { 728 ULONG GroupDescBlockCount; 729 ULONG BlockNumber; 730 PUCHAR CurrentGroupDescBlock; 731 732 TRACE("Ext2ReadGroupDescriptors()\n"); 733 734 /* Free any memory previously allocated */ 735 if (Volume->GroupDescriptors != NULL) 736 { 737 FrLdrTempFree(Volume->GroupDescriptors, TAG_EXT_GROUP_DESC); 738 Volume->GroupDescriptors = NULL; 739 } 740 741 /* Now allocate the memory to hold the group descriptors */ 742 GroupDescBlockCount = ROUND_UP(Volume->GroupCount, Volume->GroupDescPerBlock) / Volume->GroupDescPerBlock; 743 Volume->GroupDescriptors = (PEXT2_GROUP_DESC)FrLdrTempAlloc(GroupDescBlockCount * Volume->BlockSizeInBytes, TAG_EXT_GROUP_DESC); 744 if (Volume->GroupDescriptors == NULL) 745 { 746 FileSystemError("Out of memory."); 747 return FALSE; 748 } 749 750 // Now read the group descriptors 751 CurrentGroupDescBlock = (PUCHAR)Volume->GroupDescriptors; 752 BlockNumber = Volume->SuperBlock->first_data_block + 1; 753 754 while (GroupDescBlockCount--) 755 { 756 if (!Ext2ReadBlock(Volume, BlockNumber, CurrentGroupDescBlock)) 757 { 758 return FALSE; 759 } 760 761 BlockNumber++; 762 CurrentGroupDescBlock += Volume->BlockSizeInBytes; 763 } 764 765 return TRUE; 766 } 767 768 BOOLEAN Ext2ReadDirectory(PEXT2_VOLUME_INFO Volume, ULONG Inode, PVOID* DirectoryBuffer, PEXT2_INODE InodePointer) 769 { 770 EXT2_FILE_INFO DirectoryFileInfo; 771 772 TRACE("Ext2ReadDirectory() Inode = %d\n", Inode); 773 774 // Read the directory inode 775 if (!Ext2ReadInode(Volume, Inode, InodePointer)) 776 { 777 return FALSE; 778 } 779 780 // Make sure it is a directory inode 781 if ((InodePointer->mode & EXT2_S_IFMT) != EXT2_S_IFDIR) 782 { 783 FileSystemError("Inode is not a directory."); 784 return FALSE; 785 } 786 787 // Fill in file info struct so we can call Ext2ReadFileBig() 788 RtlZeroMemory(&DirectoryFileInfo, sizeof(EXT2_FILE_INFO)); 789 DirectoryFileInfo.Volume = Volume; 790 DirectoryFileInfo.FileBlockList = Ext2ReadBlockPointerList(Volume, InodePointer); 791 DirectoryFileInfo.FilePointer = 0; 792 DirectoryFileInfo.FileSize = Ext2GetInodeFileSize(InodePointer); 793 794 if (DirectoryFileInfo.FileBlockList == NULL) 795 { 796 return FALSE; 797 } 798 799 // 800 // Now allocate the memory to hold the group descriptors 801 // 802 ASSERT(DirectoryFileInfo.FileSize <= 0xFFFFFFFF); 803 *DirectoryBuffer = (PEXT2_DIR_ENTRY)FrLdrTempAlloc((ULONG)DirectoryFileInfo.FileSize, TAG_EXT_BUFFER); 804 805 // 806 // Make sure we got the memory 807 // 808 if (*DirectoryBuffer == NULL) 809 { 810 FrLdrTempFree(DirectoryFileInfo.FileBlockList, TAG_EXT_BLOCK_LIST); 811 FileSystemError("Out of memory."); 812 return FALSE; 813 } 814 815 // Now read the root directory data 816 if (!Ext2ReadFileBig(&DirectoryFileInfo, DirectoryFileInfo.FileSize, NULL, *DirectoryBuffer)) 817 { 818 FrLdrTempFree(*DirectoryBuffer, TAG_EXT_BUFFER); 819 *DirectoryBuffer = NULL; 820 FrLdrTempFree(DirectoryFileInfo.FileBlockList, TAG_EXT_BLOCK_LIST); 821 return FALSE; 822 } 823 824 FrLdrTempFree(DirectoryFileInfo.FileBlockList, TAG_EXT_BLOCK_LIST); 825 return TRUE; 826 } 827 828 BOOLEAN Ext2ReadBlock(PEXT2_VOLUME_INFO Volume, ULONG BlockNumber, PVOID Buffer) 829 { 830 CHAR ErrorString[80]; 831 832 TRACE("Ext2ReadBlock() BlockNumber = %d Buffer = 0x%x\n", BlockNumber, Buffer); 833 834 // Make sure its a valid block 835 if (BlockNumber > Volume->SuperBlock->total_blocks) 836 { 837 sprintf(ErrorString, "Error reading block %d - block out of range.", (int) BlockNumber); 838 FileSystemError(ErrorString); 839 return FALSE; 840 } 841 842 // Check to see if this is a sparse block 843 if (BlockNumber == 0) 844 { 845 TRACE("Block is part of a sparse file. Zeroing input buffer.\n"); 846 847 RtlZeroMemory(Buffer, Volume->BlockSizeInBytes); 848 849 return TRUE; 850 } 851 852 return Ext2ReadVolumeSectors(Volume, (ULONGLONG)BlockNumber * Volume->BlockSizeInSectors, Volume->BlockSizeInSectors, Buffer); 853 } 854 855 /* 856 * Ext2ReadPartialBlock() 857 * Reads part of a block into memory 858 */ 859 BOOLEAN Ext2ReadPartialBlock(PEXT2_VOLUME_INFO Volume, ULONG BlockNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer) 860 { 861 PVOID TempBuffer; 862 863 TRACE("Ext2ReadPartialBlock() BlockNumber = %d StartingOffset = %d Length = %d Buffer = 0x%x\n", BlockNumber, StartingOffset, Length, Buffer); 864 865 TempBuffer = FrLdrTempAlloc(Volume->BlockSizeInBytes, TAG_EXT_BUFFER); 866 867 if (!Ext2ReadBlock(Volume, BlockNumber, TempBuffer)) 868 { 869 FrLdrTempFree(TempBuffer, TAG_EXT_BUFFER); 870 return FALSE; 871 } 872 873 RtlCopyMemory(Buffer, ((PUCHAR)TempBuffer + StartingOffset), Length); 874 875 FrLdrTempFree(TempBuffer, TAG_EXT_BUFFER); 876 877 return TRUE; 878 } 879 880 #if 0 881 ULONG Ext2GetGroupDescBlockNumber(PEXT2_VOLUME_INFO Volume, ULONG Group) 882 { 883 return (((Group * sizeof(EXT2_GROUP_DESC)) / Volume->GroupDescPerBlock) + Volume->SuperBlock->first_data_block + 1); 884 } 885 886 ULONG Ext2GetGroupDescOffsetInBlock(PEXT2_VOLUME_INFO Volume, ULONG Group) 887 { 888 return ((Group * sizeof(EXT2_GROUP_DESC)) % Volume->GroupDescPerBlock); 889 } 890 #endif 891 892 ULONG Ext2GetInodeGroupNumber(PEXT2_VOLUME_INFO Volume, ULONG Inode) 893 { 894 return ((Inode - 1) / Volume->SuperBlock->inodes_per_group); 895 } 896 897 ULONG Ext2GetInodeBlockNumber(PEXT2_VOLUME_INFO Volume, ULONG Inode) 898 { 899 return (((Inode - 1) % Volume->SuperBlock->inodes_per_group) / Volume->InodesPerBlock); 900 } 901 902 ULONG Ext2GetInodeOffsetInBlock(PEXT2_VOLUME_INFO Volume, ULONG Inode) 903 { 904 return (((Inode - 1) % Volume->SuperBlock->inodes_per_group) % Volume->InodesPerBlock); 905 } 906 907 BOOLEAN Ext2ReadInode(PEXT2_VOLUME_INFO Volume, ULONG Inode, PEXT2_INODE InodeBuffer) 908 { 909 ULONG InodeGroupNumber; 910 ULONG InodeBlockNumber; 911 ULONG InodeOffsetInBlock; 912 CHAR ErrorString[80]; 913 EXT2_GROUP_DESC GroupDescriptor; 914 915 TRACE("Ext2ReadInode() Inode = %d\n", Inode); 916 917 // Make sure its a valid inode 918 if ((Inode < 1) || (Inode > Volume->SuperBlock->total_inodes)) 919 { 920 sprintf(ErrorString, "Error reading inode %ld - inode out of range.", Inode); 921 FileSystemError(ErrorString); 922 return FALSE; 923 } 924 925 // Get inode group & block number and offset in block 926 InodeGroupNumber = Ext2GetInodeGroupNumber(Volume, Inode); 927 InodeBlockNumber = Ext2GetInodeBlockNumber(Volume, Inode); 928 InodeOffsetInBlock = Ext2GetInodeOffsetInBlock(Volume, Inode); 929 TRACE("InodeGroupNumber = %d\n", InodeGroupNumber); 930 TRACE("InodeBlockNumber = %d\n", InodeBlockNumber); 931 TRACE("InodeOffsetInBlock = %d\n", InodeOffsetInBlock); 932 933 // Read the group descriptor 934 if (!Ext2ReadGroupDescriptor(Volume, InodeGroupNumber, &GroupDescriptor)) 935 { 936 return FALSE; 937 } 938 939 // Add the start block of the inode table to the inode block number 940 InodeBlockNumber += GroupDescriptor.inode_table_id; 941 TRACE("InodeBlockNumber (after group desc correction) = %d\n", InodeBlockNumber); 942 943 // Read the block 944 if (!Ext2ReadPartialBlock(Volume, 945 InodeBlockNumber, 946 (InodeOffsetInBlock * EXT2_INODE_SIZE(Volume->SuperBlock)), 947 sizeof(EXT2_INODE), 948 InodeBuffer)) 949 { 950 return FALSE; 951 } 952 953 TRACE("Dumping inode information:\n"); 954 TRACE("mode = 0x%x\n", InodeBuffer->mode); 955 TRACE("uid = %d\n", InodeBuffer->uid); 956 TRACE("size = %d\n", InodeBuffer->size); 957 TRACE("atime = %d\n", InodeBuffer->atime); 958 TRACE("ctime = %d\n", InodeBuffer->ctime); 959 TRACE("mtime = %d\n", InodeBuffer->mtime); 960 TRACE("dtime = %d\n", InodeBuffer->dtime); 961 TRACE("gid = %d\n", InodeBuffer->gid); 962 TRACE("nlinks = %d\n", InodeBuffer->nlinks); 963 TRACE("blockcnt = %d\n", InodeBuffer->blockcnt); 964 TRACE("flags = 0x%x\n", InodeBuffer->flags); 965 TRACE("osd1 = 0x%x\n", InodeBuffer->osd1); 966 TRACE("dir_blocks = { %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u }\n", 967 InodeBuffer->blocks.dir_blocks[0], InodeBuffer->blocks.dir_blocks[1], InodeBuffer->blocks.dir_blocks[ 2], InodeBuffer->blocks.dir_blocks[ 3], 968 InodeBuffer->blocks.dir_blocks[4], InodeBuffer->blocks.dir_blocks[5], InodeBuffer->blocks.dir_blocks[ 6], InodeBuffer->blocks.dir_blocks[ 7], 969 InodeBuffer->blocks.dir_blocks[8], InodeBuffer->blocks.dir_blocks[9], InodeBuffer->blocks.dir_blocks[10], InodeBuffer->blocks.dir_blocks[11]); 970 TRACE("indir_block = %u\n", InodeBuffer->blocks.indir_block); 971 TRACE("double_indir_block = %u\n", InodeBuffer->blocks.double_indir_block); 972 TRACE("tripple_indir_block = %u\n", InodeBuffer->blocks.tripple_indir_block); 973 TRACE("version = %d\n", InodeBuffer->version); 974 TRACE("acl = %d\n", InodeBuffer->acl); 975 TRACE("dir_acl = %d\n", InodeBuffer->dir_acl); 976 TRACE("fragment_addr = %d\n", InodeBuffer->fragment_addr); 977 TRACE("osd2 = { %d, %d, %d }\n", 978 InodeBuffer->osd2[0], InodeBuffer->osd2[1], InodeBuffer->osd2[2]); 979 980 return TRUE; 981 } 982 983 BOOLEAN Ext2ReadGroupDescriptor(PEXT2_VOLUME_INFO Volume, ULONG Group, PEXT2_GROUP_DESC GroupBuffer) 984 { 985 TRACE("Ext2ReadGroupDescriptor()\n"); 986 987 #if 0 988 if (!Ext2ReadBlock(Volume, Ext2GetGroupDescBlockNumber(Volume, Group), (PVOID)FILESYSBUFFER)) 989 { 990 return FALSE; 991 } 992 RtlCopyMemory(GroupBuffer, (PVOID)(FILESYSBUFFER + Ext2GetGroupDescOffsetInBlock(Volume, Group)), sizeof(EXT2_GROUP_DESC)); 993 #endif 994 995 RtlCopyMemory(GroupBuffer, &Volume->GroupDescriptors[Group], sizeof(EXT2_GROUP_DESC)); 996 997 TRACE("Dumping group descriptor:\n"); 998 TRACE("block_id = %d\n", GroupBuffer->block_id); 999 TRACE("inode_id = %d\n", GroupBuffer->inode_id); 1000 TRACE("inode_table_id = %d\n", GroupBuffer->inode_table_id); 1001 TRACE("free_blocks = %d\n", GroupBuffer->free_blocks); 1002 TRACE("free_inodes = %d\n", GroupBuffer->free_inodes); 1003 TRACE("used_dirs = %d\n", GroupBuffer->used_dirs); 1004 1005 return TRUE; 1006 } 1007 1008 ULONG* Ext2ReadBlockPointerList(PEXT2_VOLUME_INFO Volume, PEXT2_INODE Inode) 1009 { 1010 ULONGLONG FileSize; 1011 ULONG BlockCount; 1012 ULONG* BlockList; 1013 ULONG CurrentBlockInList; 1014 ULONG CurrentBlock; 1015 1016 TRACE("Ext2ReadBlockPointerList()\n"); 1017 1018 // Get the number of blocks this file occupies 1019 // I would just use Inode->i_blocks but it 1020 // doesn't seem to be the number of blocks 1021 // the file size corresponds to, but instead 1022 // it is much bigger. 1023 //BlockCount = Inode->i_blocks; 1024 FileSize = Ext2GetInodeFileSize(Inode); 1025 FileSize = ROUND_UP(FileSize, Volume->BlockSizeInBytes); 1026 BlockCount = (ULONG)(FileSize / Volume->BlockSizeInBytes); 1027 1028 // Allocate the memory for the block list 1029 BlockList = FrLdrTempAlloc(BlockCount * sizeof(ULONG), TAG_EXT_BLOCK_LIST); 1030 if (BlockList == NULL) 1031 { 1032 return NULL; 1033 } 1034 1035 RtlZeroMemory(BlockList, BlockCount * sizeof(ULONG)); 1036 1037 // Copy the direct block pointers 1038 for (CurrentBlockInList = CurrentBlock = 0; 1039 CurrentBlockInList < BlockCount && CurrentBlock < INDIRECT_BLOCKS; 1040 CurrentBlock++, CurrentBlockInList++) 1041 { 1042 BlockList[CurrentBlockInList] = Inode->blocks.dir_blocks[CurrentBlock]; 1043 } 1044 1045 // Copy the indirect block pointers 1046 if (CurrentBlockInList < BlockCount) 1047 { 1048 if (!Ext2CopyIndirectBlockPointers(Volume, BlockList, &CurrentBlockInList, BlockCount, Inode->blocks.indir_block)) 1049 { 1050 FrLdrTempFree(BlockList, TAG_EXT_BLOCK_LIST); 1051 return NULL; 1052 } 1053 } 1054 1055 // Copy the double indirect block pointers 1056 if (CurrentBlockInList < BlockCount) 1057 { 1058 if (!Ext2CopyDoubleIndirectBlockPointers(Volume, BlockList, &CurrentBlockInList, BlockCount, Inode->blocks.double_indir_block)) 1059 { 1060 FrLdrTempFree(BlockList, TAG_EXT_BLOCK_LIST); 1061 return NULL; 1062 } 1063 } 1064 1065 // Copy the triple indirect block pointers 1066 if (CurrentBlockInList < BlockCount) 1067 { 1068 if (!Ext2CopyTripleIndirectBlockPointers(Volume, BlockList, &CurrentBlockInList, BlockCount, Inode->blocks.tripple_indir_block)) 1069 { 1070 FrLdrTempFree(BlockList, TAG_EXT_BLOCK_LIST); 1071 return NULL; 1072 } 1073 } 1074 1075 return BlockList; 1076 } 1077 1078 ULONGLONG Ext2GetInodeFileSize(PEXT2_INODE Inode) 1079 { 1080 if ((Inode->mode & EXT2_S_IFMT) == EXT2_S_IFDIR) 1081 { 1082 return (ULONGLONG)(Inode->size); 1083 } 1084 else 1085 { 1086 return ((ULONGLONG)(Inode->size) | ((ULONGLONG)(Inode->dir_acl) << 32)); 1087 } 1088 } 1089 1090 BOOLEAN Ext2CopyIndirectBlockPointers(PEXT2_VOLUME_INFO Volume, ULONG* BlockList, ULONG* CurrentBlockInList, ULONG BlockCount, ULONG IndirectBlock) 1091 { 1092 ULONG* BlockBuffer; 1093 ULONG CurrentBlock; 1094 ULONG BlockPointersPerBlock; 1095 1096 TRACE("Ext2CopyIndirectBlockPointers() BlockCount = %d\n", BlockCount); 1097 1098 BlockPointersPerBlock = Volume->BlockSizeInBytes / sizeof(ULONG); 1099 1100 BlockBuffer = FrLdrTempAlloc(Volume->BlockSizeInBytes, TAG_EXT_BUFFER); 1101 if (!BlockBuffer) 1102 { 1103 return FALSE; 1104 } 1105 1106 if (!Ext2ReadBlock(Volume, IndirectBlock, BlockBuffer)) 1107 { 1108 return FALSE; 1109 } 1110 1111 for (CurrentBlock=0; (*CurrentBlockInList)<BlockCount && CurrentBlock<BlockPointersPerBlock; CurrentBlock++) 1112 { 1113 BlockList[(*CurrentBlockInList)] = BlockBuffer[CurrentBlock]; 1114 (*CurrentBlockInList)++; 1115 } 1116 1117 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER); 1118 1119 return TRUE; 1120 } 1121 1122 BOOLEAN Ext2CopyDoubleIndirectBlockPointers(PEXT2_VOLUME_INFO Volume, ULONG* BlockList, ULONG* CurrentBlockInList, ULONG BlockCount, ULONG DoubleIndirectBlock) 1123 { 1124 ULONG* BlockBuffer; 1125 ULONG CurrentBlock; 1126 ULONG BlockPointersPerBlock; 1127 1128 TRACE("Ext2CopyDoubleIndirectBlockPointers() BlockCount = %d\n", BlockCount); 1129 1130 BlockPointersPerBlock = Volume->BlockSizeInBytes / sizeof(ULONG); 1131 1132 BlockBuffer = (ULONG*)FrLdrTempAlloc(Volume->BlockSizeInBytes, TAG_EXT_BUFFER); 1133 if (BlockBuffer == NULL) 1134 { 1135 return FALSE; 1136 } 1137 1138 if (!Ext2ReadBlock(Volume, DoubleIndirectBlock, BlockBuffer)) 1139 { 1140 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER); 1141 return FALSE; 1142 } 1143 1144 for (CurrentBlock=0; (*CurrentBlockInList)<BlockCount && CurrentBlock<BlockPointersPerBlock; CurrentBlock++) 1145 { 1146 if (!Ext2CopyIndirectBlockPointers(Volume, BlockList, CurrentBlockInList, BlockCount, BlockBuffer[CurrentBlock])) 1147 { 1148 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER); 1149 return FALSE; 1150 } 1151 } 1152 1153 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER); 1154 return TRUE; 1155 } 1156 1157 BOOLEAN Ext2CopyTripleIndirectBlockPointers(PEXT2_VOLUME_INFO Volume, ULONG* BlockList, ULONG* CurrentBlockInList, ULONG BlockCount, ULONG TripleIndirectBlock) 1158 { 1159 ULONG* BlockBuffer; 1160 ULONG CurrentBlock; 1161 ULONG BlockPointersPerBlock; 1162 1163 TRACE("Ext2CopyTripleIndirectBlockPointers() BlockCount = %d\n", BlockCount); 1164 1165 BlockPointersPerBlock = Volume->BlockSizeInBytes / sizeof(ULONG); 1166 1167 BlockBuffer = (ULONG*)FrLdrTempAlloc(Volume->BlockSizeInBytes, TAG_EXT_BUFFER); 1168 if (BlockBuffer == NULL) 1169 { 1170 return FALSE; 1171 } 1172 1173 if (!Ext2ReadBlock(Volume, TripleIndirectBlock, BlockBuffer)) 1174 { 1175 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER); 1176 return FALSE; 1177 } 1178 1179 for (CurrentBlock=0; (*CurrentBlockInList)<BlockCount && CurrentBlock<BlockPointersPerBlock; CurrentBlock++) 1180 { 1181 if (!Ext2CopyDoubleIndirectBlockPointers(Volume, BlockList, CurrentBlockInList, BlockCount, BlockBuffer[CurrentBlock])) 1182 { 1183 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER); 1184 return FALSE; 1185 } 1186 } 1187 1188 FrLdrTempFree(BlockBuffer, TAG_EXT_BUFFER); 1189 return TRUE; 1190 } 1191 1192 ARC_STATUS Ext2Close(ULONG FileId) 1193 { 1194 PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId); 1195 FrLdrTempFree(FileHandle, TAG_EXT_FILE); 1196 return ESUCCESS; 1197 } 1198 1199 ARC_STATUS Ext2GetFileInformation(ULONG FileId, FILEINFORMATION* Information) 1200 { 1201 PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId); 1202 1203 RtlZeroMemory(Information, sizeof(*Information)); 1204 Information->EndingAddress.QuadPart = FileHandle->FileSize; 1205 Information->CurrentAddress.QuadPart = FileHandle->FilePointer; 1206 1207 TRACE("Ext2GetFileInformation(%lu) -> FileSize = %llu, FilePointer = 0x%llx\n", 1208 FileId, Information->EndingAddress.QuadPart, Information->CurrentAddress.QuadPart); 1209 1210 return ESUCCESS; 1211 } 1212 1213 ARC_STATUS Ext2Open(CHAR* Path, OPENMODE OpenMode, ULONG* FileId) 1214 { 1215 PEXT2_VOLUME_INFO Volume; 1216 PEXT2_FILE_INFO FileHandle; 1217 ULONG DeviceId; 1218 1219 /* Check parameters */ 1220 if (OpenMode != OpenReadOnly) 1221 return EACCES; 1222 1223 /* Get underlying device */ 1224 DeviceId = FsGetDeviceId(*FileId); 1225 Volume = Ext2Volumes[DeviceId]; 1226 1227 TRACE("Ext2Open() FileName = %s\n", Path); 1228 1229 /* Call the internal open method */ 1230 // Status = Ext2OpenFile(Volume, Path, &FileHandle); 1231 FileHandle = Ext2OpenFile(Volume, Path); 1232 if (!FileHandle) 1233 return ENOENT; 1234 1235 /* Success, remember the handle */ 1236 FsSetDeviceSpecific(*FileId, FileHandle); 1237 return ESUCCESS; 1238 } 1239 1240 ARC_STATUS Ext2Read(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count) 1241 { 1242 PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId); 1243 ULONGLONG BytesReadBig; 1244 BOOLEAN Success; 1245 1246 // 1247 // Read data 1248 // 1249 Success = Ext2ReadFileBig(FileHandle, N, &BytesReadBig, Buffer); 1250 *Count = (ULONG)BytesReadBig; 1251 1252 // 1253 // Check for success 1254 // 1255 if (Success) 1256 return ESUCCESS; 1257 else 1258 return EIO; 1259 } 1260 1261 ARC_STATUS Ext2Seek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode) 1262 { 1263 PEXT2_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId); 1264 LARGE_INTEGER NewPosition = *Position; 1265 1266 switch (SeekMode) 1267 { 1268 case SeekAbsolute: 1269 break; 1270 case SeekRelative: 1271 NewPosition.QuadPart += FileHandle->FilePointer; 1272 break; 1273 default: 1274 ASSERT(FALSE); 1275 return EINVAL; 1276 } 1277 1278 if (NewPosition.QuadPart >= FileHandle->FileSize) 1279 return EINVAL; 1280 1281 FileHandle->FilePointer = NewPosition.QuadPart; 1282 return ESUCCESS; 1283 } 1284 1285 const DEVVTBL Ext2FuncTable = 1286 { 1287 Ext2Close, 1288 Ext2GetFileInformation, 1289 Ext2Open, 1290 Ext2Read, 1291 Ext2Seek, 1292 L"ext2fs", 1293 }; 1294 1295 const DEVVTBL* Ext2Mount(ULONG DeviceId) 1296 { 1297 PEXT2_VOLUME_INFO Volume; 1298 EXT2_SUPER_BLOCK SuperBlock; 1299 LARGE_INTEGER Position; 1300 ULONG Count; 1301 ARC_STATUS Status; 1302 1303 TRACE("Enter Ext2Mount(%lu)\n", DeviceId); 1304 1305 /* Allocate data for volume information */ 1306 Volume = FrLdrTempAlloc(sizeof(EXT2_VOLUME_INFO), TAG_EXT_VOLUME); 1307 if (!Volume) 1308 return NULL; 1309 RtlZeroMemory(Volume, sizeof(EXT2_VOLUME_INFO)); 1310 1311 /* Read the SuperBlock */ 1312 Position.QuadPart = 2 * 512; 1313 Status = ArcSeek(DeviceId, &Position, SeekAbsolute); 1314 if (Status != ESUCCESS) 1315 { 1316 FrLdrTempFree(Volume, TAG_EXT_VOLUME); 1317 return NULL; 1318 } 1319 Status = ArcRead(DeviceId, &SuperBlock, sizeof(SuperBlock), &Count); 1320 if (Status != ESUCCESS || Count != sizeof(SuperBlock)) 1321 { 1322 FrLdrTempFree(Volume, TAG_EXT_VOLUME); 1323 return NULL; 1324 } 1325 1326 /* Check if SuperBlock is valid. If yes, return Ext2 function table. */ 1327 if (SuperBlock.magic != EXT2_MAGIC) 1328 { 1329 FrLdrTempFree(Volume, TAG_EXT_VOLUME); 1330 return NULL; 1331 } 1332 1333 Volume->DeviceId = DeviceId; 1334 1335 /* Really open the volume */ 1336 if (!Ext2OpenVolume(Volume)) 1337 { 1338 FrLdrTempFree(Volume, TAG_EXT_VOLUME); 1339 return NULL; 1340 } 1341 1342 /* Remember EXT2 volume information */ 1343 Ext2Volumes[DeviceId] = Volume; 1344 1345 /* Return success */ 1346 TRACE("Ext2Mount(%lu) success\n", DeviceId); 1347 return &Ext2FuncTable; 1348 } 1349 1350 #endif 1351