https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f0d73e0f7fa97b17e636c…
commit f0d73e0f7fa97b17e636c76653172ff2122f434f
Author: Sylvain Deverre <deverre.sylv(a)gmail.com>
AuthorDate: Thu Aug 17 15:07:59 2023 +0200
Commit: GitHub <noreply(a)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.sylv(a)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 <xnavara(a)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