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