https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f0d73e0f7fa97b17e636c76653172ff2122f434f
commit f0d73e0f7fa97b17e636c76653172ff2122f434f Author: Sylvain Deverre <deverre.s...@gmail.com> AuthorDate: Thu Aug 17 15:07:59 2023 +0200 Commit: GitHub <nore...@github.com> CommitDate: Thu Aug 17 15:07:59 2023 +0200 [BOOTSECT][FREELDR] Support booting from NTFS partitions (#3416) Implement NTFS boot sector that loads FreeLdr from a NTFS partition. CORE-17474 - Able to find/parse root directory; - Handle fixups in FILE MFT record; - Implement directory tree search; - Implement loading found file from disk; - Handle fixups in INDX records; - Fail if compressed or sparse; - Attempt to support 64-bit disks. Some TO-DOs for later: - Handle "weird" NTFS partition with uncommon sector sizes / sectors per cluster / sectors per index record; - Better implementation for file loading; - 64-bit LCN support. --- boot/freeldr/bootsect/CMakeLists.txt | 2 + boot/freeldr/bootsect/ntfs.S | 893 +++++++++++++++++++++++++++++++++++ boot/freeldr/freeldr/lib/fs/ntfs.c | 2 +- 3 files changed, 896 insertions(+), 1 deletion(-) diff --git a/boot/freeldr/bootsect/CMakeLists.txt b/boot/freeldr/bootsect/CMakeLists.txt index d0c527779d5..a46c9066c6a 100644 --- a/boot/freeldr/bootsect/CMakeLists.txt +++ b/boot/freeldr/bootsect/CMakeLists.txt @@ -8,6 +8,7 @@ if(ARCH STREQUAL "i386" OR ARCH STREQUAL "amd64") CreateBootSectorTarget(fat ${CMAKE_CURRENT_SOURCE_DIR}/fat.S ${CMAKE_CURRENT_BINARY_DIR}/fat.bin 7c00) CreateBootSectorTarget(fat32 ${CMAKE_CURRENT_SOURCE_DIR}/fat32.S ${CMAKE_CURRENT_BINARY_DIR}/fat32.bin 7c00) + CreateBootSectorTarget(ntfsvbr ${CMAKE_CURRENT_SOURCE_DIR}/ntfs.S ${CMAKE_CURRENT_BINARY_DIR}/ntfs.bin 7c00) CreateBootSectorTarget(btrfsvbr ${CMAKE_CURRENT_SOURCE_DIR}/btrfs.S ${CMAKE_CURRENT_BINARY_DIR}/btrfs.bin 7c00) if(SARCH STREQUAL "pc98") @@ -34,6 +35,7 @@ if(ARCH STREQUAL "i386" OR ARCH STREQUAL "amd64") add_cd_file(TARGET btrfsvbr DESTINATION loader NO_CAB FILE ${CMAKE_CURRENT_BINARY_DIR}/btrfs.bin FOR bootcd regtest) add_cd_file(TARGET fat DESTINATION loader NO_CAB FILE ${CMAKE_CURRENT_BINARY_DIR}/fat.bin FOR bootcd regtest) add_cd_file(TARGET fat32 DESTINATION loader NO_CAB FILE ${CMAKE_CURRENT_BINARY_DIR}/fat32.bin FOR bootcd regtest) + add_cd_file(TARGET ntfsvbr DESTINATION loader NO_CAB FILE ${CMAKE_CURRENT_BINARY_DIR}/ntfs.bin FOR bootcd regtest) add_cd_file(TARGET isoboot DESTINATION loader NO_CAB NOT_IN_HYBRIDCD FILE ${CMAKE_CURRENT_BINARY_DIR}/isoboot.bin FOR all hybridcd) add_cd_file(TARGET isobtrt DESTINATION loader NO_CAB NOT_IN_HYBRIDCD FILE ${CMAKE_CURRENT_BINARY_DIR}/isobtrt.bin FOR bootcd regtest) elseif(ARCH STREQUAL "arm") diff --git a/boot/freeldr/bootsect/ntfs.S b/boot/freeldr/bootsect/ntfs.S new file mode 100644 index 00000000000..85081df464a --- /dev/null +++ b/boot/freeldr/bootsect/ntfs.S @@ -0,0 +1,893 @@ +/* + * PROJECT: ReactOS Bootsector + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * FILE: boot/freeldr/bootsect/fat32.S + * PURPOSE: Implementing boot sector for NTFS + * COPYRIGHT: Copyright 2020 Sylvain Deverre (deverre.s...@gmail.com) + */ + +/* INCLUDES ******************************************************************/ + +#include <asm.inc> +#include <freeldr/include/arch/pc/x86common.h> + +.code16 + +//ORG HEX(7c00) + +start: + jmp short main + nop +OEMName: + .ASCII "NTFS " // NTFS signature +BytesPerSector: + .word 512 +SectsPerCluster: + .byte 8 +ReservedSectors: + .word 0 // Unused by NTFS +NumberOfFats: + .byte 0 // Unused by NTFS +MaxRootEntries: + .word 0 // Unused by NTFS +TotalSectors: + .word 0 // Unused by NTFS +MediaDescriptor: + .byte HEX(0f8) +SectorsPerFat: + .word 0 // Unused by NTFS +SectorsPerTrack: + .word 0 // Should contain disk geometry +NumberOfHeads: + .word 0 // Should contain disk geometry +HiddenSectors: + .long 0 // Filled by format program +TotalSectorsBig: + .long 0 // Unused by NTFS +// NTFS inserted info +BootDrive: + .byte HEX(80) +CurrentHead: + .byte 0 +BootSignature: + .byte HEX(80) +Unused: + .byte 0 +VolumeSectorCount: + .quad 0 // Must be patched by format program ! +MftLocation: + .quad 0 // Must be patched by format program ! +MftMirrorLocation: + .quad 0 // Must be patched by format program ! +ClustersPerMftRecord: + .long 0 +ClustersPerIndexRecord: + .long 0 +VolumeSerialNumber: + .quad 0 +Checksum: + .long 0 + +main: + xor ax,ax // Setup segment registers + mov ds,ax // Make DS correct + mov es,ax // Make ES correct + mov ss,ax // Make SS correct + mov sp, HEX(7c00) + mov bp, sp // Setup a stack + + cmp byte ptr [BootDrive], HEX(0ff) // If they have specified a boot drive then use it + jne GetDriveParameters + + mov byte ptr [BootDrive], dl // Save the boot drive + +GetDriveParameters: + mov ah, 8 + mov dl, byte ptr [BootDrive] // Get boot drive in dl + int HEX(13) // Request drive parameters from the bios + jnc CalcDriveSize // If the call succeeded then calculate the drive size + + // If we get here then the call to the BIOS failed + // so just set CHS equal to the maximum addressable + // size + mov cx, HEX(0ffff) + mov dh, cl + +CalcDriveSize: + // Now that we have the drive geometry + // lets calculate the drive size + mov bl, ch // Put the low 8-bits of the cylinder count into BL + mov bh, cl // Put the high 2-bits in BH + shr bh, 6 // Shift them into position, now BX contains the cylinder count + and cl, HEX(3f) // Mask off cylinder bits from sector count + // CL now contains sectors per track and DH contains head count + movzx eax, dh // Move the heads into EAX + movzx ebx, bx // Move the cylinders into EBX + movzx ecx, cl // Move the sectors per track into ECX + inc eax // Make it one based because the bios returns it zero based + inc ebx // Make the cylinder count one based also + mul ecx // Multiply heads with the sectors per track, result in edx:eax + mul ebx // Multiply the cylinders with (heads * sectors) [stored in edx:eax already] + + // We now have the total number of sectors as reported + // by the bios in eax, so store it in our variable + mov dword ptr ds:[BiosCHSDriveSize], eax + +LoadExtraBootCode: + // First we have to load our extra boot code at + // next sector into memory at [0000:7e00h] + mov eax, HEX(1) + xor edx, edx + mov cx, 4 + xor bx, bx + mov es, bx // Read sector to [0000:7e00h] + mov bx, HEX(7e00) + call ReadSectors + jmp StartSearch + + +// Reads logical sectors into [ES:BX] +// EDX:EAX has logical sector number to read +// CX has number of sectors to read +ReadSectors: + push es + add eax, dword ptr [HiddenSectors] // Add offset from the disk beginning + test edx, edx + jnz ReadSectorsLBA + cmp eax, dword ptr ds:[BiosCHSDriveSize] // Check if they are reading a sector outside CHS range + jae ReadSectorsLBA // Yes - go to the LBA routine + // If at all possible we want to use LBA routines because + // They are optimized to read more than 1 sector per read + + pushad // Save logical sector number & sector count + +CheckInt13hExtensions: // Now check if this computer supports extended reads + mov ah, HEX(41) // AH = 41h + mov bx, HEX(55aa) // BX = 55AAh + mov dl, byte ptr [BootDrive] // DL = drive (80h-FFh) + int HEX(13) // IBM/MS INT 13 Extensions - INSTALLATION CHECK + jc ReadSectorsCHS // CF set on error (extensions not supported) + cmp bx, HEX(0aa55) // BX = AA55h if installed + jne ReadSectorsCHS + test cl,1 // CX = API subset support bitmap + jz ReadSectorsCHS // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported + + popad // Restore sector count & logical sector number + +ReadSectorsLBA: + pushad // Save logical sector number & sector count + + cmp cx, 64 // Since the LBA calls only support 0x7F sectors at a time we will limit ourselves to 64 + jbe ReadSectorsSetupDiskAddressPacket // If we are reading less than 65 sectors then just do the read + mov cx, 64 // Otherwise read only 64 sectors on this loop iteration + +ReadSectorsSetupDiskAddressPacket: + mov word ptr ds:[LBASectorsRead],cx + push edx + push eax // Put 64-bit logical block address on stack + push es // Put transfer segment on stack + push bx // Put transfer offset on stack + push cx // Set transfer count + push 16 // Set size of packet to 10h + mov si, sp // Setup disk address packet on stack + + mov dl, byte ptr [BootDrive] // Drive number + mov ah, HEX(42) // Int 13h, AH = 42h - Extended Read + int HEX(13) // Call BIOS + jc PrintDiskError // If the read failed then abort + + add sp, 16 // Remove disk address packet from stack + + popad // Restore sector count & logical sector number + + push bx + mov ebx, dword ptr ds:[LBASectorsRead] + add eax, ebx // Increment sector to read + adc edx, 0 + shl ebx, 5 + mov dx, es + add dx, bx // Setup read buffer for next sector + mov es, dx + pop bx + + sub cx, word ptr ds:[LBASectorsRead] + jnz ReadSectorsLBA // Read next sector + + pop es + ret + +LBASectorsRead: + .long 0 + + +// Reads logical sectors into [ES:BX] +// EAX has logical sector number to read +// CX has number of sectors to read +ReadSectorsCHS: + popad // Get logical sector number & sector count off stack + +ReadSectorsCHSLoop: + pushad + xor edx, edx + movzx ecx, word ptr [SectorsPerTrack] + div ecx // Divide logical by SectorsPerTrack + inc dl // Sectors numbering starts at 1 not 0 + mov cl, dl // Sector in CL + mov edx, eax + shr edx, 16 + div word ptr [NumberOfHeads] // Divide logical by number of heads + mov dh, dl // Head in DH + mov dl, byte ptr [BootDrive] // Drive number in DL + mov ch, al // Cylinder in CX + ror ah, 2 // Low 8 bits of cylinder in CH, high 2 bits + // in CL shifted to bits 6 & 7 + or cl, ah // Or with sector number + mov ax, HEX(0201) + int HEX(13) // DISK - READ SECTORS INTO MEMORY + // AL = number of sectors to read, CH = track, CL = sector + // DH = head, DL = drive, ES:BX -> buffer to fill + // Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read + + jc PrintDiskError // If the read failed then abort + + popad + + inc eax // Increment Sector to Read + + mov dx, es + add dx, 32 // Increment read buffer for next sector + mov es, dx + + loop ReadSectorsCHSLoop // Read next sector + + pop es + ret + +// Displays a disk error message +// And reboots +PrintDiskError: + mov si, offset msgDiskError // Bad boot disk message + call PutChars // Display it + + jmp Reboot + +// Displays a file system error message +// And reboots +PrintFileSystemError: + mov si, offset msgFileSystemError // FreeLdr not found message + call PutChars // Display it + +Reboot: + mov si, offset msgAnyKey // Press any key message + call PutChars // Display it + xor ax, ax + int HEX(16) // Wait for a keypress + int HEX(19) // Reboot + +PutChars: + lodsb + or al, al + jz short Done + mov ah, HEX(0e) + mov bx, 7 + int HEX(10) + jmp short PutChars +Done: + ret + +msgDiskError: + .ascii "Disk error", CR, LF, NUL +msgFileSystemError: + .ascii "File system error", CR, LF, NUL +msgAnyKey: + .ascii "Press any key to restart", CR, LF, NUL + +SectsPerMFT: + .word 0 + +MFTStartSector: + .quad 0 + +BiosCHSDriveSize: + .long 0 + +.org 509 // Pad to 509 bytes +BootPartition: + .byte 1 + +BootFlagSignature: + .word HEX(0aa55) // BootSector signature + +// End of Bootsector +// Next sector starts as sector 2, since boot sector is +// as a file under NTFS ($Boot, which has inode number 7) +// and takes the two first clusters of the volume (which means +// 16 sectors, checked on Windows and mkfs.ntfs from ntfsutils) + +#define ATTRIBUTE_DATA HEX(80) +#define ATTRIBUTE_INDEX_ROOT HEX(90) +#define ATTRIBUTE_INDEX_ALLOCATION HEX(A0) +#define FILE_MAGIC HEX(454c4946) +#define INDX_MAGIC HEX(58444e49) +#define NTFS_FILE_ROOT 5 +StartSearch: + // Compute MFT start sector + mov eax, dword ptr [MftLocation] + mov cl, byte ptr [SectsPerCluster] + movzx ecx, cx + mul ecx + mov dword ptr [MFTStartSector], eax + + // Compute size of MFT entry in sectors + xor ax, ax + mov al, byte ptr [ClustersPerMftRecord] + test al, al + js NegativeOffset + mov cx, word ptr [SectsPerCluster] + mul cx + mov word ptr [SectsPerMFT], cx + jmp SearchNext + NegativeOffset: + // ClustersPerMftRecord is negative, so we need to perform 1 << (-ClustersPerMftRecord) + // to get the number of bytes needed for a MFT entry + not al + inc al + mov cl, al + xor ax, ax + inc ax + shl ax, cl + + // But here want to store sectors, so we need to divide by BytesPerSector + xor dx, dx + mov cx, word ptr [BytesPerSector] + div cx + mov word ptr [SectsPerMFT], ax + + SearchNext: + mov bp, sp + sub sp, HEX(10) + + // Read Root Directory MFT into [2000:0] + mov ax, HEX(2000) + mov es, ax + xor bx, bx + xor edx, edx + mov eax, NTFS_FILE_ROOT + call ReadInode + + // Finds freeldr.sys into index root's B-Tree + // and return its MFT index + xor ax, ax + call ExploreIndexRoot + + // Read the MFT entry of freeldr.sys into [A00:0] + push eax + mov ax, HEX(A00) + mov es, ax + pop eax + call ReadInode + + xor ax, ax + mov ebx, HEX(30) + call FindAttributeHdr + mov si, ax + // Move to the attribute data + add si, word ptr es:[si + HEX(14)] + + // We don't support compressed, sparse or encrypted Freeldr + test dword ptr es:[si + HEX(38)], HEX(4a00) + jnz CompressedFreeldr + + // Compute size in clusters + mov eax, dword ptr es:[si + HEX(28)] + mov cl, byte ptr [SectsPerCluster] + xor edx, edx + div ecx + mov cx, word ptr [BytesPerSector] + movzx ecx, cx + xor edx, edx + div ecx + mov edx, eax + + push ax + mov si, offset msgLoading + call PutChars + pop ax + + xor ax, ax + mov ebx, HEX(80) + call FindAttributeHdr + mov si, ax + add ax, word ptr es:[si + HEX(20)] + xor ecx, ecx + xor bx, bx + push FREELDR_BASE / 16 + pop fs + FreeLdrLoad: + pushad + call ReadNonResidentAttribute + popad + mov bx, fs + add bx, HEX(100) + mov fs, bx + xor bx, bx + inc ecx + cmp ecx, edx + jbe FreeLdrLoad + + mov dl, byte ptr [BootDrive] + mov dh, byte ptr [BootPartition] + ljmp16 0, FREELDR_BASE + +// Error message if Freeldr is compressed, encrypted or sparse +CompressedFreeldr: + mov si, offset msgFreeldrCompressed + call PutChars + jmp Reboot + +// Finds Freeldr.sys into the directory tree and returns the +// inode index +// INPUT: +// - ES:[AX] address of the MFT record +// OUTPUT: +// - EDX:EAX MFT number of the found file +ExploreIndexRoot: + #define fileRecordBase 2 + #define fileRecord 4 + #define indexRootData 6 + #define allocationRunList 8 + + push bp + mov bp, sp + push bx + push si + push di + sub sp, HEX(10) + mov word ptr [bp - fileRecordBase], es + mov word ptr [bp - fileRecord], ax + + mov ebx, ATTRIBUTE_INDEX_ROOT + call FindAttributeHdr + test ax, ax + jz PrintFileSystemError // fail if no INDEX_ROOT + + mov si, ax + cmp byte ptr es:[si + 8], HEX(0) + jnz PrintFileSystemError // fail if attribute is non-resident + + add si, word ptr es:[si + HEX(14)] + cmp dword ptr es:[si], HEX(30) + jnz PrintFileSystemError // fail if FILE_NAME attribute isn't indexed + + mov word ptr [bp - indexRootData], si + mov ax, word ptr [bp - fileRecord] + + test byte ptr es:[si + HEX(0c)], 1 + jz ExploreNext // Skip index allocation lookup if we don't have children + + mov ebx, ATTRIBUTE_INDEX_ALLOCATION + call FindAttributeHdr + test ax, ax + jz PrintFileSystemError // No INDEX_ALLOCATION found + + mov si, ax + cmp byte ptr es:[si + 8], 1 + jnz PrintFileSystemError // Fail if attribute is resident (shouldn't be) + + add si, word ptr es:[si + HEX(20)] + mov word ptr [bp - allocationRunList], si // save run list + + ExploreNext: + mov si, word ptr [bp - indexRootData] + lea si, [si + 32] // get the first INDEX_ENTRY + + // Main search loop. We browse the B-Tree which contains directory entries + // SI contains the current index entry. + NodeCheck: + test word ptr es:[si + HEX(0C)], 2 + jnz NodeCheckLastNode + + mov cl, byte ptr es:[si + HEX(50)] + movzx cx, cl + lea si, [si + HEX(52)] + mov di, offset FreeLdr + call CompareWideStrInsensitive + lea si, [si - HEX(52)] + test ax, ax + jz RootIndexFound + test word ptr es:[si + HEX(0C)], 1 + jz ContinueSearch + + test ax, HEX(f000) + jnz LookupChildNode // if result < 0 then explore child node + + ContinueSearch: + add si, word ptr es:[si + 8] + jmp NodeCheck + + RootIndexFound: + mov eax, dword ptr es:[si] // We found root entry, return with its MFT number + mov dx, word ptr es:[si + 4] // Return high part into edx. + // We take only the first word, the high word contains the + // sequence number + movzx edx, dx + jmp ExitIndexTree + + NodeCheckLastNode: + test word ptr es:[si + HEX(0C)], 1 + jz PrintFreeldrError + + LookupChildNode: + // Take the right LCN at the end of the index record + add si, word ptr es:[si + 8] + mov ecx, dword ptr es:[si - 8] + + // Read the fixed up LCN + mov bx, word ptr [bp - allocationRunList] + mov ax, word ptr [bp - fileRecordBase] + mov es, ax + mov ax, HEX(9000) + mov fs, ax + mov ax, bx + xor bx, bx + call ReadINDX + + // Go again to the loop but with the child node list + mov ax, HEX(9000) + mov es, ax + mov si, 0 + add si, es:[si + HEX(18)] // Go to the first node + lea si, [si + HEX(18)] + jmp NodeCheck + + ExitIndexTree: + pop di + pop si + mov sp, bp + pop bp + ret + +// 64-bit multiplication +// EDX:EAX operand +// ECX operator +Multiply64: + push bp + mov bp, sp + sub sp, 12 + // Save the high part of the multiplication + mov dword ptr [bp - 4], edx + + // Multiply the low part and save the result + mul ecx + mov dword ptr[bp - 8], eax + mov dword ptr[bp - 12], edx + + // Multiply the high part and add the carry + mov eax, dword ptr [bp - 4] + mul ecx + add eax, dword ptr [bp - 12] + + // Format correctly the number + mov edx, eax + mov eax, dword ptr [bp - 8] + mov sp, bp + pop bp + ret + +// Compare case-insensitive strings +// [ES:SI] - the source file name +// [DS:DI] - the destination file name +// CX - compare length +CompareWideStrInsensitive: + push bx + push si + push di + movzx cx, cl + CmpLoop: + mov ax, word ptr es:[si] + cmp ax, 'a' + jl NoUpper + cmp ax, 'z' + jg NoUpper + sub ax, HEX(20) + NoUpper: + mov bx, word ptr ds:[di] + sub bx, ax + test bx, bx + jnz CompareFail + add si, 2 + add di, 2 + dec cx + jnz CmpLoop + CompareFail: + mov ax, bx + pop di + pop si + pop bx + ret + +// Reads a NTFS cluster +// INPUT: +// - EDX:EAX : cluster number to read +// OUTPUT: +// - ES:BX : address to read +ReadCluster: + push ecx + mov cl, byte ptr [SectsPerCluster] // Convert clusters to sectors + movzx ecx, cx + call Multiply64 + call ReadSectors + pop ecx + ret + +// Reads a MFT entry +// INPUT: +// - EDX:EAX : MFT inode +// OUTPUT: +// - ES:[BX] : address to read +ReadInode: + push ecx + push si + push di + + push edx + mov cx, word ptr [SectsPerMFT] // Get the correct number of sectors for the FILE entry + movzx ecx, cx + mul ecx + movzx eax, ax + add eax, dword ptr [MFTStartSector] // Add it to the start of the MFT + mov cx, word ptr [SectsPerMFT] + movzx ecx, cx + pop edx + call ReadSectors + + cmp dword ptr es:[bx], FILE_MAGIC // Ensure we get a valid FILE record + jnz PrintFileSystemError + + call ApplyFixups + + pop di + pop si + pop ecx + ret + +#define UpdateSequenceOffset 4 +#define UpdateSequenceLen 6 +// Apply fixups to INDX and FILE records +// INPUT: +// - ES:[BX] - pointer to the record to fixup +ApplyFixups: + push si + push di + mov si, bx + add si, word ptr es:[bx + UpdateSequenceOffset] + xor cx, cx + inc cx + FixupLoop: + cmp cx, word ptr es:[bx + UpdateSequenceLen] + jz EndFixupLoop + mov si, bx + add si, word ptr es:[bx + UpdateSequenceOffset] + mov ax, word ptr es:[si] // Get first fixup value + + mov di, cx + shl di, 9 + add di, bx + sub di, 2 + cmp ax, word ptr es:[di] // Check fixup value + jnz PrintFileSystemError // Fixup is corrupted, so print error + inc cx + add si, cx + mov ax, word ptr es:[si] // Apply fixup + mov word ptr es:[di], ax + + jmp FixupLoop + EndFixupLoop: + + pop di + pop si + ret + +// Reads a non-resident attribute +// INPUT: +// - ES:[AX] : Address of the data runs +// - ECX : LCN to read +// OUTPUT: +// - FS:[BX] : Address to write to +ReadNonResidentAttribute: + #define currentLCN 4 + #define offsetWrite 8 + #define startReadLCN HEX(0C) + push bp + mov bp, sp + sub sp, HEX(10) + push edx + push si + mov dword ptr [bp - currentLCN], 0 // Store the current LCN + mov word ptr [bp - offsetWrite], bx + mov dword ptr [bp - startReadLCN], ecx + mov si, ax + xor edx, edx + RunLoop: + mov al, byte ptr es:[si] + test al, al + jz NotFound + call UnpackRun + add dword ptr [bp - currentLCN], eax + cmp dword ptr [bp - startReadLCN], ecx + jb FoundRun + sub dword ptr [bp - startReadLCN], ecx // Decrement the cluster + jmp RunLoop + FoundRun: + mov ebx, dword ptr [bp - currentLCN] + mov ecx, dword ptr [bp - startReadLCN] + add ebx, ecx + + push es + mov ax, fs + mov es, ax + mov eax, ebx + mov bx, word ptr [bp - offsetWrite] + call ReadCluster + pop es + jmp RunSearchEnd + NotFound: + xor ax, ax + RunSearchEnd: + pop si + pop edx + mov sp, bp + pop bp + ret + +// Decodes a run in the runlist +// INPUT: +// - ES:[SI] : address of the run +// OUTPUT: +// - EAX : Unpacked LCN +// - ECX : Unpacked run length (in sectors) +// - SI : Next run in the run list +UnpackRun: + push bp + mov bp, sp + sub sp, HEX(10) + push ebx + + // Unpack run header + mov bl, byte ptr es:[si] + inc si + + mov bh, bl + shr bh, 4 + and bl, 7 + mov byte ptr [bp-2], bh // BH contains the LCN length + mov byte ptr [bp-1], bl // BL contains the number of cluster length + + mov al, bl + call UnpackLen + mov dword ptr [bp - 8], ebx + + mov al, byte ptr [bp-2] + call UnpackLen + mov cl, byte ptr es:[si-1] // Fixup sign if last byte is > 255 + test cl, HEX(80) + jz NoSign + not eax + add ebx, eax + + NoSign: + mov eax, ebx + mov ecx, dword ptr [bp - 8] + pop ebx + mov sp, bp + pop bp + ret + + +// Auxiliary function that unpacks n bytes in the memory +// INPUT: +// - AL : size to unpack (max 4 bytes) +// OUTPUT: +// - EAX : the mask used to unpack (for negative number fixup) +// - EBX : the unpacked number +// - SI : Next byte to read +UnpackLen: + push cx + movzx ax, al + + // Read the whole DWORD and then compute a mask to remove + // unneeded bytes to get correct size + xor ebx, ebx + mov ebx, dword ptr es:[si] + add si, ax + + cmp al, 4 + jnz UnpackLen_not4 + xor eax, eax + dec eax + jmp UnpackLen_mask + +UnpackLen_not4: + mov cl, al // Compute mask (2^(8*len) - 1) + shl cl, 3 + xor eax, eax + inc eax + shl eax, cl + dec eax + +UnpackLen_mask: + and ebx, eax // Apply mask + pop cx + ret + +// Reads an INDX sector and applies fixups +// INPUT: +// - ES:[AX] : Address of the data runs +// - ECX : LCN to read +// OUTPUT: +// - FS:[BX] : Address to write to +ReadINDX: + push es + push bx + call ReadNonResidentAttribute + test ax, ax + jz PrintFileSystemError + cmp dword ptr fs:[0], INDX_MAGIC + jnz PrintFileSystemError // jump if not valid + pop bx + + mov ax, fs + mov es, ax + call ApplyFixups + pop es + ret + +// Finds an attribute header into the MFT +// INPUT: +// - ES:[AX] : pointer to the MFT entry +// - EBX : type to find +// OUTPUT: +// - ES:[AX] : Pointer to the attribute header in the MFT entry +FindAttributeHdr: + push cx + push si + push edx + mov si, ax + mov cx, word ptr es:[si+HEX(14)] // Get offset attribute + add si, cx + FindAttributeHdrLoop: + mov edx, dword ptr es:[si] // Get attribute type + cmp edx, ebx + jz AttrFound + cmp edx, HEX(ffffffff) + jz AttrNotFound + add cx, word ptr es:[si + 4] // Add size of the attribute + add si, word ptr es:[si + 4] + jmp FindAttributeHdrLoop + + AttrNotFound: + // Attribute not found, reset the offset + xor cx, cx + AttrFound: + mov ax, cx + pop edx + pop si + pop cx + ret + +PrintFreeldrError: + mov si, offset msgFreeldr + call PutChars + jmp Reboot + +FreeLdr: + .word 'F', 'R', 'E', 'E', 'L', 'D', 'R', '.', 'S', 'Y', 'S' +msgFreeldr: + .ascii "FreeLdr not found, cannot boot", CR, LF, NUL +msgLoading: + .ascii "Loading FreeLoader...", CR, LF, NUL +msgFreeldrCompressed: + .ascii "freeldr.sys is a sparse, compressed or encrypted file, cannot boot", CR, LF, NUL +.endcode16 + +END diff --git a/boot/freeldr/freeldr/lib/fs/ntfs.c b/boot/freeldr/freeldr/lib/fs/ntfs.c index c7e41c23ef8..9361f526932 100644 --- a/boot/freeldr/freeldr/lib/fs/ntfs.c +++ b/boot/freeldr/freeldr/lib/fs/ntfs.c @@ -1,7 +1,7 @@ /* * FreeLoader NTFS support * Copyright (C) 2004 Filip Navara <xnav...@volny.cz> - * Copyright (C) 2009-2010 Herv� Poussineau + * Copyright (C) 2009-2010 Herv� Poussineau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by