;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2016. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; $Revision$ ; NTFS driver ; Basic concepts: ; File is a FileRecord in the $MFT. ; $MFT is a file, that consists of FileRecords and starts with FileRecord of itself. ; FileRecord (FILE) consists of a header and attributes. ; Attribute consists of a header and a body. ; Attribute's body can be inside (resident) or outside of FileRecord. ; File's data is a body of $Data (80h) attribute. ; FileRecords is a data of the $MFT file. ; Directory is a file, that consists of index nodes. ; Resident index node is always located in a body of $IndexRoot (90h) attribute. ; Body of $IndexAllocation (A0h) attribute is always non resident ; and consists of IndexRecords. ; IndexRecord (INDX) consists of a header and an index node. ; Index node consists of a header and indexes. ; Index consists of a header and a copy of indexed attribute's body. ; Directories index $Filename (30h) attribute of all existing files. ; $IndexRoot and $IndexAllocation attributes of a directory has a name — $I30. ; Offsets: ; record header magic = 0 updateSequenceOffset = 4 updateSequenceSize = 6 ; FileRecord header reuseCounter = 16 hardLinkCounter = 12h attributeOffset = 14h recordFlags = 16h recordRealSize = 18h recordAllocatedSize = 1ch baseRecordReference = 20h ; for auxiliary records baseRecordReuse = 26h newAttributeID = 28h ; attribute header attributeType = 0 sizeWithHeader = 4 nonResidentFlag = 8 nameLength = 9 nameOffset = 10 attributeFlags = 12 attributeID = 14 ; resident attribute header sizeWithoutHeader = 10h attributeOffset = 14h indexedFlag = 16h ; non resident attribute header firstVCN = 10h lastVCN = 18h dataRunsOffset = 20h attributeAllocatedSize = 28h attributeRealSize = 30h initialDataSize = 38h ; $IndexRoot indexedAttributesType = 0 collationRule = 4 indexRecordSize = 8 indexRecordSizeClus = 12 ; in sectors if less than one cluster rootNode = 16 ; IndexRecord header recordVCN = 16 recordNode = 18h ; node header indexOffset = 0 nodeRealSize = 4 nodeAllocatedSize = 8 nonLeafFlag = 12 ; $Filename index fileRecordReference = 0 fileReferenceReuse = 6 indexAllocatedSize = 8 indexRawSize = 10 indexFlags = 12 directoryRecordReference = 16 directoryReferenceReuse = 16h fileCreated = 18h fileModified = 20h recordModified = 28h fileAccessed = 30h fileAllocatedSize = 38h fileRealSize = 40h fileFlags = 48h fileNameLength = 50h namespace = 51h fileName = 52h struct NTFS PARTITION Lock MUTEX ? ; Currently operations with one partition ; can not be executed in parallel since the legacy code is not ready. sectors_per_cluster dd ? mft_cluster dd ? ; location mftmirr_cluster dd ? ; location frs_size dd ? ; in bytes frs_buffer dd ? ; MFT fileRecord buffer mft_retrieval_end dd ? mftSize dd ? ; in sectors cur_index_size dd ? ; in sectors cur_index_buf dd ? ; index node buffer secondIndexBuffer dd ? BitmapBuffer dd ? BitmapTotalSize dd ? ; bytes reserved BitmapSize dd ? ; bytes readen BitmapLocation dd ? ; starting sector BitmapStart dd ? ; first byte after area, reserved for MFT mftBitmapBuffer dd ? ; one cluster mftBitmapSize dd ? ; bytes readen mftBitmapLocation dd ? ; starting sector attr_size dq ? attr_offs dd ? attr_list dd ? attr_iRecord dd ? attr_iBaseRecord dd ? cur_attr dd ? ; attribute type cur_iRecord dd ? ; number of fileRecord in MFT cur_offs dd ? ; attribute VCN in sectors cur_size dd ? ; max sectors to read cur_buf dd ? cur_read dd ? ; bytes readen cur_tail dd ? cur_subnode_size dd ? LastRead dd ? ; last readen block of sectors rootLastRead dd ? nodeLastRead dd ? indexRoot dd ? indexPointer dd ? newRecord dd ? fileDataStart dd ? ; starting cluster fileDataSize dd ? ; in clusters fileDataBuffer dd ? fileRealSize dd ? ; in bytes fragmentCount db ? bCanContinue db ? bFolder db ? bWriteAttr db ? ; Warning: Don't forget to turn off!!! align 256 mft_retrieval rb 768 attrlist_buf rb 1024 attrlist_mft_buf rb 1024 bitmap_buf rb 1024 ends ; NTFS external functions ; in: ; ebx -> parameter structure of sysfunc 70 ; ebp -> NTFS structure ; [esi]+[esp+4] = name ; out: ; eax, ebx = return values for sysfunc 70 iglobal align 4 ntfs_user_functions: dd ntfs_free dd (ntfs_user_functions_end - ntfs_user_functions - 4) / 4 dd ntfs_ReadFile dd ntfs_ReadFolder dd ntfs_CreateFile dd ntfs_WriteFile dd ntfs_SetFileEnd dd ntfs_GetFileInfo dd ntfs_SetFileInfo dd 0 dd ntfs_Delete dd ntfs_CreateFolder ntfs_user_functions_end: endg ntfs_test_bootsec: ; in: ebx -> buffer, edx = size of partition ; out: CF=1 -> invalid ; 1. Name=='NTFS ' cmp dword [ebx+3], 'NTFS' jnz .no cmp dword [ebx+7], ' ' jnz .no ; 2. Number of bytes per sector is the same as for physical device ; (that is, 0x200 for hard disk) cmp word [ebx+11], 0x200 jnz .no ; 3. Number of sectors per cluster must be power of 2 movzx eax, byte [ebx+13] dec eax js .no test al, [ebx+13] jnz .no ; 4. FAT parameters must be zero cmp word [ebx+14], 0 jnz .no cmp dword [ebx+16], 0 jnz .no cmp byte [ebx+20], 0 jnz .no cmp word [ebx+22], 0 jnz .no cmp dword [ebx+32], 0 jnz .no ; 5. Number of sectors <= partition size cmp dword [ebx+0x2C], 0 ja .no cmp [ebx+0x28], edx ja .no ; 6. $MFT and $MFTMirr clusters must be within partition cmp dword [ebx+0x34], 0 ja .no push edx movzx eax, byte [ebx+13] mul dword [ebx+0x30] test edx, edx pop edx jnz .no cmp eax, edx ja .no cmp dword [ebx+0x3C], 0 ja .no push edx movzx eax, byte [ebx+13] mul dword [ebx+0x38] test edx, edx pop edx jnz .no cmp eax, edx ja .no ; 7. Clusters per FRS must be either power of 2 or between -31 and -9 movsx eax, byte [ebx+0x40] cmp al, -31 jl .no cmp al, -9 jle @f dec eax js .no test [ebx+0x40], al jnz .no @@: ; 8. Same for clusters per IndexAllocationBuffer movsx eax, byte [ebx+0x44] cmp al, -31 jl .no cmp al, -9 jle @f dec eax js .no test [ebx+0x44], al jnz .no @@: ; OK, this is correct NTFS bootsector clc ret .no: ; No, this bootsector isn't NTFS stc ret ntfs_create_partition: cmp dword [esi+DISK.MediaInfo.SectorSize], 512 jnz .nope mov edx, dword [ebp+PARTITION.Length] cmp dword [esp+4], 0 jz .boot_read_ok add ebx, 512 lea eax, [edx-1] call fs_read32_sys test eax, eax jnz @f call ntfs_test_bootsec jnc .ntfs_setup @@: mov eax, edx shr eax, 1 call fs_read32_sys test eax, eax jnz .nope .boot_read_ok: call ntfs_test_bootsec jnc .ntfs_setup .nope: xor eax, eax jmp .exit .ntfs_setup: ; By given bootsector, initialize some NTFS variables stdcall kernel_alloc, 1000h test eax, eax jz .exit mov ecx, dword [ebp+PARTITION.FirstSector] mov dword [eax+NTFS.FirstSector], ecx mov ecx, dword [ebp+PARTITION.FirstSector+4] mov dword [eax+NTFS.FirstSector+4], ecx mov ecx, [ebp+PARTITION.Disk] mov [eax+NTFS.Disk], ecx mov [eax+NTFS.FSUserFunctions], ntfs_user_functions mov [eax+NTFS.bWriteAttr], 0 push ebx ebp esi mov ebp, eax lea ecx, [ebp+NTFS.Lock] call mutex_init movzx eax, byte [ebx+13] mov [ebp+NTFS.sectors_per_cluster], eax mov eax, [ebx+0x28] mov dword [ebp+NTFS.Length], eax and dword [ebp+NTFS.Length+4], 0 mov eax, [ebx+0x30] mov [ebp+NTFS.mft_cluster], eax mov eax, [ebx+0x38] mov [ebp+NTFS.mftmirr_cluster], eax movsx eax, byte [ebx+0x40] test eax, eax js @f mul [ebp+NTFS.sectors_per_cluster] shl eax, 9 jmp .1 @@: neg eax mov ecx, eax mov eax, 1 shl eax, cl .1: mov [ebp+NTFS.frs_size], eax stdcall kernel_alloc, eax test eax, eax jz .fail_free mov [ebp+NTFS.frs_buffer], eax ; read $MFT disposition mov eax, [ebp+NTFS.mft_cluster] mul [ebp+NTFS.sectors_per_cluster] mov ecx, [ebp+NTFS.frs_size] shr ecx, 9 mov ebx, [ebp+NTFS.frs_buffer] call fs_read64_sys test eax, eax jnz .usemirr cmp dword [ebx], 'FILE' jnz .usemirr call ntfs_restore_usa_frs jnc .mftok .usemirr: mov eax, [ebp+NTFS.mftmirr_cluster] mul [ebp+NTFS.sectors_per_cluster] mov ecx, [ebp+NTFS.frs_size] shr ecx, 9 mov ebx, [ebp+NTFS.frs_buffer] call fs_read64_sys test eax, eax jnz .fail_free_frs cmp dword [ebx], 'FILE' jnz .fail_free_frs call ntfs_restore_usa_frs jc .fail_free_frs .mftok: ; prepare $MFT retrieval information ; search for unnamed non-resident $DATA attribute movzx eax, word [ebx+attributeOffset] add eax, ebx .scandata: cmp dword [eax], -1 jz .fail_free_frs cmp dword [eax], 0x80 jnz @f cmp byte [eax+nameLength], 0 jz .founddata @@: add eax, [eax+sizeWithHeader] jmp .scandata .founddata: cmp byte [eax+nonResidentFlag], 0 jz .fail_free_frs movzx esi, word [eax+dataRunsOffset] add esi, eax mov edx, [eax+attributeAllocatedSize+4] mov eax, [eax+attributeAllocatedSize] shrd eax, edx, 9 mov [ebp+NTFS.mftSize], eax sub esp, 10h lea ecx, [ebp+NTFS.mft_retrieval] xor edx, edx .scanmcb: ; load descriptions of fragments call ntfs_decode_mcb_entry jnc .scanmcbend mov eax, [esp] ; block length mov [ecx], eax add edx, [esp+8] ; block addr mov [ecx+4], edx add ecx, 8 jmp .scanmcb .scanmcbend: add esp, 10h lea eax, [ebp+NTFS.attrlist_buf] cmp eax, ecx jc @f mov eax, ecx @@: mov [ebp+NTFS.mft_retrieval_end], eax ; allocate index buffers stdcall kernel_alloc, 2000h test eax, eax jz .fail_free_frs mov [ebp+NTFS.cur_index_buf], eax add eax, 1000h mov [ebp+NTFS.secondIndexBuffer], eax mov [ebp+NTFS.cur_index_size], 8 ; reserve adress space for bitmap buffer and load some part of bitmap mov eax, dword [ebp+NTFS.Length] xor edx, edx div [ebp+NTFS.sectors_per_cluster] shr eax, 3 mov [ebp+NTFS.BitmapTotalSize], eax add eax, 7FFFh and eax, not 7FFFh push eax call alloc_kernel_space test eax, eax jz .failFreeIndex mov [ebp+NTFS.BitmapBuffer], eax mov [ebp+NTFS.cur_buf], eax mov eax, [ebp+NTFS.BitmapTotalSize] add eax, [ebp+NTFS.mft_cluster] shr eax, 3+2 ; reserve 1/8 of partition for $MFT shl eax, 2 mov [ebp+NTFS.BitmapStart], eax shr eax, 15 inc eax shl eax, 3 push eax push eax shl eax, 3 mov [ebp+NTFS.cur_size], eax call alloc_pages test eax, eax pop ecx jz .failFreeBitmap add eax, 3 mov ebx, [ebp+NTFS.BitmapBuffer] call commit_pages mov [ebp+NTFS.cur_iRecord], 6 mov [ebp+NTFS.cur_attr], 0x80 mov [ebp+NTFS.cur_offs], 0 call ntfs_read_attr jc .failFreeBitmap mov eax, [ebp+NTFS.cur_read] mov [ebp+NTFS.BitmapSize], eax mov eax, [ebp+NTFS.LastRead] mov [ebp+NTFS.BitmapLocation], eax ; read MFT $BITMAP attribute mov eax, [ebp+NTFS.sectors_per_cluster] mov [ebp+NTFS.cur_size], eax shl eax, 9 stdcall kernel_alloc, eax test eax, eax jz .failFreeBitmap mov [ebp+NTFS.mftBitmapBuffer], eax mov [ebp+NTFS.cur_buf], eax mov [ebp+NTFS.cur_iRecord], 0 mov [ebp+NTFS.cur_attr], 0xB0 mov [ebp+NTFS.cur_offs], 0 call ntfs_read_attr mov eax, [ebp+NTFS.cur_read] cmp eax, 4 jc .failFreeBitmapMFT mov ecx, [ebp+NTFS.attr_offs] cmp byte [ecx+nonResidentFlag], 1 jnz .failFreeBitmapMFT mov [ebp+NTFS.mftBitmapSize], eax mov eax, [ebp+NTFS.LastRead] mov [ebp+NTFS.mftBitmapLocation], eax mov eax, ebp .pop_exit: pop esi ebp ebx .exit: cmp dword [esp+4], 0 jz @f sub ebx, 512 @@: ret .failFreeBitmapMFT: stdcall kernel_free, [ebp+NTFS.mftBitmapBuffer] .failFreeBitmap: stdcall kernel_free, [ebp+NTFS.BitmapBuffer] .failFreeIndex: mov eax, [ebp+NTFS.cur_index_buf] cmp eax, [ebp+NTFS.secondIndexBuffer] jc @f mov eax, [ebp+NTFS.secondIndexBuffer] @@: stdcall kernel_free, eax .fail_free_frs: stdcall kernel_free, [ebp+NTFS.frs_buffer] .fail_free: stdcall kernel_free, ebp xor eax, eax jmp .pop_exit ntfs_free: push ebx mov ebx, eax stdcall kernel_free, [ebx+NTFS.frs_buffer] stdcall kernel_free, [ebx+NTFS.mftBitmapBuffer] stdcall kernel_free, [ebx+NTFS.BitmapBuffer] mov eax, [ebx+NTFS.cur_index_buf] cmp eax, [ebx+NTFS.secondIndexBuffer] jc @f mov eax, [ebx+NTFS.secondIndexBuffer] @@: stdcall kernel_free, eax stdcall kernel_free, ebx pop ebx ret ntfs_lock: lea ecx, [ebp+NTFS.Lock] jmp mutex_lock ntfs_unlock: lea ecx, [ebp+NTFS.Lock] jmp mutex_unlock ntfs_read_attr: ; [ebp+NTFS.bWriteAttr]=1 -> write attribute ; in: ; [ebp+NTFS.cur_iRecord] = number of fileRecord ; [ebp+NTFS.cur_attr] = attribute type ; [ebp+NTFS.cur_offs] = attribute VCN in sectors ; [ebp+NTFS.cur_buf] -> buffer for data ; [ebp+NTFS.cur_size] = max sectors to read ; out: ; [ebp+NTFS.cur_read] = bytes readen ; CF=1 -> failed, eax = disk error code, eax=0 -> something with FS xor eax, eax pushad and [ebp+NTFS.cur_read], 0 cmp [ebp+NTFS.cur_iRecord], 0 jnz .nomft cmp [ebp+NTFS.cur_attr], 0x80 jnz .nomft ; precalculated part of $Mft $DATA mov eax, [ebp+NTFS.cur_offs] xor edx, edx div [ebp+NTFS.sectors_per_cluster] mov ebx, edx ; eax = VCN, ebx = offset in sectors from beginning of cluster lea esi, [ebp+NTFS.mft_retrieval] sub esi, 8 .mftscan: add esi, 8 cmp esi, [ebp+NTFS.mft_retrieval_end] jz .nomft mov ecx, [esi+4] sub eax, [esi] jnc .mftscan add ecx, eax add ecx, [esi] neg eax mul [ebp+NTFS.sectors_per_cluster] xchg eax, ecx mul [ebp+NTFS.sectors_per_cluster] sub ecx, ebx add eax, ebx mov ebx, [ebp+NTFS.cur_buf] cmp ecx, [ebp+NTFS.cur_size] jb @f mov ecx, [ebp+NTFS.cur_size] @@: mov [ebp+NTFS.LastRead], eax mov edi, ecx call fs_read64_sys test eax, eax jnz .errret sub [ebp+NTFS.cur_size], edi add [ebp+NTFS.cur_offs], edi shl edi, 9 add [ebp+NTFS.cur_read], edi add [ebp+NTFS.cur_buf], edi xor eax, eax xor ebx, ebx cmp [ebp+NTFS.cur_size], eax jz @f jmp .mftscan .errret2_pop: xor eax, eax .errret_pop: pop ecx pop ecx .errret: mov [esp+28], eax stc @@: popad ret .nomft: ; 1. Read file record. ; N.B. This will do recursive call of read_attr for $MFT::$Data. mov eax, [ebp+NTFS.cur_iRecord] mov [ebp+NTFS.attr_iRecord], eax and [ebp+NTFS.attr_list], 0 or dword [ebp+NTFS.attr_size], -1 or dword [ebp+NTFS.attr_size+4], -1 or [ebp+NTFS.attr_iBaseRecord], -1 call ntfs_read_file_record jc .errret ; 2. Find required attribute. mov eax, [ebp+NTFS.frs_buffer] ; a) For auxiliary records, read base record. ; If base record is present, base iRecord may be 0 (for $Mft), ; but SequenceNumber is nonzero. cmp word [eax+baseRecordReuse], 0 jz @f mov eax, [eax+baseRecordReference] .beginfindattr: mov [ebp+NTFS.attr_iRecord], eax call ntfs_read_file_record jc .errret jmp @f .newAttribute: pushad and [ebp+NTFS.cur_read], 0 @@: ; b) Scan for required attribute and for $ATTR_LIST mov eax, [ebp+NTFS.frs_buffer] movzx ecx, word [eax+attributeOffset] add eax, ecx mov ecx, [ebp+NTFS.cur_attr] and [ebp+NTFS.attr_offs], 0 .scanattr: cmp dword [eax], -1 jz .scandone cmp dword [eax], ecx jz .okattr cmp [ebp+NTFS.attr_iBaseRecord], -1 jnz .scancont cmp dword [eax], 0x20 ; $ATTR_LIST jnz .scancont mov [ebp+NTFS.attr_list], eax jmp .scancont .okattr: ; ignore named $DATA attributes (aka NTFS streams) cmp ecx, 0x80 jnz @f cmp byte [eax+nameLength], 0 jnz .scancont @@: mov [ebp+NTFS.attr_offs], eax .scancont: add eax, [eax+sizeWithHeader] jmp .scanattr .continue: pushad and [ebp+NTFS.cur_read], 0 .scandone: ; c) Check for required offset and length mov ecx, [ebp+NTFS.attr_offs] jecxz .noattr push [ebp+NTFS.cur_size] push [ebp+NTFS.cur_read] call .doreadattr pop edx pop ecx jc .ret cmp [ebp+NTFS.bCanContinue], 0 jz .ret sub edx, [ebp+NTFS.cur_read] neg edx shr edx, 9 sub ecx, edx mov [ebp+NTFS.cur_size], ecx jz .ret .noattr: cmp [ebp+NTFS.cur_attr], 0x20 jz @f mov ecx, [ebp+NTFS.attr_list] test ecx, ecx jnz .lookattr and dword [esp+28], 0 cmp [ebp+NTFS.attr_offs], 1 ; define CF .ret: popad ret .lookattr: ; required attribute or required offset was not found in base record; ; it may be present in auxiliary records; ; scan $ATTR_LIST mov eax, [ebp+NTFS.attr_iBaseRecord] cmp eax, -1 jz @f call ntfs_read_file_record jc .errret or [ebp+NTFS.attr_iBaseRecord], -1 @@: push [ebp+NTFS.cur_offs] push [ebp+NTFS.cur_size] push [ebp+NTFS.cur_read] push [ebp+NTFS.cur_buf] push dword [ebp+NTFS.attr_size] push dword [ebp+NTFS.attr_size+4] or dword [ebp+NTFS.attr_size], -1 or dword [ebp+NTFS.attr_size+4], -1 and [ebp+NTFS.cur_offs], 0 mov [ebp+NTFS.cur_size], 2 and [ebp+NTFS.cur_read], 0 lea eax, [ebp+NTFS.attrlist_buf] cmp [ebp+NTFS.cur_iRecord], 0 jnz @f lea eax, [ebp+NTFS.attrlist_mft_buf] @@: mov [ebp+NTFS.cur_buf], eax push eax call .doreadattr pop esi mov edx, 1 pop dword [ebp+NTFS.attr_size+4] pop dword [ebp+NTFS.attr_size] mov ecx, [ebp+NTFS.cur_read] pop [ebp+NTFS.cur_buf] pop [ebp+NTFS.cur_read] pop [ebp+NTFS.cur_size] pop [ebp+NTFS.cur_offs] jc .errret or edi, -1 lea ecx, [ecx+esi-1Ah] .scanliststart: push ecx mov eax, [ebp+NTFS.cur_attr] .scanlist: cmp esi, [esp] jae .scanlistdone cmp eax, [esi] jz @f .scanlistcont: movzx ecx, word [esi+4] add esi, ecx jmp .scanlist @@: ; ignore named $DATA attributes (aka NTFS streams) cmp eax, 0x80 jnz @f cmp byte [esi+6], 0 jnz .scanlistcont @@: push eax mov eax, [esi+8] test eax, eax jnz .testf mov eax, dword [ebp+NTFS.attr_size] and eax, dword [ebp+NTFS.attr_size+4] cmp eax, -1 jnz .testfz ; if attribute is in auxiliary records, its size is defined only in first mov eax, [esi+10h] call ntfs_read_file_record jc .errret_pop mov eax, [ebp+NTFS.frs_buffer] movzx ecx, word [eax+14h] add eax, ecx mov ecx, [ebp+NTFS.cur_attr] @@: cmp dword [eax], -1 jz .errret2_pop cmp dword [eax], ecx jz @f .l1: add eax, [eax+4] jmp @b @@: cmp eax, 0x80 jnz @f cmp byte [eax+9], 0 jnz .l1 @@: cmp byte [eax+8], 0 jnz .sdnores mov eax, [eax+10h] mov dword [ebp+NTFS.attr_size], eax and dword [ebp+NTFS.attr_size+4], 0 jmp .testfz .sdnores: mov ecx, [eax+30h] mov dword [ebp+NTFS.attr_size], ecx mov ecx, [eax+34h] mov dword [ebp+NTFS.attr_size+4], ecx .testfz: xor eax, eax .testf: imul eax, [ebp+NTFS.sectors_per_cluster] cmp eax, [ebp+NTFS.cur_offs] pop eax ja @f mov edi, [esi+10h] ; keep previous iRecord jmp .scanlistcont @@: pop ecx .scanlistfound: cmp edi, -1 jz .ret mov eax, [ebp+NTFS.cur_iRecord] mov [ebp+NTFS.attr_iBaseRecord], eax mov eax, edi jmp .beginfindattr .scanlistdone: pop ecx sub ecx, ebp sub ecx, NTFS.attrlist_buf-1Ah cmp [ebp+NTFS.cur_iRecord], 0 jnz @f sub ecx, NTFS.attrlist_mft_buf-NTFS.attrlist_buf @@: cmp ecx, 0x400 jnz .scanlistfound inc edx push esi edi lea esi, [ebp+NTFS.attrlist_buf+0x200] lea edi, [ebp+NTFS.attrlist_buf] cmp [ebp+NTFS.cur_iRecord], 0 jnz @f lea esi, [ebp+NTFS.attrlist_mft_buf+0x200] lea edi, [ebp+NTFS.attrlist_mft_buf] @@: mov ecx, 0x200/4 rep movsd mov eax, edi pop edi esi sub esi, 0x200 push [ebp+NTFS.cur_offs] push [ebp+NTFS.cur_size] push [ebp+NTFS.cur_read] push [ebp+NTFS.cur_buf] push dword [ebp+NTFS.attr_size] push dword [ebp+NTFS.attr_size+4] or dword [ebp+NTFS.attr_size], -1 or dword [ebp+NTFS.attr_size+4], -1 mov [ebp+NTFS.cur_offs], edx mov [ebp+NTFS.cur_size], 1 and [ebp+NTFS.cur_read], 0 mov [ebp+NTFS.cur_buf], eax mov ecx, [ebp+NTFS.attr_list] push esi edx edi call .doreadattr pop edi edx esi mov ecx, [ebp+NTFS.cur_read] pop dword [ebp+NTFS.attr_size+4] pop dword [ebp+NTFS.attr_size] pop [ebp+NTFS.cur_buf] pop [ebp+NTFS.cur_read] pop [ebp+NTFS.cur_size] pop [ebp+NTFS.cur_offs] jc .errret lea ecx, [ecx+ebp+NTFS.attrlist_buf+0x200-0x1A] cmp [ebp+NTFS.cur_iRecord], 0 jnz .scanliststart add ecx, NTFS.attrlist_mft_buf-NTFS.attrlist_buf jmp .scanliststart .doreadattr: mov [ebp+NTFS.bCanContinue], 0 cmp byte [ecx+nonResidentFlag], 0 jnz .nonresident mov eax, [ecx+sizeWithoutHeader] mov esi, eax mov edx, [ebp+NTFS.cur_offs] shr eax, 9 cmp eax, edx jb .okret shl edx, 9 sub esi, edx movzx eax, word [ecx+attributeOffset] add edx, eax add edx, ecx ; edx -> data mov eax, [ebp+NTFS.cur_size] cmp eax, (0xFFFFFFFF shr 9)+1 jbe @f mov eax, (0xFFFFFFFF shr 9)+1 @@: shl eax, 9 cmp eax, esi jbe @f mov eax, esi @@: ; eax = length, edx -> data mov [ebp+NTFS.cur_read], eax mov ecx, eax mov eax, edx mov ebx, [ebp+NTFS.cur_buf] call memmove and [ebp+NTFS.cur_size], 0 ; CF=0 ret .nonresident: ; Not all auxiliary records contain correct FileSize info mov eax, dword [ebp+NTFS.attr_size] mov edx, dword [ebp+NTFS.attr_size+4] push eax and eax, edx cmp eax, -1 pop eax jnz @f mov eax, [ecx+attributeRealSize] mov edx, [ecx+attributeRealSize+4] mov dword [ebp+NTFS.attr_size], eax mov dword [ebp+NTFS.attr_size+4], edx @@: add eax, 0x1FF adc edx, 0 shrd eax, edx, 9 sub eax, [ebp+NTFS.cur_offs] ja @f ; return with nothing read and [ebp+NTFS.cur_size], 0 .okret: clc ret @@: ; reduce read length and [ebp+NTFS.cur_tail], 0 cmp [ebp+NTFS.cur_size], eax jb @f mov [ebp+NTFS.cur_size], eax mov eax, dword [ebp+NTFS.attr_size] and eax, 0x1FF mov [ebp+NTFS.cur_tail], eax @@: mov eax, [ebp+NTFS.cur_offs] xor edx, edx div [ebp+NTFS.sectors_per_cluster] sub eax, [ecx+firstVCN] jb .okret mov ebx, edx ; eax = starting cluster, ebx = sector in the cluster cmp [ebp+NTFS.cur_attr], 0x80 jnz .sys cmp [ebp+NTFS.cur_iRecord], 0 jz .sys push fs_read64_app cmp [ebp+NTFS.bWriteAttr], 1 jnz @f mov dword[esp], fs_write64_app jmp @f .sys: push fs_read64_sys @@: sub esp, 10h movzx esi, word [ecx+dataRunsOffset] add esi, ecx xor edi, edi mov [ebp+NTFS.fragmentCount], 0 .readloop: call ntfs_decode_mcb_entry jnc .break add edi, [esp+8] sub eax, [esp] jae .readloop mov ecx, edi add ecx, eax add ecx, [esp] neg eax mul [ebp+NTFS.sectors_per_cluster] xchg eax, ecx mul [ebp+NTFS.sectors_per_cluster] sub ecx, ebx add eax, ebx mov ebx, [ebp+NTFS.cur_buf] cmp ecx, [ebp+NTFS.cur_size] jb @f mov ecx, [ebp+NTFS.cur_size] @@: mov [ebp+NTFS.LastRead], eax push ecx call dword[esp+14h] pop ecx test eax, eax jnz .errread2 sub [ebp+NTFS.cur_size], ecx add [ebp+NTFS.cur_offs], ecx shl ecx, 9 add [ebp+NTFS.cur_read], ecx add [ebp+NTFS.cur_buf], ecx inc [ebp+NTFS.fragmentCount] xor eax, eax xor ebx, ebx cmp [ebp+NTFS.cur_size], 0 jnz .readloop add esp, 14h mov eax, [ebp+NTFS.cur_tail] test eax, eax jz @f sub eax, 0x200 add [ebp+NTFS.cur_read], eax @@: clc ret .errread2: add esp, 14h stc ret .break: add esp, 14h ; CF=0 mov [ebp+NTFS.bCanContinue], 1 ret ntfs_read_file_record: ; in: eax = iRecord ; out: [ebp+NTFS.frs_buffer] -> file record ; CF=1 -> failed, eax = disk error code, eax=0 -> something with FS ; Read attr $DATA of $Mft, starting from eax*[ebp+NTFS.frs_size] push ecx edx mov ecx, [ebp+NTFS.frs_size] mul ecx shrd eax, edx, 9 shr edx, 9 jnz .errret push [ebp+NTFS.attr_iRecord] push [ebp+NTFS.attr_iBaseRecord] push [ebp+NTFS.attr_offs] push [ebp+NTFS.attr_list] push dword [ebp+NTFS.attr_size+4] push dword [ebp+NTFS.attr_size] push [ebp+NTFS.cur_iRecord] push [ebp+NTFS.cur_attr] push [ebp+NTFS.cur_offs] push [ebp+NTFS.cur_size] push [ebp+NTFS.cur_buf] push [ebp+NTFS.cur_read] mov [ebp+NTFS.cur_attr], 0x80 ; $DATA and [ebp+NTFS.cur_iRecord], 0 ; $Mft mov [ebp+NTFS.cur_offs], eax shr ecx, 9 mov [ebp+NTFS.cur_size], ecx mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.cur_buf], eax call ntfs_read_attr mov edx, [ebp+NTFS.cur_read] pop [ebp+NTFS.cur_read] pop [ebp+NTFS.cur_buf] pop [ebp+NTFS.cur_size] pop [ebp+NTFS.cur_offs] pop [ebp+NTFS.cur_attr] pop [ebp+NTFS.cur_iRecord] pop dword [ebp+NTFS.attr_size] pop dword [ebp+NTFS.attr_size+4] pop [ebp+NTFS.attr_list] pop [ebp+NTFS.attr_offs] pop [ebp+NTFS.attr_iBaseRecord] pop [ebp+NTFS.attr_iRecord] jc .ret cmp edx, [ebp+NTFS.frs_size] jnz .errret mov eax, [ebp+NTFS.frs_buffer] cmp dword [eax], 'FILE' jnz .errret push ebx mov ebx, eax call ntfs_restore_usa_frs pop ebx jc .errret .ret: pop edx ecx ret .errret: pop edx ecx xor eax, eax stc ret ntfs_restore_usa_frs: mov eax, [ebp+NTFS.frs_size] ntfs_restore_usa: ; in: ; ebx -> record ; eax = size in bytes pushad shr eax, 9 mov ecx, eax inc eax cmp [ebx+updateSequenceSize], ax jnz .err movzx eax, word [ebx+updateSequenceOffset] lea esi, [eax+ebx] lodsw mov edx, eax lea edi, [ebx+0x1FE] @@: cmp [edi], dx jnz .err lodsw stosw add edi, 0x1FE loop @b popad clc ret .err: popad stc ret ntfs_decode_mcb_entry: ; in: ; esi -> MCB entry ; esp -> buffer (16 bytes) ; out: ; esi -> next MCB entry ; esp -> data run size ; esp+8 -> cluster (delta) ; CF=0 -> MCB end push eax ecx edi lea edi, [esp+16] xor eax, eax lodsb test al, al jz .end mov ecx, eax and ecx, 0xF cmp ecx, 8 ja .end push ecx rep movsb pop ecx sub ecx, 8 neg ecx cmp byte [esi-1], 80h jae .end push eax xor eax, eax rep stosb pop ecx shr ecx, 4 cmp ecx, 8 ja .end push ecx rep movsb pop ecx sub ecx, 8 neg ecx cmp byte [esi-1], 80h cmc sbb eax, eax rep stosb stc .end: pop edi ecx eax ret unichar_toupper: push eax call uni2ansi_char cmp al, '_' jz .unk add esp, 4 call char_toupper jmp ansi2uni_char .unk: pop eax ret ntfs_find_lfn: ; in: [esi]+[esp+4] = name ; out: ; [ebp+NTFS.cur_iRecord] = target fileRecord ; eax -> index in the target node ; [ebp+NTFS.indexPointer] -> index, that points the target subnode ; [ebp+NTFS.indexRoot] -> attribute ; [ebp+NTFS.cur_size] = index record size in sectors ; [ebp+NTFS.cur_subnode_size] = index record size in clusters or sectors ; [ebp+NTFS.rootLastRead] = directory fileRecord sector ; CF=1 -> file not found, eax=0 -> error mov [ebp+NTFS.cur_iRecord], 5 ; start from root directory .doit2: mov [ebp+NTFS.cur_attr], 0x90 ; $INDEX_ROOT and [ebp+NTFS.cur_offs], 0 mov eax, [ebp+NTFS.cur_index_size] mov [ebp+NTFS.cur_size], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_buf], eax call ntfs_read_attr mov eax, 0 jc .ret cmp [ebp+NTFS.cur_read], 0x20 jc .ret pushad mov esi, [ebp+NTFS.cur_index_buf] mov eax, [esi+indexRecordSize] shr eax, 9 cmp [ebp+NTFS.cur_index_size], eax jc .realloc mov [ebp+NTFS.cur_size], eax mov al, [esi+indexRecordSizeClus] mov [ebp+NTFS.cur_subnode_size], eax add esi, rootNode mov eax, [esi+nodeRealSize] add eax, rootNode cmp [ebp+NTFS.cur_read], eax jc .err mov edi, [esp+4] mov eax, [ebp+NTFS.LastRead] mov [ebp+NTFS.rootLastRead], eax mov eax, [ebp+NTFS.attr_offs] mov [ebp+NTFS.indexRoot], eax ; edi -> name, esi -> current index node .scanloop: add esi, [esi+indexOffset] .scanloopint: test byte [esi+indexFlags], 2 jnz .subnode push esi movzx ecx, byte [esi+fileNameLength] add esi, fileName push edi @@: lodsw call unichar_toupper push eax mov al, [edi] inc edi cmp al, '/' jz .slash call char_toupper call ansi2uni_char cmp ax, [esp] pop eax loopz @b jz .found pop edi pop esi jb .subnode .scanloopcont: movzx eax, word [esi+indexAllocatedSize] add esi, eax jmp .scanloopint .realloc: mov edi, eax mov eax, [esi+indexRecordSize] shl eax, 1 stdcall kernel_alloc, eax test eax, eax jz .err mov edx, [ebp+NTFS.cur_index_buf] cmp edx, [ebp+NTFS.secondIndexBuffer] jc @f mov edx, [ebp+NTFS.secondIndexBuffer] @@: mov [ebp+NTFS.cur_index_buf], eax add eax, [esi+indexRecordSize] mov [ebp+NTFS.secondIndexBuffer], eax mov [ebp+NTFS.cur_index_size], edi stdcall kernel_free, edx popad jmp .doit2 .notfound: mov [esp+1Ch], esi .err: popad stc .ret: ret 4 .slash: pop eax pop edi pop esi .subnode: test byte [esi+indexFlags], 1 jz .notfound movzx eax, word [esi+indexAllocatedSize] mov eax, [esi+eax-8] mov edx, [ebp+NTFS.cur_size] push edx cmp edx, [ebp+NTFS.cur_subnode_size] jz @f mul [ebp+NTFS.sectors_per_cluster] @@: mov [ebp+NTFS.indexPointer], esi mov esi, [ebp+NTFS.cur_index_buf] xchg [ebp+NTFS.secondIndexBuffer], esi mov [ebp+NTFS.cur_index_buf], esi mov [ebp+NTFS.cur_buf], esi mov [ebp+NTFS.cur_attr], 0xA0 ; $INDEX_ALLOCATION mov [ebp+NTFS.cur_offs], eax call ntfs_read_attr.newAttribute pop eax mov [ebp+NTFS.cur_size], eax shl eax, 9 cmp [ebp+NTFS.cur_read], eax jnz .err cmp dword [esi], 'INDX' jnz .err mov ebx, esi call ntfs_restore_usa jc .err add esi, recordNode jmp .scanloop .found: cmp byte [edi], 0 jz .done cmp byte [edi], '/' jz .next pop edi pop esi jmp .scanloopcont .done: .next: pop esi pop esi mov eax, [esi] mov [ebp+NTFS.cur_iRecord], eax mov [esp+1Ch], esi mov [esp+4], edi popad inc esi cmp byte [esi-1], 0 jnz .doit2 cmp dword [esp+4], 0 jz .ret mov esi, [esp+4] mov dword [esp+4], 0 jmp .doit2 ;---------------------------------------------------------------- ntfs_ReadFile: cmp byte [esi], 0 jnz @f or ebx, -1 movi eax, ERROR_ACCESS_DENIED ret @@: call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jnc .found call ntfs_unlock or ebx, -1 movi eax, ERROR_FILE_NOT_FOUND ret .found: mov [ebp+NTFS.cur_attr], 0x80 ; $DATA and [ebp+NTFS.cur_offs], 0 and [ebp+NTFS.cur_size], 0 call ntfs_read_attr jnc @f call ntfs_unlock or ebx, -1 movi eax, ERROR_ACCESS_DENIED ret @@: pushad and dword [esp+10h], 0 xor eax, eax cmp dword [ebx+8], 0x200 jb @f .eof0: popad xor ebx, ebx .eof: call ntfs_unlock movi eax, ERROR_END_OF_FILE ret @@: mov ecx, [ebx+12] mov edx, [ebx+16] mov eax, [ebx+4] test eax, 0x1FF jz .alignedstart push edx mov edx, [ebx+8] shrd eax, edx, 9 pop edx mov [ebp+NTFS.cur_offs], eax mov [ebp+NTFS.cur_size], 1 lea eax, [ebp+NTFS.bitmap_buf] mov [ebp+NTFS.cur_buf], eax call ntfs_read_attr.continue mov eax, [ebx+4] and eax, 0x1FF lea esi, [ebp+NTFS.bitmap_buf+eax] sub eax, [ebp+NTFS.cur_read] jae .eof0 neg eax push ecx cmp ecx, eax jb @f mov ecx, eax @@: mov [esp+10h+4], ecx mov edi, edx rep movsb mov edx, edi pop ecx sub ecx, [esp+10h] jnz @f .retok: popad call ntfs_unlock xor eax, eax ret @@: cmp [ebp+NTFS.cur_read], 0x200 jz .alignedstart .eof_ebx: popad jmp .eof .alignedstart: mov eax, [ebx+4] push edx mov edx, [ebx+8] add eax, 511 adc edx, 0 shrd eax, edx, 9 pop edx mov [ebp+NTFS.cur_offs], eax mov [ebp+NTFS.cur_buf], edx mov eax, ecx shr eax, 9 mov [ebp+NTFS.cur_size], eax add eax, [ebp+NTFS.cur_offs] push eax call ntfs_read_attr.continue pop [ebp+NTFS.cur_offs] mov eax, [ebp+NTFS.cur_read] add [esp+10h], eax mov eax, ecx and eax, not 0x1FF cmp [ebp+NTFS.cur_read], eax jnz .eof_ebx and ecx, 0x1FF jz .retok add edx, [ebp+NTFS.cur_read] mov [ebp+NTFS.cur_size], 1 lea eax, [ebp+NTFS.bitmap_buf] mov [ebp+NTFS.cur_buf], eax call ntfs_read_attr.continue cmp [ebp+NTFS.cur_read], ecx jb @f mov [ebp+NTFS.cur_read], ecx @@: xchg ecx, [ebp+NTFS.cur_read] push ecx mov edi, edx lea esi, [ebp+NTFS.bitmap_buf] add [esp+10h+4], ecx rep movsb pop ecx xor eax, eax cmp ecx, [ebp+NTFS.cur_read] jz @f mov al, ERROR_END_OF_FILE @@: mov [esp+1Ch], eax call ntfs_unlock popad ret ;---------------------------------------------------------------- ntfs_ReadFolder: call ntfs_lock mov [ebp+NTFS.cur_iRecord], 5 ; root directory cmp byte [esi], 0 jz @f stdcall ntfs_find_lfn, [esp+4] jc ntfsNotFound @@: mov [ebp+NTFS.cur_attr], 0x10 ; $STANDARD_INFORMATION and [ebp+NTFS.cur_offs], 0 mov [ebp+NTFS.cur_size], 1 lea eax, [ebp+NTFS.bitmap_buf] mov [ebp+NTFS.cur_buf], eax call ntfs_read_attr jc ntfsFail mov [ebp+NTFS.cur_attr], 0x90 ; $INDEX_ROOT .doit: mov eax, [ebp+NTFS.cur_index_size] mov [ebp+NTFS.cur_size], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_buf], eax call ntfs_read_attr.newAttribute jc ntfsFail cmp [ebp+NTFS.cur_read], 0x20 jc ntfsFail pushad mov esi, [ebp+NTFS.cur_index_buf] mov eax, [esi+indexRecordSize] shr eax, 9 cmp [ebp+NTFS.cur_index_size], eax jc .realloc mov [ebp+NTFS.cur_subnode_size], eax add esi, rootNode mov eax, [esi+nodeRealSize] add eax, rootNode cmp [ebp+NTFS.cur_read], eax jc .err mov edx, [ebx+16] push dword [ebx+8] ; read ANSI/UNICODE name ; init header mov edi, edx mov ecx, 32/4 xor eax, eax rep stosd mov byte [edx], 1 ; version mov ecx, [ebx+12] mov ebx, [ebx+4] push edx mov edx, esp ; edi -> BDFE, esi -> current index data, ebx = first wanted block, ; ecx = number of blocks to read ; edx -> parameters block: dd , dd cmp [ebp+NTFS.cur_iRecord], 5 jz .skip_specials ; dot and dotdot entries push esi xor esi, esi call .add_special_entry inc esi call .add_special_entry pop esi .skip_specials: ; at first, dump index root add esi, [esi+indexOffset] .dump_root: test byte [esi+indexFlags], 2 jnz .dump_root_done call .add_entry movzx eax, word [esi+indexAllocatedSize] add esi, eax jmp .dump_root .realloc: mov edi, eax mov eax, [esi+indexRecordSize] shl eax, 1 stdcall kernel_alloc, eax test eax, eax jz .err mov edx, [ebp+NTFS.cur_index_buf] cmp edx, [ebp+NTFS.secondIndexBuffer] jc @f mov edx, [ebp+NTFS.secondIndexBuffer] @@: mov [ebp+NTFS.cur_index_buf], eax add eax, [esi+indexRecordSize] mov [ebp+NTFS.secondIndexBuffer], eax mov [ebp+NTFS.cur_index_size], edi stdcall kernel_free, edx popad jmp .doit .err: popad jmp ntfsFail .dump_root_done: ; now dump all subnodes push ecx edi lea edi, [ebp+NTFS.bitmap_buf] mov [ebp+NTFS.cur_buf], edi mov ecx, 0x400/4 xor eax, eax rep stosd mov [ebp+NTFS.cur_attr], 0xB0 ; $BITMAP and [ebp+NTFS.cur_offs], 0 mov [ebp+NTFS.cur_size], 2 call ntfs_read_attr.newAttribute pop edi ecx push 0 ; save offset in $BITMAP attribute and [ebp+NTFS.cur_offs], 0 .dumploop: mov [ebp+NTFS.cur_attr], 0xA0 mov eax, [ebp+NTFS.cur_subnode_size] mov [ebp+NTFS.cur_size], eax mov esi, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_buf], esi mov eax, [ebp+NTFS.cur_offs] push eax imul eax, [ebp+NTFS.cur_subnode_size] mov [ebp+NTFS.cur_offs], eax call ntfs_read_attr.newAttribute pop [ebp+NTFS.cur_offs] mov eax, [ebp+NTFS.cur_subnode_size] shl eax, 9 cmp [ebp+NTFS.cur_read], eax jnz .done push eax mov eax, [ebp+NTFS.cur_offs] and eax, 0x400*8-1 bt dword [ebp+NTFS.bitmap_buf], eax pop eax jnc .dump_subnode_done cmp dword [esi], 'INDX' jnz .dump_subnode_done push ebx mov ebx, esi call ntfs_restore_usa pop ebx jc .dump_subnode_done add esi, recordNode add esi, [esi+indexOffset] .dump_subnode: test byte [esi+indexFlags], 2 jnz .dump_subnode_done call .add_entry movzx eax, word [esi+indexAllocatedSize] add esi, eax jmp .dump_subnode .dump_subnode_done: inc [ebp+NTFS.cur_offs] test [ebp+NTFS.cur_offs], 0x400*8-1 jnz .dumploop mov [ebp+NTFS.cur_attr], 0xB0 push ecx edi lea edi, [ebp+NTFS.bitmap_buf] mov [ebp+NTFS.cur_buf], edi mov ecx, 0x400/4 xor eax, eax rep stosd pop edi ecx pop eax push [ebp+NTFS.cur_offs] inc eax mov [ebp+NTFS.cur_offs], eax mov [ebp+NTFS.cur_size], 2 push eax call ntfs_read_attr.newAttribute pop eax pop [ebp+NTFS.cur_offs] push eax jmp .dumploop .done: pop eax pop edx mov ebx, [edx+4] pop edx xor eax, eax dec ecx js @f mov al, ERROR_END_OF_FILE @@: mov [esp+1Ch], eax mov [esp+10h], ebx call ntfs_unlock popad ret .add_special_entry: mov eax, [edx] inc dword [eax+8] ; new file found dec ebx jns .ret dec ecx js .ret inc dword [eax+4] ; new file block copied mov eax, [edx+4] mov [edi+4], eax mov eax, 0x10 stosd scasd push edx mov eax, dword [ebp+NTFS.bitmap_buf] mov edx, dword [ebp+NTFS.bitmap_buf+4] call ntfs_datetime_to_bdfe mov eax, dword [ebp+NTFS.bitmap_buf+0x18] mov edx, dword [ebp+NTFS.bitmap_buf+0x1C] call ntfs_datetime_to_bdfe mov eax, dword [ebp+NTFS.bitmap_buf+8] mov edx, dword [ebp+NTFS.bitmap_buf+0xC] call ntfs_datetime_to_bdfe pop edx xor eax, eax stosd stosd mov al, '.' push edi ecx lea ecx, [esi+1] test byte [edi-0x24], 1 jz @f rep stosw pop ecx xor eax, eax stosw pop edi add edi, 520 ret @@: rep stosb pop ecx xor eax, eax stosb pop edi add edi, 264 .ret: ret .add_entry: ; do not return DOS 8.3 names cmp byte [esi+namespace], 2 jz .ret ; do not return system files ; ... note that there will be no bad effects if system files also were reported ... cmp dword [esi+fileRecordReference], 0x10 jb .ret mov eax, [edx] inc dword [eax+8] ; new file found dec ebx jns .ret dec ecx js .ret inc dword [eax+4] ; new file block copied mov eax, [edx+4] ; flags call ntfs_direntry_to_bdfe push ecx esi edi movzx ecx, byte [esi+fileNameLength] add esi, fileName test byte [edi-0x24], 1 jz .ansi shr ecx, 1 rep movsd adc ecx, ecx rep movsw and word [edi], 0 pop edi add edi, 520 pop esi ecx ret .ansi: jecxz .skip @@: lodsw call uni2ansi_char stosb loop @b .skip: xor al, al stosb pop edi add edi, 264 pop esi ecx ret ntfs_direntry_to_bdfe: mov [edi+4], eax ; ANSI/UNICODE name mov eax, [esi+fileFlags] test eax, 0x10000000 jz @f and eax, not 0x10000000 or al, 0x10 @@: stosd scasd push edx mov eax, [esi+fileCreated] mov edx, [esi+fileCreated+4] call ntfs_datetime_to_bdfe mov eax, [esi+fileAccessed] mov edx, [esi+fileAccessed+4] call ntfs_datetime_to_bdfe mov eax, [esi+fileModified] mov edx, [esi+fileModified+4] call ntfs_datetime_to_bdfe pop edx mov eax, [esi+fileRealSize] stosd mov eax, [esi+fileRealSize+4] stosd ret iglobal months db 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 months2 db 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 endg ntfs_datetime_to_bdfe: ; edx:eax = number of 100-nanosecond intervals since January 1, 1601, in UTC push ebx ecx mov ebx, eax mov eax, edx xor edx, edx mov ecx, 10000000 div ecx xchg eax, ebx div ecx .forEXT: xchg eax, ebx xor edx, edx mov ecx, 60 div ecx xchg eax, ebx div ecx mov [edi], dl mov edx, ebx ; edx:eax = number of minutes div ecx mov [edi+1], dl ; eax = number of hours xor edx, edx mov cl, 24 div ecx mov [edi+2], dx ; eax = number of days since January 1, 1601 xor edx, edx mov cx, 365 div ecx mov ebx, eax add ebx, 1601 shr eax, 2 sub edx, eax mov cl, 25 div cl xor ah, ah add edx, eax shr eax, 2 sub edx, eax jns @f dec ebx add edx, 365 test bl, 3 jnz @f inc edx @@: xor eax, eax mov ecx, months-1 test bl, 3 jnz @f add ecx, 12 @@: inc ecx inc eax sub dl, [ecx] jnc @b dec dh jns @b add dl, [ecx] inc edx mov [edi+4], dl mov [edi+5], al mov [edi+6], bx add edi, 8 pop ecx ebx ret .sec: push ebx ecx mov ebx, edx jmp .forEXT ;---------------------------------------------------------------- ntfs_CreateFolder: mov [ebp+NTFS.bFolder], 1 jmp @f ntfs_CreateFile: mov [ebp+NTFS.bFolder], 0 @@: cmp byte [esi], 0 jnz @f xor ebx, ebx movi eax, ERROR_ACCESS_DENIED ret @@: ; 1. Search file call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jc .notFound ; found, rewrite cmp [ebp+NTFS.cur_iRecord], 16 jc ntfsDenied cmp [ebp+NTFS.bFolder], 1 jz .folder cmp [ebp+NTFS.fragmentCount], 1 jnz ntfsUnsupported ; record fragmented ; edit directory node mov edi, [ebp+NTFS.cur_index_buf] cmp dword [edi], 'INDX' jz @f mov esi, [ebp+NTFS.frs_buffer] mov ecx, [esi+recordRealSize] shr ecx, 2 rep movsd mov esi, [ebp+NTFS.attr_offs] mov cl, [esi+attributeOffset] sub esi, [ebp+NTFS.frs_buffer] add eax, ecx add eax, esi @@: mov edx, [ebx+12] mov [eax+fileRealSize], edx mov dword [eax+fileRealSize+4], 0 mov eax, [ebp+NTFS.LastRead] mov [ebp+NTFS.nodeLastRead], eax mov [ebp+NTFS.cur_attr], 0x80 mov [ebp+NTFS.cur_offs], 0 mov [ebp+NTFS.cur_size], 0 call ntfs_read_attr jc ntfsFail mov ecx, [ebp+NTFS.frs_buffer] mov eax, edx xor edx, edx cmp word [ecx+baseRecordReuse], 0 jnz ntfsUnsupported ; auxiliary record mov ecx, [ebp+NTFS.attr_offs] cmp word [ecx+attributeFlags], 0 jnz ntfsUnsupported push ebx cmp byte [ecx+nonResidentFlag], 0 jz @f cmp [ecx+attributeRealSize+4], edx jnz @f cmp [ecx+attributeRealSize], eax jz ntfs_WriteFile.writeNode @@: jmp ntfs_WriteFile.resizeAttribute .folder: bt dword [eax+fileFlags], 28 jnc ntfsDenied push 0 jmp ntfsOut .notFound: ; create test eax, eax jz ntfsFail cmp [ebp+NTFS.fragmentCount], 1 jnz ntfsUnsupported ; record fragmented ; 2. Prepare directory record mov ecx, esi @@: ; count characters inc ecx cmp byte [ecx], '/' jz ntfsNotFound ; path folder not found cmp byte [ecx], 0 jnz @b sub ecx, esi push ecx ; name length shl ecx, 1 add ecx, fileName+7 and ecx, not 7 mov edi, [ebp+NTFS.cur_index_buf] mov edx, [ebx+12] mov [ebp+NTFS.fileRealSize], edx mov edx, [ebx+16] mov [ebp+NTFS.fileDataBuffer], edx push esi push ecx ; index length mov edx, ecx cmp dword [edi], 'INDX' jz .indexRecord mov esi, [ebp+NTFS.frs_buffer] ; indexRoot mov ecx, [esi+recordRealSize] add edx, ecx cmp [esi+recordAllocatedSize], edx jc .growTree mov [esi+recordRealSize], edx shr ecx, 2 rep movsd mov edi, [ebp+NTFS.attr_offs] sub edi, [ebp+NTFS.frs_buffer] add edi, [ebp+NTFS.cur_index_buf] mov esi, [esp] add [edi+sizeWithHeader], esi add [edi+sizeWithoutHeader], esi mov cl, [edi+attributeOffset] add edi, ecx add [edi+rootNode+nodeRealSize], esi add [edi+rootNode+nodeAllocatedSize], esi sub eax, [ebp+NTFS.cur_index_buf] add eax, edi mov edi, [ebp+NTFS.cur_index_buf] jmp .common .growTree: sub eax, edi sub eax, rootNode sub eax, [edi+rootNode+indexOffset] push eax xchg [ebp+NTFS.secondIndexBuffer], edi mov [ebp+NTFS.cur_index_buf], edi ; create indexRecord mov ecx, 10 xor eax, eax rep stosd mov esi, [ebp+NTFS.attr_offs] mov al, [esi+attributeOffset] add esi, eax mov al, [esi+indexRecordSizeClus] mov [ebp+NTFS.fileDataSize], eax rdtsc stosw mov eax, [esi+indexRecordSize] cmp eax, [ebp+NTFS.frs_size] jc .errorPop4 shr eax, 9 inc eax mov edi, [ebp+NTFS.cur_index_buf] mov dword[edi], 'INDX' mov byte [edi+updateSequenceOffset], 28h mov [edi+updateSequenceSize], al add edi, recordNode shl eax, 1 add eax, 28h-recordNode+7 and eax, not 7 mov [edi+indexOffset], eax mov ecx, [esi+indexRecordSize] sub ecx, recordNode mov [edi+nodeAllocatedSize], ecx add esi, rootNode push esi mov ecx, [esi+nodeRealSize] sub ecx, [esi+indexOffset] add eax, ecx mov [edi+nodeRealSize], eax shr ecx, 2 add esi, [esi+indexOffset] add edi, [edi+indexOffset] rep movsd ; copy root indexes ; clear root node mov cl, 10 mov edi, [esp] xor eax, eax rep stosd pop edi mov byte [edi+indexOffset], 16 mov byte [edi+nodeRealSize], 28h mov byte [edi+nodeAllocatedSize], 28h mov byte [edi+nonLeafFlag], 1 mov byte [edi+16+indexAllocatedSize], 18h mov byte [edi+16+indexFlags], 3 mov esi, [ebp+NTFS.attr_offs] add edi, 28h mov eax, edi sub eax, esi mov word [esi+sizeWithoutHeader], 38h xchg [esi+sizeWithHeader], eax cmp byte [esi+eax], -1 jnz .errorPop4 mov cl, 32 xor eax, eax push edi rep stosd mov edi, [ebp+NTFS.BitmapStart] call ntfsSpaceAlloc jnc @f add esp, 20 jmp ntfsDiskFull @@: ; create $IndexAllocation pop edi mov [ebp+NTFS.attr_offs], edi mov byte [edi+attributeType], 0xA0 mov byte [edi+nonResidentFlag], 1 mov byte [edi+nameLength], 4 mov byte [edi+nameOffset], 40h mov byte [edi+dataRunsOffset], 48h mov byte [edi+sizeWithHeader], 50h mov eax, [ebp+NTFS.fileDataSize] dec eax mov [edi+lastVCN], eax inc eax mul [ebp+NTFS.sectors_per_cluster] shl eax, 9 mov [edi+attributeAllocatedSize], eax mov [edi+attributeRealSize], eax mov [edi+initialDataSize], eax mov dword[edi+40h], 490024h ; unicode $I30 mov dword[edi+40h+4], 300033h push edi mov esi, edi add edi, 48h call createMcbEntry mov esi, [ebp+NTFS.frs_buffer] pop edi mov al, [esi+newAttributeID] mov [edi+attributeID], al add edi, 50h inc eax ; create $Bitmap mov [edi+attributeID], al inc eax mov [esi+newAttributeID], al mov byte [edi+attributeType], 0xB0 mov byte [edi+nameLength], 4 mov byte [edi+nameOffset], 18h mov byte [edi+attributeOffset], 20h mov byte [edi+sizeWithoutHeader], 8 mov byte [edi+sizeWithHeader], 28h mov dword[edi+18h], 490024h ; unicode $I30 mov dword[edi+18h+4], 300033h mov byte [edi+20h], 1 mov dword[edi+28h], -1 add edi, 30h sub edi, esi mov [esi+recordRealSize], edi mov [ebp+NTFS.cur_buf], esi call writeRecord ; fileRecord mov eax, [ebp+NTFS.fileDataStart] mul [ebp+NTFS.sectors_per_cluster] mov [ebp+NTFS.LastRead], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_buf], eax call writeRecord ; indexRecord mov ebx, [ebp+NTFS.cur_index_buf] mov ax, [ebx+updateSequenceSize] dec eax shl eax, 9 call ntfs_restore_usa mov edi, [ebp+NTFS.cur_index_buf] pop eax add eax, recordNode add eax, edi add eax, [edi+recordNode+indexOffset] mov edx, [esp] .indexRecord: add edi, recordNode add edx, [edi+nodeRealSize] cmp [edi+nodeAllocatedSize], edx jc .arborizeTree mov [edi+nodeRealSize], edx jmp .common .errorPop4: pop eax .errorPop3: add esp, 12 jmp ntfsUnsupported .arborizeTree: ; directory bitmap mov esi, [ebp+NTFS.attr_offs] add esi, [esi+sizeWithHeader] cmp byte [esi], 0xB0 jnz .errorPop3 movzx eax, byte [esi+attributeOffset] add esi, eax mov eax, [esi] not eax bsf eax, eax jz .errorPop3 bts [esi], eax mul [ebp+NTFS.cur_subnode_size] mov [ebp+NTFS.newRecord], eax ; find median index mov ecx, [edi+nodeRealSize] sub ecx, [edi+indexOffset] shr ecx, 1 add edi, [edi+indexOffset] xor eax, eax @@: add edi, eax mov ax, [edi+indexAllocatedSize] sub ecx, eax jnc @b mov esi, [ebp+NTFS.secondIndexBuffer] cmp dword [esi], 'INDX' jz .errorPop3 ; move index to the branch node ; move index to the root node mov esi, [ebp+NTFS.frs_buffer] mov ecx, [esi+recordRealSize] add eax, 8 add ecx, eax cmp [esi+recordAllocatedSize], ecx jc .errorPop3 ; tree grow required mov [esi+recordRealSize], ecx add esi, ecx push edi mov edi, [ebp+NTFS.indexRoot] add [ebp+NTFS.attr_offs], eax add [edi+sizeWithHeader], eax add [edi+sizeWithoutHeader], eax movzx ecx, byte [edi+attributeOffset] add ecx, edi add [ecx+rootNode+nodeRealSize], eax add [ecx+rootNode+nodeAllocatedSize], eax add ecx, [ebp+NTFS.indexPointer] sub ecx, [ebp+NTFS.secondIndexBuffer] mov edi, esi sub esi, eax neg ecx add ecx, esi shr ecx, 2 sub esi, 4 sub edi, 4 std rep movsd ; make space mov [edi], ecx mov edi, esi add edi, 4 mov esi, [esp] add word [esi+indexAllocatedSize], 8 mov byte [esi+indexFlags], 1 mov ecx, eax sub ecx, 8 shr ecx, 2 cld rep movsd ; insert index mov eax, [ebp+NTFS.newRecord] stosd ; split node mov edi, [ebp+NTFS.cur_index_buf] mov eax, edi add eax, recordNode add eax, [edi+recordNode+nodeRealSize] sub eax, esi push eax mov ecx, [edi+recordNode+indexOffset] add eax, ecx add ecx, recordNode shr ecx, 2 push esi mov esi, edi mov edi, [ebp+NTFS.secondIndexBuffer] rep movsd pop esi pop ecx shr ecx, 2 rep movsd mov edi, [ebp+NTFS.secondIndexBuffer] mov [edi+recordNode+nodeRealSize], eax pop edi mov cl, 4 xor eax, eax mov esi, edi rep stosd mov byte [esi+indexAllocatedSize], 16 mov byte [esi+indexFlags], 2 mov esi, [ebp+NTFS.cur_index_buf] mov eax, [ebp+NTFS.newRecord] movzx edx, word [esi+updateSequenceSize] dec edx mov [esi+recordVCN], eax add esi, recordNode sub edi, esi mov [esi+nodeRealSize], edi ; allocate new node add eax, [ebp+NTFS.cur_subnode_size] cmp edx, [ebp+NTFS.cur_subnode_size] jz @f mul [ebp+NTFS.sectors_per_cluster] @@: shl eax, 9 mov esi, [ebp+NTFS.attr_offs] cmp [esi+attributeAllocatedSize], eax jnc .errorPop3 call resizeAttribute jc ntfsErrorPop3 mov eax, [ebp+NTFS.secondIndexBuffer] mov [ebp+NTFS.cur_buf], eax call writeRecord mov eax, [ebp+NTFS.fileDataStart] mul [ebp+NTFS.sectors_per_cluster] mov [ebp+NTFS.LastRead], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_buf], eax call writeRecord mov eax, [ebp+NTFS.rootLastRead] mov [ebp+NTFS.LastRead], eax mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.cur_buf], eax call writeRecord mov esi, [esp+4] stdcall ntfs_find_lfn.doit2, 0 test eax, eax jz .errorPop3 mov edi, [ebp+NTFS.cur_index_buf] mov edx, [esp] jmp .indexRecord .common: add edi, edx sub edi, 4 mov esi, edi sub esi, [esp] mov ecx, esi sub ecx, eax ; eax = pointer in the record shr ecx, 2 inc ecx std rep movsd ; move forward, make space mov ecx, [esp] shr ecx, 2 xor eax, eax rep stosd cld add edi, 4 pop ecx pop esi mov [edi+indexAllocatedSize], cx ; fill index with data mov eax, [esp] shl eax, 1 add eax, 42h mov [edi+indexRawSize], ax mov eax, [ebp+NTFS.attr_iRecord] mov [edi+directoryRecordReference], eax mov eax, [ebp+NTFS.frs_buffer] mov eax, [eax+reuseCounter] mov [edi+directoryReferenceReuse], ax mov eax, [ebp+NTFS.frs_size] shr eax, 8 add ecx, 30h+48h+8+18h+8 add ecx, eax mov eax, [ebp+NTFS.fileRealSize] add ecx, eax mov [edi+fileRealSize], eax cmp [ebp+NTFS.frs_size], ecx jc @f xor eax, eax @@: mov ecx, [ebp+NTFS.sectors_per_cluster] shl ecx, 9 add eax, ecx dec eax xor edx, edx div ecx mov [ebp+NTFS.fileDataSize], eax mul ecx mov [edi+fileAllocatedSize], eax pop ecx mov [ebp+NTFS.indexPointer], edi mov [edi+fileNameLength], cl add edi, fileName @@: ; record filename lodsb call ansi2uni_char stosw dec ecx jnz @b mov eax, [ebp+NTFS.LastRead] mov [ebp+NTFS.nodeLastRead], eax cmp [ebp+NTFS.bFolder], 0 jz @f mov edi, [ebp+NTFS.indexPointer] bts dword [edi+fileFlags], 28 jmp .mftBitmap @@: ; 3. File data cmp [ebp+NTFS.fileDataSize], 0 jz .mftBitmap mov edi, [ebp+NTFS.BitmapStart] call ntfsSpaceAlloc jc ntfsDiskFull mov eax, [ebp+NTFS.fileDataStart] mul [ebp+NTFS.sectors_per_cluster] mov ecx, [ebp+NTFS.fileRealSize] add ecx, 511 shr ecx, 9 mov ebx, [ebp+NTFS.fileDataBuffer] call fs_write64_app test eax, eax jnz ntfsDevice ; 4. MFT record .mftBitmap: ; search for free record mov edi, [ebp+NTFS.mftBitmapBuffer] mov ecx, [ebp+NTFS.mftBitmapSize] mov al, -1 add edi, 3 sub ecx, 3 repz scasb dec edi movzx eax, byte [edi] not al bsf ecx, eax jz .extendBitmapMFT ; no free records bts [edi], ecx ; get record location sub edi, [ebp+NTFS.mftBitmapBuffer] shl edi, 3 add edi, ecx mov [ebp+NTFS.newRecord], edi mov eax, [ebp+NTFS.frs_size] shr eax, 9 mul edi mov [ebp+NTFS.cur_iRecord], 0 mov [ebp+NTFS.cur_attr], 0x80 mov [ebp+NTFS.cur_offs], eax push eax mov [ebp+NTFS.cur_size], 0 mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.cur_buf], eax call ntfs_read_attr pop eax jc ntfsFail cmp eax, [ebp+NTFS.mftSize] jnc .extendMFT jmp .mftRecord .extendBitmapMFT: mov eax, [ebp+NTFS.sectors_per_cluster] mov [ebp+NTFS.cur_offs], eax shl eax, 9 cmp [ebp+NTFS.mftBitmapSize], eax jnc ntfsUnsupported mov [ebp+NTFS.cur_iRecord], 0 mov [ebp+NTFS.cur_attr], 0xB0 mov [ebp+NTFS.cur_size], 0 call ntfs_read_attr jc ntfsFail mov eax, [ebp+NTFS.mft_cluster] mul [ebp+NTFS.sectors_per_cluster] cmp eax, [ebp+NTFS.LastRead] jnz ntfsUnsupported ; auxiliary record mov edi, [ebp+NTFS.mftBitmapBuffer] mov ecx, [ebp+NTFS.mftBitmapSize] add edi, ecx mov eax, ecx mov edx, [ebp+NTFS.attr_offs] add ecx, 8 mov [edx+attributeRealSize], ecx mov [edx+initialDataSize], ecx shl eax, 3 mov [ebp+NTFS.newRecord], eax mov dword [edi], 1 mov dword [edi+4], 0 mov [ebp+NTFS.cur_attr], 0x80 mov [ebp+NTFS.cur_offs], 0 call ntfs_read_attr.newAttribute jc ntfsFail mov [ebp+NTFS.mftBitmapSize], ecx .extendMFT: mov eax, [ebp+NTFS.mft_cluster] mul [ebp+NTFS.sectors_per_cluster] cmp eax, [ebp+NTFS.LastRead] jnz ntfsUnsupported ; auxiliary record mov ecx, [ebp+NTFS.attr_offs] mov eax, [ecx+attributeRealSize] mov edx, [ecx+attributeRealSize+4] xor ax, ax add eax, 10000h adc edx, 0 push [ebp+NTFS.fileDataStart] push [ebp+NTFS.fileDataSize] call resizeAttribute jc ntfsErrorPop2 mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.cur_buf], eax call writeRecord ; $MFT mov eax, [ebp+NTFS.mftmirr_cluster] mul [ebp+NTFS.sectors_per_cluster] mov ebx, [ebp+NTFS.frs_buffer] movzx ecx, word [ebx+updateSequenceSize] dec ecx call fs_write64_sys ; $MFTMirr ; update $MFT retrieval information mov edi, [ebp+NTFS.mft_retrieval_end] mov eax, [edi-4] add eax, [edi-8] mov edx, [ebp+NTFS.fileDataSize] cmp eax, [ebp+NTFS.fileDataStart] jnz .newFragment add [edi-8], edx jmp @f .newFragment: lea eax, [ebp+NTFS.attrlist_buf] cmp eax, edi jz @f mov [edi], edx mov eax, [ebp+NTFS.fileDataStart] mov [edi+4], eax add [ebp+NTFS.mft_retrieval_end], 8 @@: mov eax, [ebp+NTFS.fileDataSize] mul [ebp+NTFS.sectors_per_cluster] add [ebp+NTFS.mftSize], eax call ntfsSpaceClean pop [ebp+NTFS.fileDataSize] pop [ebp+NTFS.fileDataStart] .mftRecord: mov ecx, [ebp+NTFS.frs_size] shr ecx, 2 mov edi, [ebp+NTFS.frs_buffer] xor eax, eax rep stosd mov edi, [ebp+NTFS.frs_buffer] ; record header rdtsc mov [edi+2ah], ax mov eax, [ebp+NTFS.frs_size] mov [edi+recordAllocatedSize], eax shr eax, 9 inc eax mov [edi+updateSequenceSize], al shl eax, 1 add eax, 2ah+7 and eax, not 7 mov dword[edi], 'FILE' mov byte [edi+updateSequenceOffset], 2ah mov byte [edi+hardLinkCounter], 1 mov byte [edi+newAttributeID], 3 mov [edi+attributeOffset], al add edi, eax ; $StandardInformation mov byte [edi+attributeType], 10h mov byte [edi+sizeWithHeader], 48h mov byte [edi+sizeWithoutHeader], 30h mov byte [edi+attributeOffset], 18h add edi, 48h ; $FileName mov esi, [ebp+NTFS.indexPointer] mov byte [edi+attributeType], 30h mov byte [edi+attributeID], 1 mov byte [edi+attributeOffset], 18h mov byte [edi+indexedFlag], 1 mov cx, [esi+indexRawSize] mov [edi+sizeWithoutHeader], ecx mov cx, [esi+indexAllocatedSize] add ecx, 8 mov [edi+sizeWithHeader], ecx add edi, 18h add esi, 16 sub ecx, 18h shr ecx, 2 rep movsd mov byte [edi+sizeWithHeader], 50h mov byte [edi+attributeID], 2 cmp [ebp+NTFS.bFolder], 1 jz .indexRoot ; $Data mov byte [edi+attributeType], 80h mov eax, [ebp+NTFS.fileDataSize] test eax, eax jz .resident mov esi, [ebp+NTFS.indexPointer] dec eax mov [edi+lastVCN], eax mov byte [edi+nonResidentFlag], 1 mov byte [edi+dataRunsOffset], 40h mov eax, [esi+fileAllocatedSize] mov [edi+attributeAllocatedSize], eax mov eax, [esi+fileRealSize] mov [edi+attributeRealSize], eax mov [edi+initialDataSize], eax push edi mov esi, edi add edi, 40h call createMcbEntry inc edi jmp @f .resident: mov ecx, [ebp+NTFS.fileRealSize] mov [edi+sizeWithoutHeader], ecx mov byte [edi+attributeOffset], 18h push edi mov esi, [ebp+NTFS.fileDataBuffer] add edi, 18h rep movsb @@: mov eax, edi pop edi sub eax, edi add eax, 7 and eax, not 7 mov [edi+sizeWithHeader], eax add edi, eax mov al, 1 jmp .end .indexRoot: mov byte [edi+attributeType], 90h mov byte [edi+nameLength], 4 mov byte [edi+nameOffset], 18h mov byte [edi+sizeWithoutHeader], 30h mov byte [edi+attributeOffset], 20h mov dword[edi+18h], 490024h ; unicode $I30 mov dword[edi+18h+4], 300033h mov byte [edi+20h+indexedAttributesType], 30h mov byte [edi+20h+collationRule], 1 mov eax, [ebp+NTFS.sectors_per_cluster] mov dl, 1 shl eax, 8 @@: shl eax, 1 shl edx, 1 cmp eax, [ebp+NTFS.frs_size] jc @b shr edx, 1 mov [edi+20h+indexRecordSize], eax mov [edi+20h+indexRecordSizeClus], dl mov byte [edi+30h+indexOffset], 16 mov byte [edi+30h+nodeRealSize], 32 mov byte [edi+30h+nodeAllocatedSize], 32 mov byte [edi+40h+indexAllocatedSize], 16 mov byte [edi+40h+indexFlags], 2 add edi, 50h mov al, 3 .end: mov esi, [ebp+NTFS.frs_buffer] mov dword [edi], -1 mov dword [edi+4], 0 add edi, 8 sub edi, esi mov [ebp+NTFS.cur_buf], esi mov [esi+recordFlags], al mov [esi+recordRealSize], edi call writeRecord ; write MFT bitmap mov eax, [ebp+NTFS.newRecord] shr eax, 3+9 mov ebx, eax shl ebx, 9 add eax, [ebp+NTFS.mftBitmapLocation] add ebx, [ebp+NTFS.mftBitmapBuffer] mov ecx, 1 xor edx, edx call fs_write64_sys mov edi, [ebp+NTFS.indexPointer] mov eax, [ebp+NTFS.newRecord] mov [edi+fileRecordReference], eax ; 5. Write directory node mov eax, [ebp+NTFS.nodeLastRead] mov [ebp+NTFS.LastRead], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_buf], eax call writeRecord mov ebx, [ebp+NTFS.fileRealSize] ntfsDone: mov esi, [ebp+PARTITION.Disk] call disk_sync call ntfs_unlock xor eax, eax ret writeRecord: ; make updateSequence and write to disk ; in: ; [ebp+NTFS.cur_buf] -> record ; [ebp+NTFS.LastRead] = partition sector mov esi, [ebp+NTFS.cur_buf] mov edi, esi movzx ecx, word [esi+updateSequenceOffset] add edi, ecx mov ax, [edi] inc ax stosw mov cx, [esi+updateSequenceSize] dec ecx push ecx @@: add esi, 510 movsw mov [esi-2], ax dec ecx jnz @b mov eax, [ebp+NTFS.LastRead] mov ebx, [ebp+NTFS.cur_buf] pop ecx xor edx, edx jmp fs_write64_sys createMcbEntry: ; in: ; [ebp+NTFS.fileDataStart] = position value ; [ebp+NTFS.fileDataSize] = size value ; edi -> destination ; esi -> attribute header mov eax, [ebp+NTFS.fileDataStart] xor edx, edx shl eax, 1 jnc @f not eax @@: inc edx shr eax, 8 jnz @b mov eax, [ebp+NTFS.fileDataSize] shl eax, 1 xor ecx, ecx @@: inc ecx shr eax, 8 jnz @b lea eax, [edi+edx+1] add eax, ecx sub eax, esi sub eax, [esi+sizeWithHeader] jc @f add word [esi+sizeWithHeader], 8 ; extend attribute mov esi, [ebp+NTFS.frs_buffer] mov eax, [esi+recordRealSize] add eax, 8 cmp [esi+recordAllocatedSize], eax jc .end ; no space in the record mov [esi+recordRealSize], eax push ecx edi add esi, eax mov ecx, esi sub ecx, edi sub ecx, 8 shr ecx, 2 mov edi, esi sub edi, 4 sub esi, 12 std rep movsd cld pop edi ecx @@: mov eax, edx shl eax, 4 add eax, ecx stosb lea esi, [ebp+NTFS.fileDataSize] rep movsb lea esi, [ebp+NTFS.fileDataStart] mov ecx, edx rep movsb mov [edi], cl .end: ret resizeAttribute: ; in: ; [ebp+NTFS.frs_buffer] -> file record ; [ebp+NTFS.attr_offs] -> attribute ; edx:eax = new size ; out: ; [ebp+NTFS.fileDataSize] = clusters added (positive) ; [ebp+NTFS.fileDataStart] = added block ; CF=1 -> eax = error code mov esi, [ebp+NTFS.attr_offs] mov dword [ebp+NTFS.attr_size], eax mov dword [ebp+NTFS.attr_size+4], edx cmp byte [esi+nonResidentFlag], 0 jz .resident mov ecx, [ebp+NTFS.sectors_per_cluster] shl ecx, 9 mov [esi+attributeRealSize], eax mov [esi+attributeRealSize+4], edx mov [esi+initialDataSize], eax mov [esi+initialDataSize+4], edx sub eax, 1 sbb edx, 0 jc .makeResident div ecx mov edi, eax inc eax mul ecx mov [esi+attributeAllocatedSize], eax mov [esi+attributeAllocatedSize+4], edx mov ecx, [esi+lastVCN] mov [esi+lastVCN], edi movzx eax, byte [esi+dataRunsOffset] sub edi, ecx mov [ebp+NTFS.fileDataSize], edi jz .done jc .shrinkAttribute ; extend attribute xor edi, edi add esi, eax push edi edi edi edi @@: mov edx, eax mov eax, esi add edi, [esp+8] call ntfs_decode_mcb_entry jc @b mov [esp+4], edx mov [esp+12], edi add edi, [esp] push edi shr edi, 5 shl edi, 2 push eax cmp [ebp+NTFS.cur_iRecord], 0 jz @f cmp edi, [ebp+NTFS.BitmapStart] jc .err1 @@: call ntfsSpaceAlloc jc .err1 mov eax, [ebp+NTFS.fileDataStart] pop edi pop edx cmp edx, eax jnz .newEntry pop edx pop edi pop [ebp+NTFS.fileDataStart] mov [esp], eax push [ebp+NTFS.fileDataSize] add [ebp+NTFS.fileDataSize], edx jmp @f .newEntry: add esp, 12 pop edx push eax push [ebp+NTFS.fileDataSize] sub eax, edx mov [ebp+NTFS.fileDataStart], eax @@: mov esi, [ebp+NTFS.attr_offs] call createMcbEntry pop [ebp+NTFS.fileDataSize] pop [ebp+NTFS.fileDataStart] movi eax, ERROR_UNSUPPORTED_FS .done: ret .err1: add esp, 24 stc .err2: movi eax, ERROR_DISK_FULL ret .err3: movi eax, ERROR_FS_FAIL add esp, 20 stc ret .shrinkAttribute: add ecx, edi inc ecx add esi, eax xor edi, edi sub esp, 20 @@: mov [esp+16], esi call ntfs_decode_mcb_entry jnc .err3 add edi, [esp+8] sub ecx, [esp] jnc @b mov ebx, ecx add ecx, [esp] mov eax, [esp+8] mov [ebp+NTFS.fileDataSize], ecx mov [ebp+NTFS.fileDataStart], eax push edi add edi, ecx neg ebx call ntfsSpaceFree pop edi jc .end @@: call ntfs_decode_mcb_entry jnc .end cmp dword[esp+8], 0 jz @b add edi, [esp+8] mov ebx, [esp] call ntfsSpaceFree jnc @b .end: add esp, 16 pop edi cmp [ebp+NTFS.fileDataSize], 0 jz @f mov esi, [ebp+NTFS.attr_offs] call createMcbEntry mov [ebp+NTFS.fileDataSize], 0 @@: ret .resident: test edx, edx jnz .nonResident cmp eax, 8000h jnc .nonResident add ax, [esi+attributeOffset] sub eax, [esi+sizeWithHeader] jc @f mov edi, [ebp+NTFS.frs_buffer] mov ecx, eax add ecx, [edi+recordRealSize] cmp [edi+recordAllocatedSize], ecx jc .nonResident add eax, 7 and eax, not 7 add [edi+recordRealSize], eax add edi, [edi+recordRealSize] add [esi+sizeWithHeader], eax add esi, [esi+sizeWithHeader] mov ecx, edi sub ecx, esi shr ecx, 2 sub edi, 4 mov esi, edi sub esi, eax std rep movsd mov ecx, eax shr ecx, 2 xor eax, eax rep stosd cld mov esi, [ebp+NTFS.attr_offs] @@: mov eax, dword [ebp+NTFS.attr_size] mov [esi+sizeWithoutHeader], eax mov [ebp+NTFS.fileDataSize], 0 clc ret .nonResident: ; convert resident to non-resident mov eax, dword [ebp+NTFS.attr_size] sub eax, 1 sbb edx, 0 mov ecx, [ebp+NTFS.sectors_per_cluster] shl ecx, 9 div ecx inc eax mov [ebp+NTFS.fileDataSize], eax mov edi, [ebp+NTFS.BitmapStart] push ecx call ntfsSpaceAlloc pop ecx jc .err2 mov esi, [ebp+NTFS.attr_offs] xor eax, eax xor edx, edx @@: add eax, ecx inc edx cmp eax, [esi+sizeWithoutHeader] jc @b push edx push eax stdcall kernel_alloc, eax mov ecx, [esp] shr ecx, 2 mov edi, eax mov ebx, eax xor eax, eax rep stosd mov al, [esi+attributeOffset] mov ecx, [esi+sizeWithoutHeader] add esi, eax mov edi, ebx rep movsb mov eax, [ebp+NTFS.fileDataStart] mul [ebp+NTFS.sectors_per_cluster] pop ecx shr ecx, 9 call fs_write64_app stdcall kernel_free, ebx mov esi, [ebp+NTFS.attr_offs] add esi, [esi+sizeWithHeader] mov ecx, [ebp+NTFS.frs_buffer] add ecx, [ecx+recordRealSize] sub ecx, esi shr ecx, 2 lea edi, [ebp+NTFS.bitmap_buf] push ecx rep movsd mov edi, [ebp+NTFS.attr_offs] add edi, 16 mov cl, 6 xor eax, eax rep stosd mov edi, [ebp+NTFS.attr_offs] mov eax, [ebp+NTFS.fileDataSize] dec eax mov [edi+lastVCN], eax inc eax mov ecx, [ebp+NTFS.sectors_per_cluster] shl ecx, 9 mul ecx mov byte [edi+sizeWithHeader], 50h mov byte [edi+nonResidentFlag], 1 mov byte [edi+dataRunsOffset], 40h mov [edi+attributeAllocatedSize], eax mov [edi+attributeAllocatedSize+4], edx mov eax, dword [ebp+NTFS.attr_size] mov edx, dword [ebp+NTFS.attr_size+4] mov [edi+attributeRealSize], eax mov [edi+attributeRealSize+4], edx mov [edi+initialDataSize], eax mov [edi+initialDataSize+4], edx mov esi, edi add edi, 40h call createMcbEntry mov eax, edi mov edi, [ebp+NTFS.attr_offs] sub eax, edi add eax, 8 and eax, not 7 mov [edi+sizeWithHeader], eax pop ecx lea esi, [ebp+NTFS.bitmap_buf] add edi, eax rep movsd mov esi, [ebp+NTFS.frs_buffer] sub edi, esi mov [esi+recordRealSize], edi pop edx sub [ebp+NTFS.fileDataSize], edx add [ebp+NTFS.fileDataStart], edx ret .makeResident: ; convert non-resident to empty resident movzx eax, byte [esi+dataRunsOffset] mov byte [esi+nonResidentFlag], 0 mov dword [esi+sizeWithoutHeader], 0 mov dword [esi+attributeOffset], 18h add esi, eax xor edi, edi sub esp, 16 @@: call ntfs_decode_mcb_entry jnc @f cmp dword[esp+8], 0 jz @b add edi, [esp+8] mov ebx, [esp] call ntfsSpaceFree jnc @b @@: add esp, 16 mov [ebp+NTFS.fileDataSize], 0 ret ntfsSpaceClean: ; clean up to 16 Mb of disk space ; in: ; [ebp+NTFS.fileDataStart] = block to clean ; [ebp+NTFS.fileDataSize] = block size mov eax, [ebp+NTFS.fileDataSize] test eax, eax jz @f mul [ebp+NTFS.sectors_per_cluster] cmp eax, 8001h jnc @f push eax shl eax, 9 stdcall kernel_alloc, eax pop ecx test eax, eax jz @f push ecx shl ecx, 7 mov edi, eax mov ebx, eax xor eax, eax rep stosd mov eax, [ebp+NTFS.fileDataStart] mul [ebp+NTFS.sectors_per_cluster] mov [ebp+NTFS.LastRead], eax pop ecx call fs_write64_app stdcall kernel_free, ebx @@: ret ntfsSpaceAlloc: ; allocate disk space ; in: ; edi = offset in bitmap to start search from ; [ebp+NTFS.fileDataSize] = block size in clusters ; out: ; [ebp+NTFS.fileDataStart] = allocated block starting cluster ; CF=1 -> disk full push eax mov ecx, [ebp+NTFS.BitmapBuffer] add edi, ecx add ecx, [ebp+NTFS.BitmapSize] sub ecx, edi jnc @f call bitmapBuffering shl ecx, 2 @@: shr ecx, 2 mov eax, [ebp+NTFS.fileDataSize] shr eax, 5 jz .small mov ebx, eax ; bitmap dwords .start: mov ecx, [ebp+NTFS.BitmapBuffer] add ecx, [ebp+NTFS.BitmapSize] sub ecx, edi shr ecx, 2 @@: xor eax, eax repnz scasd ; search for empty dword jz @f call bitmapBuffering jmp @b @@: cmp ecx, ebx jnc @f call bitmapBuffering jmp @b @@: sub edi, 4 mov ecx, ebx mov esi, edi xor eax, eax repz scasd ; check following dwords jnz .start sub esi, 4 mov eax, [esi] xor edx, edx bsr edx, eax inc edx push edx ; starting bit push esi ; starting dword add esi, 4 neg edx add edx, 32 mov eax, [ebp+NTFS.fileDataSize] sub eax, edx mov edx, eax shr eax, 5 shl eax, 2 add esi, eax mov eax, [esi] bsf ecx, eax ; check last dword jz .done and edx, 31 cmp ecx, edx jnc .done add esp, 8 jmp .start .small: ; less than 32 clusters mov eax, -1 repz scasd ; search for zero bits test ecx, ecx jnz @f call bitmapBuffering jmp .small @@: sub edi, 4 mov eax, [edi] not eax @@: bsf ebx, eax ; first 0 jz .again not eax shr eax, cl shl eax, cl bsf edx, eax ; next 1 jz @f sub edx, ebx cmp edx, [ebp+NTFS.fileDataSize] jnc .got ; fits inside bsf ebx, eax not eax shr eax, cl shl eax, cl jmp @b @@: ; next dword mov eax, [edi+4] bsf edx, eax jz .got ; empty add edx, 32 sub edx, ebx cmp edx, [ebp+NTFS.fileDataSize] jnc .got ; share between dwords .again: add edi, 4 jmp .small .got: push ebx ; starting bit push edi ; starting dword .done: ; mark space mov ecx, [esp+4] cmp ecx, 32 jc @f xor ecx, ecx add dword [esp], 4 mov [esp+4], ecx @@: mov edi, [esp] xor eax, eax dec eax shr eax, cl shl eax, cl neg ecx add ecx, 32 sub ecx, [ebp+NTFS.fileDataSize] jc @f shl eax, cl ; fits inside dword shr eax, cl or [edi], eax jmp .end @@: or [edi], eax neg ecx push ecx shr ecx, 5 add edi, 4 xor eax, eax dec eax rep stosd pop ecx and ecx, 31 shr eax, cl shl eax, cl not eax or [edi], eax .end: pop eax pop ecx sub eax, [ebp+NTFS.BitmapBuffer] shl eax, 3 add eax, ecx pop ecx mov ecx, [ebp+NTFS.fileDataSize] mov [ebp+NTFS.fileDataStart], eax add ecx, eax add ecx, 4095 shr ecx, 3+9 shr eax, 3+9 sub ecx, eax mov ebx, eax shl ebx, 9 add eax, [ebp+NTFS.BitmapLocation] add ebx, [ebp+NTFS.BitmapBuffer] xor edx, edx jmp fs_write64_app ntfsSpaceFree: ; free disk space ; in: ; edi = starting cluster ; ebx = size in clusters mov eax, edi add eax, ebx shr eax, 3 inc eax cmp eax, [ebp+NTFS.BitmapSize] jc @f add eax, [ebp+NTFS.BitmapBuffer] push edi mov edi, eax call bitmapBuffering pop edi @@: push edi mov ecx, edi shr edi, 5 shl edi, 2 add edi, [ebp+NTFS.BitmapBuffer] and ecx, 31 xor eax, eax dec eax shr eax, cl shl eax, cl neg ecx add ecx, 32 sub ecx, ebx jc @f shl eax, cl ; fits inside dword shr eax, cl not eax and [edi], eax jmp .writeBitmap @@: not eax and [edi], eax neg ecx push ecx shr ecx, 5 add edi, 4 xor eax, eax rep stosd pop ecx and ecx, 31 dec eax shr eax, cl shl eax, cl and [edi], eax .writeBitmap: pop eax mov edi, eax lea ecx, [eax+ebx+4095] shr eax, 3+9 shr ecx, 3+9 sub ecx, eax mov ebx, eax shl ebx, 9 add eax, [ebp+NTFS.BitmapLocation] add ebx, [ebp+NTFS.BitmapBuffer] xor edx, edx jmp fs_write64_app bitmapBuffering: ; Extend BitmapBuffer and read next 32kb of bitmap ; Warning: $Bitmap fragmentation is not foreseen ; in: edi -> position in bitmap buffer ; out: ecx = number of buffered dwords left push ebx mov eax, [ebp+NTFS.BitmapTotalSize] cmp eax, [ebp+NTFS.BitmapSize] jz .end stdcall alloc_pages, 8 test eax, eax jz .end add eax, 3 mov ebx, [ebp+NTFS.BitmapBuffer] add ebx, [ebp+NTFS.BitmapSize] push ebx mov ecx, 8 call commit_pages mov eax, [ebp+NTFS.BitmapSize] shr eax, 9 add eax, [ebp+NTFS.BitmapLocation] pop ebx mov ecx, 64 xor edx, edx call fs_read64_app test eax, eax jnz .err add [ebp+NTFS.BitmapSize], 8000h mov eax, [ebp+NTFS.BitmapTotalSize] cmp eax, [ebp+NTFS.BitmapSize] jnc @f mov [ebp+NTFS.BitmapSize], eax @@: pop ebx mov ecx, [ebp+NTFS.BitmapBuffer] add ecx, [ebp+NTFS.BitmapSize] sub ecx, edi jc bitmapBuffering shr ecx, 2 ret .err: mov eax, [ebp+NTFS.BitmapBuffer] add eax, [ebp+NTFS.BitmapSize] mov ecx, 8 call release_pages .end: add esp, 12 ; ret stc ret ;---------------------------------------------------------------- ntfs_WriteFile: cmp byte [esi], 0 jnz @f xor ebx, ebx movi eax, ERROR_ACCESS_DENIED ret @@: call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jc ntfsNotFound cmp [ebp+NTFS.cur_iRecord], 16 jc ntfsDenied bt dword [eax+fileFlags], 28 jc ntfsDenied cmp [ebp+NTFS.fragmentCount], 1 jnz ntfsUnsupported ; record fragmented ; edit directory node mov edi, [ebp+NTFS.cur_index_buf] cmp dword [edi], 'INDX' jz @f mov esi, [ebp+NTFS.frs_buffer] mov ecx, [esi+recordRealSize] shr ecx, 2 rep movsd mov esi, [ebp+NTFS.attr_offs] mov cl, [esi+attributeOffset] sub esi, [ebp+NTFS.frs_buffer] add eax, ecx add eax, esi @@: mov ecx, [ebx+4] mov edx, [ebx+8] add ecx, [ebx+12] adc edx, 0 mov [eax+fileRealSize], ecx mov [eax+fileRealSize+4], edx mov eax, [ebp+NTFS.LastRead] mov [ebp+NTFS.nodeLastRead], eax mov [ebp+NTFS.cur_attr], 0x80 mov [ebp+NTFS.cur_offs], 0 mov [ebp+NTFS.cur_size], 0 call ntfs_read_attr jc ntfsFail mov eax, ecx mov ecx, [ebp+NTFS.frs_buffer] cmp word [ecx+baseRecordReuse], 0 jnz ntfsUnsupported ; auxiliary record mov ecx, [ebp+NTFS.attr_offs] cmp word [ecx+attributeFlags], 0 jnz ntfsUnsupported push ebx cmp byte [ecx+nonResidentFlag], 0 jz .resizeAttribute cmp edx, [ecx+attributeRealSize+4] jc .writeNode jnz .resizeAttribute cmp [ecx+attributeRealSize], eax jnc .writeNode .resizeAttribute: call resizeAttribute jc ntfsErrorPop mov ecx, [ebp+NTFS.attr_offs] cmp byte [ecx+nonResidentFlag], 1 jz @f mov ebx, [esp] movzx edi, byte [ecx+attributeOffset] add edi, ecx add edi, [ebx+4] mov ecx, [ebx+12] mov esi, [ebx+16] rep movsb @@: mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.cur_buf], eax call writeRecord ; file mov ebx, [ebp+NTFS.frs_buffer] call ntfs_restore_usa_frs .writeNode: mov eax, [ebp+NTFS.nodeLastRead] mov [ebp+NTFS.LastRead], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_buf], eax call writeRecord ; directory pop ebx mov ecx, [ebp+NTFS.attr_offs] cmp byte [ecx+nonResidentFlag], 0 jz .done mov ecx, [ebx+12] test ecx, ecx jz .done mov eax, [ebx+4] mov edx, [ebx+8] mov esi, [ebx+16] shrd eax, edx, 9 test dword[ebx+4], 1FFh jz .aligned mov [ebp+NTFS.cur_offs], eax mov [ebp+NTFS.cur_size], 1 lea edi, [ebp+NTFS.bitmap_buf] mov [ebp+NTFS.cur_buf], edi call ntfs_read_attr.continue jc ntfsDevice mov eax, [ebx+4] and eax, 1FFh add edi, eax sub eax, [ebp+NTFS.cur_read] neg eax push ecx cmp ecx, eax jb @f mov ecx, eax @@: sub [esp], ecx rep movsb push ebx mov eax, [ebp+NTFS.LastRead] lea ebx, [ebp+NTFS.bitmap_buf] mov ecx, 1 xor edx, edx call fs_write64_app pop ebx pop ecx test ecx, ecx jz .done mov eax, [ebx+4] mov edx, [ebx+8] shrd eax, edx, 9 inc eax .aligned: push ecx shr ecx, 9 mov [ebp+NTFS.cur_offs], eax mov [ebp+NTFS.cur_size], ecx mov [ebp+NTFS.cur_buf], esi add eax, ecx push eax mov [ebp+NTFS.bWriteAttr], 1 call ntfs_read_attr.continue mov [ebp+NTFS.bWriteAttr], 0 pop [ebp+NTFS.cur_offs] pop ecx jc ntfsDevice and ecx, 1FFh jz .done add esi, [ebp+NTFS.cur_read] mov [ebp+NTFS.cur_size], 1 lea edi, [ebp+NTFS.bitmap_buf] mov [ebp+NTFS.cur_buf], edi call ntfs_read_attr.continue jc ntfsDevice rep movsb push ebx mov eax, [ebp+NTFS.LastRead] lea ebx, [ebp+NTFS.bitmap_buf] mov ecx, 1 xor edx, edx call fs_write64_app pop ebx .done: mov ebx, [ebx+12] jmp ntfsDone ;---------------------------------------------------------------- ntfs_Delete: xor ebx, ebx cmp byte [esi], 0 jnz @f movi eax, ERROR_ACCESS_DENIED ret @@: call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jc ntfsNotFound cmp [ebp+NTFS.cur_iRecord], 16 jc ntfsDenied cmp [ebp+NTFS.fragmentCount], 1 jnz ntfsUnsupported ; record fragmented mov edx, [ebp+NTFS.cur_iRecord] shr edx, 3 cmp edx, [ebp+NTFS.mftBitmapSize] jnc ntfsUnsupported mov edx, [ebp+NTFS.secondIndexBuffer] mov byte [edx], 0 mov edx, [ebp+NTFS.LastRead] mov [ebp+NTFS.nodeLastRead], edx test byte [eax+indexFlags], 1 jz .deleteIndex ; no subnode movzx edx, word [eax+indexAllocatedSize] mov edi, eax mov eax, [eax+edx-8] mov edx, [ebp+NTFS.cur_size] push edx cmp edx, [ebp+NTFS.cur_subnode_size] jz @f mul [ebp+NTFS.sectors_per_cluster] @@: mov [ebp+NTFS.cur_attr], 0xA0 mov [ebp+NTFS.cur_offs], eax push eax mov ebx, [ebp+NTFS.secondIndexBuffer] mov esi, ebx mov [ebp+NTFS.cur_buf], ebx call ntfs_read_attr.newAttribute pop [ebp+NTFS.cur_offs] pop eax jc ntfsFail cmp dword [esi], 'INDX' jnz ntfsFail mov [ebp+NTFS.cur_size], eax shl eax, 9 call ntfs_restore_usa jc ntfsFail add esi, recordNode cmp byte [esi+nonLeafFlag], 0 jnz ntfsUnsupported ; non leaf node add esi, [esi+indexOffset] test byte [esi+indexFlags], 2 jnz .deleteSubnode ; empty node xor eax, eax @@: add esi, eax mov ax, [esi+indexAllocatedSize] test byte [esi+eax+indexFlags], 2 jz @b movzx edx, word [edi+indexAllocatedSize] sub edx, eax sub edx, 8 mov ebx, esi mov eax, edi jmp @f .deleteSubnode: mov esi, [ebp+NTFS.attr_offs] add esi, [esi+sizeWithHeader] cmp byte [esi], 0xB0 jnz ntfsFail movzx eax, byte [esi+attributeOffset] add esi, eax mov eax, [ebp+NTFS.cur_offs] xor edx, edx div [ebp+NTFS.cur_size] mov edx, eax shr eax, 3 and edx, 7 btr [esi+eax], edx mov dx, [edi+indexAllocatedSize] mov eax, edi mov edi, [ebp+NTFS.secondIndexBuffer] mov byte [edi], 0 xor ebx, ebx mov esi, [ebp+NTFS.cur_index_buf] cmp dword [esi], 'INDX' jnz @f mov esi, [ebp+NTFS.frs_buffer] mov ecx, [esi+recordRealSize] shr ecx, 2 rep movsd jmp @f .deleteIndex: movzx edx, word [eax+indexAllocatedSize] mov ecx, [eax+fileRecordReference] cmp [eax+edx+fileRecordReference], ecx jnz @f add dx, [eax+edx+indexAllocatedSize] @@: mov edi, [ebp+NTFS.cur_index_buf] cmp dword [edi], 'INDX' jz .indexRecord mov esi, [ebp+NTFS.frs_buffer] ; indexRoot mov ecx, [esi+recordRealSize] shr ecx, 2 rep movsd mov esi, [ebp+NTFS.cur_index_buf] mov edi, [ebp+NTFS.indexRoot] sub edi, [ebp+NTFS.frs_buffer] add edi, esi sub [edi+sizeWithHeader], edx sub [edi+sizeWithoutHeader], edx mov cl, [edi+attributeOffset] add edi, ecx sub [edi+rootNode+nodeRealSize], edx sub [edi+rootNode+nodeAllocatedSize], edx sub eax, esi add eax, edi sub [esi+recordRealSize], edx mov ecx, [esi+recordRealSize] cmp [esi+recordAllocatedSize], ecx jc ntfsUnsupported jmp @f .indexRecord: add edi, recordNode sub [edi+nodeRealSize], edx mov ecx, [edi+nodeRealSize] cmp [edi+nodeAllocatedSize], ecx jc ntfsUnsupported add ecx, recordNode @@: add ecx, [ebp+NTFS.cur_index_buf] sub ecx, eax mov esi, eax add esi, edx mov edi, eax test edx, edx jns @f neg edx add edx, ecx sub edx, 4 add esi, edx add edi, edx std @@: jz @f shr ecx, 2 rep movsd cld @@: test ebx, ebx jz @f ; copy index from the subnode to replace deleted pointing index movzx ecx, word [ebx+indexAllocatedSize] mov edx, ecx shr ecx, 2 mov esi, ebx mov edi, eax rep movsd mov edi, [ebp+NTFS.cur_index_buf] xchg [ebp+NTFS.secondIndexBuffer], edi mov [ebp+NTFS.cur_index_buf], edi add word [eax+indexAllocatedSize], 8 mov byte [eax+indexFlags], 1 mov eax, [ebp+NTFS.LastRead] xchg [ebp+NTFS.nodeLastRead], eax mov [ebp+NTFS.rootLastRead], eax mov eax, ebx xor ebx, ebx jmp .indexRecord @@: ; examine file record mov [ebp+NTFS.cur_attr], 0x80 mov [ebp+NTFS.cur_offs], 0 mov [ebp+NTFS.cur_size], 0 call ntfs_read_attr jc .folder mov esi, [ebp+NTFS.frs_buffer] cmp word [esi+baseRecordReuse], 0 jnz ntfsUnsupported ; auxiliary record mov esi, [ebp+NTFS.attr_offs] cmp byte [esi+nonResidentFlag], 0 jz .writeBitmapMFT movzx eax, byte [esi+dataRunsOffset] add esi, eax xor edi, edi sub esp, 16 @@: ; "delete" file data call ntfs_decode_mcb_entry jnc @f cmp dword[esp+8], 0 jz @b add edi, [esp+8] mov ebx, [esp] call ntfsSpaceFree jnc @b @@: add esp, 16 jmp .writeBitmapMFT .folder: ; empty? lea esi, [ebp+NTFS.bitmap_buf] mov [ebp+NTFS.cur_buf], esi mov [ebp+NTFS.cur_attr], 0x90 mov [ebp+NTFS.cur_offs], 0 mov [ebp+NTFS.cur_size], 1 call ntfs_read_attr cmp [ebp+NTFS.cur_read], 48 jnz ntfsDenied test byte [esi+32+indexFlags], 1 jnz ntfsDenied .writeBitmapMFT: ; "delete" file record mov eax, [ebp+NTFS.cur_iRecord] mov ecx, eax shr eax, 3 and ecx, 7 mov edi, [ebp+NTFS.mftBitmapBuffer] btr [edi+eax], ecx shr eax, 9 mov ebx, eax shl ebx, 9 add eax, [ebp+NTFS.mftBitmapLocation] add ebx, edi mov ecx, 1 xor edx, edx call fs_write64_sys mov esi, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.cur_buf], esi mov byte [esi+recordFlags], 0 call writeRecord ; write directory node mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_buf], eax mov eax, [ebp+NTFS.nodeLastRead] mov [ebp+NTFS.LastRead], eax call writeRecord mov eax, [ebp+NTFS.secondIndexBuffer] cmp byte [eax], 0 jz ntfsDone mov [ebp+NTFS.cur_buf], eax mov eax, [ebp+NTFS.rootLastRead] mov [ebp+NTFS.LastRead], eax call writeRecord jmp ntfsDone ;---------------------------------------------------------------- ntfs_SetFileEnd: cmp byte [esi], 0 jnz @f xor ebx, ebx movi eax, ERROR_ACCESS_DENIED ret @@: call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jc ntfsNotFound cmp [ebp+NTFS.cur_iRecord], 16 jc ntfsDenied bt dword [eax+fileFlags], 28 jc ntfsDenied cmp [ebp+NTFS.fragmentCount], 1 jnz ntfsUnsupported ; record fragmented ; edit directory node mov edi, [ebp+NTFS.cur_index_buf] cmp dword [edi], 'INDX' jz @f mov esi, [ebp+NTFS.frs_buffer] mov ecx, [esi+recordRealSize] shr ecx, 2 rep movsd mov esi, [ebp+NTFS.attr_offs] mov cl, [esi+attributeOffset] sub esi, [ebp+NTFS.frs_buffer] add eax, ecx add eax, esi @@: mov ecx, [ebx+4] mov edx, [ebx+8] mov [eax+fileRealSize], ecx mov [eax+fileRealSize+4], edx mov eax, [ebp+NTFS.LastRead] mov [ebp+NTFS.nodeLastRead], eax mov [ebp+NTFS.cur_attr], 0x80 mov [ebp+NTFS.cur_offs], 0 mov [ebp+NTFS.cur_size], 0 call ntfs_read_attr jc ntfsFail mov eax, ecx mov ecx, [ebp+NTFS.frs_buffer] cmp word [ecx+baseRecordReuse], 0 jnz ntfsUnsupported ; auxiliary record mov ecx, [ebp+NTFS.attr_offs] cmp word [ecx+attributeFlags], 0 jnz ntfsUnsupported cmp byte [ecx+nonResidentFlag], 0 jz .resizeAttribute cmp [ecx+attributeRealSize+4], edx jnz .resizeAttribute cmp [ecx+attributeRealSize], eax jnc .resizeAttribute mov eax, [ecx+attributeRealSize] mov ecx, [ebp+NTFS.sectors_per_cluster] mov [ebp+NTFS.cur_size], ecx shl ecx, 9 div ecx test edx, edx jz .aligned push edx push ecx mul [ebp+NTFS.sectors_per_cluster] mov [ebp+NTFS.cur_offs], eax stdcall kernel_alloc, ecx pop ecx pop edi mov esi, eax sub ecx, edi add edi, eax mov [ebp+NTFS.cur_buf], eax push [ebp+NTFS.LastRead] call ntfs_read_attr.continue jc @f xor eax, eax rep stosb push ebx mov eax, [ebp+NTFS.LastRead] mov ebx, esi mov ecx, [ebp+NTFS.sectors_per_cluster] xor edx, edx call fs_write64_app pop ebx @@: pop [ebp+NTFS.LastRead] stdcall kernel_free, esi .aligned: mov eax, [ebx+4] mov edx, [ebx+8] .resizeAttribute: call resizeAttribute jc ntfsError mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.cur_buf], eax call writeRecord ; file mov eax, [ebp+NTFS.nodeLastRead] mov [ebp+NTFS.LastRead], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_buf], eax call writeRecord ; directory call ntfsSpaceClean jmp ntfsDone ;---------------------------------------------------------------- ntfs_SetFileInfo: movi eax, ERROR_UNSUPPORTED_FS ret ;---------------------------------------------------------------- ntfs_GetFileInfo: cmp byte [esi], 0 jnz @f movi eax, ERROR_UNSUPPORTED_FS ret @@: call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jnc .found test eax, eax jz ntfsFail jmp ntfsNotFound .found: push esi edi mov esi, eax mov edi, [ebx+16] xor eax, eax call ntfs_direntry_to_bdfe pop edi esi call ntfs_unlock xor eax, eax ret ntfsUnsupported: push ERROR_UNSUPPORTED_FS jmp ntfsOut ntfsDevice: push ERROR_DEVICE jmp ntfsOut ntfsNotFound: push ERROR_FILE_NOT_FOUND jmp ntfsOut ntfsDenied: push ERROR_ACCESS_DENIED jmp ntfsOut ntfsFail: push ERROR_FS_FAIL jmp ntfsOut ntfsDiskFull: push ERROR_DISK_FULL jmp ntfsOut ntfsErrorPop3: pop ebx ntfsErrorPop2: pop ebx ntfsErrorPop: pop ebx ntfsError: push eax ntfsOut: call ntfs_unlock xor ebx, ebx pop eax ret