cheng_5103
驱动牛犊
驱动牛犊
  • 注册日期2003-10-06
  • 最后登录2012-03-21
  • 粉丝0
  • 关注0
  • 积分23分
  • 威望228点
  • 贡献值0点
  • 好评度45点
  • 原创分0分
  • 专家分0分
阅读:1959回复:0

大家一起来分析NTFS引导源代码吗?

楼主#
更多 发布于:2005-04-21 16:03
page    ,132
        title   ntfsboot - NTFS boot loader
        name    ntfsboot

; The ROM in the IBM PC starts the boot process by performing a hardware
; initialization and a verification of all external devices.  If all goes
; well, it will then load from the boot drive the sector from track 0, head 0,
; sector 1.  This sector is placed at physical address 07C00h.
;
; The boot code\'s sole resposiblity is to find NTLDR, load it at
; address 2000:0000, and then jump to it.
;
; The boot code understands the structure of the NTFS root directory,
; and is capable of reading files.  There is no contiguity restriction.
;

MASM    equ     1
        .xlist
        .286

A_DEFINED EQU 1

    include ntfs.inc

DoubleWord      struc
lsw     dw      ?
msw     dw      ?
DoubleWord      ends

;
; The following are various segments used by the boot loader.  The first
; two are the segments where the boot sector is initially loaded and where
; the boot sector is relocated to.  The third is the static location
; where the NTLDR is loaded.
;

BootSeg segment at 07c0h        ; this is where the ROM loads us initially.
BootSeg ends

NewSeg  segment at 0d00h        ; this is where we\'ll relocate to.
NewSeg  ends                    ; enough for 16 boot sectors +
                                ;       4-sector scratch
                                ; below where we\'ll load NTLDR.

LdrSeg segment at 2000h         ; we want to load the loader at 2000:0000
LdrSeg ends

;/********************** START OF SPECIFICATIONS ************************/
;/*                                                                     */
;/* SUBROUTINE NAME: ntfsboot                                           */
;/*                                                                     */
;/* DESCRIPTIVE NAME: Bootstrap loader                                  */
;/*                                                                     */
;/* FUNCTION:    To load NTLDR into memory.                             */
;/*                                                                     */
;/* NOTES:       ntfsboot is loaded by the ROM BIOS (Int 19H) at        */
;/*              physical memory location 0000:7C00H.                   */
;/*              ntfsboot runs in real mode.                            */
;/*              This boot record is for NTFS volumes only.             */
;/*                                                                     */
;/* ENTRY POINT: ntfsboot                                               */
;/* LINKAGE:     Jump (far) from Int 19H                                */
;/*                                                                     */
;/* INPUT:       CS:IP = 0000:7C00H                                     */
;/*              SS:SP = 0030:00FAH (CBIOS dependent)                   */
;/*                                                                     */
;/* EXIT-NORMAL: DL = INT 13 drive number we booted from                */
;/*              Jmp to main in NTLDR                                   */
;/*                                                                     */
;/* EXIT-ERROR:  None                                                   */
;/*                                                                     */
;/* EFFECTS:     NTLDR is loaded into the physical memory               */
;/*                location 00020000H                                   */
;/*                                                                     */
;/* MESSAGES:    A disk read error occurred.                            */
;/*              The file NTLDR cannot be found.                        */
;/*              Insert a system diskette and restart the system.       */
;/*                                                                     */
;/*********************** END OF SPECIFICATIONS *************************/
BootCode segment        ;would like to use BootSeg here, but LINK flips its lid
        assume  cs:BootCode,ds:nothing,es:nothing,ss:nothing

        org     0               ; start at beginning of segment, not 0100h.

        public  _ntfsboot
_ntfsboot proc   far
        jmp     start
    .errnz  ($-_ntfsboot) GT (3),

    org 3
;
;       This is a template BPB--anyone who writes boot code to disk
;       should either preserve the existing BPB and NTFS information
;       or create it anew.
;
Version                 db      \"NTFS    \"      ; Must be 8 characters
BPB                     label   byte
BytesPerSector          dw      0               ; Size of a physical sector
SectorsPerCluster       db      0               ; Sectors per allocation unit
ReservedSectors         dw      0               ; Number of reserved sectors
Fats                    db      0               ; Number of fats
DirectoryEntries        dw      0               ; Number of directory entries
Sectors                 dw      0               ; No. of sectors - no. of hidden sectors
Media                   db      0               ; Media byte
FatSectors              dw      0               ; Number of fat sectors
SectorsPerTrack         dw      0               ; Sectors per track
Heads                   dw      0               ; Number of surfaces
HiddenSectors           dd      0               ; Number of hidden sectors
SectorsLong             dd      0               ; Number of sectors iff Sectors = 0
;
; The following is the rest of the NTFS Sector Zero information.
; The position and order of DriveNumber and CurrentHead are especially
; important, since those two variables are loaded into a single 16-bit
; register for the BIOS with one instruction.
;
DriveNumber         db      80h             ; Physical drive number (0 or 80h)
CurrentHead         db      ?               ; Variable to store current head no.

SectorZeroPad1      dw      0
SectorsOnVolume     db (size LARGE_INTEGER) dup (0)
MftStartLcn         db (size LARGE_INTEGER) dup (0)
Mft2StartLcn        db (size LARGE_INTEGER) dup (0)
ClustersPerFrs      dd      0
DefClustersPerBuf   dd      0
SerialNumber        db (size LARGE_INTEGER) dup (0)
CheckSum            dd      0
;
; The following variables are not part of the Extended BPB;  they\'re just
; scratch variables for the boot code.
;
SectorBase      dd      ?               ; next sector to read
CurrentTrack    dw      ?               ; current track
CurrentSector   db      ?               ; current sector
SectorCount     dw      ?               ; number of sectors to read

;****************************************************************************
start:
;
;       First of all, set up the segments we need (stack and data).
;
        cli
        xor     ax, ax                  ; Set up the stack to just before
        mov     ss, ax                  ; this code.  It\'ll be moved after
        mov     sp, 7c00h               ; we relocate.
        sti

        mov     ax, Bootseg             ; Address our BPB with DS.
        mov     ds, ax
        assume  ds:BootCode
;
;       Now read the 16-sector boot block into memory.  Then jump to that
;       new version of the boot block, starting in the second sector
;       (after the bootrecord sig).
;
        mov     SectorBase.lsw, 0       ; read sector zero.
        mov     SectorBase.msw, 0

        mov     word ptr [SectorCount], 16 ; read boot area
        mov     ax, NewSeg              ; read it at NewSeg.
        mov     es, ax
        sub     bx, bx                  ; at NewSeg:0000.
        call    DoReadLL                ; Call low-level DoRead routine

;
        push    NewSeg                  ; we\'ll jump to NewSeg:0200h.
        push    offset mainboot         ; (the second sector).
        ret                             ; \"return\" to the second sector.
_ntfsboot endp

;*******************************************************************************
;
; Low-level read routine that doesn\'t work across a 64k addr boundary.
;
;       Read SectorCount sectors (starting at SectorBase) to es:bx.
;
;       As a side effect, SectorBase is updated (but es:bx are not)
;       and SectorCount is reduced to zero.
;
DoReadLL proc
        push    ax                      ; save important registers
        push    bx
        push    cx
        push    dx
        push    es

DoRead$Loop:

.386
        mov     eax, SectorBase
        add     eax, HiddenSectors
        xor     edx,edx
;EDX:EAX = absolute sector number
        movzx   ecx,word ptr SectorsPerTrack    ; get into 32 bit value
        div     ecx         ; (EDX) = sector within track, (EAX)=track
        inc     dl          ; sector numbers are 1-based, not 0
        mov     CurrentSector, dl
        mov     edx,eax
        shr     edx,16
.286
        div     Heads                   ; (DX) = head no., (AX) = cylinder
        mov     CurrentHead, dl
        mov     CurrentTrack, ax

; CurrentHead is the head for this next disk request
; CurrentTrack is the track for this next request
; CurrentSector is the beginning sector number for this request
;
; Compute the number of sectors that we may be able to read in a single ROM
; request.
;
        mov     ax, SectorsPerTrack             ; could read up to this much
        sub     al, CurrentSector               ; offset within this track
        inc     ax                              ; CurrentSector was 1-based
;
; AX is the number of sectors that we may read.
;
        cmp     ax, SectorCount                 ; do we need to read whole trk?
        jbe     DoRead$FullTrack                ; yes we do.
        mov     ax, SectorCount                 ; no, read a partial track.
;
; AX is now the number of sectors that we SHOULD read.
;
DoRead$FullTrack:
        push    ax                      ; save sector count for later calc.
        mov     ah, 2                   ; \"read sectors\"
        mov     dx, CurrentTrack        ; at this cylinder
        mov     cl, 6
        shl     dh, cl                  ; high 2 bits of DH = bits 8,9 of DX
        or      dh, CurrentSector       ; (DH)=cyl bits | 6-bit sector no.
        mov     cx, dx                  ; (CX)=cylinder/sector no. combination
        xchg    ch, cl                  ; in the right order
        mov     dh, CurrentHead
        mov     dl, 80h                 ; should be DriveNumber, but...

        int     13h                     ; call BIOS.

        pop     ax
        jb      BootErr$he              ; If errors report
        add     SectorBase.lsw, ax      ; increment logical sector position
        adc     SectorBase.msw, 0
        sub     SectorCount, ax         ; exhausted entire sector run?
        jbe     DoRead$Exit             ; yes, we\'re all done.
        shl     ax, 9 - 4               ; (AX)=paragraphs read from last track
        mov     dx, es                  ; (DX)=segment we last read at
        add     dx, ax                  ; (DX)=segment right after last read
        mov     es, dx                  ; (ES)=segment to read next track at
        jmp     DoRead$Loop
;
DoRead$Exit:
        pop     es
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret

DoReadLL  endp


;****************************************************************************
;
; BootErr - print error message and hang the system.
;
BootErr proc
BootErr$fnf:
        mov     si,offset TXT_MSG_SYSINIT_FILE_NOT_FD +2
        jmp     short BootErr2
BootErr$ntc:
        mov     si,offset TXT_MSG_SYSINIT_NTLDR_CMPRS +2
        jmp     short BootErr2
BootErr$he:
        mov     si,offset TXT_MSG_SYSINIT_BOOT_ERROR +2
BootErr2:
        call    BootErr$print
        mov     si,offset TXT_MSG_SYSINIT_INSER_DK +2
        call    BootErr$print
        sti
        jmp     $                       ;Wait forever
BootErr$print:
        lodsb                           ; Get next character
        cmp     al, 0
        je      BootErr$Done
        mov     ah,14                   ; Write teletype
        mov     bx,7                    ; Attribute
        int     10h                     ; Print it
        jmp     BootErr$print
BootErr$Done:
        ret
BootErr endp

;****************************************************************************
        include ntfsboot.inc     ;suck in the message text


ReservedForFuture DB    2 dup(?) ;reserve remaining bytes to prevent NLS
                                 ;messages from using them

   .errnz   ($-_ntfsboot) GT (512-2),

   org   512-2
        db      55h,0aah

;   Name we look for.  ntldr_length is the number of characters,
;   ntldr_name is the name itself.  Note that it is not NULL
;   terminated, and doesn\'t need to be.
;
ntldr_name_length   dw  5
ntldr_name          dw  \'N\', \'T\', \'L\', \'D\', \'R\'

;   Predefined name for index-related attributes associated with an
;   index over $FILE_NAME
;
index_name_length   dw 4
index_name          dw \'$\', \'I\', \'3\', \'0\'

;   Global variables.  These offsets are all relative to NewSeg.
;
AttrList       dd 0e000h; Offset of buffer to hold attribute list
MftFrs          dd   3000h; Offset of first MFT FRS
SegmentsInMft       dd    ?   ; number of FRS\'s with MFT Data attribute records
RootIndexFrs       dd    ?   ; Offset of Root Index FRS
AllocationIndexFrs  dd    ?   ; Offset of Allocation Index FRS        ; KPeery
BitmapIndexFrs       dd    ?   ; Offset of Bitmap Index FRS            ; KPeery
IndexRoot           dd    ?   ; Offset of Root Index $INDEX_ROOT attribute
IndexAllocation     dd    ?   ; Offset of Root Index $INDEX_ALLOCATION attribute
IndexBitmap           dd    ?   ; Offset of Root Index $BITMAP attribute
NtldrFrs           dd    ?   ; Offset of NTLDR FRS
NtldrData           dd    ?   ; Offset of NTLDR $DATA attribute
IndexBlockBuffer    dd    ?   ; Offset of current index buffer
IndexBitmapBuffer   dd    ?   ; Offset of index bitmap buffer
NextBuffer           dd    ?   ; Offset of next free byte in buffer space

BytesPerCluster     dd  ?       ; Bytes per cluster
BytesPerFrs         dd  ?       ; Bytes per File Record Segment
SectorsPerFrs       dd  ?       ; Sectors per File Record Segment
BytesPerIndexBlock  dd  ?       ; Bytes per index alloc block in root index
ClustersPerIndexBlock   dd  ?   ; Clusters per index alloc block in root index
SectorsPerIndexBlock    dd  ?   ; Sectors per index block in root index

.386

SAVE_ALL    macro

    push    es
    push    ds
    pushad

endm

RESTORE_ALL macro

    popad
    nop
    pop     ds
    pop     es

endm


;****************************************************************************
;
; mainboot -
;
;
mainboot proc   far

;       Get the new ds and the new stack.  Note that ss is zero.
;
        mov     ax, cs                  ; Set DS to CS
        mov     ds, ax

        shl     ax, 4                   ; convert to an offset.
        cli
        mov     sp, ax                  ; load new stack, just before boot code.
        sti

;       Set up the FRS buffers.  The MFT buffer is in a fixed
;       location, and the other three come right after it.  The
;       buffer for index allocation blocks comes after that.
;

;       Compute the useful constants associated with the volume
;
        movzx   eax, BytesPerSector     ; eax = Bytes per Sector
        movzx   ebx, SectorsPerCluster  ; ebx = Sectors Per Cluster
        mul     ebx                     ; eax = Bytes per Cluster
        mov     BytesPerCluster, eax

        mov     ecx, ClustersPerFrs     ; ecx = clusters per frs
        cmp     cl, 0                   ; is ClustersPerFrs less than zero?
        jg      mainboot$1

;       If the ClustersPerFrs field is negative, we calculate the number
;       of bytes per FRS by negating the value and using that as a shif count.
;

        neg     cl
        mov     eax, 1
        shl     eax, cl                 ; eax = bytes per frs
        jmp     mainboot$2

mainboot$1:

;       Otherwise if ClustersPerFrs was positive, we multiply by bytes
;       per cluster.

        mov     eax, BytesPerCluster
        mul     ecx                     ; eax = bytes per frs

mainboot$2:

        mov     BytesPerFrs, eax
        movzx   ebx, BytesPerSector
        xor     edx, edx                ; zero high part of dividend
        div     ebx                     ; eax = sectors per frs
        mov     SectorsPerFrs, eax


;       Set up the MFT FRS\'s---this will read all the $DATA attribute
;       records for the MFT.
;

        call    SetupMft

;       Set up the remaining FRS buffers.  The RootIndex FRS comes
;       directly after the last MFT FRS, followed by the NTLdr FRS
;       and the Index Block buffer.
;
        mov     ecx, NextBuffer
        mov     RootIndexFrs, ecx

        add     ecx, BytesPerFrs            ; AllocationFrs may be different
        mov     AllocationIndexFrs, ecx     ; from RootIndexFrs - KPeery

        add     ecx, BytesPerFrs            ; BitmapFrs may be different
        mov     BitmapIndexFrs, ecx         ; from RootIndexFrs - KPeery

        add     ecx, BytesPerFrs
        mov     NtldrFrs, ecx

        add     ecx, BytesPerFrs
        mov     IndexBlockBuffer, ecx

;
;       Read the root index, allocation index and bitmap FRS\'s and locate
;       the interesting attributes.
;

        mov     eax, $INDEX_ROOT
        mov     ecx, RootIndexFrs
        call    LoadIndexFrs

        or      eax, eax
        jz      BootErr$he

        mov     IndexRoot, eax          ; offset in Frs buffer

        mov     eax, $INDEX_ALLOCATION  ; Attribute type code
        mov     ecx, AllocationIndexFrs ; FRS to search
        call    LoadIndexFrs

        mov     IndexAllocation, eax

        mov     eax, $BITMAP            ; Attribute type code
        mov     ecx, BitmapIndexFrs     ; FRS to search
        call    LoadIndexFrs

        mov     IndexBitmap, eax

;       Consistency check: the index root must exist, and it
;       must be resident.
;
        mov     eax, IndexRoot
        or      eax, eax
        jz      BootErr$he


        cmp     [eax].ATTR_FormCode, RESIDENT_FORM
        jne     BootErr$he


;       Determine the size of the index allocation buffer based
;       on information in the $INDEX_ROOT attribute.  The index
;       bitmap buffer comes immediately after the index block buffer.
;
;       eax -> $INDEX_ROOT attribute record
;
        lea     edx, [eax].ATTR_FormUnion   ; edx -> resident info
        add     ax, [edx].RES_ValueOffset   ; eax -> value of $INDEX_ROOT

        movzx   ecx, [eax].IR_ClustersPerBuffer
        mov     ClustersPerIndexBlock, ecx

        mov     ecx, [eax].IR_BytesPerBuffer
        mov     BytesPerIndexBlock, ecx

        mov     eax, BytesPerIndexBlock
        movzx   ecx, BytesPerSector
        xor     edx, edx
        div     ecx                         ; eax = sectors per index block
        mov     SectorsPerIndexBlock, eax

        mov     eax, IndexBlockBuffer
        add     eax, BytesPerIndexBlock
        mov     IndexBitmapBuffer, eax

;       Next consistency check: if the $INDEX_ALLOCATION attribute
;       exists, the $INDEX_BITMAP attribute must also exist.
;
        cmp     IndexAllocation, 0
        je      mainboot30

        cmp     IndexBitmap, 0      ; since IndexAllocation exists, the
        je      BootErr$he          ;  bitmap must exist, too.

;       Since the bitmap exists, we need to read it into the bitmap
;       buffer.  If it\'s resident, we can just copy the data.
;

        mov     ebx, IndexBitmap        ; ebx -> index bitmap attribute
        push    ds
        pop     es
        mov     edi, IndexBitmapBuffer  ; es:edi -> index bitmap buffer

        call    ReadWholeAttribute

mainboot30:
;
;       OK, we\'ve got the index-related attributes.
;
        movzx   ecx, ntldr_name_length  ; ecx = name length in characters
        mov     eax, offset ntldr_name  ; eax -> name

        call    FindFile

        or      eax, eax
        jz      BootErr$fnf

;       Read the FRS for NTLDR and find its data attribute.
;
;       eax -> Index Entry for NTLDR.
;
        mov     eax, [eax].IE_FileReference.REF_LowPart


        push    ds
        pop     es              ; es:edi = target buffer
        mov     edi, NtldrFrs

        call    ReadFrs

        mov     eax, NtldrFrs   ; pointer to FRS
        mov     ebx, $DATA      ; requested attribute type
        mov     ecx, 0          ; attribute name length in characters
        mov     edx, 0          ; attribute name (NULL if none)

        call    LocateAttributeRecord

;       eax -> $DATA attribute for NTLDR
;
        or      eax, eax        ; if eax is zero, attribute not found.
        jz      BootErr$fnf

;       Get the attribute record header flags, and make sure none of the
;       `compressed\' bits are set

        movzx   ebx, [eax].ATTR_Flags
        and     ebx, ATTRIBUTE_FLAG_COMPRESSION_MASK
        jnz     BootErr$ntc

        mov     ebx, eax        ; ebx -> $DATA attribute for NTLDR

        push    LdrSeg
        pop     es              ; es = segment addres to read into
        sub     edi, edi        ; es:edi = buffer address

        call    ReadWholeAttribute

;
; We\'ve loaded NTLDR--jump to it.
;
; Before we go to NTLDR, set up the registers the way it wants them:
;       DL = INT 13 drive number we booted from
;
        mov     dl, DriveNumber
        mov     ax,1000
        mov     es, ax                  ; we don\'t really need this
        lea     si, BPB
        sub     ax,ax
        push    LdrSeg
        push    ax
        retf                            ; \"return\" to NTLDR.


mainboot endp

;****************************************************************************
;
; DoRead - read SectorCount sectors into ES:BX starting from sector
;          SectorBase.
;
; NOTE: This code WILL NOT WORK if ES:BX does not point to an address whose
; physical address (ES * 16 + BX) MOD 512 != 0.
;
; DoRead adds to ES rather than BX in the main loop so that runs longer than
; 64K can be read with a single call to DoRead.
;
; Note that DoRead (unlike DoReadLL) saves and restores SectorCount
; and SectorBase
;
.286
DoRead  proc
        push    ax                      ; save important registers
        push    bx
        push    cx
        push    dx
        push    es
        push    SectorCount             ; save state variables too
        push    SectorBase.lsw
        push    SectorBase.msw
;
; Calculate how much we can read into what\'s left of the current 64k
; physical address block, and read it.
;
;
        mov     ax,bx

        shr     ax,4
        mov     cx,es              
        add     ax,cx                   ; ax = paragraph addr

;
; Now calc maximum number of paragraphs that we can read safely:
;       4k - ( ax mod 4k )
;

        and     ax,0fffh
        sub     ax,1000h
        neg     ax

;
; Calc CX = number of paragraphs to be read
;
        mov     cx,SectorCount          ; convert SectorCount to paragraph cnt
        shl     cx,9-4

DoRead$Loop64:
        push    cx                      ; save cpRead

        cmp     ax,cx                   ; ax = min(cpReadSafely, cpRead)
        jbe     @F
        mov     ax,cx
@@:
        push    ax
;
; Calculate new SectorCount from amount we can read
;
        shr     ax,9-4
        mov     SectorCount,ax

        call    DoReadLL

        pop     ax                      ; ax = cpActuallyRead
        pop     cx                      ; cx = cpRead

        sub     cx,ax                   ; Any more to read?
        jbe     DoRead$Exit64           ; Nope.
;
; Adjust ES:BX by amount read
;
        mov     dx,es
        add     dx,ax
        mov     es,dx
;
; Since we\'re now reading on a 64k byte boundary, cpReadSafely == 4k.
;
        mov     ax,01000h               ; 16k paragraphs per 64k segment
        jmp     short DoRead$Loop64     ; and go read some more.

DoRead$Exit64:
        pop     SectorBase.msw          ; restore all this crap
        pop     SectorBase.lsw
        pop     SectorCount
        pop     es
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
DoRead  endp

.386
;****************************************************************************
;
;   ReadClusters - Reads a run of clusters from the disk.
;
;   ENTRY:  eax == LCN to read
;           edx == clusters to read
;           es:edi -> Target buffer
;
;   USES:   none (preserves all registers)
;
ReadClusters proc near

    SAVE_ALL

    mov     ebx, edx                ; ebx = clusters to read.
    movzx   ecx, SectorsPerCluster  ; ecx = cluster factor

    mul     ecx                 ; Convert LCN to sectors (wipes out edx!)
    mov     SectorBase, eax     ; Store starting sector in SectorBase

    mov     eax, ebx            ; eax = number of clusters
    mul     ecx                 ; Convert EAX to sectors (wipes out edx!)
    mov     SectorCount, ax     ; Store number of sectors in SectorCount


;   Note that ReadClusters gets its target buffer in es:edi but calls
;   the DoRead worker function that takes a target in es:bx--we need
;   to normalize es:edi so that we don\'t overflow bx.
;
    mov     bx, di
    and     bx, 0Fh
    mov     ax, es
    shr     edi, 4
    add     ax, di              ; ax:bx -> target buffer

    push    ax
    pop     es                  ; es:bx -> target buffer

    call    DoRead

    RESTORE_ALL
    ret

ReadClusters endp

;
;****************************************************************************
;
;   LocateAttributeRecord   --  Find an attribute record in an FRS.
;
;   ENTRY:  EAX -- pointer to FRS
;           EBX -- desired attribute type code
;           ECX -- length of attribute name in characters
;           EDX -- pointer to attribute name
;
;   EXIT:   EAX points at attribute record (0 indicates not found)
;
;   USES:   All
;
LocateAttributeRecord proc near

; get the first attribute record.
;
        add     ax, word ptr[eax].FRS_FirstAttribute

;       eax -> next attribute record to investigate.
;       ebx == desired type
;       ecx == name length
;       edx -> pointer to name
;
lar10:
        cmp     [eax].ATTR_TypeCode, 0ffffffffh
        je      lar99

        cmp     dword ptr[eax].ATTR_TypeCode, ebx
        jne     lar80

;       this record is a potential match.  Compare the names:
;
;       eax -> candidate record
;       ebx == desired type
;       ecx == name length
;       edx -> pointer to name
;
        or      ecx, ecx    ; Did the caller pass in a name length?
        jnz     lar20

;       We want an attribute with no name--the current record is
;       a match if and only if it has no name.
;
        cmp     [eax].ATTR_NameLength, 0
        jne     lar80       ; Not a match.

;       It\'s a match, and eax is set up correctly, so return.
;
        ret

;       We want a named attribute.
;
;       eax -> candidate record
;       ebx == desired type
;       ecx == name length
;       edx -> pointer to name
;
lar20:
        cmp     cl, [eax].ATTR_NameLength
        jne     lar80       ; Not a match.

;       Convert name in current record to uppercase.
;
        mov     esi, eax
        add     si, word ptr[eax].ATTR_NameOffset

        call    UpcaseName

;       eax -> candidate record
;       ebx == desired type
;       ecx == name length
;       edx -> pointer to name
;       esi -> Name in current record (upcased)
;
        push    ecx         ; save cx

        push    ds          ; Copy data segment into es
        pop     es
        mov     edi, edx    ; note that esi is already set up.

        repe cmpsw          ; zero flag is set if equal

        pop     ecx         ; restore cx

        jnz     lar80       ; not a match

;       eax points at a matching record.
;
        ret

;
;   This record doesn\'t match; go on to the next.
;
;       eax -> rejected candidate attribute record
;       ebx == desired type
;       ecx == Name length
;       edx -> desired name
;
lar80:  cmp     [eax].ATTR_RecordLength, 0  ; if the record length is zero
        je      lar99                       ; the FRS is corrupt.

        add     eax, [eax].ATTR_RecordLength; Go to next record
        jmp     lar10                       ; and try again

;       Didn\'t find it.
;
lar99:  sub     eax, eax
        ret

LocateAttributeRecord endp

;****************************************************************************
;
;   LocateIndexEntry   --  Find an index entry in a file name index
;
;   ENTRY:  EAX -> pointer to index header
;           EBX -> file name to find
;           ECX == length of file name in characters
;
;   EXIT:   EAX points at index entry.  NULL to indicate failure.
;
;   USES:   All
;
LocateIndexEntry proc near

;       Convert the input name to upper-case
;

        mov     esi, ebx
        call    UpcaseName

;       DEBUG CODE
;
;       call    PrintName
;       call    Debug2
;
;       END DEBUG CODE

        add     eax, [eax].IH_FirstIndexEntry

;       EAX -> current entry
;       EBX -> file name to find
;       ECX == length of file name in characters
;
lie10:  test    [eax].IE_Flags, INDEX_ENTRY_END ; Is it the end entry?
        jnz     lie99

        lea     edx, [eax].IE_Value         ; edx -> FILE_NAME attribute value

;       DEBUG CODE -- list file names as they are examined
;
;       SAVE_ALL
;
;       call    Debug3
;       movzx   ecx, [edx].FN_FileNameLength    ; ecx = chars in name
;       lea     esi, [edx].FN_FileName          ; esi -> name
;       call    PrintName
;
;       RESTORE_ALL
;
;       END DEBUG CODE

;       EAX -> current entry
;       EBX -> file name to find
;       ECX == length of file name in characters
;       EDX -> FILE_NAME attribute
;
        cmp     cl, [edx].FN_FileNameLength ; Is name the right length?
        jne     lie80

        lea     esi, [edx].FN_FileName      ; Get name from FILE_NAME structure

        call    UpcaseName

        push    ecx         ; save ecx

        push    ds
        pop     es          ; copy data segment into es for cmpsw
        mov     edi, ebx    ; edi->search name (esi already set up)
        repe    cmpsw       ; zero flag is set if they\'re equal

        pop     ecx         ; restore ecx

        jnz     lie80

;       the current entry matches the search name, and eax points at it.
;
        ret

;       The current entry is not a match--get the next one.
;           EAX -> current entry
;           EBX -> file name to find
;           ECX == length of file name in characters
;
lie80:  cmp     [eax].IE_Length, 0      ; If the entry length is zero
        je      lie99                   ; then the index block is corrupt.

        add     ax, [eax].IE_Length     ; Get the next entry.

        jmp     lie10


;   Name not found in this block.  Set eax to zero and return
;
lie99:  xor     eax, eax
        ret

LocateIndexEntry endp

;****************************************************************************
;
;   ReadWholeAttribute - Read an entire attribute value
;
;   ENTRY:  ebx -> attribute
;           es:edi -> target buffer
;
;   USES:   ALL
;
ReadWholeAttribute proc near

        cmp     [ebx].ATTR_FormCode, RESIDENT_FORM
        jne      rwa10

;       The attribute is resident.
;       ebx -> attribute
;       es:edi -> target buffer
;

        SAVE_ALL

        lea     edx, [ebx].ATTR_FormUnion   ; edx -> resident form info
        mov     ecx, [edx].RES_ValueLength  ; ecx = bytes in value
        mov     esi, ebx                    ; esi -> attribute
        add     si, [edx].RES_ValueOffset   ; esi -> attribute value

        rep     movsb                       ; copy bytes from value to buffer

        RESTORE_ALL

        ret                                 ; That\'s all!

rwa10:
;
;       The attribute type is non-resident.  Just call
;       ReadNonresidentAttribute starting at VCN 0 and
;       asking for the whole thing.
;
;       ebx -> attribute
;       es:edi -> target buffer
;
        lea     edx, [ebx].ATTR_FormUnion   ; edx -> nonresident form info
        mov     ecx, [edx].NONRES_HighestVcn.LowPart; ecx = HighestVcn
        inc     ecx                         ; ecx = clusters in attribute

        sub     eax, eax                    ; eax = 0 (first VCN to read)

        call    ReadNonresidentAttribute

        ret

ReadWholeAttribute endp

;****************************************************************************
;
;   ReadNonresidentAttribute - Read clusters from a nonresident attribute
;
;   ENTRY:  EAX == First VCN to read
;           EBX -> Attribute
;           ECX == Number of clusters to read
;           ES:EDI == Target of read
;
;   EXIT:   None.
;
;   USES:   None (preserves all registers with SAVE_ALL/RESTORE_ALL)
;
ReadNonresidentAttribute proc near

        SAVE_ALL

        cmp     [ebx].ATTR_FormCode, NONRESIDENT_FORM
        je      ReadNR10

;       This attribute is not resident--the disk is corrupt.

        jmp     BootErr$he


ReadNR10:
;       eax == Next VCN to read
;       ebx -> Attribute
;       ecx -> Remaining clusters to read
;       es:edi -> Target of read
;

        cmp     ecx, 0
        jne     ReadNR20

;       Nothing left to read--return success.
;
        RESTORE_ALL
        ret

ReadNR20:
        push    ebx ; pointer to attribute
        push    eax ; Current VCN

        push    ecx
        push    edi
        push    es

        call    ComputeLcn  ; eax = LCN to read, ecx = run length
        mov     edx, ecx    ; edx = remaining run length

        pop     es
        pop     edi
        pop     ecx


;       eax == LCN to read
;       ecx == remaining clusters to read
;       edx == remaining clusters in current run
;       es:edi == Target of read
;       TOS == Current VCN
;       TOS + 4 == pointer to attribute
;
        cmp     ecx, edx
        jge     ReadNR30

;       Run length is greater than remaining request; only read
;       remaining request.
;
        mov     edx, ecx    ; edx = Remaining request

ReadNR30:
;       eax == LCN to read
;       ecx == remaining clusters to read
;       edx == clusters to read in current run
;       es:edi == Target of read
;       TOS == Current VCN
;       TOS +  == pointer to attribute
;

        call    ReadClusters

        sub     ecx, edx            ; Decrement clusters remaining in request
        mov     ebx, edx            ; ebx = clusters read

        mov     eax, edx            ; eax = clusters read
        movzx   edx, SectorsPerCluster
        mul     edx                 ; eax = sectors read (wipes out edx!)
        movzx   edx, BytesPerSector
        mul     edx                 ; eax = bytes read (wipes out edx!)

        add     edi, eax            ; Update target of read

        pop     eax                 ; eax = previous VCN
        add     eax, ebx            ; update VCN to read

        pop     ebx                 ; ebx -> attribute
        jmp     ReadNR10


ReadNonresidentAttribute endp

;****************************************************************************
;
;   ReadIndexBlockSectors - Read sectors from an index allocation attribute
;
;   ENTRY:  EAX == First VBN to read
;           EBX -> Attribute
;           ECX == Number of sectors to read
;           ES:EDI == Target of read
;
;   EXIT:   None.
;
;   USES:   None (preserves all registers with SAVE_ALL/RESTORE_ALL)
;
ReadIndexBlockSectors proc near

        SAVE_ALL

        cmp     [ebx].ATTR_FormCode, NONRESIDENT_FORM
        je      ReadIBS_10

;       This attribute is resident--the disk is corrupt.

        jmp     BootErr$he


ReadIBS_10:
;       eax == Next VBN to read
;       ebx -> Attribute
;       ecx -> Remaining sectors to read
;       es:edi -> Target of read
;

        cmp     ecx, 0
        jne     ReadIBS_20

;       Nothing left to read--return success.
;


        RESTORE_ALL
        ret

ReadIBS_20:
        push    ebx ; pointer to attribute
        push    eax ; Current VBN

        push    ecx
        push    edi
        push    es

        ; Convert eax from a VBN back to a VCN by dividing by SectorsPerCluster.
        ; The remainder of this division is the sector offset in the cluster we
        ; want.  Then use the mapping information to get the LCN for this VCN,
        ; then multiply to get back to LBN.
        ;

        push    ecx         ; save remaining sectors in request

        xor     edx, edx    ; zero high part of dividend
        movzx   ecx, SectorsPerCluster
        div     ecx         ; edx = remainder
        push    edx         ; save remainder

        call    ComputeLcn  ; eax = LCN to read, ecx = remaining run length

        movzx   ebx, SectorsPerCluster
        mul     ebx         ; eax = LBN of cluster, edx = 0
        pop     edx         ; edx = remainder
        add     eax, edx    ; eax = LBN we want
        push    eax         ; save LBN

        movzx   eax, SectorsPerCluster
        mul     ecx         ; eax = remaining run length in sectors, edx = 0
        mov     edx, eax    ; edx = remaining run length

        pop     eax         ; eax = LBN
        pop     ecx         ; ecx = remaining sectors in request

        pop     es
        pop     edi
        pop     ecx


;       eax == LBN to read
;       ecx == remaining sectors to read
;       edx == remaining sectors in current run
;       es:edi == Target of read
;       TOS == Current VCN
;       TOS + 4 == pointer to attribute
;
        cmp     ecx, edx
        jge     ReadIBS_30

;       Run length is greater than remaining request; only read
;       remaining request.
;
        mov     edx, ecx    ; edx = Remaining request

ReadIBS_30:
;       eax == LBN to read
;       ecx == remaining sectors to read
;       edx == sectors to read in current run
;       es:edi == Target of read
;       TOS == Current VCN
;       TOS +  == pointer to attribute
;

        mov     SectorBase, eax
        mov     SectorCount, dx

;       We have a pointer to the target buffer in es:edi, but we want that
;       in es:bx for DoRead.
;

        SAVE_ALL

        mov     bx, di
        and     bx, 0Fh
        mov     ax, es
        shr     edi, 4
        add     ax, di              ; ax:bx -> target buffer

        push    ax
        pop     es                  ; es:bx -> target buffer

        call    DoRead

        RESTORE_ALL

        sub     ecx, edx            ; Decrement sectors remaining in request
        mov     ebx, edx            ; ebx = sectors read

        mov     eax, edx            ; eax = sectors read
        movzx   edx, BytesPerSector
        mul     edx                 ; eax = bytes read (wipes out edx!)

        add     edi, eax            ; Update target of read

        pop     eax                 ; eax = previous VBN
        add     eax, ebx            ; update VBN to read

        pop     ebx                 ; ebx -> attribute
        jmp     ReadIBS_10


ReadIndexBlockSectors endp


;****************************************************************************
;
;   MultiSectorFixup - fixup a structure read off the disk
;                      to reflect Update Sequence Array.
;
;   ENTRY:  ES:EDI = Target buffer
;
;   USES:   none (preserves all registers with SAVE_ALL/RESTORE_ALL)
;
;   Note: ES:EDI must point at a structure which is protected
;         by an update sequence array, and which begins with
;         a multi-sector-header structure.
;
MultiSectorFixup proc near

    SAVE_ALL

    movzx   ebx, es:[edi].MSH_UpdateArrayOfs    ; ebx = update array offset
    movzx   ecx, es:[edi].MSH_UpdateArraySize   ; ecx = update array size

    or      ecx, ecx        ; if the size of the update sequence array
    jz      BootErr$he      ; is zero, this structure is corrupt.

    add     ebx, edi        ; es:ebx -> update sequence array count word
    add     ebx, 2          ; es:ebx -> 1st entry of update array

    add     edi, SEQUENCE_NUMBER_STRIDE - 2 ; es:edi->last word of first chunk
    dec     ecx             ; decrement to reflect count word

MSF10:

;   ecx = number of entries remaining in update sequence array
;   es:ebx -> next entry in update sequence array
;   es:edi -> next target word for update sequence array

    or      ecx, ecx
    jz      MSF30

    mov     ax, word ptr es:[ebx]   ; copy next update sequence array entry
    mov     word ptr es:[edi], ax   ; to next target word

    add     ebx, 2                      ; go on to next entry
    add     edi, SEQUENCE_NUMBER_STRIDE ; go on to next target

    dec     ecx


    jmp     MSF10

MSF30:

    RESTORE_ALL

    ret

MultiSectorFixup endp

;****************************************************************************
;
;   SetupMft - Reads MFT File Record Segments into memory.
;
;   ENTRY:  none.
;
;   EXIT:   NextBuffer is set to the free byte after the last MFT FRS
;           SegmentsInMft is initialized
;
;
SetupMft proc near

        SAVE_ALL

;       Initialize SegmentsInMft and NextBuffer as if the MFT
;       had only one FRS.
;
        mov     eax, 1
        mov     SegmentsInMft, eax

        mov     eax, MftFrs
        add     eax, BytesPerFrs
        mov     NextBuffer, eax

;       Read FRS 0 into the first MFT FRS buffer, being sure
;       to resolve the Update Sequence Array.
;

        mov     eax, MftStartLcn.LowPart
        movzx   ebx, SectorsPerCluster
        mul     ebx                             ; eax = mft starting sector
        mov     SectorBase, eax                 ; SectorBase = mft starting sector

        mov     eax, SectorsPerFrs
        mov     SectorCount, ax                 ; SectorCount = SectorsPerFrs

        mov     ebx, MftFrs

        push    ds
        pop     es

        call    DoRead
        movzx   edi, bx                         ; es:edi = buffer
        call    MultiSectorFixup

;       Determine whether the MFT has an Attribute List attribute

        mov     eax, MftFrs
        mov     ebx, $ATTRIBUTE_LIST
        mov     ecx, 0
        mov     edx, 0

        call    LocateAttributeRecord

        or      eax, eax        ; If there\'s no Attribute list,
        jz      SetupMft99      ;    we\'re done!

;       Read the attribute list.
;       eax -> attribute list attribute
;
        mov     ebx, eax        ; ebx -> attribute list attribute
        push    ds
        pop     es              ; copy ds into es
        mov     edi, AttrList   ; ds:edi->attribute list buffer

        call    ReadWholeAttribute

        mov     ebx, AttrList   ; ebx -> first attribute list entry

;       Now, traverse the attribute list looking for the first
;       entry for the $DATA type.  We know it must have at least
;       one.
;
;       ebx -> first attribute list entry
;
SetupMft10:
        cmp     [ebx].ATTRLIST_TypeCode, $DATA
        je      SetupMft20

        add     bx,[ebx].ATTRLIST_Length
        jmp     SetupMft10


SetupMft20:
;       Scan forward through the attribute list entries for the
;       $DATA attribute, reading each referenced FRS.  Note that
;       there will be at least one non-$DATA entry after the entries
;       for the $DATA attribute, since there\'s a $BITMAP.
;
;       ebx -> Next attribute list entry
;       NextBuffer    -> Target for next read
;       SegmentsInMft == number of MFT segments read so far
;
        cmp     [ebx].ATTRLIST_TypeCode, $DATA
        jne     SetupMft99

;       Read the FRS referred to by this attribute list entry into
;       the next buffer, and increment NextBuffer and SegmentsInMft.
;
        push    ebx

        mov     eax, [ebx].ATTRLIST_SegmentReference.REF_LowPart
        mov     edi, NextBuffer
        push    ds
        pop     es              ; copy ds into es

        call    ReadFrs

        pop     ebx

;       Increment NextBuffer and SegmentsInMft

        mov     eax, BytesPerFrs
        add     NextBuffer, eax

        inc     SegmentsInMft

;       Go on to the next attribute list entry

        add     bx, [ebx].ATTRLIST_Length
        jmp     SetupMft20

SetupMft99:

        RESTORE_ALL
        ret

SetupMft endp

;****************************************************************************
;
;   ComputeMftLcn   --  Computes the LCN for a cluster of the MFT
;
;
;   ENTRY:  EAX == VCN
;
;   EXIT:   EAX == LCN
;
;   USES:   ALL
;
ComputeMftLcn proc near

        mov     edx, eax            ; edx = VCN

        mov     ecx, SegmentsInMft  ; ecx = # of FRS\'s to search
        mov     eax, MftFrs         ; eax -> first FRS to search

MftLcn10:
;       EAX -> Next FRS to search
;       ECX == number of remaining FRS\'s to search
;       EDX == VCN
;
        push    edx
        push    eax
        push    ecx
        push    edx         ; Yes, I meant to push it twice

        mov     ebx, $DATA
        mov     ecx, 0
        mov     edx, 0

        call    LocateAttributeRecord

;       EAX -> $DATA attribute
;       TOS == VCN
;       TOS + 4 == number of remaining FRS\'s to search
;       TOS + 8 -> FRS being searched
;       TOS +12 == VCN

        or      eax, eax
        jz      BootErr$he  ; No $DATA attribute in this FRS!

        mov     ebx, eax    ; ebx -> attribute
        pop     eax         ; eax = VCN

;       EAX == VCN
;       EBX -> $DATA attribute
;       TOS number of remaining FRS\'s to search
;       TOS + 4 == FRS being searched
;       TOS + 8 == VCN

        call    ComputeLcn

        or      eax, eax
        jz      MftLcn20

;       Found our LCN.  Clean up the stack and return.
;
;       EAX == LCN
;       TOS number of remaining FRS\'s to search
;       TOS + 4 == FRS being searched
;       TOS + 8 == VCN
;
        pop     ebx
        pop     ebx
        pop     ebx     ; clean up the stack

        ret

MftLcn20:
;
;       Didn\'t find the VCN in this FRS; try the next one.
;
;       TOS number of remaining FRS\'s to search
;       TOS + 4 -> FRS being searched
;       TOS + 8 == VCN
;
        pop     ecx     ; ecx = number of FRS\'s remaining, including current
        pop     eax     ; eax -> current FRS
        pop     edx     ; edx = VCN

        add     eax, BytesPerFrs    ; eax -> next FRS
        loop    MftLcn10            ; decrement ecx and try next FRS

;       This VCN was not found.
;
        xor     eax, eax
        ret


ComputeMftLcn endp

;****************************************************************************
;
;   ReadMftSectors - Read sectors from the MFT
;
;   ENTRY:  EAX == starting VBN
;           ECX == number of sectors to read
;           ES:EDI == Target buffer
;
;   USES:   none (preserves all registers with SAVE_ALL/RESTORE_ALL)
;
ReadMftSectors proc near

    SAVE_ALL

RMS$Again:

    push    eax                     ; save starting VBN
    push    ecx                     ; save sector count


;   Divide the VBN by SectorsPerCluster to get the VCN

    xor     edx, edx                ; zero high part of dividend
    movzx   ebx, SectorsPerCluster
    div     ebx                     ; eax = VCN
    push    edx                     ; save remainder

    call    ComputeMftLcn           ; eax = LCN

    or      eax, eax                ; LCN equal to zero?
    jz      BootErr$he              ; zero is not a possible LCN

;   Change the LCN back into a LBN and add the remainder back in to get
;   the sector we want to read, which goes into SectorBase.
;

    movzx   ebx, SectorsPerCluster
    mul     ebx                     ; eax = cluster first LBN
    pop     edx                     ; edx = sector remainder
    add     eax, edx                ; eax = desired LBN

    mov     SectorBase, eax

;
;   Figure out how many sectors to read this time; we never attempt
;   to read more than one cluster at a time.
;

    pop     ecx                     ; ecx = sectors to read

    movzx   ebx, SectorsPerCluster
    cmp     ecx,ebx
    jle     RMS10    

;
;   Read only a single cluster at a time, to avoid problems with fragmented
;   runs in the mft.
;

    mov     SectorCount, bx         ; this time read 1 cluster
    sub     ecx, ebx                ; ecx = sectors remaining to read

    pop     eax                     ; eax = VBN
    add     eax, ebx                ; VBN += sectors this read


    push    eax                     ; save next VBN
    push    ecx                     ; save remaining sector count



    jmp     RMS20

RMS10:

    pop     eax                     ; eax = VBN
    add     eax, ecx                ; VBN += sectors this read
    push    eax                     ; save next VBN

    mov     SectorCount, cx
    mov     ecx, 0
    push    ecx                     ; save remaining sector count (0)

RMS20:


;   The target buffer was passed in es:edi, but we want it in es:bx.
;   Do the conversion.
;

    push    es                      ; save buffer pointer
    push    edi

    mov     bx, di
    and     bx, 0Fh
    mov     ax, es
    shr     edi, 4
    add     ax, di                  ; ax:bx -> target buffer

    push    ax
    pop     es                      ; es:bx -> target buffer

    call    DoRead

    pop     edi                     ; restore buffer pointer
    pop     es

    add     edi, BytesPerCluster    ; increment buf ptr by one cluster

    pop     ecx                     ; restore remaining sector count
    pop     eax                     ; restore starting VBN

    cmp     ecx, 0                  ; are we done?
    jg      RMS$Again               ; repeat until desired == 0


    RESTORE_ALL
    ret

ReadMftSectors endp


;****************************************************************************
;
;   ReadFrs - Read an FRS
;
;   ENTRY:  EAX == FRS number
;           ES:EDI == Target buffer
;
;   USES:  none (preserves all registers with SAVE_ALL/RESTORE_ALL)
;
ReadFrs proc near

    SAVE_ALL

    mul     SectorsPerFrs       ; eax = sector number in MFT DATA attribute
                                ; (note that mul wipes out edx!)

    mov     ecx, SectorsPerFrs  ; number of sectors to read

    call    ReadMftSectors
    call    MultiSectorFixup

    RESTORE_ALL
    ret

ReadFrs endp

;****************************************************************************
;
;   ReadIndexBlock - read an index block from the root index.
;
;   ENTRY:  EAX == Block number
;
;   USES:  none (preserves all registers with SAVE_ALL/RESTORE_ALL)
;
ReadIndexBlock proc near

    SAVE_ALL

    mul     SectorsPerIndexBlock        ; eax = first VBN to read
                                        ; (note that mul wipes out edx!)
    mov     ebx, IndexAllocation        ; ebx -> $INDEX_ALLOCATION attribute
    mov     ecx, SectorsPerIndexBlock   ; ecx == Sectors to read

    push    ds
    pop     es
    mov     edi, IndexBlockBuffer       ; es:edi -> index block buffer

    call    ReadIndexBlockSectors
    call    MultiSectorFixup

    RESTORE_ALL
    ret

ReadIndexBlock endp

;****************************************************************************
;
;   IsBlockInUse - Checks the index bitmap to see if an index
;                  allocation block is in use.
;
;   ENTRY:  EAX == block number
;
;   EXIT:   Carry flag clear if block is in use
;           Carry flag set   if block is not in use.
;
IsBlockInUse proc near

        push    eax
        push    ebx
        push    ecx

        mov     ebx, IndexBitmapBuffer

        mov     ecx, eax    ; ecx = block number
        shr     eax, 3      ; eax = byte number
        and     ecx, 7      ; ecx = bit number in byte

        add     ebx, eax    ; ebx -> byte to test

        mov     eax, 1
        shl     eax, cl     ; eax = mask

        test    byte ptr[ebx], al

        jz      IBU10

        clc                 ; Block is not in use.
        jmp     IBU20

IBU10:  stc                 ; Block is in use.

IBU20:
        pop     ecx
        pop     ebx
        pop     eax         ; restore registers

        ret

IsBlockInUse endp

;****************************************************************************
;
;   ComputeLcn - Converts a VCN into an LCN
;
;   ENTRY:  EAX -> VCN
;           EBX -> Attribute
;
;   EXIT:   EAX -> LCN  (zero indicates not found)
;           ECX -> Remaining run length
;
;   USES:   ALL.
;
ComputeLcn proc near

        cmp     [ebx].ATTR_FormCode, NONRESIDENT_FORM
        je      clcn10

        sub     eax, eax    ; This is a resident attribute.
        ret

clcn10: lea     esi, [ebx].ATTR_FormUnion   ; esi -> nonresident info of attrib

;       eax -> VCN
;       ebx -> Attribute
;       esi -> Nonresident information of attribute record
;
;       See if the desired VCN is in range.

        mov     edx, [esi].NONRES_HighestVcn.LowPart ; edx = HighestVcn
        cmp     eax, edx
        ja      clcn15      ; VCN is greater than HighestVcn

        mov     edx, [esi].NONRES_LowestVcn.LowPart ; edx = LowestVcn
        cmp     eax, edx
        jae     clcn20

clcn15:
        sub     eax, eax    ; VCN is not in range
        ret

clcn20:
;       eax -> VCN
;       ebx -> Attribute
;       esi -> Nonresident information of attribute record
;       edx -> LowestVcn
;
        add     bx, [esi].NONRES_MappingPairOffset  ; ebx -> mapping pairs
        sub     esi, esi                            ; esi = 0

clcn30:
;       eax == VCN to find
;       ebx -> Current mapping pair count byte
;       edx == Current VCN
;       esi == Current LCN
;
        cmp     byte ptr[ebx], 0    ; if count byte is zero...
        je      clcn99              ;  ... we\'re done (and didn\'t find it)

;       Update CurrentLcn
;
        call    LcnFromMappingPair
        add     esi, ecx            ; esi = current lcn for this mapping pair

        call    VcnFromMappingPair

;       eax == VCN to find
;       ebx -> Current mapping pair count byte
;       ecx == DeltaVcn for current mapping pair
;       edx == Current VCN
;       esi == Current LCN
;
        add     ecx, edx            ; ecx = NextVcn

        cmp     eax, ecx            ; If target < NextVcn ...
        jl      clcn80              ;   ... we found the right mapping pair.

;       Go on to next mapping pair.
;
        mov     edx, ecx            ; CurrentVcn = NextVcn

        push    eax

        movzx   ecx, byte ptr[ebx]  ; ecx = count byte
        mov     eax, ecx            ; eax = count byte
        and     eax, 0fh            ; eax = number of vcn bytes
        shr     ecx, 4              ; ecx = number of lcn bytes

        add     ebx, ecx
        add     ebx, eax
        inc     ebx                 ; ebx -> next count byte

        pop     eax
        jmp     clcn30

clcn80:
;       We found the mapping pair we want.
;
;       eax == target VCN
;       ebx -> mapping pair count byte
;       edx == Starting VCN of run
;       ecx == Next VCN (ie. start of next run)
;       esi == starting LCN of run
;
        sub     ecx, eax            ; ecx = remaining run length
        sub     eax, edx            ; eax = offset into run
        add     eax, esi            ; eax = LCN to return

        ret

;       The target VCN is not in this attribute.

clcn99: sub     eax, eax    ; Not found.
        ret


ComputeLcn endp

;****************************************************************************
;
;   VcnFromMappingPair
;
;   ENTRY:  EBX -> Mapping Pair count byte
;
;   EXIT:   ECX == DeltaVcn from mapping pair
;
;   USES:   ECX
;
VcnFromMappingPair proc near

        sub     ecx, ecx            ; ecx = 0
        mov     cl, byte ptr[ebx]   ; ecx = count byte
        and     cl, 0fh             ; ecx = v

        cmp     ecx, 0              ; if ecx is zero, volume is corrupt.
        jne     VFMP5

        sub     ecx, ecx
        ret

VFMP5:
        push    ebx
        push    edx

        add     ebx, ecx            ; ebx -> last byte of compressed vcn

        movsx   edx, byte ptr[ebx]
        dec     ecx
        dec     ebx

;       ebx -> Next byte to add in
;       ecx == Number of bytes remaining
;       edx == Accumulated value
;
VFMP10: cmp     ecx, 0              ; When ecx == 0, we\'re done.
        je      VFMP20

        shl     edx, 8
        mov     dl, byte ptr[ebx]

        dec     ebx                 ; Back up through bytes to process.
        dec     ecx                 ; One less byte to process.

        jmp     VFMP10

VFMP20:
;       edx == Accumulated value to return

        mov     ecx, edx

        pop     edx
        pop     ebx

        ret

VcnFromMappingPair endp


;****************************************************************************
;
;   LcnFromMappingPair
;
;   ENTRY:  EBX -> Mapping Pair count byte
;
;   EXIT:   ECX == DeltaLcn from mapping pair
;
;   USES:   ECX
;
LcnFromMappingPair proc near

        push    ebx
        push    edx

        sub     edx, edx            ; edx = 0
        mov     dl, byte ptr[ebx]   ; edx = count byte
        and     edx, 0fh            ; edx = v

        sub     ecx, ecx            ; ecx = 0
        mov     cl, byte ptr[ebx]   ; ecx = count byte
        shr     cl, 4               ; ecx = l

        cmp     ecx, 0              ; if ecx is zero, volume is corrupt.
        jne     LFMP5

        sub     ecx, ecx

        pop     edx
        pop     ebx
        ret

LFMP5:
;       ebx -> count byte
;       ecx == l
;       edx == v
;

        add     ebx, edx            ; ebx -> last byte of compressed vcn
        add     ebx, ecx            ; ebx -> last byte of compressed lcn

        movsx   edx, byte ptr[ebx]
        dec     ecx
        dec     ebx

;       ebx -> Next byte to add in
;       ecx == Number of bytes remaining
;       edx == Accumulated value
;
LFMP10: cmp     ecx, 0              ; When ecx == 0, we\'re done.
        je      LFMP20

        shl     edx, 8
        mov     dl, byte ptr[ebx]

        dec     ebx                 ; Back up through bytes to process.
        dec     ecx                 ; One less byte to process.

        jmp     LFMP10

LFMP20:
;       edx == Accumulated value to return

        mov     ecx, edx

        pop     edx
        pop     ebx

        ret

LcnFromMappingPair endp

;****************************************************************************
;
; UpcaseName - Converts the name of the file to all upper-case
;
;       ENTRY:  ESI -> Name
;               ECX -> Length of name
;
;       USES:   none
;
UpcaseName proc   near


        or      ecx, ecx
        jnz     UN5

        ret

UN5:
        push    ecx
        push    esi

UN10:
        cmp     word ptr[esi], \'a\'      ; if it\'s less than \'a\'
        jl      UN20                    ; leave it alone

        cmp     word ptr[esi], \'z\'      ; if it\'s greater than \'z\'
        jg      UN20                    ; leave it alone.

        sub     word ptr[esi], \'a\'-\'A\'  ; the letter is lower-case--convert it.
UN20:
        add     esi, 2                  ; move on to next unicode character
        loop    UN10

        pop     esi
        pop     ecx

        ret
UpcaseName endp

;****************************************************************************
;
;   FindFile - Locates the index entry for a file in the root index.
;
;   ENTRY:  EAX -> name to find
;           ECX == length of file name in characters
;
;   EXIT:   EAX -> Index Entry.  NULL to indicate failure.
;
;   USES:   ALL
;
FindFile proc near

        push    eax     ; name address
        push    ecx     ; name length

;       First, search the index root.
;
;       eax -> name to find
;       ecx == name length
;       TOS == name length
;       TOS+4 -> name to find
;
        mov     edx, eax                    ; edx -> name to find
        mov     eax, IndexRoot              ; eax -> &INDEX_ROOT attribute
        lea     ebx, [eax].ATTR_FormUnion   ; ebx -> resident info
        add     ax, [ebx].RES_ValueOffset   ; eax -> Index Root value

        lea     eax, [eax].IR_IndexHeader   ; eax -> Index Header

        mov     ebx, edx                    ; ebx -> name to find

        call    LocateIndexEntry

        or      eax, eax
        jz      FindFile20

;       Found it in the root!  The result is already in eax.
;       Clean up the stack and return.
;
        pop     ecx
        pop     ecx
        ret

FindFile20:
;
;       We didn\'t find the index entry we want in the root, so we have to
;       crawl through the index allocation buffers.
;
;       TOS == name length
;       TOS+4 -> name to find
;
        mov     eax, IndexAllocation
        or      eax, eax
        jnz     FindFile30

;       There is no index allocation attribute; clean up
;       the stack and return failure.
;
        pop     ecx
        pop     ecx
        xor     eax, eax
        ret

FindFile30:
;
;       Search the index allocation blocks for the name we want.
;       Instead of searching in tree order, we\'ll just start with
;       the last one and work our way backwards.
;
;       TOS == name length
;       TOS+4 -> name to find
;
        mov     edx, IndexAllocation        ; edx -> index allocation attr.
        lea     edx, [edx].ATTR_FormUnion   ; edx -> nonresident form info
        mov     eax, [edx].NONRES_HighestVcn.LowPart; eax = HighestVcn
        inc     eax                         ; eax = clusters in attribute

        mov     ebx, BytesPerCluster
        mul     ebx                         ; eax = bytes in attribute

        xor     edx, edx
        div     BytesPerIndexBlock          ; convert bytes to index blocks

        push    eax                         ; number of blocks to process

FindFile40:
;
;       TOS == remaining index blocks to search
;       TOS + 4 == name length
;       TOS + 8 -> name to find
;
        pop     eax         ; eax == number of remaining blocks

        or      eax, eax
        jz      FindFile90

        dec     eax         ; eax == number of next block to process
                            ;        and number of remaining blocks

        push    eax

;       eax == block number to process
;       TOS == remaining index blocks to search
;       TOS + 4 == name length
;       TOS + 8 -> name to find
;
;       See if the block is in use; if not, go on to next.

        call    IsBlockInUse
        jc      FindFile40      ; c set if not in use

;       eax == block number to process
;       TOS == remaining index blocks to search
;       TOS + 4 == name length
;       TOS + 8 -> name to find
;

        call    ReadIndexBlock

        pop     edx         ; edx == remaining buffers to search
        pop     ecx         ; ecx == name length
        pop     ebx         ; ebx -> name

        push    ebx
        push    ecx
        push    edx

;       ebx -> name to find
;       ecx == name length in characters
;       TOS == remaining blocks to process
;       TOS + 4 == name length
;       TOS + 8 -> name
;
;       Index buffer to search is in index allocation block buffer.
;
        mov     eax, IndexBlockBuffer       ; eax -> Index allocation block
        lea     eax, [eax].IB_IndexHeader   ; eax -> Index Header

        call    LocateIndexEntry            ; eax -> found entry

        or      eax, eax
        jz      FindFile40

;       Found it!
;
;       eax -> Found entry
;       TOS == remaining blocks to process
;       TOS + 4 == name length
;       TOS + 8 -> name
;
        pop     ecx
        pop     ecx
        pop     ecx ; clean up stack
        ret

FindFile90:
;
;       Name not found.
;
;       TOS == name length
;       TOS + 4 -> name to find
;
        pop     ecx
        pop     ecx         ; clean
cheng
游客

返回顶部