b50e632506
git-svn-id: svn://kolibrios.org@6311 a494cfbc-eb01-0410-851d-a64ba20cac60
3701 lines
109 KiB
PHP
3701 lines
109 KiB
PHP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; 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
|
|
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 dd ?
|
|
mft_retrieval_size dd ?
|
|
mft_retrieval_alloc dd ?
|
|
mft_retrieval_end dd ?
|
|
cur_index_size dd ? ; in sectors
|
|
cur_index_buf dd ? ; index node buffer
|
|
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
|
|
|
|
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
|
|
LastRead dd ? ; last readen block of sectors
|
|
newMftRecord dd ? ; number of fileRecord in MFT
|
|
fileDataStart dd ? ; starting cluster
|
|
fileDataSize dd ? ; in clusters
|
|
fileDataBuffer dd ?
|
|
fileRealSize dd ? ; in bytes
|
|
indexOffset dd ?
|
|
nodeLastRead dd ?
|
|
fragmentCount db ?
|
|
bCanContinue db ?
|
|
bFolder db ?
|
|
bWriteAttr db ? ; Warning: Don't forget to turn off!!!
|
|
|
|
cur_subnode_size dd ?
|
|
attr_iRecord dd ?
|
|
attr_iBaseRecord dd ?
|
|
attr_offs dd ?
|
|
attr_list dd ?
|
|
attr_size dq ?
|
|
cur_tail dd ?
|
|
|
|
attrlist_buf rb 0x400
|
|
attrlist_mft_buf rb 0x400
|
|
bitmap_buf rb 0x400
|
|
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
|
|
|
|
; By given bootsector, initialize some NTFS variables
|
|
.ntfs_setup:
|
|
movi eax, sizeof.NTFS
|
|
call malloc
|
|
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]
|
|
call ntfs_read_frs_sector
|
|
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]
|
|
call ntfs_read_frs_sector
|
|
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:
|
|
; read $MFT table retrieval information
|
|
; start with one page, increase if not enough (when MFT too fragmented)
|
|
push ebx
|
|
stdcall kernel_alloc, 0x1000
|
|
pop ebx
|
|
test eax, eax
|
|
jz .fail_free_frs
|
|
mov [ebp+NTFS.mft_retrieval], eax
|
|
and [ebp+NTFS.mft_retrieval_size], 0
|
|
mov [ebp+NTFS.mft_retrieval_alloc], 0x1000/8
|
|
; $MFT base record must contain unnamed non-resident $DATA attribute
|
|
movzx eax, word [ebx+14h]
|
|
add eax, ebx
|
|
.scandata:
|
|
cmp dword [eax], -1
|
|
jz .fail_free_mft
|
|
cmp dword [eax], 0x80
|
|
jnz @f
|
|
cmp byte [eax+9], 0
|
|
jz .founddata
|
|
@@:
|
|
add eax, [eax+4]
|
|
jmp .scandata
|
|
|
|
.founddata:
|
|
cmp byte [eax+8], 0
|
|
jz .fail_free_mft
|
|
; load first portion of $DATA attribute retrieval information
|
|
mov edx, [eax+0x18]
|
|
mov [ebp+NTFS.mft_retrieval_end], edx
|
|
mov esi, eax
|
|
movzx eax, word [eax+0x20]
|
|
add esi, eax
|
|
sub esp, 10h
|
|
.scanmcb:
|
|
call ntfs_decode_mcb_entry
|
|
jnc .scanmcbend
|
|
call .get_mft_retrieval_ptr
|
|
mov edx, [esp] ; block length
|
|
mov [eax], edx
|
|
mov edx, [esp+8] ; block addr (relative)
|
|
mov [eax+4], edx
|
|
inc [ebp+NTFS.mft_retrieval_size]
|
|
jmp .scanmcb
|
|
|
|
.scanmcbend:
|
|
add esp, 10h
|
|
; there may be other portions of $DATA attribute in auxiliary records;
|
|
; if they will be needed, they will be loaded later
|
|
mov [ebp+NTFS.cur_index_size], 0x1000/0x200
|
|
stdcall kernel_alloc, 0x1000
|
|
test eax, eax
|
|
jz .fail_free_mft
|
|
mov [ebp+NTFS.cur_index_buf], eax
|
|
; 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, [ebx+NTFS.mftBitmapBuffer]
|
|
.failFreeBitmap:
|
|
stdcall kernel_free, [ebx+NTFS.BitmapBuffer]
|
|
.failFreeIndex:
|
|
stdcall kernel_free, [ebp+NTFS.cur_index_buf]
|
|
.fail_free_mft:
|
|
stdcall kernel_free, [ebp+NTFS.mft_retrieval]
|
|
.fail_free_frs:
|
|
stdcall kernel_free, [ebp+NTFS.frs_buffer]
|
|
.fail_free:
|
|
mov eax, ebp
|
|
call free
|
|
xor eax, eax
|
|
jmp .pop_exit
|
|
|
|
.get_mft_retrieval_ptr:
|
|
pushad
|
|
mov eax, [ebp+NTFS.mft_retrieval_size]
|
|
cmp eax, [ebp+NTFS.mft_retrieval_alloc]
|
|
jnz .ok
|
|
add eax, 0x1000/8
|
|
mov [ebp+NTFS.mft_retrieval_alloc], eax
|
|
shl eax, 3
|
|
stdcall kernel_alloc, eax
|
|
test eax, eax
|
|
jnz @f
|
|
popad
|
|
add esp, 14h
|
|
jmp .fail_free_mft
|
|
|
|
@@:
|
|
mov esi, [ebp+NTFS.mft_retrieval]
|
|
mov edi, eax
|
|
mov ecx, [ebp+NTFS.mft_retrieval_size]
|
|
add ecx, ecx
|
|
rep movsd
|
|
push [ebp+NTFS.mft_retrieval]
|
|
mov [ebp+NTFS.mft_retrieval], eax
|
|
call kernel_free
|
|
mov eax, [ebp+NTFS.mft_retrieval_size]
|
|
.ok:
|
|
shl eax, 3
|
|
add eax, [ebp+NTFS.mft_retrieval]
|
|
mov [esp+28], eax
|
|
popad
|
|
ret
|
|
|
|
ntfs_free:
|
|
push ebx
|
|
mov ebx, eax
|
|
stdcall kernel_free, [ebx+NTFS.frs_buffer]
|
|
stdcall kernel_free, [ebx+NTFS.mft_retrieval]
|
|
stdcall kernel_free, [ebx+NTFS.cur_index_buf]
|
|
stdcall kernel_free, [ebx+NTFS.mftBitmapBuffer]
|
|
stdcall kernel_free, [ebx+NTFS.BitmapBuffer]
|
|
mov eax, ebx
|
|
pop ebx
|
|
jmp free
|
|
|
|
ntfs_lock:
|
|
lea ecx, [ebp+NTFS.Lock]
|
|
jmp mutex_lock
|
|
|
|
ntfs_unlock:
|
|
lea ecx, [ebp+NTFS.Lock]
|
|
jmp mutex_unlock
|
|
|
|
ntfs_read_frs_sector:
|
|
push ecx
|
|
mov ebx, [ebp+NTFS.frs_buffer]
|
|
push ebx
|
|
mov ecx, [ebp+NTFS.frs_size]
|
|
shr ecx, 9
|
|
push ecx
|
|
mov ecx, eax
|
|
@@:
|
|
mov eax, ecx
|
|
call fs_read32_sys
|
|
test eax, eax
|
|
jnz .fail
|
|
add ebx, 0x200
|
|
inc ecx
|
|
dec dword [esp]
|
|
jnz @b
|
|
pop eax
|
|
.fail:
|
|
pop ebx
|
|
pop ecx
|
|
ret
|
|
|
|
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
|
|
mov eax, [ebp+NTFS.mft_retrieval_end]
|
|
inc eax
|
|
mul [ebp+NTFS.sectors_per_cluster]
|
|
cmp eax, [ebp+NTFS.cur_offs]
|
|
jbe .nomft
|
|
; precalculated part of $Mft $DATA
|
|
mov esi, [ebp+NTFS.mft_retrieval]
|
|
mov eax, [ebp+NTFS.cur_offs]
|
|
xor edx, edx
|
|
div [ebp+NTFS.sectors_per_cluster]
|
|
; eax = VCN, edx = offset in sectors from beginning of cluster
|
|
xor ecx, ecx ; ecx will contain LCN
|
|
.mftscan:
|
|
add ecx, [esi+4]
|
|
sub eax, [esi]
|
|
jb @f
|
|
add esi, 8
|
|
push eax
|
|
mov eax, [ebp+NTFS.mft_retrieval_end]
|
|
shl eax, 3
|
|
add eax, [ebp+NTFS.mft_retrieval]
|
|
cmp eax, esi
|
|
pop eax
|
|
jnz .mftscan
|
|
jmp .nomft
|
|
|
|
@@:
|
|
push ecx
|
|
add ecx, eax
|
|
add ecx, [esi]
|
|
push eax
|
|
push edx
|
|
mov eax, [ebp+NTFS.sectors_per_cluster]
|
|
mul ecx
|
|
; eax = sector on partition
|
|
pop edx
|
|
add eax, edx
|
|
mov ebx, [ebp+NTFS.cur_buf]
|
|
pop ecx
|
|
neg ecx
|
|
imul ecx, [ebp+NTFS.sectors_per_cluster]
|
|
sub ecx, edx
|
|
mov [ebp+NTFS.LastRead], eax
|
|
cmp ecx, [ebp+NTFS.cur_size]
|
|
jb @f
|
|
mov ecx, [ebp+NTFS.cur_size]
|
|
@@:
|
|
; ecx = number of sequential sectors to read
|
|
push eax
|
|
call fs_read32_sys
|
|
pop edx
|
|
test eax, eax
|
|
jnz .errread
|
|
add [ebp+NTFS.cur_read], 0x200
|
|
dec [ebp+NTFS.cur_size]
|
|
inc [ebp+NTFS.cur_offs]
|
|
add ebx, 0x200
|
|
mov [ebp+NTFS.cur_buf], ebx
|
|
lea eax, [edx+1]
|
|
loop @b
|
|
pop ecx
|
|
xor eax, eax
|
|
xor edx, edx
|
|
cmp [ebp+NTFS.cur_size], eax
|
|
jz @f
|
|
add esi, 8
|
|
push eax
|
|
mov eax, [ebp+NTFS.mft_retrieval_end]
|
|
shl eax, 3
|
|
add eax, [ebp+NTFS.mft_retrieval]
|
|
cmp eax, esi
|
|
pop eax
|
|
jz .nomft
|
|
jmp .mftscan
|
|
|
|
.errret2_pop:
|
|
xor eax, eax
|
|
.errret_pop:
|
|
pop ecx
|
|
.errread:
|
|
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
|
|
.ret_is_attr:
|
|
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
|
|
@@:
|
|
cmp [ebp+NTFS.cur_size], 0
|
|
jz .okret
|
|
mov eax, [ebp+NTFS.cur_offs]
|
|
xor edx, edx
|
|
div [ebp+NTFS.sectors_per_cluster]
|
|
sub eax, [ecx+firstVCN]
|
|
jb .okret
|
|
; eax = cluster, edx = starting sector
|
|
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
|
|
push ecx
|
|
push eax
|
|
add eax, [esp+8]
|
|
add eax, edi
|
|
imul eax, [ebp+NTFS.sectors_per_cluster]
|
|
add eax, edx
|
|
pop ecx
|
|
neg ecx
|
|
imul ecx, [ebp+NTFS.sectors_per_cluster]
|
|
sub ecx, edx
|
|
cmp ecx, [ebp+NTFS.cur_size]
|
|
jb @f
|
|
mov ecx, [ebp+NTFS.cur_size]
|
|
@@:
|
|
mov ebx, [ebp+NTFS.cur_buf]
|
|
mov [ebp+NTFS.LastRead], eax
|
|
push ecx
|
|
xor edx, edx
|
|
call dword[esp+18h]
|
|
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]
|
|
pop ecx
|
|
xor eax, eax
|
|
xor edx, edx
|
|
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:
|
|
pop ecx
|
|
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] = number of MFT fileRecord
|
|
; eax -> index in the parent index node
|
|
; 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 edx, [esi+indexRecordSize]
|
|
shr edx, 9
|
|
cmp [ebp+NTFS.cur_index_size], edx
|
|
jc .realloc
|
|
add esi, rootNode
|
|
mov eax, [esi+nodeRealSize]
|
|
add eax, rootNode
|
|
cmp [ebp+NTFS.cur_read], eax
|
|
jc .err
|
|
mov edi, [esp+4]
|
|
; edi -> name, esi -> current index node, edx = subnode size
|
|
.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, edx
|
|
stdcall kernel_alloc, [esi+indexRecordSize]
|
|
test eax, eax
|
|
jz .err
|
|
push [ebp+NTFS.cur_index_buf]
|
|
mov [ebp+NTFS.cur_index_buf], eax
|
|
call kernel_free
|
|
mov [ebp+NTFS.cur_index_size], edi
|
|
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]
|
|
imul eax, [ebp+NTFS.sectors_per_cluster]
|
|
mov [ebp+NTFS.cur_offs], eax
|
|
mov [ebp+NTFS.cur_attr], 0xA0 ; $INDEX_ALLOCATION
|
|
mov [ebp+NTFS.cur_size], edx
|
|
mov eax, [ebp+NTFS.cur_index_buf]
|
|
mov esi, eax
|
|
mov [ebp+NTFS.cur_buf], eax
|
|
call ntfs_read_attr.newAttribute
|
|
mov eax, edx
|
|
shl eax, 9
|
|
cmp [ebp+NTFS.cur_read], eax
|
|
jnz .err
|
|
cmp dword [esi], 'INDX'
|
|
jnz .err
|
|
mov [ebp+NTFS.cur_buf], esi
|
|
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 edx, [esi+indexRecordSize]
|
|
shr edx, 9
|
|
cmp [ebp+NTFS.cur_index_size], edx
|
|
jc .realloc
|
|
mov [ebp+NTFS.cur_subnode_size], edx
|
|
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 <output>, dd <flags>
|
|
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, edx
|
|
stdcall kernel_alloc, [esi+indexRecordSize]
|
|
test eax, eax
|
|
jz .err
|
|
push [ebp+NTFS.cur_index_buf]
|
|
mov [ebp+NTFS.cur_index_buf], eax
|
|
call kernel_free
|
|
mov [ebp+NTFS.cur_index_size], edi
|
|
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
|
|
|
|
@@:
|
|
add esp, 16
|
|
jmp ntfsUnsupported
|
|
|
|
.growTree:
|
|
sub eax, rootNode
|
|
sub eax, [edi+rootNode+indexOffset]
|
|
push eax
|
|
; create indexRecord
|
|
mov ecx, 10
|
|
xor eax, eax
|
|
rep stosd
|
|
rdtsc
|
|
stosw
|
|
mov esi, [ebp+NTFS.attr_offs]
|
|
mov cl, [esi+attributeOffset]
|
|
add esi, ecx
|
|
mov eax, [esi+indexRecordSizeClus]
|
|
cmp eax, 129
|
|
jnc @b
|
|
mov [ebp+NTFS.fileDataSize], eax
|
|
mov eax, [esi+indexRecordSize]
|
|
cmp eax, [ebp+NTFS.frs_size]
|
|
jc @b
|
|
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 @b
|
|
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 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+6]
|
|
dec eax
|
|
shl eax, 9
|
|
call ntfs_restore_usa
|
|
mov edi, [ebp+NTFS.cur_index_buf]
|
|
pop eax
|
|
add eax, recordNode
|
|
add eax, [edi+recordNode+indexOffset]
|
|
mov edx, [esp]
|
|
.indexRecord:
|
|
add edi, recordNode
|
|
add edx, [edi+nodeRealSize]
|
|
cmp [edi+nodeAllocatedSize], edx
|
|
jnc @f
|
|
add esp, 12
|
|
jmp ntfsUnsupported ; new node required
|
|
|
|
@@: ; index fits in the node
|
|
mov [edi+nodeRealSize], edx
|
|
.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.indexOffset], 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.indexOffset]
|
|
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.newMftRecord], 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
|
|
mov [ebp+NTFS.cur_size], 1
|
|
mov eax, [ebp+NTFS.frs_buffer]
|
|
mov [ebp+NTFS.cur_buf], eax
|
|
call ntfs_read_attr
|
|
cmp [ebp+NTFS.cur_read], 0
|
|
jz .extendMFT
|
|
jmp .mftRecord
|
|
|
|
.extendBitmapMFT:
|
|
mov eax, [ebp+NTFS.sectors_per_cluster]
|
|
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_offs], 0
|
|
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.newMftRecord], eax
|
|
mov dword [edi], 1
|
|
mov dword [edi+4], 0
|
|
mov [ebp+NTFS.cur_attr], 0x80
|
|
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
|
|
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.indexOffset]
|
|
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.indexOffset]
|
|
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.newMftRecord]
|
|
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.indexOffset]
|
|
mov eax, [ebp+NTFS.newMftRecord]
|
|
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:
|
|
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
|
|
cmp [ebp+NTFS.fragmentCount], 1
|
|
jnz ntfsUnsupported ; record fragmented
|
|
test byte [eax+indexFlags], 1
|
|
jnz ntfsUnsupported ; index has a subnode
|
|
mov edx, [ebp+NTFS.cur_iRecord]
|
|
shr edx, 3
|
|
cmp edx, [ebp+NTFS.mftBitmapSize]
|
|
jnc ntfsUnsupported
|
|
; delete index from the node
|
|
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.attr_offs]
|
|
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]
|
|
jmp @f
|
|
|
|
.indexRecord:
|
|
add edi, recordNode+nodeRealSize
|
|
sub [edi], edx
|
|
mov ecx, [edi]
|
|
add ecx, recordNode
|
|
@@:
|
|
add ecx, [ebp+NTFS.cur_index_buf]
|
|
sub ecx, eax
|
|
shr ecx, 2
|
|
mov esi, eax
|
|
add esi, edx
|
|
mov edi, eax
|
|
rep movsd
|
|
mov eax, [ebp+NTFS.LastRead]
|
|
mov [ebp+NTFS.nodeLastRead], eax
|
|
; 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.nodeLastRead]
|
|
mov [ebp+NTFS.LastRead], eax
|
|
mov eax, [ebp+NTFS.cur_index_buf]
|
|
mov [ebp+NTFS.cur_buf], 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
|
|
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, [ebp+NTFS.cur_buf]
|
|
mov ecx, [ebp+NTFS.sectors_per_cluster]
|
|
xor edx, edx
|
|
call fs_write64_app
|
|
pop ebx
|
|
@@:
|
|
pop [ebp+NTFS.LastRead]
|
|
stdcall kernel_free, [ebp+NTFS.cur_buf]
|
|
.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
|
|
ntfsErrorPop2:
|
|
pop ebx
|
|
ntfsErrorPop:
|
|
pop ebx
|
|
ntfsError:
|
|
push eax
|
|
ntfsOut:
|
|
call ntfs_unlock
|
|
xor ebx, ebx
|
|
pop eax
|
|
ret
|