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

Reply via email to