Source code of Windows XP (NT5)
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.

2993 lines
82 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. call ReadWholeAttribute
  536. mainboot30:
  537. ;
  538. ; OK, we've got the index-related attributes.
  539. ;
  540. movzx ecx, ntldr_name_length ; ecx = name length in characters
  541. mov eax, offset ntldr_name ; eax -> name
  542. call FindFile
  543. or eax, eax
  544. jz BootErr$fnf
  545. ; Read the FRS for NTLDR and find its data attribute.
  546. ;
  547. ; eax -> Index Entry for NTLDR.
  548. ;
  549. mov eax, [eax].IE_FileReference.REF_LowPart
  550. push ds
  551. pop es ; es:edi = target buffer
  552. mov edi, NtldrFrs
  553. call ReadFrs
  554. mov eax, NtldrFrs ; pointer to FRS
  555. mov ebx, $DATA ; requested attribute type
  556. mov ecx, 0 ; attribute name length in characters
  557. mov edx, 0 ; attribute name (NULL if none)
  558. call LocateAttributeRecord
  559. ; eax -> $DATA attribute for NTLDR
  560. ;
  561. or eax, eax ; if eax is zero, attribute not found.
  562. jnz mainboot$FoundData
  563. ;
  564. ; The ntldr $DATA segment is fragmented. Search the attribute list
  565. ; for the $DATA member. And load it from there.
  566. ;
  567. mov ecx, $DATA ; Attribute type code
  568. mov eax, NtldrFrs ; FRS to search
  569. call SearchAttrList ; search attribute list for FRN
  570. ; of specified ($DATA)
  571. or eax, eax ; if eax is zero, attribute not found.
  572. jz BootErr$fnf
  573. ;
  574. ; We found the FRN of the $DATA attribute; load that into memory.
  575. ;
  576. push ds
  577. pop es ; es:edi = target buffer
  578. mov edi, NtldrFrs
  579. call ReadFrs
  580. ;
  581. ; Determine the beginning offset of the $DATA in the FRS
  582. ;
  583. mov eax, NtldrFrs ; pointer to FRS
  584. mov ebx, $DATA ; requested attribute type
  585. mov ecx, 0 ; attribute name length in characters
  586. mov edx, 0 ; attribute name (NULL if none)
  587. call LocateAttributeRecord
  588. ; eax -> $DATA attribute for NTLDR
  589. ;
  590. or eax, eax ; if eax is zero, attribute not found.
  591. jz BootErr$fnf
  592. mainboot$FoundData:
  593. ; Get the attribute record header flags, and make sure none of the
  594. ; `compressed' bits are set
  595. movzx ebx, [eax].ATTR_Flags
  596. and ebx, ATTRIBUTE_FLAG_COMPRESSION_MASK
  597. jnz BootErr$ntc
  598. mov ebx, eax ; ebx -> $DATA attribute for NTLDR
  599. push LdrSeg
  600. pop es ; es = segment addres to read into
  601. sub edi, edi ; es:edi = buffer address
  602. call ReadWholeAttribute
  603. ;
  604. ; We've loaded NTLDR--jump to it.
  605. ;
  606. ; Before we go to NTLDR, set up the registers the way it wants them:
  607. ; DL = INT 13 drive number we booted from
  608. ;
  609. mov dl, DriveNumber
  610. mov ax,1000
  611. mov es, ax ; we don't really need this
  612. lea si, BPB
  613. sub ax,ax
  614. push LdrSeg
  615. push ax
  616. retf ; "return" to NTLDR.
  617. mainboot endp
  618. .386
  619. ;****************************************************************************
  620. ;
  621. ; ReadClusters - Reads a run of clusters from the disk.
  622. ;
  623. ; ENTRY: eax == LCN to read
  624. ; edx == clusters to read
  625. ; es:edi -> Target buffer
  626. ;
  627. ; USES: none (preserves all registers)
  628. ;
  629. ReadClusters proc near
  630. SAVE_ALL
  631. mov ebx, edx ; ebx = clusters to read.
  632. movzx ecx, SectorsPerCluster ; ecx = cluster factor
  633. mul ecx ; Convert LCN to sectors (wipes out edx!)
  634. mov SectorBase, eax ; Store starting sector in SectorBase
  635. mov eax, ebx ; eax = number of clusters
  636. mul ecx ; Convert EAX to sectors (wipes out edx!)
  637. mov SectorCount, ax ; Store number of sectors in SectorCount
  638. ;
  639. ; Note that ReadClusters gets its target buffer in es:edi but calls
  640. ; the ReadSectors worker function that takes a target in es:bx--we need
  641. ; to normalize es:edi so that we don't overflow bx.
  642. ;
  643. mov bx, di
  644. and bx, 0Fh
  645. mov ax, es
  646. shr edi, 4
  647. add ax, di ; ax:bx -> target buffer
  648. push ax
  649. pop es ; es:bx -> target buffer
  650. call ReadSectors
  651. RESTORE_ALL
  652. ret
  653. ReadClusters endp
  654. ;
  655. ;****************************************************************************
  656. ;
  657. ; LocateAttributeRecord -- Find an attribute record in an FRS.
  658. ;
  659. ; ENTRY: EAX -- pointer to FRS
  660. ; EBX -- desired attribute type code
  661. ; ECX -- length of attribute name in characters
  662. ; EDX -- pointer to attribute name
  663. ;
  664. ; EXIT: EAX points at attribute record (0 indicates not found)
  665. ;
  666. ; USES: All
  667. ;
  668. LocateAttributeRecord proc near
  669. ; get the first attribute record.
  670. ;
  671. add ax, word ptr[eax].FRS_FirstAttribute
  672. ; eax -> next attribute record to investigate.
  673. ; ebx == desired type
  674. ; ecx == name length
  675. ; edx -> pointer to name
  676. ;
  677. lar10:
  678. cmp [eax].ATTR_TypeCode, 0ffffffffh
  679. je lar99
  680. cmp dword ptr[eax].ATTR_TypeCode, ebx
  681. jne lar80
  682. ; this record is a potential match. Compare the names:
  683. ;
  684. ; eax -> candidate record
  685. ; ebx == desired type
  686. ; ecx == name length
  687. ; edx -> pointer to name
  688. ;
  689. or ecx, ecx ; Did the caller pass in a name length?
  690. jnz lar20
  691. ; We want an attribute with no name--the current record is
  692. ; a match if and only if it has no name.
  693. ;
  694. cmp [eax].ATTR_NameLength, 0
  695. jne lar80 ; Not a match.
  696. ; It's a match, and eax is set up correctly, so return.
  697. ;
  698. ret
  699. ; We want a named attribute.
  700. ;
  701. ; eax -> candidate record
  702. ; ebx == desired type
  703. ; ecx == name length
  704. ; edx -> pointer to name
  705. ;
  706. lar20:
  707. cmp cl, [eax].ATTR_NameLength
  708. jne lar80 ; Not a match.
  709. ; Convert name in current record to uppercase.
  710. ;
  711. mov esi, eax
  712. add si, word ptr[eax].ATTR_NameOffset
  713. call UpcaseName
  714. ; eax -> candidate record
  715. ; ebx == desired type
  716. ; ecx == name length
  717. ; edx -> pointer to name
  718. ; esi -> Name in current record (upcased)
  719. ;
  720. push ecx ; save cx
  721. push ds ; Copy data segment into es
  722. pop es
  723. mov edi, edx ; note that esi is already set up.
  724. repe cmpsw ; zero flag is set if equal
  725. pop ecx ; restore cx
  726. jnz lar80 ; not a match
  727. ; eax points at a matching record.
  728. ;
  729. ret
  730. ;
  731. ; This record doesn't match; go on to the next.
  732. ;
  733. ; eax -> rejected candidate attribute record
  734. ; ebx == desired type
  735. ; ecx == Name length
  736. ; edx -> desired name
  737. ;
  738. lar80: cmp [eax].ATTR_RecordLength, 0 ; if the record length is zero
  739. je lar99 ; the FRS is corrupt.
  740. add eax, [eax].ATTR_RecordLength; Go to next record
  741. jmp lar10 ; and try again
  742. ; Didn't find it.
  743. ;
  744. lar99: sub eax, eax
  745. ret
  746. LocateAttributeRecord endp
  747. ;****************************************************************************
  748. ;
  749. ; LocateIndexEntry -- Find an index entry in a file name index
  750. ;
  751. ; ENTRY: EAX -> pointer to index header
  752. ; EBX -> file name to find
  753. ; ECX == length of file name in characters
  754. ;
  755. ; EXIT: EAX points at index entry. NULL to indicate failure.
  756. ;
  757. ; USES: All
  758. ;
  759. LocateIndexEntry proc near
  760. ; Convert the input name to upper-case
  761. ;
  762. mov esi, ebx
  763. call UpcaseName
  764. ifdef DEBUG
  765. call PrintName
  766. call Debug2
  767. endif ; DEBUG
  768. add eax, [eax].IH_FirstIndexEntry
  769. ; EAX -> current entry
  770. ; EBX -> file name to find
  771. ; ECX == length of file name in characters
  772. ;
  773. lie10: test [eax].IE_Flags, INDEX_ENTRY_END ; Is it the end entry?
  774. jnz lie99
  775. lea edx, [eax].IE_Value ; edx -> FILE_NAME attribute value
  776. ifdef DEBUG
  777. ; DEBUG CODE -- list file names as they are examined
  778. SAVE_ALL
  779. call Debug3
  780. movzx ecx, [edx].FN_FileNameLength ; ecx = chars in name
  781. lea esi, [edx].FN_FileName ; esi -> name
  782. call PrintName
  783. RESTORE_ALL
  784. endif ; DEBUG
  785. ; EAX -> current entry
  786. ; EBX -> file name to find
  787. ; ECX == length of file name in characters
  788. ; EDX -> FILE_NAME attribute
  789. cmp cl, [edx].FN_FileNameLength ; Is name the right length?
  790. jne lie80
  791. lea esi, [edx].FN_FileName ; Get name from FILE_NAME structure
  792. call UpcaseName
  793. push ecx ; save ecx
  794. push ds
  795. pop es ; copy data segment into es for cmpsw
  796. mov edi, ebx ; edi->search name (esi already set up)
  797. repe cmpsw ; zero flag is set if they're equal
  798. pop ecx ; restore ecx
  799. jnz lie80
  800. ; the current entry matches the search name, and eax points at it.
  801. ;
  802. ret
  803. ; The current entry is not a match--get the next one.
  804. ; EAX -> current entry
  805. ; EBX -> file name to find
  806. ; ECX == length of file name in characters
  807. ;
  808. lie80: cmp [eax].IE_Length, 0 ; If the entry length is zero
  809. je lie99 ; then the index block is corrupt.
  810. add ax, [eax].IE_Length ; Get the next entry.
  811. jmp lie10
  812. ; Name not found in this block. Set eax to zero and return
  813. ;
  814. lie99: xor eax, eax
  815. ret
  816. LocateIndexEntry endp
  817. ;****************************************************************************
  818. ;
  819. ; ReadWholeAttribute - Read an entire attribute value
  820. ;
  821. ; ENTRY: ebx -> attribute
  822. ; es:edi -> target buffer
  823. ;
  824. ; USES: ALL
  825. ;
  826. ReadWholeAttribute proc near
  827. cmp [ebx].ATTR_FormCode, RESIDENT_FORM
  828. jne rwa10
  829. ; The attribute is resident.
  830. ; ebx -> attribute
  831. ; es:edi -> target buffer
  832. ;
  833. SAVE_ALL
  834. lea edx, [ebx].ATTR_FormUnion ; edx -> resident form info
  835. mov ecx, [edx].RES_ValueLength ; ecx = bytes in value
  836. mov esi, ebx ; esi -> attribute
  837. add si, [edx].RES_ValueOffset ; esi -> attribute value
  838. rep movsb ; copy bytes from value to buffer
  839. RESTORE_ALL
  840. ret ; That's all!
  841. rwa10:
  842. ;
  843. ; The attribute type is non-resident. Just call
  844. ; ReadNonresidentAttribute starting at VCN 0 and
  845. ; asking for the whole thing.
  846. ;
  847. ; ebx -> attribute
  848. ; es:edi -> target buffer
  849. ;
  850. lea edx, [ebx].ATTR_FormUnion ; edx -> nonresident form info
  851. mov ecx, [edx].NONRES_HighestVcn.LowPart; ecx = HighestVcn
  852. inc ecx ; ecx = clusters in attribute
  853. sub eax, eax ; eax = 0 (first VCN to read)
  854. call ReadNonresidentAttribute
  855. ret
  856. ReadWholeAttribute endp
  857. ;****************************************************************************
  858. ;
  859. ; ReadNonresidentAttribute - Read clusters from a nonresident attribute
  860. ;
  861. ; ENTRY: EAX == First VCN to read
  862. ; EBX -> Attribute
  863. ; ECX == Number of clusters to read
  864. ; ES:EDI == Target of read
  865. ;
  866. ; EXIT: None.
  867. ;
  868. ; USES: None (preserves all registers with SAVE_ALL/RESTORE_ALL)
  869. ;
  870. ReadNonresidentAttribute proc near
  871. SAVE_ALL
  872. cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
  873. je ReadNR10
  874. ; This attribute is not resident--the disk is corrupt.
  875. jmp BootErr$he
  876. ReadNR10:
  877. ; eax == Next VCN to read
  878. ; ebx -> Attribute
  879. ; ecx -> Remaining clusters to read
  880. ; es:edi -> Target of read
  881. ;
  882. cmp ecx, 0
  883. jne ReadNR20
  884. ; Nothing left to read--return success.
  885. ;
  886. RESTORE_ALL
  887. ret
  888. ReadNR20:
  889. push ebx ; pointer to attribute
  890. push eax ; Current VCN
  891. push ecx
  892. push edi
  893. push es
  894. call ComputeLcn ; eax = LCN to read, ecx = run length
  895. mov edx, ecx ; edx = remaining run length
  896. pop es
  897. pop edi
  898. pop ecx
  899. ; eax == LCN to read
  900. ; ecx == remaining clusters to read
  901. ; edx == remaining clusters in current run
  902. ; es:edi == Target of read
  903. ; TOS == Current VCN
  904. ; TOS + 4 == pointer to attribute
  905. ;
  906. cmp ecx, edx
  907. jge ReadNR30
  908. ; Run length is greater than remaining request; only read
  909. ; remaining request.
  910. ;
  911. mov edx, ecx ; edx = Remaining request
  912. ReadNR30:
  913. ; eax == LCN to read
  914. ; ecx == remaining clusters to read
  915. ; edx == clusters to read in current run
  916. ; es:edi == Target of read
  917. ; TOS == Current VCN
  918. ; TOS + == pointer to attribute
  919. ;
  920. call ReadClusters
  921. sub ecx, edx ; Decrement clusters remaining in request
  922. mov ebx, edx ; ebx = clusters read
  923. mov eax, edx ; eax = clusters read
  924. movzx edx, SectorsPerCluster
  925. mul edx ; eax = sectors read (wipes out edx!)
  926. movzx edx, BytesPerSector
  927. mul edx ; eax = bytes read (wipes out edx!)
  928. add edi, eax ; Update target of read
  929. pop eax ; eax = previous VCN
  930. add eax, ebx ; update VCN to read
  931. pop ebx ; ebx -> attribute
  932. jmp ReadNR10
  933. ReadNonresidentAttribute endp
  934. ;****************************************************************************
  935. ;
  936. ; ReadIndexBlockSectors - Read sectors from an index allocation attribute
  937. ;
  938. ; ENTRY: EAX == First VBN to read
  939. ; EBX -> Attribute
  940. ; ECX == Number of sectors to read
  941. ; ES:EDI == Target of read
  942. ;
  943. ; EXIT: None.
  944. ;
  945. ; USES: None (preserves all registers with SAVE_ALL/RESTORE_ALL)
  946. ;
  947. ReadIndexBlockSectors proc near
  948. SAVE_ALL
  949. cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
  950. je ReadIBS_10
  951. ; This attribute is resident--the disk is corrupt.
  952. jmp BootErr$he
  953. ReadIBS_10:
  954. ; eax == Next VBN to read
  955. ; ebx -> Attribute
  956. ; ecx -> Remaining sectors to read
  957. ; es:edi -> Target of read
  958. ;
  959. cmp ecx, 0
  960. jne ReadIBS_20
  961. ; Nothing left to read--return success.
  962. ;
  963. RESTORE_ALL
  964. ret
  965. ReadIBS_20:
  966. push ebx ; pointer to attribute
  967. push eax ; Current VBN
  968. push ecx
  969. push edi
  970. push es
  971. ; Convert eax from a VBN back to a VCN by dividing by SectorsPerCluster.
  972. ; The remainder of this division is the sector offset in the cluster we
  973. ; want. Then use the mapping information to get the LCN for this VCN,
  974. ; then multiply to get back to LBN.
  975. ;
  976. push ecx ; save remaining sectors in request
  977. xor edx, edx ; zero high part of dividend
  978. movzx ecx, SectorsPerCluster
  979. div ecx ; edx = remainder
  980. push edx ; save remainder
  981. call ComputeLcn ; eax = LCN to read, ecx = remaining run length
  982. movzx ebx, SectorsPerCluster
  983. mul ebx ; eax = LBN of cluster, edx = 0
  984. pop edx ; edx = remainder
  985. add eax, edx ; eax = LBN we want
  986. push eax ; save LBN
  987. movzx eax, SectorsPerCluster
  988. mul ecx ; eax = remaining run length in sectors, edx = 0
  989. mov edx, eax ; edx = remaining run length
  990. pop eax ; eax = LBN
  991. pop ecx ; ecx = remaining sectors in request
  992. pop es
  993. pop edi
  994. pop ecx
  995. ; eax == LBN to read
  996. ; ecx == remaining sectors to read
  997. ; edx == remaining sectors in current run
  998. ; es:edi == Target of read
  999. ; TOS == Current VCN
  1000. ; TOS + 4 == pointer to attribute
  1001. ;
  1002. cmp ecx, edx
  1003. jge ReadIBS_30
  1004. ; Run length is greater than remaining request; only read
  1005. ; remaining request.
  1006. ;
  1007. mov edx, ecx ; edx = Remaining request
  1008. ReadIBS_30:
  1009. ; eax == LBN to read
  1010. ; ecx == remaining sectors to read
  1011. ; edx == sectors to read in current run
  1012. ; es:edi == Target of read
  1013. ; TOS == Current VCN
  1014. ; TOS + == pointer to attribute
  1015. ;
  1016. mov SectorBase, eax
  1017. mov SectorCount, dx
  1018. ; We have a pointer to the target buffer in es:edi, but we want that
  1019. ; in es:bx for ReadSectors.
  1020. ;
  1021. SAVE_ALL
  1022. mov bx, di
  1023. and bx, 0Fh
  1024. mov ax, es
  1025. shr edi, 4
  1026. add ax, di ; ax:bx -> target buffer
  1027. push ax
  1028. pop es ; es:bx -> target buffer
  1029. call ReadSectors
  1030. RESTORE_ALL
  1031. sub ecx, edx ; Decrement sectors remaining in request
  1032. mov ebx, edx ; ebx = sectors read
  1033. mov eax, edx ; eax = sectors read
  1034. movzx edx, BytesPerSector
  1035. mul edx ; eax = bytes read (wipes out edx!)
  1036. add edi, eax ; Update target of read
  1037. pop eax ; eax = previous VBN
  1038. add eax, ebx ; update VBN to read
  1039. pop ebx ; ebx -> attribute
  1040. jmp ReadIBS_10
  1041. ReadIndexBlockSectors endp
  1042. ;****************************************************************************
  1043. ;
  1044. ; MultiSectorFixup - fixup a structure read off the disk
  1045. ; to reflect Update Sequence Array.
  1046. ;
  1047. ; ENTRY: ES:EDI = Target buffer
  1048. ;
  1049. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1050. ;
  1051. ; Note: ES:EDI must point at a structure which is protected
  1052. ; by an update sequence array, and which begins with
  1053. ; a multi-sector-header structure.
  1054. ;
  1055. MultiSectorFixup proc near
  1056. SAVE_ALL
  1057. movzx ebx, es:[edi].MSH_UpdateArrayOfs ; ebx = update array offset
  1058. movzx ecx, es:[edi].MSH_UpdateArraySize ; ecx = update array size
  1059. or ecx, ecx ; if the size of the update sequence array
  1060. jz BootErr$he ; is zero, this structure is corrupt.
  1061. add ebx, edi ; es:ebx -> update sequence array count word
  1062. add ebx, 2 ; es:ebx -> 1st entry of update array
  1063. add edi, SEQUENCE_NUMBER_STRIDE - 2 ; es:edi->last word of first chunk
  1064. dec ecx ; decrement to reflect count word
  1065. MSF10:
  1066. ; ecx = number of entries remaining in update sequence array
  1067. ; es:ebx -> next entry in update sequence array
  1068. ; es:edi -> next target word for update sequence array
  1069. or ecx, ecx
  1070. jz MSF30
  1071. mov ax, word ptr es:[ebx] ; copy next update sequence array entry
  1072. mov word ptr es:[edi], ax ; to next target word
  1073. add ebx, 2 ; go on to next entry
  1074. add edi, SEQUENCE_NUMBER_STRIDE ; go on to next target
  1075. dec ecx
  1076. jmp MSF10
  1077. MSF30:
  1078. RESTORE_ALL
  1079. ret
  1080. MultiSectorFixup endp
  1081. ;****************************************************************************
  1082. ;
  1083. ; SetupMft - Reads MFT File Record Segments into the LBN array
  1084. ;
  1085. ; ENTRY: none.
  1086. ;
  1087. ; EXIT: NextBuffer is set to the free byte after the last MFT FRS
  1088. ; SegmentsInMft is initialized
  1089. ;
  1090. ;
  1091. SetupMft proc near
  1092. SAVE_ALL
  1093. ; Initialize SegmentsInMft and NextBuffer as if the MFT
  1094. ; had only one FRS.
  1095. ;
  1096. mov eax, 1
  1097. mov SegmentsInMft, eax
  1098. mov eax, MftFrs ; this is the scratch mft buffer
  1099. add eax, BytesPerFrs
  1100. mov MftLcnFrs,eax ; this is the scratch mft buffer for lookup
  1101. add eax, BytesPerFrs
  1102. mov NextBuffer, eax
  1103. ; Read FRS 0 into the first MFT FRS buffer, being sure
  1104. ; to resolve the Update Sequence Array. Remember the physical
  1105. ; location in the Lbn array.
  1106. ;
  1107. mov eax, MftStartLcn.LowPart
  1108. movzx ebx, SectorsPerCluster
  1109. mul ebx ; eax = mft starting sector
  1110. mov ebx, NextBuffer ; Store this location in the Lbn array
  1111. mov [bx], eax
  1112. mov SectorBase, eax ; SectorBase = mft starting sector for read
  1113. add bx, 4
  1114. mov eax, SectorsPerFrs
  1115. mov [bx], eax ; Store the sector count in the Lcn array
  1116. mov SectorCount, ax ; SectorCount = SectorsPerFrs
  1117. add bx, 4
  1118. mov NextBuffer, ebx ; Remember the next Lbn array location
  1119. mov ebx, MftFrs ; Read the sectors into the MftFrs scratch buffer
  1120. push ds
  1121. pop es
  1122. call ReadSectors
  1123. mov edi, ebx ; es:edi = buffer
  1124. call MultiSectorFixup
  1125. ; Determine whether the MFT has an Attribute List attribute
  1126. mov eax, MftFrs
  1127. mov ebx, $ATTRIBUTE_LIST
  1128. mov ecx, 0
  1129. mov edx, 0
  1130. call LocateAttributeRecord
  1131. or eax, eax ; If there's no Attribute list,
  1132. jz SetupMft99 ; we're done!
  1133. ; Read the attribute list.
  1134. ; eax -> attribute list attribute
  1135. ;
  1136. mov ebx, eax ; ebx -> attribute list attribute
  1137. push ds
  1138. pop es ; copy ds into es
  1139. mov edi, AttrList ; ds:edi->attribute list buffer
  1140. call ReadWholeAttribute
  1141. mov ebx, AttrList ; ebx -> first attribute list entry
  1142. ; Now, traverse the attribute list looking for the first
  1143. ; entry for the $DATA type. We know it must have at least
  1144. ; one.
  1145. ;
  1146. ; ebx -> first attribute list entry
  1147. ;
  1148. SetupMft10:
  1149. cmp [bx].ATTRLIST_TypeCode, $DATA
  1150. je SetupMft30
  1151. add bx,[bx].ATTRLIST_Length
  1152. jmp SetupMft10
  1153. SetupMft20:
  1154. ; Scan forward through the attribute list entries for the
  1155. ; $DATA attribute, reading each referenced FRS. Note that
  1156. ; there will be at least one non-$DATA entry after the entries
  1157. ; for the $DATA attribute, since there's a $BITMAP.
  1158. ;
  1159. ; ebx -> Next attribute list entry
  1160. ; NextBuffer -> Target for next mapping information
  1161. ; MftFrs -> Target of next read
  1162. ; SegmentsInMft == number of MFT segments read so far
  1163. ;
  1164. ; Find the physical sector and sector count for the runs for this
  1165. ; file record (max 2 runs). The mapping for this must already
  1166. ; be in a file record already visited. Find the Vcn and cluster
  1167. ; offset for this FRS. Use LookupMftLcn to find the Lcn.
  1168. push ebx ; Save the current position in the attribute list
  1169. ; Convert from Frs to sectors, then to Vcn
  1170. mov eax, [bx].ATTRLIST_SegmentReference.REF_LowPart
  1171. mul SectorsPerFrs
  1172. push eax ; Remember the VBN
  1173. xor edx, edx
  1174. movzx ebx, SectorsPerCluster
  1175. div ebx ; eax = VCN
  1176. push edx ; save remainder, this is cluster offset
  1177. call ComputeMftLcn ; eax = LCN
  1178. or eax, eax ; LCN equal to zero?
  1179. jz BootErr$he ; zero is not a possible LCN
  1180. mov ecx, SectorsPerFrs ; ecx = Number of sectors remaining for this file record
  1181. ; Change the LCN back into an LBN and add the remainder back in to get
  1182. ; the sector we want to read.
  1183. movzx ebx, SectorsPerCluster
  1184. mul ebx ; eax = cluster first LBN
  1185. pop edx ; edx = sector remainder
  1186. add eax, edx ; eax = desired LBN
  1187. ; Store this in the current Lcn array slot
  1188. mov ebx, NextBuffer
  1189. mov [bx], eax ; Store the starting sector
  1190. add bx, 4
  1191. movzx eax, SectorsPerCluster
  1192. sub eax, edx
  1193. cmp eax, ecx ; Check if we have too many sectors
  1194. jbe SetupMft60
  1195. mov eax, ecx ; Limit ourselves to the sectors remaining
  1196. SetupMft60:
  1197. mov [bx], eax ; Store the sector count
  1198. ; If we have a complete file record skip to process the attribute entry
  1199. SetupMft70:
  1200. sub ecx, eax ; Subtract these sectors from remaining sectors
  1201. pop edx ; Get the previous starting VBN (restores stack also)
  1202. jz SetupMft50
  1203. ; This may be a split file record. Go ahead and get the next piece.
  1204. add eax, edx ; Add the sector count for the last run to the start Vbn for the run
  1205. ; This is the next Vbn to read
  1206. push eax ; Save the Vbn
  1207. xor edx, edx ; Convert to Vcn, there should be no remainder this time
  1208. movzx ebx, SectorsPerCluster
  1209. div ebx ; eax = VCN
  1210. push ecx ; Save the remaining sectors
  1211. call ComputeMftLcn ; eax = LCN
  1212. pop ecx ; Restore the remaining sectors
  1213. or eax, eax ; LCN equal to zero?
  1214. jz BootErr$he ; zero is not a possible LCN
  1215. ; Change the LCN back into a LBN to get the starting sector we want to read.
  1216. movzx ebx, SectorsPerCluster
  1217. mul ebx ; eax = cluster first LBN
  1218. ; If this sector is the contiguous with the other half of the run
  1219. ; make it appear to be single longer run.
  1220. mov ebx, NextBuffer ; Recover the last run
  1221. mov edx, [bx]
  1222. add bx, 4
  1223. add edx, [bx] ; This is the next potential LBN
  1224. cmp edx, eax ; Check if we are at the contiguous LBN
  1225. jne SetupMft80
  1226. ; Append this to the previous run.
  1227. movzx eax, SectorsPerCluster
  1228. cmp eax, ecx ; Check if have more sectors than we need
  1229. jbe SetupMft90
  1230. mov eax, ecx
  1231. SetupMft90:
  1232. add [bx], eax
  1233. jmp SetupMft70 ; Loop to see if there more work to do
  1234. ; This is multiple runs. Update the next entry.
  1235. SetupMft80:
  1236. add bx, 4
  1237. mov NextBuffer, ebx ; advance our NextBuffer pointer
  1238. mov [bx], eax ; fill in the next run start sector
  1239. add bx, 4
  1240. movzx eax, SectorsPerCluster
  1241. cmp eax, ecx ; Check if have more sectors than we need
  1242. jbe SetupMft100
  1243. mov eax, ecx
  1244. SetupMft100:
  1245. mov [bx], eax ; and count
  1246. jmp SetupMft70 ; Loop to see if there is more work to do
  1247. SetupMft50:
  1248. ; Advance the count of Frs segments and the NextBuffer pointer
  1249. add bx, 4
  1250. inc SegmentsInMft
  1251. mov NextBuffer, ebx
  1252. pop ebx
  1253. ; Go on to the next attribute list entry
  1254. SetupMft30:
  1255. add bx,[bx].ATTRLIST_Length
  1256. cmp [bx].ATTRLIST_TypeCode, $DATA
  1257. je SetupMft20
  1258. SetupMft99:
  1259. RESTORE_ALL
  1260. ret
  1261. SetupMft endp
  1262. ;****************************************************************************
  1263. ;
  1264. ; ComputeMftLcn -- Computes the LCN for a cluster of the MFT
  1265. ;
  1266. ;
  1267. ; ENTRY: EAX == VCN
  1268. ;
  1269. ; EXIT: EAX == LCN
  1270. ;
  1271. ; USES: ALL
  1272. ;
  1273. ComputeMftLcn proc near
  1274. mov edx, eax ; edx = VCN
  1275. mov ecx, SegmentsInMft ; ecx = # of FRS's to search
  1276. mov esi,MftLcnFrs
  1277. add esi,BytesPerFrs ; si -> FRS LBN list
  1278. MftLcn10:
  1279. ; ECX == number of remaining FRS's to search
  1280. ; EDX == VCN
  1281. ; EBX == Buffer to read into
  1282. ; ESI == LBN array
  1283. ; EDI == Number of sectors to read
  1284. ;
  1285. push edx ; save VCN
  1286. push ecx ; save MFT segment count
  1287. push edx ; save VCN again
  1288. ; Read the sectors for the given FRS
  1289. mov ebx,MftLcnFrs
  1290. mov edi,SectorsPerFrs
  1291. ; Read these sectors
  1292. MftLcn40:
  1293. mov eax,[si] ; Get the start sector and sector count
  1294. mov SectorBase,eax
  1295. add si,4
  1296. mov eax,[si]
  1297. mov SectorCount,ax
  1298. add si,4
  1299. push ds
  1300. pop es
  1301. call ReadSectors
  1302. ; Check if we have more data to read
  1303. sub edi, eax
  1304. je MftLcn30
  1305. ; Read the next run
  1306. mul BytesPerSector ; move forward in the buffer, results in ax:dx
  1307. add bx,ax
  1308. jmp MftLcn40
  1309. MftLcn30:
  1310. ; Do the multi sector fixup
  1311. mov edi,MftLcnFrs
  1312. push ds
  1313. pop es
  1314. call MultiSectorFixup
  1315. mov eax, MftLcnFrs
  1316. mov ebx, $DATA
  1317. mov ecx, 0
  1318. mov edx, ecx
  1319. call LocateAttributeRecord
  1320. ; EAX -> $DATA attribute
  1321. ; TOS == VCN
  1322. ; TOS + 4 == number of remaining FRS's to search
  1323. ; TOS + 8 -> FRS being searched
  1324. ; TOS +12 == VCN
  1325. or eax, eax
  1326. jz BootErr$he ; No $DATA attribute in this FRS!
  1327. mov ebx, eax ; ebx -> attribute
  1328. pop eax ; eax = VCN
  1329. ; EAX == VCN
  1330. ; EBX -> $DATA attribute
  1331. ; TOS number of remaining FRS's to search
  1332. ; TOS + 4 == FRS being searched
  1333. ; TOS + 8 == VCN
  1334. push esi
  1335. call ComputeLcn
  1336. pop esi
  1337. or eax, eax
  1338. jz MftLcn20
  1339. ; Found our LCN. Clean up the stack and return.
  1340. ;
  1341. ; EAX == LCN
  1342. ; TOS number of remaining FRS's to search
  1343. ; TOS + 4 == FRS being searched
  1344. ; TOS + 8 == VCN
  1345. ;
  1346. pop ebx
  1347. pop ebx ; clean up the stack
  1348. ret
  1349. MftLcn20:
  1350. ;
  1351. ; Didn't find the VCN in this FRS; try the next one.
  1352. ;
  1353. ; TOS number of remaining FRS's to search
  1354. ; TOS + 4 -> FRS being searched
  1355. ; TOS + 8 == VCN
  1356. ;
  1357. pop ecx ; ecx = number of FRS's remaining, including current
  1358. pop edx ; edx = VCN
  1359. loop MftLcn10 ; decrement cx and try next FRS
  1360. ; This VCN was not found.
  1361. ;
  1362. xor eax, eax
  1363. ret
  1364. ComputeMftLcn endp
  1365. ;****************************************************************************
  1366. ;
  1367. ; ReadMftSectors - Read sectors from the MFT
  1368. ;
  1369. ; ENTRY: EAX == starting VBN
  1370. ; ECX == number of sectors to read
  1371. ; ES:EDI == Target buffer
  1372. ;
  1373. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1374. ;
  1375. ReadMftSectors proc near
  1376. SAVE_ALL
  1377. RMS$Again:
  1378. push eax ; save starting VBN
  1379. push ecx ; save sector count
  1380. ; Divide the VBN by SectorsPerCluster to get the VCN
  1381. xor edx, edx ; zero high part of dividend
  1382. movzx ebx, SectorsPerCluster
  1383. div ebx ; eax = VCN
  1384. push edx ; save remainder
  1385. push edi ; save the target buffer
  1386. call ComputeMftLcn ; eax = LCN
  1387. pop edi ; recover the buffer
  1388. or eax, eax ; LCN equal to zero?
  1389. jz BootErr$he ; zero is not a possible LCN
  1390. ; Change the LCN back into a LBN and add the remainder back in to get
  1391. ; the sector we want to read, which goes into SectorBase.
  1392. ;
  1393. movzx ebx, SectorsPerCluster
  1394. mul ebx ; eax = cluster first LBN
  1395. pop edx ; edx = sector remainder
  1396. add eax, edx ; eax = desired LBN
  1397. mov SectorBase, eax
  1398. ;
  1399. ; Figure out how many sectors to read this time; we never attempt
  1400. ; to read more than one cluster at a time.
  1401. ;
  1402. pop ecx ; ecx = sectors to read
  1403. movzx ebx, SectorsPerCluster
  1404. cmp ecx,ebx
  1405. jle RMS10
  1406. ;
  1407. ; Read only a single cluster at a time, to avoid problems with fragmented
  1408. ; runs in the mft.
  1409. ;
  1410. mov SectorCount, bx ; this time read 1 cluster
  1411. sub ecx, ebx ; ecx = sectors remaining to read
  1412. pop eax ; eax = VBN
  1413. add eax, ebx ; VBN += sectors this read
  1414. push eax ; save next VBN
  1415. push ecx ; save remaining sector count
  1416. jmp RMS20
  1417. RMS10:
  1418. pop eax ; eax = VBN
  1419. add eax, ecx ; VBN += sectors this read
  1420. push eax ; save next VBN
  1421. mov SectorCount, cx
  1422. mov ecx, 0
  1423. push ecx ; save remaining sector count (0)
  1424. RMS20:
  1425. ; The target buffer was passed in es:edi, but we want it in es:bx.
  1426. ; Do the conversion.
  1427. ;
  1428. push es ; save buffer pointer
  1429. push edi
  1430. mov bx, di
  1431. and bx, 0Fh
  1432. mov ax, es
  1433. shr edi, 4
  1434. add ax, di ; ax:bx -> target buffer
  1435. push ax
  1436. pop es ; es:bx -> target buffer
  1437. call ReadSectors
  1438. pop edi ; restore buffer pointer
  1439. pop es
  1440. add edi, BytesPerCluster ; increment buf ptr by one cluster
  1441. pop ecx ; restore remaining sector count
  1442. pop eax ; restore starting VBN
  1443. cmp ecx, 0 ; are we done?
  1444. jg RMS$Again ; repeat until desired == 0
  1445. RESTORE_ALL
  1446. ret
  1447. ReadMftSectors endp
  1448. ;****************************************************************************
  1449. ;
  1450. ; ReadFrs - Read an FRS
  1451. ;
  1452. ; ENTRY: EAX == FRS number
  1453. ; ES:EDI == Target buffer
  1454. ;
  1455. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1456. ;
  1457. ReadFrs proc near
  1458. SAVE_ALL
  1459. mul SectorsPerFrs ; eax = sector number in MFT DATA attribute
  1460. ; (note that mul wipes out edx!)
  1461. mov ecx, SectorsPerFrs ; number of sectors to read
  1462. call ReadMftSectors
  1463. call MultiSectorFixup
  1464. RESTORE_ALL
  1465. ret
  1466. ReadFrs endp
  1467. ;****************************************************************************
  1468. ;
  1469. ; ReadIndexBlock - read an index block from the root index.
  1470. ;
  1471. ; ENTRY: EAX == Block number
  1472. ;
  1473. ; USES: none (preserves all registers with SAVE_ALL/RESTORE_ALL)
  1474. ;
  1475. ReadIndexBlock proc near
  1476. SAVE_ALL
  1477. mul SectorsPerIndexBlock ; eax = first VBN to read
  1478. ; (note that mul wipes out edx!)
  1479. mov ebx, IndexAllocation ; ebx -> $INDEX_ALLOCATION attribute
  1480. mov ecx, SectorsPerIndexBlock ; ecx == Sectors to read
  1481. push ds
  1482. pop es
  1483. mov edi, IndexBlockBuffer ; es:edi -> index block buffer
  1484. call ReadIndexBlockSectors
  1485. call MultiSectorFixup
  1486. RESTORE_ALL
  1487. ret
  1488. ReadIndexBlock endp
  1489. ;****************************************************************************
  1490. ;
  1491. ; IsBlockInUse - Checks the index bitmap to see if an index
  1492. ; allocation block is in use.
  1493. ;
  1494. ; ENTRY: EAX == block number
  1495. ;
  1496. ; EXIT: Carry flag clear if block is in use
  1497. ; Carry flag set if block is not in use.
  1498. ;
  1499. IsBlockInUse proc near
  1500. push eax
  1501. push ebx
  1502. push ecx
  1503. mov ebx, IndexBitmapBuffer
  1504. mov ecx, eax ; ecx = block number
  1505. shr eax, 3 ; eax = byte number
  1506. and ecx, 7 ; ecx = bit number in byte
  1507. add ebx, eax ; ebx -> byte to test
  1508. mov eax, 1
  1509. shl eax, cl ; eax = mask
  1510. test byte ptr[ebx], al
  1511. jz IBU10
  1512. clc ; Block is not in use.
  1513. jmp IBU20
  1514. IBU10: stc ; Block is in use.
  1515. IBU20:
  1516. pop ecx
  1517. pop ebx
  1518. pop eax ; restore registers
  1519. ret
  1520. IsBlockInUse endp
  1521. ;****************************************************************************
  1522. ;
  1523. ; ComputeLcn - Converts a VCN into an LCN
  1524. ;
  1525. ; ENTRY: EAX -> VCN
  1526. ; EBX -> Attribute
  1527. ;
  1528. ; EXIT: EAX -> LCN (zero indicates not found)
  1529. ; ECX -> Remaining run length
  1530. ;
  1531. ; USES: ALL.
  1532. ;
  1533. ComputeLcn proc near
  1534. cmp [ebx].ATTR_FormCode, NONRESIDENT_FORM
  1535. je clcn10
  1536. sub eax, eax ; This is a resident attribute.
  1537. ret
  1538. clcn10: lea esi, [ebx].ATTR_FormUnion ; esi -> nonresident info of attrib
  1539. ; eax -> VCN
  1540. ; ebx -> Attribute
  1541. ; esi -> Nonresident information of attribute record
  1542. ;
  1543. ; See if the desired VCN is in range.
  1544. mov edx, [esi].NONRES_HighestVcn.LowPart ; edx = HighestVcn
  1545. cmp eax, edx
  1546. ja clcn15 ; VCN is greater than HighestVcn
  1547. mov edx, [esi].NONRES_LowestVcn.LowPart ; edx = LowestVcn
  1548. cmp eax, edx
  1549. jae clcn20
  1550. clcn15:
  1551. sub eax, eax ; VCN is not in range
  1552. ret
  1553. clcn20:
  1554. ; eax -> VCN
  1555. ; ebx -> Attribute
  1556. ; esi -> Nonresident information of attribute record
  1557. ; edx -> LowestVcn
  1558. ;
  1559. add bx, [esi].NONRES_MappingPairOffset ; ebx -> mapping pairs
  1560. sub esi, esi ; esi = 0
  1561. clcn30:
  1562. ; eax == VCN to find
  1563. ; ebx -> Current mapping pair count byte
  1564. ; edx == Current VCN
  1565. ; esi == Current LCN
  1566. ;
  1567. cmp byte ptr[ebx], 0 ; if count byte is zero...
  1568. je clcn99 ; ... we're done (and didn't find it)
  1569. ; Update CurrentLcn
  1570. ;
  1571. call LcnFromMappingPair
  1572. add esi, ecx ; esi = current lcn for this mapping pair
  1573. call VcnFromMappingPair
  1574. ; eax == VCN to find
  1575. ; ebx -> Current mapping pair count byte
  1576. ; ecx == DeltaVcn for current mapping pair
  1577. ; edx == Current VCN
  1578. ; esi == Current LCN
  1579. ;
  1580. add ecx, edx ; ecx = NextVcn
  1581. cmp eax, ecx ; If target < NextVcn ...
  1582. jl clcn80 ; ... we found the right mapping pair.
  1583. ; Go on to next mapping pair.
  1584. ;
  1585. mov edx, ecx ; CurrentVcn = NextVcn
  1586. push eax
  1587. movzx ecx, byte ptr[ebx] ; ecx = count byte
  1588. mov eax, ecx ; eax = count byte
  1589. and eax, 0fh ; eax = number of vcn bytes
  1590. shr ecx, 4 ; ecx = number of lcn bytes
  1591. add ebx, ecx
  1592. add ebx, eax
  1593. inc ebx ; ebx -> next count byte
  1594. pop eax
  1595. jmp clcn30
  1596. clcn80:
  1597. ; We found the mapping pair we want.
  1598. ;
  1599. ; eax == target VCN
  1600. ; ebx -> mapping pair count byte
  1601. ; edx == Starting VCN of run
  1602. ; ecx == Next VCN (ie. start of next run)
  1603. ; esi == starting LCN of run
  1604. ;
  1605. sub ecx, eax ; ecx = remaining run length
  1606. sub eax, edx ; eax = offset into run
  1607. add eax, esi ; eax = LCN to return
  1608. ret
  1609. ; The target VCN is not in this attribute.
  1610. clcn99: sub eax, eax ; Not found.
  1611. ret
  1612. ComputeLcn endp
  1613. ;****************************************************************************
  1614. ;
  1615. ; VcnFromMappingPair
  1616. ;
  1617. ; ENTRY: EBX -> Mapping Pair count byte
  1618. ;
  1619. ; EXIT: ECX == DeltaVcn from mapping pair
  1620. ;
  1621. ; USES: ECX
  1622. ;
  1623. VcnFromMappingPair proc near
  1624. sub ecx, ecx ; ecx = 0
  1625. mov cl, byte ptr[ebx] ; ecx = count byte
  1626. and cl, 0fh ; ecx = v
  1627. cmp ecx, 0 ; if ecx is zero, volume is corrupt.
  1628. jne VFMP5
  1629. sub ecx, ecx
  1630. ret
  1631. VFMP5:
  1632. push ebx
  1633. push edx
  1634. add ebx, ecx ; ebx -> last byte of compressed vcn
  1635. movsx edx, byte ptr[ebx]
  1636. dec ecx
  1637. dec ebx
  1638. ; ebx -> Next byte to add in
  1639. ; ecx == Number of bytes remaining
  1640. ; edx == Accumulated value
  1641. ;
  1642. VFMP10: cmp ecx, 0 ; When ecx == 0, we're done.
  1643. je VFMP20
  1644. shl edx, 8
  1645. mov dl, byte ptr[ebx]
  1646. dec ebx ; Back up through bytes to process.
  1647. dec ecx ; One less byte to process.
  1648. jmp VFMP10
  1649. VFMP20:
  1650. ; edx == Accumulated value to return
  1651. mov ecx, edx
  1652. pop edx
  1653. pop ebx
  1654. ret
  1655. VcnFromMappingPair endp
  1656. ;****************************************************************************
  1657. ;
  1658. ; LcnFromMappingPair
  1659. ;
  1660. ; ENTRY: EBX -> Mapping Pair count byte
  1661. ;
  1662. ; EXIT: ECX == DeltaLcn from mapping pair
  1663. ;
  1664. ; USES: ECX
  1665. ;
  1666. LcnFromMappingPair proc near
  1667. push ebx
  1668. push edx
  1669. sub edx, edx ; edx = 0
  1670. mov dl, byte ptr[ebx] ; edx = count byte
  1671. and edx, 0fh ; edx = v
  1672. sub ecx, ecx ; ecx = 0
  1673. mov cl, byte ptr[ebx] ; ecx = count byte
  1674. shr cl, 4 ; ecx = l
  1675. cmp ecx, 0 ; if ecx is zero, volume is corrupt.
  1676. jne LFMP5
  1677. sub ecx, ecx
  1678. pop edx
  1679. pop ebx
  1680. ret
  1681. LFMP5:
  1682. ; ebx -> count byte
  1683. ; ecx == l
  1684. ; edx == v
  1685. ;
  1686. add ebx, edx ; ebx -> last byte of compressed vcn
  1687. add ebx, ecx ; ebx -> last byte of compressed lcn
  1688. movsx edx, byte ptr[ebx]
  1689. dec ecx
  1690. dec ebx
  1691. ; ebx -> Next byte to add in
  1692. ; ecx == Number of bytes remaining
  1693. ; edx == Accumulated value
  1694. ;
  1695. LFMP10: cmp ecx, 0 ; When ecx == 0, we're done.
  1696. je LFMP20
  1697. shl edx, 8
  1698. mov dl, byte ptr[ebx]
  1699. dec ebx ; Back up through bytes to process.
  1700. dec ecx ; One less byte to process.
  1701. jmp LFMP10
  1702. LFMP20:
  1703. ; edx == Accumulated value to return
  1704. mov ecx, edx
  1705. pop edx
  1706. pop ebx
  1707. ret
  1708. LcnFromMappingPair endp
  1709. ;****************************************************************************
  1710. ;
  1711. ; UpcaseName - Converts the name of the file to all upper-case
  1712. ;
  1713. ; ENTRY: ESI -> Name
  1714. ; ECX -> Length of name
  1715. ;
  1716. ; USES: none
  1717. ;
  1718. UpcaseName proc near
  1719. or ecx, ecx
  1720. jnz UN5
  1721. ret
  1722. UN5:
  1723. push ecx
  1724. push esi
  1725. UN10:
  1726. cmp word ptr[esi], 'a' ; if it's less than 'a'
  1727. jl UN20 ; leave it alone
  1728. cmp word ptr[esi], 'z' ; if it's greater than 'z'
  1729. jg UN20 ; leave it alone.
  1730. sub word ptr[esi], 'a'-'A' ; the letter is lower-case--convert it.
  1731. UN20:
  1732. add esi, 2 ; move on to next unicode character
  1733. loop UN10
  1734. pop esi
  1735. pop ecx
  1736. ret
  1737. UpcaseName endp
  1738. ;****************************************************************************
  1739. ;
  1740. ; FindFile - Locates the index entry for a file in the root index.
  1741. ;
  1742. ; ENTRY: EAX -> name to find
  1743. ; ECX == length of file name in characters
  1744. ;
  1745. ; EXIT: EAX -> Index Entry. NULL to indicate failure.
  1746. ;
  1747. ; USES: ALL
  1748. ;
  1749. FindFile proc near
  1750. push eax ; name address
  1751. push ecx ; name length
  1752. ; First, search the index root.
  1753. ;
  1754. ; eax -> name to find
  1755. ; ecx == name length
  1756. ; TOS == name length
  1757. ; TOS+4 -> name to find
  1758. ;
  1759. mov edx, eax ; edx -> name to find
  1760. mov eax, IndexRoot ; eax -> &INDEX_ROOT attribute
  1761. lea ebx, [eax].ATTR_FormUnion ; ebx -> resident info
  1762. add ax, [ebx].RES_ValueOffset ; eax -> Index Root value
  1763. lea eax, [eax].IR_IndexHeader ; eax -> Index Header
  1764. mov ebx, edx ; ebx -> name to find
  1765. call LocateIndexEntry
  1766. or eax, eax
  1767. jz FindFile20
  1768. ; Found it in the root! The result is already in eax.
  1769. ; Clean up the stack and return.
  1770. ;
  1771. pop ecx
  1772. pop ecx
  1773. ret
  1774. FindFile20:
  1775. ;
  1776. ; We didn't find the index entry we want in the root, so we have to
  1777. ; crawl through the index allocation buffers.
  1778. ;
  1779. ; TOS == name length
  1780. ; TOS+4 -> name to find
  1781. ;
  1782. mov eax, IndexAllocation
  1783. or eax, eax
  1784. jnz FindFile30
  1785. ; There is no index allocation attribute; clean up
  1786. ; the stack and return failure.
  1787. ;
  1788. pop ecx
  1789. pop ecx
  1790. xor eax, eax
  1791. ret
  1792. FindFile30:
  1793. ;
  1794. ; Search the index allocation blocks for the name we want.
  1795. ; Instead of searching in tree order, we'll just start with
  1796. ; the last one and work our way backwards.
  1797. ;
  1798. ; TOS == name length
  1799. ; TOS+4 -> name to find
  1800. ;
  1801. mov edx, IndexAllocation ; edx -> index allocation attr.
  1802. lea edx, [edx].ATTR_FormUnion ; edx -> nonresident form info
  1803. mov eax, [edx].NONRES_HighestVcn.LowPart; eax = HighestVcn
  1804. inc eax ; eax = clusters in attribute
  1805. mov ebx, BytesPerCluster
  1806. mul ebx ; eax = bytes in attribute
  1807. xor edx, edx
  1808. div BytesPerIndexBlock ; convert bytes to index blocks
  1809. push eax ; number of blocks to process
  1810. FindFile40:
  1811. ;
  1812. ; TOS == remaining index blocks to search
  1813. ; TOS + 4 == name length
  1814. ; TOS + 8 -> name to find
  1815. ;
  1816. pop eax ; eax == number of remaining blocks
  1817. or eax, eax
  1818. jz FindFile90
  1819. dec eax ; eax == number of next block to process
  1820. ; and number of remaining blocks
  1821. push eax
  1822. ; eax == block number to process
  1823. ; TOS == remaining index blocks to search
  1824. ; TOS + 4 == name length
  1825. ; TOS + 8 -> name to find
  1826. ;
  1827. ; See if the block is in use; if not, go on to next.
  1828. call IsBlockInUse
  1829. jc FindFile40 ; c set if not in use
  1830. ; eax == block number to process
  1831. ; TOS == remaining index blocks to search
  1832. ; TOS + 4 == name length
  1833. ; TOS + 8 -> name to find
  1834. ;
  1835. call ReadIndexBlock
  1836. pop edx ; edx == remaining buffers to search
  1837. pop ecx ; ecx == name length
  1838. pop ebx ; ebx -> name
  1839. push ebx
  1840. push ecx
  1841. push edx
  1842. ; ebx -> name to find
  1843. ; ecx == name length in characters
  1844. ; TOS == remaining blocks to process
  1845. ; TOS + 4 == name length
  1846. ; TOS + 8 -> name
  1847. ;
  1848. ; Index buffer to search is in index allocation block buffer.
  1849. ;
  1850. mov eax, IndexBlockBuffer ; eax -> Index allocation block
  1851. lea eax, [eax].IB_IndexHeader ; eax -> Index Header
  1852. call LocateIndexEntry ; eax -> found entry
  1853. or eax, eax
  1854. jz FindFile40
  1855. ; Found it!
  1856. ;
  1857. ; eax -> Found entry
  1858. ; TOS == remaining blocks to process
  1859. ; TOS + 4 == name length
  1860. ; TOS + 8 -> name
  1861. ;
  1862. pop ecx
  1863. pop ecx
  1864. pop ecx ; clean up stack
  1865. ret
  1866. FindFile90:
  1867. ;
  1868. ; Name not found.
  1869. ;
  1870. ; TOS == name length
  1871. ; TOS + 4 -> name to find
  1872. ;
  1873. pop ecx
  1874. pop ecx ; clean up stack.
  1875. xor eax, eax ; zero out eax.
  1876. ret
  1877. FindFile endp
  1878. ifdef DEBUG
  1879. ;****************************************************************************
  1880. ;
  1881. ; DumpIndexBlock - dumps the index block buffer
  1882. ;
  1883. DumpIndexBlock proc near
  1884. SAVE_ALL
  1885. mov esi, IndexBlockBuffer
  1886. mov ecx, 20h ; dwords to dump
  1887. DIB10:
  1888. test ecx, 3
  1889. jnz DIB20
  1890. call DebugNewLine
  1891. DIB20:
  1892. lodsd
  1893. call PrintNumber
  1894. loop DIB10
  1895. RESTORE_ALL
  1896. ret
  1897. DumpIndexBlock endp
  1898. ;****************************************************************************
  1899. ;
  1900. ; DebugNewLine
  1901. ;
  1902. DebugNewLine proc near
  1903. SAVE_ALL
  1904. xor eax, eax
  1905. xor ebx, ebx
  1906. mov al, 0dh
  1907. mov ah, 14
  1908. mov bx, 7
  1909. int 10h
  1910. mov al, 0ah
  1911. mov ah, 14
  1912. mov bx, 7
  1913. int 10h
  1914. RESTORE_ALL
  1915. ret
  1916. DebugNewLine endp
  1917. ;****************************************************************************
  1918. ;
  1919. ; PrintName - Display a unicode name
  1920. ;
  1921. ; ENTRY: DS:ESI -> null-terminated string
  1922. ; ECX == characters in string
  1923. ;
  1924. ; USES: None.
  1925. ;
  1926. PrintName proc near
  1927. SAVE_ALL
  1928. or ecx, ecx
  1929. jnz PrintName10
  1930. call DebugNewLine
  1931. RESTORE_ALL
  1932. ret
  1933. PrintName10:
  1934. xor eax, eax
  1935. xor ebx, ebx
  1936. lodsw
  1937. mov ah, 14 ; write teletype
  1938. mov bx, 7 ; attribute
  1939. int 10h ; print it
  1940. loop PrintName10
  1941. call DebugNewLine
  1942. RESTORE_ALL
  1943. ret
  1944. PrintName endp
  1945. ;****************************************************************************
  1946. ;
  1947. ; DebugPrint - Display a debug string.
  1948. ;
  1949. ; ENTRY: DS:SI -> null-terminated string
  1950. ;
  1951. ; USES: None.
  1952. ;
  1953. .286
  1954. DebugPrint proc near
  1955. pusha
  1956. DbgPr20:
  1957. lodsb
  1958. cmp al, 0
  1959. je DbgPr30
  1960. mov ah, 14 ; write teletype
  1961. mov bx, 7 ; attribute
  1962. int 10h ; print it
  1963. jmp DbgPr20
  1964. DbgPr30:
  1965. popa
  1966. nop
  1967. ret
  1968. DebugPrint endp
  1969. ;****************************************************************************
  1970. ;
  1971. ;
  1972. ; PrintNumber
  1973. ;
  1974. ; ENTRY: EAX == number to print
  1975. ;
  1976. ; PRESERVES ALL REGISTERS
  1977. ;
  1978. .386
  1979. PrintNumber proc near
  1980. SAVE_ALL
  1981. mov ecx, 8 ; number of digits in a DWORD
  1982. PrintNumber10:
  1983. mov edx, eax
  1984. and edx, 0fh ; edx = lowest-order digit
  1985. push edx ; put it on the stack
  1986. shr eax, 4 ; drop low-order digit
  1987. loop PrintNumber10
  1988. mov ecx, 8 ; number of digits on stack.
  1989. PrintNumber20:
  1990. pop eax ; eax = next digit to print
  1991. cmp eax, 9
  1992. jg PrintNumber22
  1993. add eax, '0'
  1994. jmp PrintNumber25
  1995. PrintNumber22:
  1996. sub eax, 10
  1997. add eax, 'A'
  1998. PrintNumber25:
  1999. xor ebx, ebx
  2000. mov ah, 14
  2001. mov bx, 7
  2002. int 10h
  2003. loop PrintNumber20
  2004. ; Print a space to separate numbers
  2005. mov al, ' '
  2006. mov ah, 14
  2007. mov bx, 7
  2008. int 10h
  2009. RESTORE_ALL
  2010. call Pause
  2011. ret
  2012. PrintNumber endp
  2013. ;****************************************************************************
  2014. ;
  2015. ; Debug0 - Print debug string 0 -- used for checkpoints in mainboot
  2016. ;
  2017. Debug0 proc near
  2018. SAVE_ALL
  2019. mov si, offset DbgString0
  2020. call BootErr$Print1
  2021. RESTORE_ALL
  2022. ret
  2023. Debug0 endp
  2024. ;****************************************************************************
  2025. ;
  2026. ; Debug1 - Print debug string 1 --
  2027. ;
  2028. Debug1 proc near
  2029. SAVE_ALL
  2030. mov si, offset DbgString1
  2031. call BootErr$Print1
  2032. RESTORE_ALL
  2033. ret
  2034. Debug1 endp
  2035. ;****************************************************************************
  2036. ;
  2037. ; Debug2 - Print debug string 2
  2038. ;
  2039. Debug2 proc near
  2040. SAVE_ALL
  2041. mov si, offset DbgString2
  2042. call BootErr$Print1
  2043. RESTORE_ALL
  2044. ret
  2045. Debug2 endp
  2046. ;****************************************************************************
  2047. ;
  2048. ; Debug3 - Print debug string 3 --
  2049. ;
  2050. Debug3 proc near
  2051. SAVE_ALL
  2052. mov si, offset DbgString3
  2053. call BootErr$Print1
  2054. RESTORE_ALL
  2055. ret
  2056. Debug3 endp
  2057. ;****************************************************************************
  2058. ;
  2059. ; Debug4 - Print debug string 4
  2060. ;
  2061. Debug4 proc near
  2062. SAVE_ALL
  2063. mov si, offset DbgString4
  2064. call BootErr$Print1
  2065. RESTORE_ALL
  2066. ret
  2067. Debug4 endp
  2068. ;****************************************************************************
  2069. ;
  2070. ; Pause - Pause for about 1/2 a second. Simply count until you overlap
  2071. ; to zero.
  2072. ;
  2073. Pause proc near
  2074. push eax
  2075. mov eax, 0fff10000h
  2076. PauseLoopy:
  2077. inc eax
  2078. or eax, eax
  2079. jnz PauseLoopy
  2080. pop eax
  2081. ret
  2082. Pause endp
  2083. endif ; DEBUG
  2084. ;*************************************************************************
  2085. ;
  2086. ; LoadIndexFrs - For the requested index type code locate and
  2087. ; load the associated Frs.
  2088. ;
  2089. ; ENTRY: EAX - requested index type code
  2090. ; ECX - Points to empty Frs buffer
  2091. ;
  2092. ; EXIT: EAX - points to offset in Frs buffer of requested index type
  2093. ; code or Zero if not found.
  2094. ; USES: All
  2095. ;
  2096. LoadIndexFrs proc near
  2097. push ecx ; save FRS buffer for later
  2098. push eax ; save index type code for later
  2099. mov eax, ROOT_FILE_NAME_INDEX_NUMBER
  2100. push ds
  2101. pop es
  2102. mov edi, ecx ; es:edi = target buffer
  2103. call ReadFrs
  2104. mov eax, ecx ; FRS to search
  2105. pop ebx ; Attribute type code
  2106. push ebx
  2107. movzx ecx, index_name_length ; Attribute name length
  2108. mov edx, offset index_name ; Attribute name
  2109. call LocateAttributeRecord
  2110. pop ebx
  2111. pop ecx
  2112. or eax, eax
  2113. jnz LoadIndexFrs$Exit ; if found in root return
  2114. ;
  2115. ; if not found in current Frs, search in attribute list
  2116. ;
  2117. ; EBX - holds Attribute type code
  2118. mov eax, ecx ; FRS to search
  2119. mov ecx, ebx ; type code
  2120. push eax ; save Frs
  2121. push ebx ; save type code
  2122. call SearchAttrList ; search attribute list for FRN
  2123. ; of specified ($INDEX_ROOT,
  2124. ; $INDEX_ALLOCATION, or $BITMAP)
  2125. ; EAX - holds FRN for Frs, or Zero
  2126. pop ebx ; Attribute type code (used later)
  2127. pop edi ; es:edi = target buffer
  2128. or eax, eax ; if we cann't find it in attribute
  2129. jz LoadIndexFrs$Exit ; list then we are hosed
  2130. ; We should now have the File Record Number where the index for the
  2131. ; specified type code we are searching for is, load this into the
  2132. ; Frs target buffer.
  2133. ;
  2134. ; EAX - holds FRN
  2135. ; EBX - holds type code
  2136. ; EDI - holds target buffer
  2137. push ds
  2138. pop es
  2139. call ReadFrs
  2140. ;
  2141. ; Now determine the offset in the Frs of the index
  2142. ;
  2143. ; EBX - holds type code
  2144. mov eax, edi ; Frs to search
  2145. movzx ecx, index_name_length ; Attribute name length
  2146. mov edx, offset index_name ; Attribute name
  2147. call LocateAttributeRecord
  2148. ; EAX - holds offset or Zero.
  2149. LoadIndexFrs$Exit:
  2150. ret
  2151. LoadIndexFrs endp
  2152. ;****************************************************************************
  2153. ;
  2154. ; SearchAttrList
  2155. ;
  2156. ; Search the Frs for the attribute list. Then search the attribute list
  2157. ; for the specifed type code. When you find it return the FRN in the
  2158. ; attribute list entry found or Zero if no match found.
  2159. ;
  2160. ; ENTRY: ECX - type code to search attrib list for
  2161. ; EAX - Frs buffer holding head of attribute list
  2162. ; EXIT: EAX - FRN file record number to load, Zero if none.
  2163. ;
  2164. ; USES: All
  2165. ;
  2166. SearchAttrList proc near
  2167. push ecx ; type code to search for in
  2168. ; attrib list
  2169. ; EAX - holds Frs to search
  2170. mov ebx, $ATTRIBUTE_LIST ; Attribute type code
  2171. mov ecx, 0 ; Attribute name length
  2172. mov edx, 0 ; Attribute name
  2173. call LocateAttributeRecord
  2174. or eax, eax ; If there's no Attribute list,
  2175. jz SearchAttrList$NotFoundIndex1 ; We are done
  2176. ; Read the attribute list.
  2177. ; eax -> attribute list attribute
  2178. mov ebx, eax ; ebx -> attribute list attribute
  2179. push ds
  2180. pop es ; copy ds into es
  2181. mov edi, AttrList ; ds:edi->attribute list buffer
  2182. call ReadWholeAttribute
  2183. push ds
  2184. pop es
  2185. mov ebx, AttrList ; es:ebx -> first attribute list entry
  2186. ; Now, traverse the attribute list looking for the entry for
  2187. ; the Index type code.
  2188. ;
  2189. ; ebx -> first attribute list entry
  2190. ;
  2191. pop ecx ; Get Index Type code
  2192. SearchAttrList$LookingForIndex:
  2193. ifdef DEBUG
  2194. SAVE_ALL
  2195. mov eax, es:[bx].ATTRLIST_TypeCode
  2196. call PrintNumber
  2197. movzx eax, es:[bx].ATTRLIST_Length
  2198. call PrintNumber
  2199. mov eax, es
  2200. call PrintNumber
  2201. mov eax, ebx
  2202. call PrintNumber
  2203. push es
  2204. pop ds
  2205. movzx ecx, es:[bx].ATTRLIST_NameLength ; ecx = chars in name
  2206. lea esi, es:[bx].ATTRLIST_Name ; esi -> name
  2207. call PrintName
  2208. RESTORE_ALL
  2209. endif ; DEBUG
  2210. cmp es:[bx].ATTRLIST_TypeCode, ecx
  2211. je SearchAttrList$FoundIndex
  2212. cmp es:[bx].ATTRLIST_TypeCode, $END ; reached invalid attribute
  2213. je SearchAttrList$NotFoundIndex2 ; so must be at end
  2214. cmp es:[bx].ATTRLIST_Length, 0
  2215. je SearchAttrList$NotFoundIndex2 ; reached end of list and
  2216. ; nothing found
  2217. movzx eax, es:[bx].ATTRLIST_Length
  2218. add bx, ax
  2219. mov ax, bx
  2220. and ax, 08000h ; test for roll over
  2221. jz SearchAttrList$LookingForIndex
  2222. ; If we rolled over then increment to the next es 32K segment and
  2223. ; zero off the high bits of bx
  2224. mov ax, es
  2225. add ax, 800h
  2226. mov es, ax
  2227. and bx, 07fffh
  2228. jmp SearchAttrList$LookingForIndex
  2229. SearchAttrList$FoundIndex:
  2230. ; found the index, return the FRN
  2231. mov eax, es:[bx].ATTRLIST_SegmentReference.REF_LowPart
  2232. ret
  2233. SearchAttrList$NotFoundIndex1:
  2234. pop ecx
  2235. SearchAttrList$NotFoundIndex2:
  2236. xor eax, eax
  2237. ret
  2238. SearchAttrList endp
  2239. ;
  2240. ; Boot message printing, relocated from sector 0 to sace space
  2241. ;
  2242. BootErr$fnf:
  2243. mov al,byte ptr TXT_MSG_SYSINIT_FILE_NOT_FD
  2244. jmp BootErr2
  2245. BootErr$ntc:
  2246. mov al,byte ptr TXT_MSG_SYSINIT_NTLDR_CMPRS
  2247. jmp BootErr2
  2248. ifdef DEBUG
  2249. DbgString0 db "Debug Point 0", 0Dh, 0Ah, 0
  2250. DbgString1 db "Debug Point 1", 0Dh, 0Ah, 0
  2251. DbgString2 db "Debug Point 2", 0Dh, 0Ah, 0
  2252. DbgString3 db "Debug Point 3", 0Dh, 0Ah, 0
  2253. DbgString4 db "Debug Point 4", 0Dh, 0Ah, 0
  2254. endif ; DEBUG
  2255. .errnz ($-_ntfsboot) GT 8192 ; <FATAL PROBLEM: main boot record exceeds available space>
  2256. org 8192
  2257. BootCode ends
  2258. end _ntfsboot