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