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