Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3096 lines
89 KiB

  1. page ,132
  2. title ntfsboot - NTFS boot loader
  3. name ntfsboot
  4. ; The ROM in the IBM PC starts the boot process by performing a hardware
  5. ; initialization and a verification of all external devices. If all goes
  6. ; well, it will then load from the boot drive the sector from track 0, head 0,
  7. ; sector 1. This sector is placed at physical address 07C00h.
  8. ;
  9. ; The boot code's sole resposiblity is to find NTLDR, load it at
  10. ; address 2000:0000, and then jump to it.
  11. ;
  12. ; The boot code understands the structure of the NTFS root directory,
  13. ; and is capable of reading files. There is no contiguity restriction.
  14. ;
  15. ; DEBUG EQU 1
  16. MASM equ 1
  17. .xlist
  18. .286
  19. A_DEFINED EQU 1
  20. include ntfs.inc
  21. DoubleWord struc
  22. lsw dw ?
  23. msw dw ?
  24. DoubleWord ends
  25. ;
  26. ; The following are various segments used by the boot loader. The first
  27. ; two are the segments where the boot sector is initially loaded and where
  28. ; the boot sector is relocated to. The third is the static location
  29. ; where the NTLDR is loaded.
  30. ;
  31. BootSeg segment at 07c0h ; this is where the MBR loads us initially.
  32. BootSeg ends
  33. NewSeg segment at 0d00h ; this is where we'll relocate to.
  34. NewSeg ends ; enough for 16 boot sectors +
  35. ; 4-sector scratch
  36. ; below where we'll load NTLDR.
  37. LdrSeg segment at 2000h ; we want to load the loader at 2000:0000
  38. LdrSeg ends
  39. ;/********************** START OF SPECIFICATIONS ************************/
  40. ;/* */
  41. ;/* SUBROUTINE NAME: ntfsboot */
  42. ;/* */
  43. ;/* DESCRIPTIVE NAME: Bootstrap loader */
  44. ;/* */
  45. ;/* FUNCTION: To load NTLDR into memory. */
  46. ;/* */
  47. ;/* NOTES: ntfsboot is loaded by the ROM BIOS (Int 19H) at */
  48. ;/* physical memory location 0000:7C00H. */
  49. ;/* ntfsboot runs in real mode. */
  50. ;/* This boot record is for NTFS volumes only. */
  51. ;/* */
  52. ;/* ENTRY POINT: ntfsboot */
  53. ;/* LINKAGE: Jump (far) from Int 19H */
  54. ;/* */
  55. ;/* INPUT: CS:IP = 0000:7C00H */
  56. ;/* SS:SP = 0030:00FAH (CBIOS dependent) */
  57. ;/* */
  58. ;/* EXIT-NORMAL: DL = INT 13 drive number we booted from */
  59. ;/* Jmp to main in NTLDR */
  60. ;/* */
  61. ;/* EXIT-ERROR: None */
  62. ;/* */
  63. ;/* EFFECTS: NTLDR is loaded into the physical memory */
  64. ;/* location 00020000H */
  65. ;/* */
  66. ;/*********************** END OF SPECIFICATIONS *************************/
  67. BootCode segment ;would like to use BootSeg here, but LINK flips its lid
  68. assume cs:BootCode,ds:nothing,es:nothing,ss:nothing
  69. org 0 ; start at beginning of segment, not 0100h.
  70. public _ntfsboot
  71. _ntfsboot label near
  72. jmp start
  73. .errnz ($-_ntfsboot) GT (3) ;<FATAL PROBLEM: JMP is more than three bytes>
  74. org 3
  75. Version db "NTFS " ; Signature, must be "NTFS "
  76. BPB label byte
  77. BytesPerSector dw 512 ; Size of a physical sector
  78. SectorsPerCluster db 1 ; Sectors per allocation unit
  79. ;
  80. ; Traditionally the next 7 bytes were the reserved sector count, fat count,
  81. ; root dir entry count, and the small volume sector count. However all of
  82. ; these fields must be 0 on NTFS volumes.
  83. ;
  84. ; We use this space to store some temporary variables used by the boot code,
  85. ; which avoids the need for separate space in sector 0 to store them.
  86. ; We also take advantage of the free 0-initialization to save some space
  87. ; by avoiding the code to initialize them.
  88. ;
  89. ; Note that ideally we'd want to use an unused field for the SectorCount
  90. ; and initialize it to 16. This would let us save a few bytes by avoiding
  91. ; code to explicitly initialize this value before we read the 16 boot sectors.
  92. ; However setup and other code tends to preserve the entire bpb area when
  93. ; it updates boot code, so we avoid a dependency here and initialize
  94. ; the value explicitly to 16 in the first part of the boot code.
  95. ;
  96. SectorCount dw 0 ; number of sectors to read
  97. SectorBase dd 0 ; start sector for read request
  98. HaveXInt13 db 0 ; extended int13 available flag
  99. Media db 0f8h ; Media byte
  100. FatSectors dw 0 ; (always 0 on NTFS)
  101. SectorsPerTrack dw 0 ; Sectors per track
  102. Heads dw 0 ; Number of surfaces
  103. HiddenSectors dd 0 ; partition start LBA
  104. ;
  105. ; The field below is traditionally the large sector count and is
  106. ; always 0 on NTFS. We use it here for a value the boot code calculates,
  107. ; namely the number of sectors visible on the drive via conventional int13.
  108. ;
  109. Int13Sectors dd 0
  110. DriveNumber db 80h ; int13 unit number
  111. db 3 dup (?) ; alignment filler
  112. ;
  113. ; The following is the rest of the NTFS Sector Zero information.
  114. ; The offsets of most of these fields cannot be changed without changing
  115. ; all code that validates, formats, recognizes, etc, NTFS volumes.
  116. ; In other words, don't change it.
  117. ;
  118. SectorsOnVolume db (size LARGE_INTEGER) dup (?)
  119. MftStartLcn db (size LARGE_INTEGER) dup (?)
  120. Mft2StartLcn db (size LARGE_INTEGER) dup (?)
  121. ClustersPerFrs dd ?
  122. DefClustersPerBuf dd ?
  123. SerialNumber db (size LARGE_INTEGER) dup (?)
  124. CheckSum dd ?
  125. ;
  126. ; Make sure size of fields matches what fs_rec.sys thinks is should be
  127. ;
  128. .errnz ($-_ntfsboot) NE (54h)
  129. ;****************************************************************************
  130. start:
  131. ;
  132. ; First of all, set up the segments we need (stack and data).
  133. ;
  134. cli
  135. xor ax,ax ; Set up the stack to just before
  136. mov ss,ax ; this code. It'll be moved after
  137. mov sp,7c00h ; we relocate.
  138. sti
  139. mov ax,Bootseg ; Address BPB with DS.
  140. mov ds,ax
  141. assume ds:BootCode
  142. call Int13SecCnt ; determine range of regular int13
  143. ;
  144. ; Read bootcode and jump to code in sector 1.
  145. ; Assumes HaveXInt13 and SectorBase are initialized to 0,
  146. ; which they are since they are stored in areas of the BPB
  147. ; that must be 0 on NTFS volumes.
  148. ;
  149. mov ax,NewSeg
  150. mov es,ax
  151. xor bx,bx ; es:bx = transfer address
  152. mov byte ptr SectorCount,16 ; word field is 0 on-disk so byte init is OK
  153. call ReadSectors
  154. push NewSeg
  155. push offset mainboot
  156. retf
  157. ;
  158. ; Determine the number of sectors addressable via
  159. ; conventional int13. If we can't get drive params for some reason
  160. ; then something is very wrong -- we'll try to force the caller
  161. ; to use conventional int13 by maxing out the sector count.
  162. ;
  163. ; Input: ds addresses start of first sector
  164. ;
  165. ; Output: in eax; also stored in Int13Sectors variable.
  166. ;
  167. ; Preserves: assume none.
  168. ;
  169. Int13SecCnt proc near
  170. mov dl,DriveNumber ; int13 unit number
  171. mov ah,8 ; get drive params
  172. int 13h ; call BIOS
  173. jnc @f ; no error, procede
  174. mov cx,-1 ; strange case, fake registers to force
  175. mov dh,cl ; use of standard int13 (set all vals to max)
  176. @@:
  177. .386
  178. movzx eax,dh ; eax = max head # (0-255)
  179. inc ax ; eax = heads (1-256)
  180. movzx edx,cl ; edx = sectors per track + cyl bits
  181. and dl,3fh ; edx = sectors per track (1-63)
  182. mul dx ; eax = sectors per cylinder, edx = 0
  183. xchg cl,ch
  184. shr ch,6 ; cx = max cylinder # (0-1023)
  185. inc cx ; cx = cylinders (1-1024)
  186. movzx ecx,cx ; ecx = cylinders (1-1024)
  187. mul ecx ; eax = sectors visible via int13, edx = 0
  188. mov Int13Sectors,eax ; save # sectors addressable via int13
  189. .286
  190. ret
  191. Int13SecCnt endp
  192. ;
  193. ; Determine whether extended int13 services are available on the boot drive.
  194. ;
  195. ; Stores result (boolean) in HaveXInt13 variable.
  196. ;
  197. ; Preserves: assume none.
  198. ;
  199. HaveInt13Ext proc near
  200. mov ah,41h
  201. mov bx,055aah
  202. mov dl,DriveNumber
  203. int 13h
  204. jc @f ; error from int13 means no xint13
  205. cmp bx,0aa55h ; absence of sig means no xint13
  206. jne @f
  207. test cl,1 ; bit 0 off means no xint13
  208. jz @f
  209. inc byte ptr HaveXInt13 ; xint13 is available
  210. @@: ret
  211. HaveInt13Ext endp
  212. ;
  213. ; Read SectorCount sectors starting at logical sector SectorBase,
  214. ; into es:bx, using extended int13 if necessary.
  215. ;
  216. ; Preserves all
  217. ;
  218. ReadSectors proc near
  219. .386
  220. pushad ; save registers
  221. push ds
  222. push es
  223. read_loop:
  224. mov eax,SectorBase ; logical starting sector
  225. add eax,HiddenSectors ; eax = physical starting sector
  226. cmp eax,Int13Sectors ; determine if standard int13 is ok
  227. jb stdint13
  228. push ds ; preserve ds
  229. db 66h ; hand-coded 32-bit push of 8-bit immediate
  230. push 0 ; high 32 bits of sector #
  231. push eax ; low 32 bits of sector #
  232. push es
  233. push bx ; transfer address
  234. push dword ptr 10010h ; transfer 1 sector, packet size = 16
  235. cmp byte ptr HaveXInt13,0
  236. jne @f ; already know we have xint13 available
  237. call HaveInt13Ext ; see if we have it
  238. cmp byte ptr HaveXInt13,0
  239. je BootErr$he ; need it but don't have it
  240. @@: mov ah,42h ; extended read
  241. mov dl,DriveNumber ; dl = int13 unit #
  242. push ss
  243. pop ds
  244. mov si,sp ; ds:si -> param packet
  245. int 13h
  246. pop eax ; throw away first 4 bytes of param packet
  247. pop bx ; restore es:bx from param packet
  248. pop es
  249. pop eax ; throw away last 8 bytes of param packet
  250. pop eax ; without clobbering carry flag
  251. pop ds ; restore ds
  252. jmp short did_read
  253. stdint13:
  254. xor edx,edx ; edx:eax = absolute sector number
  255. movzx ecx,SectorsPerTrack ; ecx = sectors per track
  256. div ecx ; eax = track, edx = sector within track (0-62)
  257. inc dl ; dl = sector within track (1-63)
  258. mov cl,dl ; cl = sector within track
  259. mov edx,eax
  260. shr edx,16 ; dx:ax = track
  261. div Heads ; ax = cylinder (0-1023), dx = head (0-255)
  262. xchg dl,dh ; dh = head
  263. mov dl,DriveNumber ; dl = int13 unit #
  264. mov ch,al ; ch = bits 0-7 of cylinder
  265. shl ah,6
  266. or cl,ah ; bits 6-7 of cl = bits 8-9 of cylinder
  267. mov ax,201h ; read 1 sector
  268. int 13h
  269. did_read:
  270. jc BootErr$he
  271. read_next:
  272. mov ax,es ; advance transfer address
  273. add ax,20h ; by moving segment register along
  274. mov es,ax ; thus no 64K limit on transfer length
  275. inc SectorBase ; advance sector number
  276. dec SectorCount ; see if done
  277. jnz read_loop ; not done
  278. pop es
  279. pop ds
  280. popad ; restore registers
  281. .286
  282. ret
  283. ReadSectors endp
  284. BootErr$he:
  285. mov al,byte ptr TXT_MSG_SYSINIT_BOOT_ERROR
  286. BootErr2:
  287. call BootErr$print
  288. mov al,byte ptr TXT_MSG_SYSINIT_REBOOT
  289. call BootErr$print
  290. sti
  291. jmp $ ; Wait forever
  292. BootErr$print:
  293. ;
  294. ; al is offset - 256 of message. Adjust to form real offset
  295. ; and stick in si so lodsb below will work.
  296. ;
  297. mov ah,1
  298. mov si,ax
  299. BootErr$print1:
  300. lodsb ; Get next character
  301. cmp al,0
  302. je BootErr$Done
  303. mov ah,14 ; Write teletype
  304. mov bx,7 ; Attribute
  305. int 10h ; Print it
  306. jmp BootErr$print1
  307. BootErr$Done:
  308. ret
  309. ;****************************************************************************
  310. ;
  311. ; Message table.
  312. ;
  313. ; We put English messages here as a placeholder only, so that in case
  314. ; anyone uses bootntfs.h without patching new messages in, things will
  315. ; still be correct (in English, but at least functional).
  316. ;
  317. MSG_SYSINIT_BOOT_ERROR:
  318. DB 13,10,'A disk read error occurred',0
  319. MSG_SYSINIT_FILE_NOT_FD:
  320. DB 13,10,'NTLDR is missing',0
  321. MSG_SYSINIT_NTLDR_CMPRS:
  322. DB 13,10,'NTLDR is compressed',0
  323. MSG_SYSINIT_REBOOT:
  324. DB 13,10,'Press Ctrl+Alt+Del to restart',13,10,0
  325. ;
  326. ; Now build a table with the low byte of the offset to each message.
  327. ; Code that patches the boot sector messages updates this table.
  328. ;
  329. .errnz ($-_ntfsboot) GT (512-8)
  330. ORG 512 - 8
  331. TXT_MSG_SYSINIT_BOOT_ERROR:
  332. db OFFSET (MSG_SYSINIT_BOOT_ERROR - _ntfsboot) - 256
  333. TXT_MSG_SYSINIT_FILE_NOT_FD:
  334. db OFFSET (MSG_SYSINIT_FILE_NOT_FD - _ntfsboot) - 256
  335. TXT_MSG_SYSINIT_NTLDR_CMPRS:
  336. db OFFSET (MSG_SYSINIT_NTLDR_CMPRS - _ntfsboot) - 256
  337. TXT_MSG_SYSINIT_REBOOT:
  338. db OFFSET (MSG_SYSINIT_REBOOT - _ntfsboot) - 256
  339. .errnz ($-_ntfsboot) NE (512-4)
  340. db 0,0,55h,0aah
  341. ; Name we look for. ntldr_length is the number of characters,
  342. ; ntldr_name is the name itself. Note that it is not NULL
  343. ; terminated, and doesn't need to be.
  344. ;
  345. ntldr_name_length dw 5
  346. ntldr_name dw 'N', 'T', 'L', 'D', 'R'
  347. ; Predefined name for index-related attributes associated with an
  348. ; index over $FILE_NAME
  349. ;
  350. index_name_length dw 4
  351. index_name dw '$', 'I', '3', '0'
  352. ; Global variables. These offsets are all relative to NewSeg.
  353. ;
  354. AttrList dd 0e000h ; Offset of buffer to hold attribute list
  355. MftFrs dd 3000h ; Offset of general scratch buffer for FRS
  356. SegmentsInMft dd ? ; number of FRS's with MFT Data attribute records
  357. RootIndexFrs dd ? ; Offset of Root Index FRS
  358. AllocationIndexFrs dd ? ; Offset of Allocation Index FRS ; KPeery
  359. BitmapIndexFrs dd ? ; Offset of Bitmap Index FRS ; KPeery
  360. IndexRoot dd ? ; Offset of Root Index $INDEX_ROOT attribute
  361. IndexAllocation dd ? ; Offset of Root Index $INDEX_ALLOCATION attribute
  362. IndexBitmap dd ? ; Offset of Root Index $BITMAP attribute
  363. NtldrFrs dd ? ; Offset of NTLDR FRS
  364. NtldrData dd ? ; Offset of NTLDR $DATA attribute
  365. IndexBlockBuffer dd ? ; Offset of current index buffer
  366. IndexBitmapBuffer dd ? ; Offset of index bitmap buffer
  367. NextBuffer dd ? ; Offset of next free byte in buffer space
  368. BytesPerCluster dd ? ; Bytes per cluster
  369. BytesPerFrs dd ? ; Bytes per File Record Segment
  370. ;
  371. ; For floppyless booting, winnt32.exe creates c:\$win_nt$.~bt\bootsec.dat and
  372. ; places an entry in boot.ini for it (the boot selection says something
  373. ; like "Windows NT Setup or Upgrade"). When that is selected, the boot loader
  374. ; loads 16 sectors worth of data from bootsect.dat into d000 (which is where
  375. ; the first sector of this code would have loaded it) and jumps into it at
  376. ; a known location of 256h. That was correct in earlier versions of NT
  377. ; but is not correct now because the 4 fields below were added to this sector.
  378. ;
  379. ; Note that 0000 is "add [bx+si],al" which because of the way the boot loader
  380. ; is written happens to be a benign add of 0 to something in segment 7c0,
  381. ; which doesn't seem to hose anything but is still somewhat random.
  382. ;
  383. ; We code in a jump here so as this new code proliferates we get this
  384. ; cleaned up.
  385. ;
  386. .errnz $-_ntfsboot ne 256h
  387. SectorsPerFrs label dword ; Sectors per File Record Segment
  388. jmp short mainboot
  389. nop
  390. nop
  391. .errnz $-_ntfsboot ne 25ah
  392. MftLcnFrs dd ? ; Offset scratch FRS buffer for LookupMftLcn
  393. BytesPerIndexBlock dd ? ; Bytes per index alloc block in root index
  394. ClustersPerIndexBlock dd ? ; Clusters per index alloc block in root index
  395. SectorsPerIndexBlock dd ? ; Sectors per index block in root index
  396. .386
  397. SAVE_ALL macro
  398. push es
  399. push ds
  400. pushad
  401. endm
  402. RESTORE_ALL macro
  403. popad
  404. nop
  405. pop ds
  406. pop es
  407. endm
  408. ;****************************************************************************
  409. ;
  410. ; mainboot - entry point after 16 boot sectors have been read in
  411. ;
  412. ;
  413. mainboot proc far
  414. ; Get the new ds and the new stack. Note that ss is zero.
  415. ;
  416. mov ax, cs ; Set DS to CS
  417. mov ds, ax
  418. shl ax, 4 ; convert to an offset.
  419. cli
  420. mov sp, ax ; load new stack, just before boot code.
  421. sti
  422. ;
  423. ; Reinitialize xint13-related variables
  424. ;
  425. call Int13SecCnt ; determine range of regular int13
  426. ; Set up the FRS buffers. The MFT buffer is in a fixed
  427. ; location, and the other three come right after it. The
  428. ; buffer for index allocation blocks comes after that.
  429. ;
  430. ; Compute the useful constants associated with the volume
  431. ;
  432. movzx eax, BytesPerSector ; eax = Bytes per Sector
  433. movzx ebx, SectorsPerCluster ; ebx = Sectors Per Cluster
  434. mul ebx ; eax = Bytes per Cluster
  435. mov BytesPerCluster, eax
  436. mov ecx, ClustersPerFrs ; ecx = clusters per frs
  437. cmp cl, 0 ; is ClustersPerFrs less than zero?
  438. jg mainboot$1
  439. ; If the ClustersPerFrs field is negative, we calculate the number
  440. ; of bytes per FRS by negating the value and using that as a shif count.
  441. ;
  442. neg cl
  443. mov eax, 1
  444. shl eax, cl ; eax = bytes per frs
  445. jmp mainboot$2
  446. mainboot$1:
  447. ; Otherwise if ClustersPerFrs was positive, we multiply by bytes
  448. ; per cluster.
  449. mov eax, BytesPerCluster
  450. mul ecx ; eax = bytes per frs
  451. mainboot$2:
  452. mov BytesPerFrs, eax
  453. movzx ebx, BytesPerSector
  454. xor edx, edx ; zero high part of dividend
  455. div ebx ; eax = sectors per frs
  456. mov SectorsPerFrs, eax
  457. ; Set up the MFT FRS's---this will read all the $DATA attribute
  458. ; records for the MFT.
  459. ;
  460. call SetupMft
  461. ; Set up the remaining FRS buffers. The RootIndex FRS comes
  462. ; directly after the last MFT FRS, followed by the NTLdr FRS
  463. ; and the Index Block buffer.
  464. ;
  465. mov ecx, NextBuffer
  466. mov RootIndexFrs, ecx
  467. add ecx, BytesPerFrs ; AllocationFrs may be different
  468. mov AllocationIndexFrs, ecx ; from RootIndexFrs - KPeery
  469. add ecx, BytesPerFrs ; BitmapFrs may be different
  470. mov BitmapIndexFrs, ecx ; from RootIndexFrs - KPeery
  471. add ecx, BytesPerFrs
  472. mov NtldrFrs, ecx
  473. add ecx, BytesPerFrs
  474. mov IndexBlockBuffer, ecx
  475. ;
  476. ; Read the root index, allocation index and bitmap FRS's and locate
  477. ; the interesting attributes.
  478. ;
  479. mov eax, $INDEX_ROOT
  480. mov ecx, RootIndexFrs
  481. call LoadIndexFrs
  482. or eax, eax
  483. jz BootErr$he
  484. mov IndexRoot, eax ; offset in Frs buffer
  485. mov eax, $INDEX_ALLOCATION ; Attribute type code
  486. mov ecx, AllocationIndexFrs ; FRS to search
  487. call LoadIndexFrs
  488. mov IndexAllocation, eax
  489. mov eax, $BITMAP ; Attribute type code
  490. mov ecx, BitmapIndexFrs ; FRS to search
  491. call LoadIndexFrs
  492. mov IndexBitmap, eax
  493. ; Consistency check: the index root must exist, and it
  494. ; must be resident.
  495. ;
  496. mov eax, IndexRoot
  497. or eax, eax
  498. jz BootErr$he
  499. cmp [eax].ATTR_FormCode, RESIDENT_FORM
  500. jne BootErr$he
  501. ; Determine the size of the index allocation buffer based
  502. ; on information in the $INDEX_ROOT attribute. The index
  503. ; bitmap buffer comes immediately after the index block buffer.
  504. ;
  505. ; eax -> $INDEX_ROOT attribute record
  506. ;
  507. lea edx, [eax].ATTR_FormUnion ; edx -> resident info
  508. add ax, [edx].RES_ValueOffset ; eax -> value of $INDEX_ROOT
  509. movzx ecx, [eax].IR_ClustersPerBuffer
  510. mov ClustersPerIndexBlock, ecx
  511. mov ecx, [eax].IR_BytesPerBuffer
  512. mov BytesPerIndexBlock, ecx
  513. mov eax, BytesPerIndexBlock
  514. movzx ecx, BytesPerSector
  515. xor edx, edx
  516. div ecx ; eax = sectors per index block
  517. mov SectorsPerIndexBlock, eax
  518. mov eax, IndexBlockBuffer
  519. add eax, BytesPerIndexBlock
  520. mov IndexBitmapBuffer, eax
  521. ; Next consistency check: if the $INDEX_ALLOCATION attribute
  522. ; exists, the $INDEX_BITMAP attribute must also exist.
  523. ;
  524. cmp IndexAllocation, 0
  525. je mainboot30
  526. cmp IndexBitmap, 0 ; since IndexAllocation exists, the
  527. je BootErr$he ; bitmap must exist, too.
  528. ; Since the bitmap exists, we need to read it into the bitmap
  529. ; buffer. If it's resident, we can just copy the data.
  530. ;
  531. mov ebx, IndexBitmap ; ebx -> index bitmap attribute
  532. push ds
  533. pop es
  534. mov edi, IndexBitmapBuffer ; es:edi -> index bitmap buffer
  535. mov eax, BitmapIndexFrs ; FRS containing attribute
  536. call ReadWholeAttribute
  537. mainboot30:
  538. ;
  539. ; OK, we've got the index-related attributes.
  540. ;
  541. movzx ecx, ntldr_name_length ; ecx = name length in characters
  542. mov eax, offset ntldr_name ; eax -> name
  543. call FindFile
  544. or eax, eax
  545. jz BootErr$fnf
  546. ; Read the FRS for NTLDR and find its data attribute.
  547. ;
  548. ; eax -> Index Entry for NTLDR.
  549. ;
  550. mov eax, [eax].IE_FileReference.REF_LowPart
  551. push ds
  552. pop es ; es:edi = target buffer
  553. mov edi, NtldrFrs
  554. call ReadFrs
  555. ;
  556. ; See if the loader FRS has an attribute list.
  557. ;
  558. mov eax, NtldrFrs ; FRS buffer
  559. mov ebx, $ATTRIBUTE_LIST ; Type code
  560. mov ecx, 0 ; Attribute name length
  561. mov edx, 0 ; Attribute name
  562. call LocateAttributeRecord
  563. test eax, eax ; Check for attribute list.
  564. jnz mainboot$AttribList ; If attribute list present, use it.
  565. ;
  566. ; No attribute list was found. Just search the base FRS.
  567. ;
  568. mov eax, NtldrFrs ; pointer to FRS
  569. mov ebx, $DATA ; requested attribute type
  570. mov ecx, 0 ; attribute name length in characters
  571. mov edx, 0 ; attribute name (NULL if none)
  572. call LocateAttributeRecord
  573. ; eax -> $DATA attribute for NTLDR
  574. ;
  575. or eax, eax ; if eax is zero, attribute not found.
  576. jnz mainboot$FoundData
  577. jmp BootErr$fnf ; Data was not found.
  578. mainboot$AttribList:
  579. ;
  580. ; The ntldr $DATA segment is fragmented. Search the attribute list
  581. ; for the $DATA member. And load it from there.
  582. ;
  583. xor edx, edx ; VCN zero
  584. mov ecx, $DATA ; Attribute type code
  585. mov eax, NtldrFrs ; FRS to search
  586. call SearchAttrList ; search attribute list for FRN
  587. ; of specified ($DATA)
  588. or eax, eax ; if eax is zero, attribute not found.
  589. jz BootErr$fnf
  590. ;
  591. ; We found the FRN of the $DATA attribute; load that into memory.
  592. ;
  593. push ds
  594. pop es ; es:edi = target buffer
  595. mov edi, NtldrFrs
  596. call ReadFrs
  597. ;
  598. ; Determine the beginning offset of the $DATA in the FRS
  599. ;
  600. mov eax, NtldrFrs ; pointer to FRS
  601. mov ebx, $DATA ; requested attribute type
  602. mov ecx, 0 ; attribute name length in characters
  603. mov edx, 0 ; attribute name (NULL if none)
  604. call LocateAttributeRecord
  605. ; eax -> $DATA attribute for NTLDR
  606. ;
  607. or eax, eax ; if eax is zero, attribute not found.
  608. jz BootErr$fnf
  609. mainboot$FoundData:
  610. ; Get the attribute record header flags, and make sure none of the
  611. ; `compressed' bits are set
  612. movzx ebx, [eax].ATTR_Flags
  613. and ebx, ATTRIBUTE_FLAG_COMPRESSION_MASK
  614. jnz BootErr$ntc
  615. mov ebx, eax ; ebx -> $DATA attribute for NTLDR
  616. push LdrSeg
  617. pop es ; es = segment addres to read into
  618. sub edi, edi ; es:edi = buffer address
  619. mov eax, NtldrFrs ; FRS containing attribute
  620. call ReadWholeAttribute
  621. ;
  622. ; We've loaded NTLDR--jump to it.
  623. ;
  624. ; Before we go to NTLDR, set up the registers the way it wants them:
  625. ; DL = INT 13 drive number we booted from
  626. ;
  627. mov dl, DriveNumber
  628. mov ax,1000
  629. mov es, ax ; we don't really need this
  630. lea si, BPB
  631. sub ax,ax
  632. push LdrSeg
  633. push ax
  634. retf ; "return" to NTLDR.
  635. mainboot endp
  636. .386
  637. ;****************************************************************************
  638. ;
  639. ; ReadClusters - Reads a run of clusters from the disk.
  640. ;
  641. ; ENTRY: eax == LCN to read
  642. ; edx == clusters to read
  643. ; es:edi -> Target buffer
  644. ;
  645. ; USES: none (preserves all registers)
  646. ;
  647. ReadClusters proc near
  648. SAVE_ALL
  649. mov ebx, edx ; ebx = clusters to read.
  650. movzx ecx, SectorsPerCluster ; ecx = cluster factor
  651. mul ecx ; Convert LCN to sectors (wipes out edx!)
  652. mov SectorBase, eax ; Store starting sector in SectorBase
  653. mov eax, ebx ; eax = number of clusters
  654. mul ecx ; Convert EAX to sectors (wipes out edx!)
  655. mov SectorCount, ax ; Store number of sectors in SectorCount
  656. ;
  657. ; Note that ReadClusters gets its target buffer in es:edi but calls
  658. ; the ReadSectors worker function that takes a target in es:bx--we need
  659. ; to normalize es:edi so that we don't overflow bx.
  660. ;
  661. mov bx, di
  662. and bx, 0Fh
  663. mov ax, es
  664. shr edi, 4
  665. add ax, di ; ax:bx -> target buffer
  666. push ax
  667. pop es ; es:bx -> target buffer
  668. call ReadSectors
  669. RESTORE_ALL
  670. ret
  671. ReadClusters endp
  672. ;
  673. ;****************************************************************************
  674. ;
  675. ; LocateAttributeRecord -- Find an attribute record in an FRS.
  676. ;
  677. ; ENTRY: EAX -- pointer to FRS
  678. ; EBX -- desired attribute type code
  679. ; ECX -- length of attribute name in characters
  680. ; EDX -- pointer to attribute name
  681. ;
  682. ; EXIT: EAX points at attribute record (0 indicates not found)
  683. ;
  684. ; USES: All
  685. ;
  686. LocateAttributeRecord proc near
  687. ; get the first attribute record.
  688. ;
  689. add ax, word ptr[eax].FRS_FirstAttribute
  690. ; eax -> next attribute record to investigate.
  691. ; ebx == desired type
  692. ; ecx == name length
  693. ; edx -> pointer to name
  694. ;
  695. lar10:
  696. cmp [eax].ATTR_TypeCode, 0ffffffffh
  697. je lar99
  698. cmp dword ptr[eax].ATTR_TypeCode, ebx
  699. jne lar80
  700. ; this record is a potential match. Compare the names:
  701. ;
  702. ; eax -> candidate record
  703. ; ebx == desired type
  704. ; ecx == name length
  705. ; edx -> pointer to name
  706. ;
  707. or ecx, ecx ; Did the caller pass in a name length?
  708. jnz lar20
  709. ; We want an attribute with no name--the current record is
  710. ; a match if and only if it has no name.
  711. ;
  712. cmp [eax].ATTR_NameLength, 0
  713. jne lar80 ; Not a match.
  714. ; It's a match, and eax is set up correctly, so return.
  715. ;
  716. ret
  717. ; We want a named attribute.
  718. ;
  719. ; eax -> candidate record
  720. ; ebx == desired type
  721. ; ecx == name length
  722. ; edx -> pointer to name
  723. ;
  724. lar20:
  725. cmp cl, [eax].ATTR_NameLength
  726. jne lar80 ; Not a match.
  727. ; Convert name in current record to uppercase.
  728. ;
  729. mov esi, eax
  730. add si, word ptr[eax].ATTR_NameOffset
  731. call UpcaseName
  732. ; eax -> candidate record
  733. ; ebx == desired type
  734. ; ecx == name length
  735. ; edx -> pointer to name
  736. ; esi -> Name in current record (upcased)
  737. ;
  738. push ecx ; save cx
  739. push ds ; Copy data segment into es
  740. pop es
  741. mov edi, edx ; note that esi is already set up.
  742. repe cmpsw ; zero flag is set if equal
  743. pop ecx ; restore cx
  744. jnz lar80 ; not a match
  745. ; eax points at a matching record.
  746. ;
  747. ret
  748. ;
  749. ; This record doesn't match; go on to the next.
  750. ;
  751. ; eax -> rejected candidate attribute record
  752. ; ebx == desired type
  753. ; ecx == Name length
  754. ; edx -> desired name
  755. ;
  756. lar80: cmp [eax].ATTR_RecordLength, 0 ; if the record length is zero
  757. je lar99 ; the FRS is corrupt.
  758. add eax, [eax].ATTR_RecordLength; Go to next record
  759. jmp lar10 ; and try again
  760. ; Didn't find it.
  761. ;
  762. lar99: sub eax, eax
  763. ret
  764. LocateAttributeRecord endp
  765. ;****************************************************************************
  766. ;
  767. ; LocateIndexEntry -- Find an index entry in a file name index
  768. ;
  769. ; ENTRY: EAX -> pointer to index header
  770. ; EBX -> file name to find
  771. ; ECX == length of file name in characters
  772. ;
  773. ; EXIT: EAX points at index entry. NULL to indicate failure.
  774. ;
  775. ; USES: All
  776. ;
  777. LocateIndexEntry proc near
  778. ; Convert the input name to upper-case
  779. ;
  780. mov esi, ebx
  781. call UpcaseName
  782. ifdef DEBUG
  783. call PrintName
  784. call Debug2
  785. endif ; DEBUG
  786. add eax, [eax].IH_FirstIndexEntry
  787. ; EAX -> current entry
  788. ; EBX -> file name to find
  789. ; ECX == length of file name in characters
  790. ;
  791. lie10: test [eax].IE_Flags, INDEX_ENTRY_END ; Is it the end entry?
  792. jnz lie99
  793. lea edx, [eax].IE_Value ; edx -> FILE_NAME attribute value
  794. ifdef DEBUG
  795. ; DEBUG CODE -- list file names as they are examined
  796. SAVE_ALL
  797. call Debug3
  798. movzx ecx, [edx].FN_FileNameLength ; ecx = chars in name
  799. lea esi, [edx].FN_FileName ; esi -> name
  800. call PrintName
  801. RESTORE_ALL
  802. endif ; DEBUG
  803. ; EAX -> current entry
  804. ; EBX -> file name to find
  805. ; ECX == length of file name in characters
  806. ; EDX -> FILE_NAME attribute
  807. cmp cl, [edx].FN_FileNameLength ; Is name the right length?
  808. jne lie80
  809. lea esi, [edx].FN_FileName ; Get name from FILE_NAME structure
  810. call UpcaseName
  811. push ecx ; save ecx
  812. push ds
  813. pop es ; copy data segment into es for cmpsw
  814. mov edi, ebx ; edi->search name (esi already set up)
  815. repe cmpsw ; zero flag is set if they're equal
  816. pop ecx ; restore ecx
  817. jnz lie80
  818. ; the current entry matches the search name, and eax points at it.
  819. ;
  820. ret
  821. ; The current entry is not a match--get the next one.
  822. ; EAX -> current entry
  823. ; EBX -> file name to find
  824. ; ECX == length of file name in characters
  825. ;
  826. lie80: cmp [eax].IE_Length, 0 ; If the entry length is zero
  827. je lie99 ; then the index block is corrupt.
  828. add ax, [eax].IE_Length ; Get the next entry.
  829. jmp lie10
  830. ; Name not found in this block. Set eax to zero and return
  831. ;
  832. lie99: xor eax, eax
  833. ret
  834. LocateIndexEntry endp
  835. ;****************************************************************************
  836. ;
  837. ; ReadWholeAttribute - Read an entire attribute value
  838. ;
  839. ; ENTRY: ebx -> attribute
  840. ; es:edi -> target buffer
  841. ; eax -> FRS containing attribute (NULL means use highest VCN in this attribute record)
  842. ;
  843. ; USES: ALL
  844. ;
  845. ReadWholeAttribute proc near
  846. cmp [ebx].ATTR_FormCode, RESIDENT_FORM
  847. jne rwa10
  848. ; The attribute is resident.
  849. ; ebx -> attribute
  850. ; es:edi -> target buffer
  851. ;
  852. SAVE_ALL
  853. lea edx, [ebx].ATTR_FormUnion ; edx -> resident form info
  854. mov ecx, [edx].RES_ValueLength ; ecx = bytes in value
  855. mov esi, ebx ; esi -> attribute
  856. add si, [edx].RES_ValueOffset ; esi -> attribute value
  857. rep movsb ; copy bytes from value to buffer
  858. RESTORE_ALL
  859. ret ; That's all!
  860. rwa10:
  861. ;
  862. ; The attribute type is non-resident. Just call
  863. ; ReadNonresidentAttribute starting at VCN 0 and
  864. ; asking for the whole thing.
  865. ;
  866. ; ebx -> attribute
  867. ; es:edi -> target buffer
  868. ;
  869. push eax ; Save FRS.
  870. lea edx, [ebx].ATTR_FormUnion ; edx -> nonresident form info
  871. test eax, eax ; See if caller gave us an FRS.
  872. jnz rwa20 ; If FRS was provided, calculate the highest VCN.
  873. mov ecx, [edx].NONRES_HighestVcn.LowPart; ecx = HighestVcn
  874. inc ecx ; ecx = clusters in attribute
  875. jmp rwa30
  876. rwa20:
  877. mov eax, [edx].NONRES_AllocatedLength.REF_LowPart ; Size of attribute in bytes
  878. xor edx, edx ; Zero high order of dividend.
  879. div BytesPerCluster ; Highest VCN
  880. mov ecx, eax
  881. rwa30:
  882. sub eax, eax ; eax = 0 (first VCN to read)
  883. pop esi ; Restore FRS.
  884. call ReadNonresidentAttribute
  885. ret
  886. ReadWholeAttribute endp
  887. ;****************************************************************************
  888. ;
  889. ; ReadNonresidentAttribute - Read clusters from a nonresident attribute
  890. ;
  891. ; ENTRY: EAX == First VCN to read
  892. ; EBX -> Attribute
  893. ; ECX == Number of clusters to read
  894. ; ESI == FRS contianing attribute (can be NULL if requested data is in passed in attribute)
  895. ; ES:EDI == Target of read
  896. ;
  897. ; EXIT: None.
  898. ;
  899. ; USES: None (preserves all registers with SAVE_ALL/RESTORE_ALL)
  900. ;
  901. ReadNonresidentAttribute proc near
  902. SAVE_ALL
  903. cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
  904. je ReadNR10
  905. ; This attribute is not resident--the disk is corrupt.
  906. jmp BootErr$he
  907. ReadNR10:
  908. ; eax == Next VCN to read
  909. ; ebx -> Attribute
  910. ; ecx -> Remaining clusters to read
  911. ; es:edi -> Target of read
  912. ;
  913. cmp ecx, 0
  914. jne ReadNR20
  915. ; Nothing left to read--return success.
  916. ;
  917. RESTORE_ALL
  918. ret
  919. ReadNR20:
  920. push ebx ; pointer to attribute
  921. push eax ; Current VCN
  922. push ecx
  923. push esi
  924. push edi
  925. push es
  926. call ComputeLcn ; eax = LCN to read, ecx = run length
  927. mov edx, ecx ; edx = remaining run length
  928. pop es
  929. pop edi
  930. pop esi
  931. pop ecx
  932. test eax, eax ; See if it found the LCN.
  933. jz ReadNR40 ; If not found, get the next piece of the attribute.
  934. ; eax == LCN to read
  935. ; ecx == remaining clusters to read
  936. ; edx == remaining clusters in current run
  937. ; es:edi == Target of read
  938. ; TOS == Current VCN
  939. ; TOS + 4 == pointer to attribute
  940. ;
  941. cmp ecx, edx
  942. jge ReadNR30
  943. ; Run length is greater than remaining request; only read
  944. ; remaining request.
  945. ;
  946. mov edx, ecx ; edx = Remaining request
  947. ReadNR30:
  948. ; eax == LCN to read
  949. ; ecx == remaining clusters to read
  950. ; edx == clusters to read in current run
  951. ; es:edi == Target of read
  952. ; TOS == Current VCN
  953. ; TOS + == pointer to attribute
  954. ;
  955. call ReadClusters
  956. sub ecx, edx ; Decrement clusters remaining in request
  957. mov ebx, edx ; ebx = clusters read
  958. mov eax, edx ; eax = clusters read
  959. movzx edx, SectorsPerCluster
  960. mul edx ; eax = sectors read (wipes out edx!)
  961. movzx edx, BytesPerSector
  962. mul edx ; eax = bytes read (wipes out edx!)
  963. add edi, eax ; Update target of read
  964. pop eax ; eax = previous VCN
  965. add eax, ebx ; update VCN to read
  966. pop ebx ; ebx -> attribute
  967. jmp ReadNR10
  968. ReadNR40:
  969. ;
  970. ; Get the next chunk of the attribute from the attribute list. If
  971. ; there are no more chunks, something is wrong.
  972. ;
  973. test esi, esi ; Make sure we have an FRS buffer.
  974. jz BootErr$he ; If not, something is wrong.
  975. push ecx ; Save remaining clusters.
  976. push edi ; Save read buffer.
  977. push es ; Save read buffer segment.
  978. ;
  979. ; As soon as we read in a new FRS, the attribute we currently have is
  980. ; useless. So, save off the information we need to the stack.
  981. ; First save off the name, if there is one.
  982. ;
  983. movzx eax, [ebx].ATTR_NameLength ; Get the name length
  984. test eax, eax ; See if there is a name.
  985. jz ReadNR50 ; If no name, store NULL for the name length and buffer.
  986. shl eax, 1 ; Double the length, since it's unicode.
  987. sub esp, eax ; Reserve space for the name.
  988. mov edi, esp ; Setup the destination buffer.
  989. push esp ; Store the name buffer.
  990. push esi ; Save the FRS buffer.
  991. movzx esi, [ebx].ATTR_NameOffset ; Setup the source buffer.
  992. add esi, ebx ; It is at an offset within the attribute.
  993. mov ecx, eax ; Setup the length.
  994. rep movsb ; Do the copy.
  995. pop esi ; Restore the FRS buffer.
  996. jmp ReadNR60
  997. ReadNR50:
  998. push eax ; Name buffer (NULL, since the length is zero.)
  999. ReadNR60:
  1000. ;
  1001. ; The name buffer is done. Now save the name length, type code, and highest VCN.
  1002. ;
  1003. push eax ; Name length
  1004. mov eax, [ebx].ATTR_TypeCode
  1005. push eax
  1006. mov eax, [ebx].ATTR_FormUnion.NONRES_HighestVcn.LowPart
  1007. push eax
  1008. ;
  1009. ; At this point our stack looks like this:
  1010. ;
  1011. ; TOS = Highest VCN of attribute
  1012. ; TOS + 4 = Type code of attribute
  1013. ; TOS + 8 = Length of attriubte name (in bytes)
  1014. ; TOS + c = Pointer to attribute name buffer
  1015. ; TOS + 10 = Name buffer (if needed)
  1016. ; TOS + ? = Buffer segment
  1017. ; TOS + ? = Buffer address
  1018. ; TOS + ? = Remaining clusters
  1019. ; TOS + ? = Current VCB
  1020. ; TOS + ? = Current attribute
  1021. ;
  1022. ;
  1023. ; See if this is the base FRS. If not, get the base FRS.
  1024. ;
  1025. mov edx, [esi].FRS_BaseFRS.REF_LowPart ; Get the base FRS number.
  1026. test edx, edx ; See if this is the base FRS.
  1027. jz ReadNR70
  1028. ;
  1029. ; Read in the base FRS.
  1030. ;
  1031. mov edi, esi ; FRS buffer
  1032. push ds
  1033. pop es
  1034. mov eax, edx ; FRS number
  1035. call ReadFrs ; Read in the base FRS.
  1036. ReadNR70:
  1037. ;
  1038. ; The base FRS is in our buffer. Find the next chunk of the attribute.
  1039. ;
  1040. mov eax, esi ; Frs Buffer
  1041. pop edx ; Highest VCN of current attribute
  1042. pop ecx ; Type code
  1043. inc edx ; VCN we will look for
  1044. push ecx ; Save type code.
  1045. push esi ; Save FRS buffer.
  1046. call SearchAttrList
  1047. test eax, eax ; If we didn't get back an FRS number, something is wrong.
  1048. jz BootErr$he
  1049. pop esi ; Restore FRS buffer.
  1050. pop ecx ; Restore type code.
  1051. ; EAX contains FRS number
  1052. mov edi, esi ; FRS buffer
  1053. push ds
  1054. pop es
  1055. call ReadFrs ; Read in the FRS.
  1056. ;
  1057. ; The FRS containing the next chunk is in memory. Find the next chunk and
  1058. ; continue processing.
  1059. ;
  1060. mov eax, esi ; FRS buffer
  1061. mov ebx, ecx ; Type code
  1062. pop ecx ; Attribute name length (in bytes)
  1063. pop edx ; Attribute name
  1064. push ecx ; Save attribute name length (in bytes).
  1065. push esi ; Save FRS buffer.
  1066. shr ecx, 1 ; Convert name length back to characters.
  1067. call LocateAttributeRecord
  1068. test eax, eax ; Make sure we found the attribute.
  1069. jz BootErr$he ; If not, something is wrong.
  1070. pop esi ; Restore FRS buffer.
  1071. pop ecx ; Restore attribute name length (in bytes).
  1072. add esp, ecx ; Give back storage for name buffer.
  1073. pop es ; Restore read buffer segment.
  1074. pop edi ; Restore read buffer.
  1075. pop ecx ; Restore remaining clusters.
  1076. ;
  1077. ; Switch the attribute chunk being looked at and continue.
  1078. ;
  1079. mov edx, eax ; Hold new attribute temporarily.
  1080. pop eax ; Restore current VCN.
  1081. pop ebx ; Restore old attribute.
  1082. mov ebx, edx ; Switch to new attribute.
  1083. jmp ReadNR20 ; Coninue.
  1084. ReadNonresidentAttribute endp
  1085. ;****************************************************************************
  1086. ;
  1087. ; MultiSectorFixup - fixup a structure read off the disk
  1088. ; to reflect Update Sequence Array.
  1089. ;
  1090. ; ENTRY: ES:EDI = Target buffer
  1091. ;
  1092. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1093. ;
  1094. ; Note: ES:EDI must point at a structure which is protected
  1095. ; by an update sequence array, and which begins with
  1096. ; a multi-sector-header structure.
  1097. ;
  1098. MultiSectorFixup proc near
  1099. SAVE_ALL
  1100. movzx ebx, es:[edi].MSH_UpdateArrayOfs ; ebx = update array offset
  1101. movzx ecx, es:[edi].MSH_UpdateArraySize ; ecx = update array size
  1102. or ecx, ecx ; if the size of the update sequence array
  1103. jz BootErr$he ; is zero, this structure is corrupt.
  1104. add ebx, edi ; es:ebx -> update sequence array count word
  1105. add ebx, 2 ; es:ebx -> 1st entry of update array
  1106. add edi, SEQUENCE_NUMBER_STRIDE - 2 ; es:edi->last word of first chunk
  1107. dec ecx ; decrement to reflect count word
  1108. MSF10:
  1109. ; ecx = number of entries remaining in update sequence array
  1110. ; es:ebx -> next entry in update sequence array
  1111. ; es:edi -> next target word for update sequence array
  1112. or ecx, ecx
  1113. jz MSF30
  1114. mov ax, word ptr es:[ebx] ; copy next update sequence array entry
  1115. mov word ptr es:[edi], ax ; to next target word
  1116. add ebx, 2 ; go on to next entry
  1117. add edi, SEQUENCE_NUMBER_STRIDE ; go on to next target
  1118. dec ecx
  1119. jmp MSF10
  1120. MSF30:
  1121. RESTORE_ALL
  1122. ret
  1123. MultiSectorFixup endp
  1124. ;****************************************************************************
  1125. ;
  1126. ; SetupMft - Reads MFT File Record Segments into the LBN array
  1127. ;
  1128. ; ENTRY: none.
  1129. ;
  1130. ; EXIT: NextBuffer is set to the free byte after the last MFT FRS
  1131. ; SegmentsInMft is initialized
  1132. ;
  1133. ;
  1134. SetupMft proc near
  1135. SAVE_ALL
  1136. ; Initialize SegmentsInMft and NextBuffer as if the MFT
  1137. ; had only one FRS.
  1138. ;
  1139. mov eax, 1
  1140. mov SegmentsInMft, eax
  1141. mov eax, MftFrs ; this is the scratch mft buffer
  1142. add eax, BytesPerFrs
  1143. mov MftLcnFrs,eax ; this is the scratch mft buffer for lookup
  1144. add eax, BytesPerFrs
  1145. mov NextBuffer, eax
  1146. ; Read FRS 0 into the first MFT FRS buffer, being sure
  1147. ; to resolve the Update Sequence Array. Remember the physical
  1148. ; location in the Lbn array.
  1149. ;
  1150. mov eax, MftStartLcn.LowPart
  1151. movzx ebx, SectorsPerCluster
  1152. mul ebx ; eax = mft starting sector
  1153. mov ebx, NextBuffer ; Store this location in the Lbn array
  1154. mov [bx], eax
  1155. mov SectorBase, eax ; SectorBase = mft starting sector for read
  1156. add bx, 4
  1157. mov eax, SectorsPerFrs
  1158. mov [bx], eax ; Store the sector count in the Lcn array
  1159. mov SectorCount, ax ; SectorCount = SectorsPerFrs
  1160. add bx, 4
  1161. mov NextBuffer, ebx ; Remember the next Lbn array location
  1162. mov ebx, MftFrs ; Read the sectors into the MftFrs scratch buffer
  1163. push ds
  1164. pop es
  1165. call ReadSectors
  1166. mov edi, ebx ; es:edi = buffer
  1167. call MultiSectorFixup
  1168. ; Determine whether the MFT has an Attribute List attribute
  1169. mov eax, MftFrs
  1170. mov ebx, $ATTRIBUTE_LIST
  1171. mov ecx, 0
  1172. mov edx, 0
  1173. call LocateAttributeRecord
  1174. or eax, eax ; If there's no Attribute list,
  1175. jz SetupMft99 ; we're done!
  1176. ; Read the attribute list.
  1177. ; eax -> attribute list attribute
  1178. ;
  1179. mov ebx, eax ; ebx -> attribute list attribute
  1180. push ds
  1181. pop es ; copy ds into es
  1182. mov edi, AttrList ; ds:edi->attribute list buffer
  1183. xor eax, eax ; FRS Buffer is NULL since attribute lists can only be in one FRS.
  1184. call ReadWholeAttribute
  1185. mov ebx, AttrList ; ebx -> first attribute list entry
  1186. ; Now, traverse the attribute list looking for the first
  1187. ; entry for the $DATA type. We know it must have at least
  1188. ; one.
  1189. ;
  1190. ; ebx -> first attribute list entry
  1191. ;
  1192. SetupMft10:
  1193. cmp [bx].ATTRLIST_TypeCode, $DATA
  1194. je SetupMft30
  1195. add bx,[bx].ATTRLIST_Length
  1196. jmp SetupMft10
  1197. SetupMft20:
  1198. ; Scan forward through the attribute list entries for the
  1199. ; $DATA attribute, reading each referenced FRS. Note that
  1200. ; there will be at least one non-$DATA entry after the entries
  1201. ; for the $DATA attribute, since there's a $BITMAP.
  1202. ;
  1203. ; ebx -> Next attribute list entry
  1204. ; NextBuffer -> Target for next mapping information
  1205. ; MftFrs -> Target of next read
  1206. ; SegmentsInMft == number of MFT segments read so far
  1207. ;
  1208. ; Find the physical sector and sector count for the runs for this
  1209. ; file record (max 2 runs). The mapping for this must already
  1210. ; be in a file record already visited. Find the Vcn and cluster
  1211. ; offset for this FRS. Use LookupMftLcn to find the Lcn.
  1212. push ebx ; Save the current position in the attribute list
  1213. ; Convert from Frs to sectors, then to Vcn
  1214. mov eax, [bx].ATTRLIST_SegmentReference.REF_LowPart
  1215. mul SectorsPerFrs
  1216. push eax ; Remember the VBN
  1217. xor edx, edx
  1218. movzx ebx, SectorsPerCluster
  1219. div ebx ; eax = VCN
  1220. push edx ; save remainder, this is cluster offset
  1221. call ComputeMftLcn ; eax = LCN
  1222. or eax, eax ; LCN equal to zero?
  1223. jz BootErr$he ; zero is not a possible LCN
  1224. mov ecx, SectorsPerFrs ; ecx = Number of sectors remaining for this file record
  1225. ; Change the LCN back into an LBN and add the remainder back in to get
  1226. ; the sector we want to read.
  1227. movzx ebx, SectorsPerCluster
  1228. mul ebx ; eax = cluster first LBN
  1229. pop edx ; edx = sector remainder
  1230. add eax, edx ; eax = desired LBN
  1231. ; Store this in the current Lcn array slot
  1232. mov ebx, NextBuffer
  1233. mov [bx], eax ; Store the starting sector
  1234. add bx, 4
  1235. movzx eax, SectorsPerCluster
  1236. sub eax, edx
  1237. cmp eax, ecx ; Check if we have too many sectors
  1238. jbe SetupMft60
  1239. mov eax, ecx ; Limit ourselves to the sectors remaining
  1240. SetupMft60:
  1241. mov [bx], eax ; Store the sector count
  1242. ; If we have a complete file record skip to process the attribute entry
  1243. SetupMft70:
  1244. sub ecx, eax ; Subtract these sectors from remaining sectors
  1245. pop edx ; Get the previous starting VBN (restores stack also)
  1246. jz SetupMft50
  1247. ; This may be a split file record. Go ahead and get the next piece.
  1248. add eax, edx ; Add the sector count for the last run to the start Vbn for the run
  1249. ; This is the next Vbn to read
  1250. push eax ; Save the Vbn
  1251. xor edx, edx ; Convert to Vcn, there should be no remainder this time
  1252. movzx ebx, SectorsPerCluster
  1253. div ebx ; eax = VCN
  1254. push ecx ; Save the remaining sectors
  1255. call ComputeMftLcn ; eax = LCN
  1256. pop ecx ; Restore the remaining sectors
  1257. or eax, eax ; LCN equal to zero?
  1258. jz BootErr$he ; zero is not a possible LCN
  1259. ; Change the LCN back into a LBN to get the starting sector we want to read.
  1260. movzx ebx, SectorsPerCluster
  1261. mul ebx ; eax = cluster first LBN
  1262. ; If this sector is the contiguous with the other half of the run
  1263. ; make it appear to be single longer run.
  1264. mov ebx, NextBuffer ; Recover the last run
  1265. mov edx, [bx]
  1266. add bx, 4
  1267. add edx, [bx] ; This is the next potential LBN
  1268. cmp edx, eax ; Check if we are at the contiguous LBN
  1269. jne SetupMft80
  1270. ; Append this to the previous run.
  1271. movzx eax, SectorsPerCluster
  1272. cmp eax, ecx ; Check if have more sectors than we need
  1273. jbe SetupMft90
  1274. mov eax, ecx
  1275. SetupMft90:
  1276. add [bx], eax
  1277. jmp SetupMft70 ; Loop to see if there more work to do
  1278. ; This is multiple runs. Update the next entry.
  1279. SetupMft80:
  1280. add bx, 4
  1281. mov NextBuffer, ebx ; advance our NextBuffer pointer
  1282. mov [bx], eax ; fill in the next run start sector
  1283. add bx, 4
  1284. movzx eax, SectorsPerCluster
  1285. cmp eax, ecx ; Check if have more sectors than we need
  1286. jbe SetupMft100
  1287. mov eax, ecx
  1288. SetupMft100:
  1289. mov [bx], eax ; and count
  1290. jmp SetupMft70 ; Loop to see if there is more work to do
  1291. SetupMft50:
  1292. ; Advance the count of Frs segments and the NextBuffer pointer
  1293. add bx, 4
  1294. inc SegmentsInMft
  1295. mov NextBuffer, ebx
  1296. pop ebx
  1297. ; Go on to the next attribute list entry
  1298. SetupMft30:
  1299. add bx,[bx].ATTRLIST_Length
  1300. cmp [bx].ATTRLIST_TypeCode, $DATA
  1301. je SetupMft20
  1302. SetupMft99:
  1303. RESTORE_ALL
  1304. ret
  1305. SetupMft endp
  1306. ;****************************************************************************
  1307. ;
  1308. ; ComputeMftLcn -- Computes the LCN for a cluster of the MFT
  1309. ;
  1310. ;
  1311. ; ENTRY: EAX == VCN
  1312. ;
  1313. ; EXIT: EAX == LCN
  1314. ;
  1315. ; USES: ALL
  1316. ;
  1317. ComputeMftLcn proc near
  1318. mov edx, eax ; edx = VCN
  1319. mov ecx, SegmentsInMft ; ecx = # of FRS's to search
  1320. mov esi,MftLcnFrs
  1321. add esi,BytesPerFrs ; si -> FRS LBN list
  1322. MftLcn10:
  1323. ; ECX == number of remaining FRS's to search
  1324. ; EDX == VCN
  1325. ; EBX == Buffer to read into
  1326. ; ESI == LBN array
  1327. ; EDI == Number of sectors to read
  1328. ;
  1329. push edx ; save VCN
  1330. push ecx ; save MFT segment count
  1331. push edx ; save VCN again
  1332. ; Read the sectors for the given FRS
  1333. mov ebx,MftLcnFrs
  1334. mov edi,SectorsPerFrs
  1335. ; Read these sectors
  1336. MftLcn40:
  1337. mov eax,[si] ; Get the start sector and sector count
  1338. mov SectorBase,eax
  1339. add si,4
  1340. mov eax,[si]
  1341. mov SectorCount,ax
  1342. add si,4
  1343. push ds
  1344. pop es
  1345. call ReadSectors
  1346. ; Check if we have more data to read
  1347. sub edi, eax
  1348. je MftLcn30
  1349. ; Read the next run
  1350. mul BytesPerSector ; move forward in the buffer, results in ax:dx
  1351. add bx,ax
  1352. jmp MftLcn40
  1353. MftLcn30:
  1354. ; Do the multi sector fixup
  1355. mov edi,MftLcnFrs
  1356. push ds
  1357. pop es
  1358. call MultiSectorFixup
  1359. mov eax, MftLcnFrs
  1360. mov ebx, $DATA
  1361. mov ecx, 0
  1362. mov edx, ecx
  1363. call LocateAttributeRecord
  1364. ; EAX -> $DATA attribute
  1365. ; TOS == VCN
  1366. ; TOS + 4 == number of remaining FRS's to search
  1367. ; TOS + 8 -> FRS being searched
  1368. ; TOS +12 == VCN
  1369. or eax, eax
  1370. jz BootErr$he ; No $DATA attribute in this FRS!
  1371. mov ebx, eax ; ebx -> attribute
  1372. pop eax ; eax = VCN
  1373. ; EAX == VCN
  1374. ; EBX -> $DATA attribute
  1375. ; TOS number of remaining FRS's to search
  1376. ; TOS + 4 == FRS being searched
  1377. ; TOS + 8 == VCN
  1378. push esi
  1379. call ComputeLcn
  1380. pop esi
  1381. or eax, eax
  1382. jz MftLcn20
  1383. ; Found our LCN. Clean up the stack and return.
  1384. ;
  1385. ; EAX == LCN
  1386. ; TOS number of remaining FRS's to search
  1387. ; TOS + 4 == FRS being searched
  1388. ; TOS + 8 == VCN
  1389. ;
  1390. pop ebx
  1391. pop ebx ; clean up the stack
  1392. ret
  1393. MftLcn20:
  1394. ;
  1395. ; Didn't find the VCN in this FRS; try the next one.
  1396. ;
  1397. ; TOS number of remaining FRS's to search
  1398. ; TOS + 4 -> FRS being searched
  1399. ; TOS + 8 == VCN
  1400. ;
  1401. pop ecx ; ecx = number of FRS's remaining, including current
  1402. pop edx ; edx = VCN
  1403. loop MftLcn10 ; decrement cx and try next FRS
  1404. ; This VCN was not found.
  1405. ;
  1406. xor eax, eax
  1407. ret
  1408. ComputeMftLcn endp
  1409. ;****************************************************************************
  1410. ;
  1411. ; ReadMftSectors - Read sectors from the MFT
  1412. ;
  1413. ; ENTRY: EAX == starting VBN
  1414. ; ECX == number of sectors to read
  1415. ; ES:EDI == Target buffer
  1416. ;
  1417. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1418. ;
  1419. ReadMftSectors proc near
  1420. SAVE_ALL
  1421. RMS$Again:
  1422. push eax ; save starting VBN
  1423. push ecx ; save sector count
  1424. ; Divide the VBN by SectorsPerCluster to get the VCN
  1425. xor edx, edx ; zero high part of dividend
  1426. movzx ebx, SectorsPerCluster
  1427. div ebx ; eax = VCN
  1428. push edx ; save remainder
  1429. push edi ; save the target buffer
  1430. call ComputeMftLcn ; eax = LCN
  1431. pop edi ; recover the buffer
  1432. or eax, eax ; LCN equal to zero?
  1433. jz BootErr$he ; zero is not a possible LCN
  1434. ; Change the LCN back into a LBN and add the remainder back in to get
  1435. ; the sector we want to read, which goes into SectorBase.
  1436. ;
  1437. movzx ebx, SectorsPerCluster
  1438. mul ebx ; eax = cluster first LBN
  1439. pop edx ; edx = sector remainder
  1440. add eax, edx ; eax = desired LBN
  1441. mov SectorBase, eax
  1442. ;
  1443. ; Figure out how many sectors to read this time; we never attempt
  1444. ; to read more than one cluster at a time.
  1445. ;
  1446. pop ecx ; ecx = sectors to read
  1447. movzx ebx, SectorsPerCluster
  1448. cmp ecx,ebx
  1449. jle RMS10
  1450. ;
  1451. ; Read only a single cluster at a time, to avoid problems with fragmented
  1452. ; runs in the mft.
  1453. ;
  1454. mov SectorCount, bx ; this time read 1 cluster
  1455. sub ecx, ebx ; ecx = sectors remaining to read
  1456. pop eax ; eax = VBN
  1457. add eax, ebx ; VBN += sectors this read
  1458. push eax ; save next VBN
  1459. push ecx ; save remaining sector count
  1460. jmp RMS20
  1461. RMS10:
  1462. pop eax ; eax = VBN
  1463. add eax, ecx ; VBN += sectors this read
  1464. push eax ; save next VBN
  1465. mov SectorCount, cx
  1466. mov ecx, 0
  1467. push ecx ; save remaining sector count (0)
  1468. RMS20:
  1469. ; The target buffer was passed in es:edi, but we want it in es:bx.
  1470. ; Do the conversion.
  1471. ;
  1472. push es ; save buffer pointer
  1473. push edi
  1474. mov bx, di
  1475. and bx, 0Fh
  1476. mov ax, es
  1477. shr edi, 4
  1478. add ax, di ; ax:bx -> target buffer
  1479. push ax
  1480. pop es ; es:bx -> target buffer
  1481. call ReadSectors
  1482. pop edi ; restore buffer pointer
  1483. pop es
  1484. add edi, BytesPerCluster ; increment buf ptr by one cluster
  1485. pop ecx ; restore remaining sector count
  1486. pop eax ; restore starting VBN
  1487. cmp ecx, 0 ; are we done?
  1488. jg RMS$Again ; repeat until desired == 0
  1489. RESTORE_ALL
  1490. ret
  1491. ReadMftSectors endp
  1492. ;****************************************************************************
  1493. ;
  1494. ; ReadFrs - Read an FRS
  1495. ;
  1496. ; ENTRY: EAX == FRS number
  1497. ; ES:EDI == Target buffer
  1498. ;
  1499. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1500. ;
  1501. ReadFrs proc near
  1502. SAVE_ALL
  1503. mul SectorsPerFrs ; eax = sector number in MFT DATA attribute
  1504. ; (note that mul wipes out edx!)
  1505. mov ecx, SectorsPerFrs ; number of sectors to read
  1506. call ReadMftSectors
  1507. call MultiSectorFixup
  1508. RESTORE_ALL
  1509. ret
  1510. ReadFrs endp
  1511. ;****************************************************************************
  1512. ;
  1513. ; ReadIndexBlock - read an index block from the root index.
  1514. ;
  1515. ; ENTRY: EAX == Block number
  1516. ;
  1517. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1518. ;
  1519. ReadIndexBlock proc near
  1520. SAVE_ALL
  1521. mul ClustersPerIndexBlock ; First VCN to read
  1522. ; (note that mul wipes out edx!)
  1523. mov ebx, IndexAllocation ; ebx -> $INDEX_ALLOCATION attribute
  1524. mov ecx, ClustersPerIndexBlock ; Clusters to read
  1525. mov esi, AllocationIndexFrs ; FRS containing index allocation attribute
  1526. push ds
  1527. pop es
  1528. mov edi, IndexBlockBuffer ; es:edi -> index block buffer
  1529. call ReadNonresidentAttribute
  1530. call MultiSectorFixup
  1531. RESTORE_ALL
  1532. ret
  1533. ReadIndexBlock endp
  1534. ;****************************************************************************
  1535. ;
  1536. ; IsBlockInUse - Checks the index bitmap to see if an index
  1537. ; allocation block is in use.
  1538. ;
  1539. ; ENTRY: EAX == block number
  1540. ;
  1541. ; EXIT: Carry flag clear if block is in use
  1542. ; Carry flag set if block is not in use.
  1543. ;
  1544. IsBlockInUse proc near
  1545. push eax
  1546. push ebx
  1547. push ecx
  1548. mov ebx, IndexBitmapBuffer
  1549. mov ecx, eax ; ecx = block number
  1550. shr eax, 3 ; eax = byte number
  1551. and ecx, 7 ; ecx = bit number in byte
  1552. add ebx, eax ; ebx -> byte to test
  1553. mov eax, 1
  1554. shl eax, cl ; eax = mask
  1555. test byte ptr[ebx], al
  1556. jz IBU10
  1557. clc ; Block is not in use.
  1558. jmp IBU20
  1559. IBU10: stc ; Block is in use.
  1560. IBU20:
  1561. pop ecx
  1562. pop ebx
  1563. pop eax ; restore registers
  1564. ret
  1565. IsBlockInUse endp
  1566. ;****************************************************************************
  1567. ;
  1568. ; ComputeLcn - Converts a VCN into an LCN
  1569. ;
  1570. ; ENTRY: EAX -> VCN
  1571. ; EBX -> Attribute
  1572. ;
  1573. ; EXIT: EAX -> LCN (zero indicates not found)
  1574. ; ECX -> Remaining run length
  1575. ;
  1576. ; USES: ALL.
  1577. ;
  1578. ComputeLcn proc near
  1579. cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
  1580. je clcn10
  1581. sub eax, eax ; This is a resident attribute.
  1582. ret
  1583. clcn10: lea esi, [ebx].ATTR_FormUnion ; esi -> nonresident info of attrib
  1584. ; eax -> VCN
  1585. ; ebx -> Attribute
  1586. ; esi -> Nonresident information of attribute record
  1587. ;
  1588. ; See if the desired VCN is in range.
  1589. mov edx, [esi].NONRES_HighestVcn.LowPart ; edx = HighestVcn
  1590. cmp eax, edx
  1591. ja clcn15 ; VCN is greater than HighestVcn
  1592. mov edx, [esi].NONRES_LowestVcn.LowPart ; edx = LowestVcn
  1593. cmp eax, edx
  1594. jae clcn20
  1595. clcn15:
  1596. sub eax, eax ; VCN is not in range
  1597. ret
  1598. clcn20:
  1599. ; eax -> VCN
  1600. ; ebx -> Attribute
  1601. ; esi -> Nonresident information of attribute record
  1602. ; edx -> LowestVcn
  1603. ;
  1604. add bx, [esi].NONRES_MappingPairOffset ; ebx -> mapping pairs
  1605. sub esi, esi ; esi = 0
  1606. clcn30:
  1607. ; eax == VCN to find
  1608. ; ebx -> Current mapping pair count byte
  1609. ; edx == Current VCN
  1610. ; esi == Current LCN
  1611. ;
  1612. cmp byte ptr[ebx], 0 ; if count byte is zero...
  1613. je clcn99 ; ... we're done (and didn't find it)
  1614. ; Update CurrentLcn
  1615. ;
  1616. call LcnFromMappingPair
  1617. add esi, ecx ; esi = current lcn for this mapping pair
  1618. call VcnFromMappingPair
  1619. ; eax == VCN to find
  1620. ; ebx -> Current mapping pair count byte
  1621. ; ecx == DeltaVcn for current mapping pair
  1622. ; edx == Current VCN
  1623. ; esi == Current LCN
  1624. ;
  1625. add ecx, edx ; ecx = NextVcn
  1626. cmp eax, ecx ; If target < NextVcn ...
  1627. jl clcn80 ; ... we found the right mapping pair.
  1628. ; Go on to next mapping pair.
  1629. ;
  1630. mov edx, ecx ; CurrentVcn = NextVcn
  1631. push eax
  1632. movzx ecx, byte ptr[ebx] ; ecx = count byte
  1633. mov eax, ecx ; eax = count byte
  1634. and eax, 0fh ; eax = number of vcn bytes
  1635. shr ecx, 4 ; ecx = number of lcn bytes
  1636. add ebx, ecx
  1637. add ebx, eax
  1638. inc ebx ; ebx -> next count byte
  1639. pop eax
  1640. jmp clcn30
  1641. clcn80:
  1642. ; We found the mapping pair we want.
  1643. ;
  1644. ; eax == target VCN
  1645. ; ebx -> mapping pair count byte
  1646. ; edx == Starting VCN of run
  1647. ; ecx == Next VCN (ie. start of next run)
  1648. ; esi == starting LCN of run
  1649. ;
  1650. sub ecx, eax ; ecx = remaining run length
  1651. sub eax, edx ; eax = offset into run
  1652. add eax, esi ; eax = LCN to return
  1653. ret
  1654. ; The target VCN is not in this attribute.
  1655. clcn99: sub eax, eax ; Not found.
  1656. ret
  1657. ComputeLcn endp
  1658. ;****************************************************************************
  1659. ;
  1660. ; VcnFromMappingPair
  1661. ;
  1662. ; ENTRY: EBX -> Mapping Pair count byte
  1663. ;
  1664. ; EXIT: ECX == DeltaVcn from mapping pair
  1665. ;
  1666. ; USES: ECX
  1667. ;
  1668. VcnFromMappingPair proc near
  1669. sub ecx, ecx ; ecx = 0
  1670. mov cl, byte ptr[ebx] ; ecx = count byte
  1671. and cl, 0fh ; ecx = v
  1672. cmp ecx, 0 ; if ecx is zero, volume is corrupt.
  1673. jne VFMP5
  1674. sub ecx, ecx
  1675. ret
  1676. VFMP5:
  1677. push ebx
  1678. push edx
  1679. add ebx, ecx ; ebx -> last byte of compressed vcn
  1680. movsx edx, byte ptr[ebx]
  1681. dec ecx
  1682. dec ebx
  1683. ; ebx -> Next byte to add in
  1684. ; ecx == Number of bytes remaining
  1685. ; edx == Accumulated value
  1686. ;
  1687. VFMP10: cmp ecx, 0 ; When ecx == 0, we're done.
  1688. je VFMP20
  1689. shl edx, 8
  1690. mov dl, byte ptr[ebx]
  1691. dec ebx ; Back up through bytes to process.
  1692. dec ecx ; One less byte to process.
  1693. jmp VFMP10
  1694. VFMP20:
  1695. ; edx == Accumulated value to return
  1696. mov ecx, edx
  1697. pop edx
  1698. pop ebx
  1699. ret
  1700. VcnFromMappingPair endp
  1701. ;****************************************************************************
  1702. ;
  1703. ; LcnFromMappingPair
  1704. ;
  1705. ; ENTRY: EBX -> Mapping Pair count byte
  1706. ;
  1707. ; EXIT: ECX == DeltaLcn from mapping pair
  1708. ;
  1709. ; USES: ECX
  1710. ;
  1711. LcnFromMappingPair proc near
  1712. push ebx
  1713. push edx
  1714. sub edx, edx ; edx = 0
  1715. mov dl, byte ptr[ebx] ; edx = count byte
  1716. and edx, 0fh ; edx = v
  1717. sub ecx, ecx ; ecx = 0
  1718. mov cl, byte ptr[ebx] ; ecx = count byte
  1719. shr cl, 4 ; ecx = l
  1720. cmp ecx, 0 ; if ecx is zero, volume is corrupt.
  1721. jne LFMP5
  1722. sub ecx, ecx
  1723. pop edx
  1724. pop ebx
  1725. ret
  1726. LFMP5:
  1727. ; ebx -> count byte
  1728. ; ecx == l
  1729. ; edx == v
  1730. ;
  1731. add ebx, edx ; ebx -> last byte of compressed vcn
  1732. add ebx, ecx ; ebx -> last byte of compressed lcn
  1733. movsx edx, byte ptr[ebx]
  1734. dec ecx
  1735. dec ebx
  1736. ; ebx -> Next byte to add in
  1737. ; ecx == Number of bytes remaining
  1738. ; edx == Accumulated value
  1739. ;
  1740. LFMP10: cmp ecx, 0 ; When ecx == 0, we're done.
  1741. je LFMP20
  1742. shl edx, 8
  1743. mov dl, byte ptr[ebx]
  1744. dec ebx ; Back up through bytes to process.
  1745. dec ecx ; One less byte to process.
  1746. jmp LFMP10
  1747. LFMP20:
  1748. ; edx == Accumulated value to return
  1749. mov ecx, edx
  1750. pop edx
  1751. pop ebx
  1752. ret
  1753. LcnFromMappingPair endp
  1754. ;****************************************************************************
  1755. ;
  1756. ; UpcaseName - Converts the name of the file to all upper-case
  1757. ;
  1758. ; ENTRY: ESI -> Name
  1759. ; ECX -> Length of name
  1760. ;
  1761. ; USES: none
  1762. ;
  1763. UpcaseName proc near
  1764. or ecx, ecx
  1765. jnz UN5
  1766. ret
  1767. UN5:
  1768. push ecx
  1769. push esi
  1770. UN10:
  1771. cmp word ptr[esi], 'a' ; if it's less than 'a'
  1772. jl UN20 ; leave it alone
  1773. cmp word ptr[esi], 'z' ; if it's greater than 'z'
  1774. jg UN20 ; leave it alone.
  1775. sub word ptr[esi], 'a'-'A' ; the letter is lower-case--convert it.
  1776. UN20:
  1777. add esi, 2 ; move on to next unicode character
  1778. loop UN10
  1779. pop esi
  1780. pop ecx
  1781. ret
  1782. UpcaseName endp
  1783. ;****************************************************************************
  1784. ;
  1785. ; FindFile - Locates the index entry for a file in the root index.
  1786. ;
  1787. ; ENTRY: EAX -> name to find
  1788. ; ECX == length of file name in characters
  1789. ;
  1790. ; EXIT: EAX -> Index Entry. NULL to indicate failure.
  1791. ;
  1792. ; USES: ALL
  1793. ;
  1794. FindFile proc near
  1795. push eax ; name address
  1796. push ecx ; name length
  1797. ; First, search the index root.
  1798. ;
  1799. ; eax -> name to find
  1800. ; ecx == name length
  1801. ; TOS == name length
  1802. ; TOS+4 -> name to find
  1803. ;
  1804. mov edx, eax ; edx -> name to find
  1805. mov eax, IndexRoot ; eax -> &INDEX_ROOT attribute
  1806. lea ebx, [eax].ATTR_FormUnion ; ebx -> resident info
  1807. add ax, [ebx].RES_ValueOffset ; eax -> Index Root value
  1808. lea eax, [eax].IR_IndexHeader ; eax -> Index Header
  1809. mov ebx, edx ; ebx -> name to find
  1810. call LocateIndexEntry
  1811. or eax, eax
  1812. jz FindFile20
  1813. ; Found it in the root! The result is already in eax.
  1814. ; Clean up the stack and return.
  1815. ;
  1816. pop ecx
  1817. pop ecx
  1818. ret
  1819. FindFile20:
  1820. ;
  1821. ; We didn't find the index entry we want in the root, so we have to
  1822. ; crawl through the index allocation buffers.
  1823. ;
  1824. ; TOS == name length
  1825. ; TOS+4 -> name to find
  1826. ;
  1827. mov eax, IndexAllocation
  1828. or eax, eax
  1829. jnz FindFile30
  1830. ; There is no index allocation attribute; clean up
  1831. ; the stack and return failure.
  1832. ;
  1833. pop ecx
  1834. pop ecx
  1835. xor eax, eax
  1836. ret
  1837. FindFile30:
  1838. ;
  1839. ; Search the index allocation blocks for the name we want.
  1840. ; Instead of searching in tree order, we'll just start with
  1841. ; the first one and work our way forwards.
  1842. ;
  1843. ; TOS == name length
  1844. ; TOS+4 -> name to find
  1845. ;
  1846. mov edx, IndexAllocation ; edx -> index allocation attr.
  1847. lea edx, [edx].ATTR_FormUnion ; edx -> nonresident form info
  1848. mov eax, [edx].NONRES_AllocatedLength.LowPart; Size of attribute in bytes
  1849. xor edx, edx
  1850. div BytesPerIndexBlock ; convert bytes to index blocks
  1851. xor esi, esi ; Start at block zero.
  1852. push eax ; number of blocks to process
  1853. push esi ; Current block
  1854. FindFile40:
  1855. ;
  1856. ; TOS == Next block to search
  1857. ; TOS + 4 == Total blocks to search
  1858. ; TOS + 8 == name length
  1859. ; TOS + c -> name to find
  1860. ;
  1861. pop eax ; Block to process
  1862. pop esi ; Total blocks
  1863. cmp eax, esi
  1864. je FindFile90
  1865. push esi ; Save Total.
  1866. inc eax ; Save the next block to the stack,
  1867. push eax ; but keep using the current block.
  1868. dec eax
  1869. ; eax == block number to process
  1870. ; TOS == Next block to search
  1871. ; TOS + 4 == Total blocks to search
  1872. ; TOS + 8 == name length
  1873. ; TOS + c -> name to find
  1874. ;
  1875. ; See if the block is in use; if not, go on to next.
  1876. call IsBlockInUse
  1877. jc FindFile40 ; c set if not in use
  1878. ; eax == block number to process
  1879. ; TOS == Next block to search
  1880. ; TOS + 4 == Total blocks to search
  1881. ; TOS + 8 == name length
  1882. ; TOS + c -> name to find
  1883. ;
  1884. call ReadIndexBlock
  1885. pop edx ; Current block
  1886. pop esi ; Total blocks
  1887. pop ecx ; ecx == name length
  1888. pop ebx ; ebx -> name
  1889. push ebx
  1890. push ecx
  1891. push esi
  1892. push edx
  1893. ; ebx -> name to find
  1894. ; ecx == name length in characters
  1895. ; TOS == Next block to search
  1896. ; TOS + 4 == Total blocks to search
  1897. ; TOS + 8 == name length
  1898. ; TOS + c -> name to find
  1899. ;
  1900. ; Index buffer to search is in index allocation block buffer.
  1901. ;
  1902. mov eax, IndexBlockBuffer ; eax -> Index allocation block
  1903. lea eax, [eax].IB_IndexHeader ; eax -> Index Header
  1904. call LocateIndexEntry ; eax -> found entry
  1905. or eax, eax
  1906. jz FindFile40
  1907. ; Found it!
  1908. ;
  1909. ; eax -> Found entry
  1910. ; TOS == Next block to search
  1911. ; TOS + 4 == Total blocks to search
  1912. ; TOS + 8 == name length
  1913. ; TOS + c -> name to find
  1914. ;
  1915. pop ecx
  1916. pop ecx
  1917. pop ecx
  1918. pop ecx ; clean up stack
  1919. ret
  1920. FindFile90:
  1921. ;
  1922. ; Name not found.
  1923. ;
  1924. ; TOS == name length
  1925. ; TOS + 4 -> name to find
  1926. ;
  1927. pop ecx
  1928. pop ecx ; clean up stack.
  1929. xor eax, eax ; zero out eax.
  1930. ret
  1931. FindFile endp
  1932. ifdef DEBUG
  1933. ;****************************************************************************
  1934. ;
  1935. ; DumpIndexBlock - dumps the index block buffer
  1936. ;
  1937. DumpIndexBlock proc near
  1938. SAVE_ALL
  1939. mov esi, IndexBlockBuffer
  1940. mov ecx, 20h ; dwords to dump
  1941. DIB10:
  1942. test ecx, 3
  1943. jnz DIB20
  1944. call DebugNewLine
  1945. DIB20:
  1946. lodsd
  1947. call PrintNumber
  1948. loop DIB10
  1949. RESTORE_ALL
  1950. ret
  1951. DumpIndexBlock endp
  1952. ;****************************************************************************
  1953. ;
  1954. ; DebugNewLine
  1955. ;
  1956. DebugNewLine proc near
  1957. SAVE_ALL
  1958. xor eax, eax
  1959. xor ebx, ebx
  1960. mov al, 0dh
  1961. mov ah, 14
  1962. mov bx, 7
  1963. int 10h
  1964. mov al, 0ah
  1965. mov ah, 14
  1966. mov bx, 7
  1967. int 10h
  1968. RESTORE_ALL
  1969. ret
  1970. DebugNewLine endp
  1971. ;****************************************************************************
  1972. ;
  1973. ; PrintName - Display a unicode name
  1974. ;
  1975. ; ENTRY: DS:ESI -> null-terminated string
  1976. ; ECX == characters in string
  1977. ;
  1978. ; USES: None.
  1979. ;
  1980. PrintName proc near
  1981. SAVE_ALL
  1982. or ecx, ecx
  1983. jnz PrintName10
  1984. call DebugNewLine
  1985. RESTORE_ALL
  1986. ret
  1987. PrintName10:
  1988. xor eax, eax
  1989. xor ebx, ebx
  1990. lodsw
  1991. mov ah, 14 ; write teletype
  1992. mov bx, 7 ; attribute
  1993. int 10h ; print it
  1994. loop PrintName10
  1995. call DebugNewLine
  1996. RESTORE_ALL
  1997. ret
  1998. PrintName endp
  1999. ;****************************************************************************
  2000. ;
  2001. ; DebugPrint - Display a debug string.
  2002. ;
  2003. ; ENTRY: DS:SI -> null-terminated string
  2004. ;
  2005. ; USES: None.
  2006. ;
  2007. .286
  2008. DebugPrint proc near
  2009. pusha
  2010. DbgPr20:
  2011. lodsb
  2012. cmp al, 0
  2013. je DbgPr30
  2014. mov ah, 14 ; write teletype
  2015. mov bx, 7 ; attribute
  2016. int 10h ; print it
  2017. jmp DbgPr20
  2018. DbgPr30:
  2019. popa
  2020. nop
  2021. ret
  2022. DebugPrint endp
  2023. ;****************************************************************************
  2024. ;
  2025. ;
  2026. ; PrintNumber
  2027. ;
  2028. ; ENTRY: EAX == number to print
  2029. ;
  2030. ; PRESERVES ALL REGISTERS
  2031. ;
  2032. .386
  2033. PrintNumber proc near
  2034. SAVE_ALL
  2035. mov ecx, 8 ; number of digits in a DWORD
  2036. PrintNumber10:
  2037. mov edx, eax
  2038. and edx, 0fh ; edx = lowest-order digit
  2039. push edx ; put it on the stack
  2040. shr eax, 4 ; drop low-order digit
  2041. loop PrintNumber10
  2042. mov ecx, 8 ; number of digits on stack.
  2043. PrintNumber20:
  2044. pop eax ; eax = next digit to print
  2045. cmp eax, 9
  2046. jg PrintNumber22
  2047. add eax, '0'
  2048. jmp PrintNumber25
  2049. PrintNumber22:
  2050. sub eax, 10
  2051. add eax, 'A'
  2052. PrintNumber25:
  2053. xor ebx, ebx
  2054. mov ah, 14
  2055. mov bx, 7
  2056. int 10h
  2057. loop PrintNumber20
  2058. ; Print a space to separate numbers
  2059. mov al, ' '
  2060. mov ah, 14
  2061. mov bx, 7
  2062. int 10h
  2063. RESTORE_ALL
  2064. call Pause
  2065. ret
  2066. PrintNumber endp
  2067. ;****************************************************************************
  2068. ;
  2069. ; Debug0 - Print debug string 0 -- used for checkpoints in mainboot
  2070. ;
  2071. Debug0 proc near
  2072. SAVE_ALL
  2073. mov si, offset DbgString0
  2074. call BootErr$Print1
  2075. RESTORE_ALL
  2076. ret
  2077. Debug0 endp
  2078. ;****************************************************************************
  2079. ;
  2080. ; Debug1 - Print debug string 1 --
  2081. ;
  2082. Debug1 proc near
  2083. SAVE_ALL
  2084. mov si, offset DbgString1
  2085. call BootErr$Print1
  2086. RESTORE_ALL
  2087. ret
  2088. Debug1 endp
  2089. ;****************************************************************************
  2090. ;
  2091. ; Debug2 - Print debug string 2
  2092. ;
  2093. Debug2 proc near
  2094. SAVE_ALL
  2095. mov si, offset DbgString2
  2096. call BootErr$Print1
  2097. RESTORE_ALL
  2098. ret
  2099. Debug2 endp
  2100. ;****************************************************************************
  2101. ;
  2102. ; Debug3 - Print debug string 3 --
  2103. ;
  2104. Debug3 proc near
  2105. SAVE_ALL
  2106. mov si, offset DbgString3
  2107. call BootErr$Print1
  2108. RESTORE_ALL
  2109. ret
  2110. Debug3 endp
  2111. ;****************************************************************************
  2112. ;
  2113. ; Debug4 - Print debug string 4
  2114. ;
  2115. Debug4 proc near
  2116. SAVE_ALL
  2117. mov si, offset DbgString4
  2118. call BootErr$Print1
  2119. RESTORE_ALL
  2120. ret
  2121. Debug4 endp
  2122. ;****************************************************************************
  2123. ;
  2124. ; Pause - Pause for about 1/2 a second. Simply count until you overlap
  2125. ; to zero.
  2126. ;
  2127. Pause proc near
  2128. push eax
  2129. mov eax, 0fff10000h
  2130. PauseLoopy:
  2131. inc eax
  2132. or eax, eax
  2133. jnz PauseLoopy
  2134. pop eax
  2135. ret
  2136. Pause endp
  2137. endif ; DEBUG
  2138. ;*************************************************************************
  2139. ;
  2140. ; LoadIndexFrs - For the requested index type code locate and
  2141. ; load the associated Frs.
  2142. ;
  2143. ; ENTRY: EAX - requested index type code
  2144. ; ECX - Points to empty Frs buffer
  2145. ;
  2146. ; EXIT: EAX - points to offset in Frs buffer of requested index type
  2147. ; code or Zero if not found.
  2148. ; USES: All
  2149. ;
  2150. LoadIndexFrs proc near
  2151. push ecx ; save FRS buffer for later
  2152. push eax ; save index type code for later
  2153. mov eax, ROOT_FILE_NAME_INDEX_NUMBER
  2154. push ds
  2155. pop es
  2156. mov edi, ecx ; es:edi = target buffer
  2157. call ReadFrs
  2158. ;
  2159. ; See if an attribute list exists.
  2160. ;
  2161. mov eax, ecx ; Base FRS
  2162. mov ebx, $ATTRIBUTE_LIST ; Attribute type code
  2163. mov ecx, 0 ; Attribute name length
  2164. mov edx, 0 ; Attribute name
  2165. call LocateAttributeRecord
  2166. pop ebx ; Restore type code.
  2167. pop ecx ; Restore FRS.
  2168. test eax, eax ; If attribute list exists, search it.
  2169. jnz LoadIndexFrs$AttribList
  2170. mov eax, ecx ; FRS to search
  2171. ; EBX contains type code.
  2172. movzx ecx, index_name_length ; Attribute name length
  2173. mov edx, offset index_name ; Attribute name
  2174. call LocateAttributeRecord
  2175. jmp LoadIndexFrs$Exit ; Return with results from search above.
  2176. LoadIndexFrs$AttribList:
  2177. ;
  2178. ; Search in attribute list.
  2179. ;
  2180. xor edx, edx ; VCN zero
  2181. mov eax, ecx ; FRS to search
  2182. mov ecx, ebx ; type code
  2183. push eax ; save Frs
  2184. push ebx ; save type code
  2185. call SearchAttrList ; search attribute list for FRN
  2186. ; of specified ($INDEX_ROOT,
  2187. ; $INDEX_ALLOCATION, or $BITMAP)
  2188. ; EAX - holds FRN for Frs, or Zero
  2189. pop ebx ; Attribute type code (used later)
  2190. pop edi ; es:edi = target buffer
  2191. or eax, eax ; if we cann't find it in attribute
  2192. jz LoadIndexFrs$Exit ; list then we are hosed
  2193. ; We should now have the File Record Number where the index for the
  2194. ; specified type code we are searching for is, load this into the
  2195. ; Frs target buffer.
  2196. ;
  2197. ; EAX - holds FRN
  2198. ; EBX - holds type code
  2199. ; EDI - holds target buffer
  2200. push ds
  2201. pop es
  2202. call ReadFrs
  2203. ;
  2204. ; Now determine the offset in the Frs of the index
  2205. ;
  2206. ; EBX - holds type code
  2207. mov eax, edi ; Frs to search
  2208. movzx ecx, index_name_length ; Attribute name length
  2209. mov edx, offset index_name ; Attribute name
  2210. call LocateAttributeRecord
  2211. ; EAX - holds offset or Zero.
  2212. LoadIndexFrs$Exit:
  2213. ret
  2214. LoadIndexFrs endp
  2215. ;****************************************************************************
  2216. ;
  2217. ; SearchAttrList
  2218. ;
  2219. ; Search the Frs for the attribute list. Then search the attribute list
  2220. ; for the specifed type code. When you find it return the FRN in the
  2221. ; attribute list entry found or Zero if no match found.
  2222. ;
  2223. ; ENTRY: ECX - type code to search attrib list for
  2224. ; EAX - Frs buffer holding head of attribute list
  2225. ; EDX - Lowest VCN
  2226. ;
  2227. ; EXIT: EAX - FRN file record number to load, Zero if none.
  2228. ;
  2229. ; USES: All
  2230. ;
  2231. SearchAttrList proc near
  2232. push edx ; Save VCN.
  2233. push ecx ; type code to search for in
  2234. ; attrib list
  2235. ; EAX - holds Frs to search
  2236. mov ebx, $ATTRIBUTE_LIST ; Attribute type code
  2237. mov ecx, 0 ; Attribute name length
  2238. mov edx, 0 ; Attribute name
  2239. call LocateAttributeRecord
  2240. or eax, eax ; If there's no Attribute list,
  2241. jz SearchAttrList$NotFoundIndex1 ; We are done
  2242. ; Read the attribute list.
  2243. ; eax -> attribute list attribute
  2244. mov ebx, eax ; ebx -> attribute list attribute
  2245. push ds
  2246. pop es ; copy ds into es
  2247. mov edi, AttrList ; ds:edi->attribute list buffer
  2248. xor eax, eax ; FRS Buffer is NULL since attribute lists can only be in one FRS.
  2249. call ReadWholeAttribute
  2250. push ds
  2251. pop es
  2252. mov ebx, AttrList ; es:ebx -> first attribute list entry
  2253. ; Now, traverse the attribute list looking for the entry for
  2254. ; the Index type code.
  2255. ;
  2256. ; ebx -> first attribute list entry
  2257. ;
  2258. pop ecx ; Get Index Type code
  2259. pop edx ; Restore VCN.
  2260. SearchAttrList$LookingForIndex:
  2261. ifdef DEBUG
  2262. SAVE_ALL
  2263. mov eax, es:[bx].ATTRLIST_TypeCode
  2264. call PrintNumber
  2265. movzx eax, es:[bx].ATTRLIST_Length
  2266. call PrintNumber
  2267. mov eax, es
  2268. call PrintNumber
  2269. mov eax, ebx
  2270. call PrintNumber
  2271. push es
  2272. pop ds
  2273. movzx ecx, es:[bx].ATTRLIST_NameLength ; ecx = chars in name
  2274. lea esi, es:[bx].ATTRLIST_Name ; esi -> name
  2275. call PrintName
  2276. RESTORE_ALL
  2277. endif ; DEBUG
  2278. cmp es:[bx].ATTRLIST_TypeCode, ecx
  2279. jne SearchAttrList$NotFoundIndex3 ; This is not the right attribute.
  2280. cmp es:[bx].ATTRLIST_LowestVcn.LowPart, edx
  2281. je SearchAttrList$FoundIndex ; Found it.
  2282. jmp SearchAttrList$NotFoundIndex4 ; Wrong VCN.
  2283. SearchAttrList$NotFoundIndex3:
  2284. cmp es:[bx].ATTRLIST_TypeCode, $END ; reached invalid attribute
  2285. je SearchAttrList$NotFoundIndex2 ; so must be at end
  2286. cmp es:[bx].ATTRLIST_Length, 0
  2287. je SearchAttrList$NotFoundIndex2 ; reached end of list and
  2288. ; nothing found
  2289. SearchAttrList$NotFoundIndex4:
  2290. movzx eax, es:[bx].ATTRLIST_Length
  2291. add bx, ax
  2292. mov ax, bx
  2293. and ax, 08000h ; test for roll over
  2294. jz SearchAttrList$LookingForIndex
  2295. ; If we rolled over then increment to the next es 32K segment and
  2296. ; zero off the high bits of bx
  2297. mov ax, es
  2298. add ax, 800h
  2299. mov es, ax
  2300. and bx, 07fffh
  2301. jmp SearchAttrList$LookingForIndex
  2302. SearchAttrList$FoundIndex:
  2303. ; found the index, return the FRN
  2304. mov eax, es:[bx].ATTRLIST_SegmentReference.REF_LowPart
  2305. ret
  2306. SearchAttrList$NotFoundIndex1:
  2307. pop ecx
  2308. pop edx
  2309. SearchAttrList$NotFoundIndex2:
  2310. xor eax, eax
  2311. ret
  2312. SearchAttrList endp
  2313. ;
  2314. ; Boot message printing, relocated from sector 0 to sace space
  2315. ;
  2316. BootErr$fnf:
  2317. mov al,byte ptr TXT_MSG_SYSINIT_FILE_NOT_FD
  2318. jmp BootErr2
  2319. BootErr$ntc:
  2320. mov al,byte ptr TXT_MSG_SYSINIT_NTLDR_CMPRS
  2321. jmp BootErr2
  2322. ifdef DEBUG
  2323. DbgString0 db "Debug Point 0", 0Dh, 0Ah, 0
  2324. DbgString1 db "Debug Point 1", 0Dh, 0Ah, 0
  2325. DbgString2 db "Debug Point 2", 0Dh, 0Ah, 0
  2326. DbgString3 db "Debug Point 3", 0Dh, 0Ah, 0
  2327. DbgString4 db "Debug Point 4", 0Dh, 0Ah, 0
  2328. endif ; DEBUG
  2329. .errnz ($-_ntfsboot) GT 8192 ; <FATAL PROBLEM: main boot record exceeds available space>
  2330. org 8192
  2331. BootCode ends
  2332. end _ntfsboot