7,33 → 7,113 |
|
$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 |
updateSequenceOffset = 4 |
updateSequenceSize = 6 |
reuseCounter = 16 |
hardLinkCounter = 12h |
attributeOffset = 14h |
recordFlags = 16h |
recordRealSize = 18h |
recordAllocatedSize = 1ch |
newAttributeID = 28h |
; attribute header |
attributeType = 0 |
sizeWithHeader = 4 |
nonResidentFlag = 8 |
nameLength = 9 |
nameOffset = 10 |
attributeID = 14 |
sizeWithoutHeader = 16 |
attributeFlags = 16h |
; non resident attribute header |
lastVCN = 18h |
dataRunsOffset = 20h |
attributeAllocatedSize = 28h |
attributeRealSize = 30h |
initialDataSize = 38h |
; $IndexRoot |
collationRule = 4 |
indexRecordSize = 8 |
indexRecordSizeClus = 12 |
; node header |
indexOffset = 0 |
nodeRealSize = 4 |
nodeAllocatedSize = 8 |
; $Filename index |
fileRecordReference = 0 |
fileReferenceReuse = 6 |
indexAllocatedSize = 8 |
indexRawSize = 10 |
indexFlags = 12 |
directoryRecordReference = 16 |
directoryReferenceReuse = 16h |
fileAllocatedSize = 38h |
fileRealSize = 40h |
fileFlags = 48h |
fileNameLength = 50h |
|
struct NTFS PARTITION |
Lock MUTEX ? ; currently operations with one partition |
; can not be executed in parallel since the |
; legacy code is not ready; this mutex guards |
; all operations |
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 ? |
mftmirr_cluster dd ? |
frs_size dd ? ; FRS size in bytes |
iab_size dd ? ; IndexAllocationBuffer size in bytes |
frs_buffer dd ? |
iab_buffer 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 ? |
cur_index_buf 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 |
|
ntfs_cur_attr dd ? |
ntfs_cur_iRecord dd ? |
ntfs_cur_offs dd ? ; in sectors |
ntfs_cur_size dd ? ; in sectors |
ntfs_cur_attr dd ? ; attribute type |
ntfs_cur_iRecord dd ? ; number of fileRecord in MFT |
ntfs_cur_offs dd ? ; attribute VCN in sectors |
ntfs_cur_size dd ? ; max sectors to read |
ntfs_cur_buf dd ? |
ntfs_cur_read dd ? ; [output] |
ntfs_cur_read dd ? ; bytes readen |
ntfsLastRead dd ? ; last readen block of sectors |
newMftRecord dd ? ; number of fileRecord in MFT |
fileDataStart dd ? ; starting cluster |
fileDataSize dd ? ; in clusters |
fileRealSize dd ? ; in bytes |
indexOffset dd ? |
nodeLastRead dd ? |
ntfs_bCanContinue db ? |
rb 3 |
ntfsNotFound db ? |
ntfsFolder db ? |
ntfsFragmentCount db ? |
|
cur_subnode_size dd ? |
ntfs_attr_iRecord dd ? |
48,14 → 128,21 |
ntfs_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_Read |
dd ntfs_ReadFile |
dd ntfs_ReadFolder |
dd ntfs_Rewrite |
dd ntfs_CreateFile |
dd ntfs_Write |
dd ntfs_SetFileEnd |
dd ntfs_GetFileInfo |
68,7 → 155,7 |
|
ntfs_test_bootsec: |
; in: ebx->buffer, edx=size of partition |
; out: CF set <=> invalid |
; out: CF=1 -> invalid |
; 1. Name=='NTFS ' |
cmp dword [ebx+3], 'NTFS' |
jnz .no |
121,7 → 208,7 |
jnz .no |
cmp eax, edx |
ja .no |
; 7. Clusters per FRS must be either negative and in [-31,-9] or positive and power of 2 |
; 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 |
131,8 → 218,7 |
js .no |
test [ebx+0x40], al |
jnz .no |
@@: |
; 8. Same for clusters per IndexAllocationBuffer |
@@: ; 8. Same for clusters per IndexAllocationBuffer |
movsx eax, byte [ebx+0x44] |
cmp al, -31 |
jl .no |
142,16 → 228,14 |
js .no |
test [ebx+0x44], al |
jnz .no |
@@: |
; OK, this is correct NTFS bootsector |
@@: ; OK, this is correct NTFS bootsector |
clc |
ret |
.no: |
; No, this bootsector isn't NTFS |
.no: ; No, this bootsector isn't NTFS |
stc |
ret |
|
proc ntfs_create_partition |
ntfs_create_partition: |
cmp dword [esi+DISK.MediaInfo.SectorSize], 512 |
jnz .nope |
mov edx, dword [ebp+PARTITION.Length] |
169,7 → 253,7 |
shr eax, 1 |
call fs_read32_sys |
test eax, eax |
jnz .nope ; no chance... |
jnz .nope |
.boot_read_ok: |
call ntfs_test_bootsec |
jnc .ntfs_setup |
176,9 → 260,8 |
.nope: |
xor eax, eax |
jmp .exit |
|
; By given bootsector, initialize some NTFS variables |
.ntfs_setup: |
; By given bootsector, initialize some NTFS variables |
movi eax, sizeof.NTFS |
call malloc |
test eax, eax |
194,12 → 277,11 |
mov ecx, [ebp+PARTITION.Disk] |
mov [eax+NTFS.Disk], ecx |
mov [eax+NTFS.FSUserFunctions], ntfs_user_functions |
|
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] |
211,39 → 293,21 |
mov [ebp+NTFS.mftmirr_cluster], eax |
movsx eax, byte [ebx+0x40] |
test eax, eax |
js .1 |
js @f |
mul [ebp+NTFS.sectors_per_cluster] |
shl eax, 9 |
jmp .2 |
.1: |
jmp .1 |
@@: |
neg eax |
mov ecx, eax |
mov eax, 1 |
shl eax, cl |
.2: |
.1: |
mov [ebp+NTFS.frs_size], eax |
movsx eax, byte [ebx+0x44] |
stdcall kernel_alloc, eax |
test eax, eax |
js .3 |
mul [ebp+NTFS.sectors_per_cluster] |
shl eax, 9 |
jmp .4 |
.3: |
neg eax |
mov ecx, eax |
mov eax, 1 |
shl eax, cl |
.4: |
mov [ebp+NTFS.iab_size], eax |
; allocate space for buffers |
add eax, [ebp+NTFS.frs_size] |
push eax |
call kernel_alloc |
test eax, eax |
jz .fail_free |
mov [ebp+NTFS.frs_buffer], eax |
add eax, [ebp+NTFS.frs_size] |
mov [ebp+NTFS.iab_buffer], eax |
; read $MFT disposition |
mov eax, [ebp+NTFS.mft_cluster] |
mul [ebp+NTFS.sectors_per_cluster] |
259,38 → 323,16 |
mul [ebp+NTFS.sectors_per_cluster] |
call ntfs_read_frs_sector |
test eax, eax |
jnz @f |
jnz .fail_free_frs |
cmp dword [ebx], 'FILE' |
jnz @f |
jnz .fail_free_frs |
call ntfs_restore_usa_frs |
jnc .mftok |
@@: |
; $MFT and $MFTMirr invalid! |
.fail_free_frs: |
push [ebp+NTFS.frs_buffer] |
call kernel_free |
.fail_free: |
mov eax, ebp |
call free |
xor eax, eax |
.pop_exit: |
pop esi ebp ebx |
.exit: |
cmp dword [esp+4], 0 |
jz @f |
sub ebx, 512 |
@@: |
ret |
.fail_free_mft: |
push [ebp+NTFS.mft_retrieval] |
call kernel_free |
jmp .fail_free_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 |
push 0x1000 |
call kernel_alloc |
stdcall kernel_alloc, 0x1000 |
pop ebx |
test eax, eax |
jz .fail_free_frs |
334,17 → 376,98 |
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 |
push 0x1000 |
call kernel_alloc |
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.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.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.ntfs_cur_iRecord], 6 |
mov [ebp+NTFS.ntfs_cur_attr], 0x80 |
mov [ebp+NTFS.ntfs_cur_offs], 0 |
call ntfs_read_attr |
jc .failFreeBitmap |
mov eax, [ebp+NTFS.ntfs_cur_read] |
mov [ebp+NTFS.BitmapSize], eax |
mov eax, [ebp+NTFS.ntfsLastRead] |
mov [ebp+NTFS.BitmapLocation], eax |
; read MFT $BITMAP attribute |
mov eax, [ebp+NTFS.sectors_per_cluster] |
mov [ebp+NTFS.ntfs_cur_size], eax |
shl eax, 9 |
stdcall kernel_alloc, eax |
test eax, eax |
jz .failFreeBitmap |
mov [ebp+NTFS.mftBitmapBuffer], eax |
mov [ebp+NTFS.ntfs_cur_buf], eax |
mov [ebp+NTFS.ntfs_cur_iRecord], 0 |
mov [ebp+NTFS.ntfs_cur_attr], 0xB0 |
mov [ebp+NTFS.ntfs_cur_offs], 0 |
call ntfs_read_attr |
mov eax, [ebp+NTFS.ntfs_cur_read] |
cmp eax, 4 |
jc .failFreeBitmapMFT |
mov [ebp+NTFS.mftBitmapSize], eax |
mov eax, [ebp+NTFS.ntfsLastRead] |
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 |
endp |
|
.get_mft_retrieval_ptr: |
pushad |
354,8 → 477,7 |
add eax, 0x1000/8 |
mov [ebp+NTFS.mft_retrieval_alloc], eax |
shl eax, 3 |
push eax |
call kernel_alloc |
stdcall kernel_alloc, eax |
test eax, eax |
jnz @f |
popad |
378,27 → 500,25 |
popad |
ret |
|
proc ntfs_free |
ntfs_free: |
push ebx |
xchg ebx, eax |
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] |
xchg ebx, eax |
call free |
stdcall kernel_free, [ebx+NTFS.mftBitmapBuffer] |
stdcall kernel_free, [ebx+NTFS.BitmapBuffer] |
mov eax, ebx |
pop ebx |
ret |
endp |
jmp free |
|
proc ntfs_lock |
ntfs_lock: |
lea ecx, [ebp+NTFS.Lock] |
jmp mutex_lock |
endp |
|
proc ntfs_unlock |
ntfs_unlock: |
lea ecx, [ebp+NTFS.Lock] |
jmp mutex_unlock |
endp |
|
ntfs_read_frs_sector: |
push ecx |
424,9 → 544,15 |
ret |
|
ntfs_read_attr: |
; in: variables in ebp+NTFS.* |
; out: [ebp+NTFS.ntfs_cur_read] |
; out: CF=1 => notfound, in this case eax=0 => disk ok, otherwise eax=disk error code |
; in: |
; [ebp+NTFS.ntfs_cur_iRecord] = number of fileRecord |
; [ebp+NTFS.ntfs_cur_attr] = attribute type |
; [ebp+NTFS.ntfs_cur_offs] = attribute VCN in sectors |
; [ebp+NTFS.ntfs_cur_buf] -> buffer for data |
; [ebp+NTFS.ntfs_cur_size] = max sectors to read |
; out: |
; [ebp+NTFS.ntfs_cur_read] = bytes readen |
; CF=1 -> failed, eax = disk error code, eax=0 -> something with FS |
xor eax, eax |
pushad |
and [ebp+NTFS.ntfs_cur_read], 0 |
475,6 → 601,7 |
neg ecx |
imul ecx, [ebp+NTFS.sectors_per_cluster] |
sub ecx, edx |
mov [ebp+NTFS.ntfsLastRead], eax |
cmp ecx, [ebp+NTFS.ntfs_cur_size] |
jb @f |
mov ecx, [ebp+NTFS.ntfs_cur_size] |
869,6 → 996,7 |
movzx esi, word [ecx+20h] ; mcb_info_ofs |
add esi, ecx |
xor edi, edi |
mov [ebp+NTFS.ntfsFragmentCount], 0 |
.readloop: |
call ntfs_decode_mcb_entry |
jnc .break |
890,27 → 1018,27 |
mov ecx, [ebp+NTFS.ntfs_cur_size] |
@@: |
mov ebx, [ebp+NTFS.ntfs_cur_buf] |
@@: |
push eax |
mov [ebp+NTFS.ntfsLastRead], eax |
push ecx |
xor edx, edx |
cmp [ebp+NTFS.ntfs_cur_attr], 0x80 |
jnz .sys |
cmp [ebp+NTFS.ntfs_cur_iRecord], 0 |
jz .sys |
call fs_read32_app |
call fs_read64_app |
jmp .appsys |
.sys: |
call fs_read32_sys |
call fs_read64_sys |
.appsys: |
pop edx |
pop ecx |
test eax, eax |
jnz .errread2 |
add ebx, 0x200 |
mov [ebp+NTFS.ntfs_cur_buf], ebx |
lea eax, [edx+1] |
add [ebp+NTFS.ntfs_cur_read], 0x200 |
dec [ebp+NTFS.ntfs_cur_size] |
inc [ebp+NTFS.ntfs_cur_offs] |
loop @b |
sub [ebp+NTFS.ntfs_cur_size], ecx |
add [ebp+NTFS.ntfs_cur_offs], ecx |
shl ecx, 9 |
add [ebp+NTFS.ntfs_cur_read], ecx |
add [ebp+NTFS.ntfs_cur_buf], ecx |
inc [ebp+NTFS.ntfsFragmentCount] |
pop ecx |
xor eax, eax |
xor edx, edx |
937,8 → 1065,8 |
|
ntfs_read_file_record: |
; in: eax=iRecord |
; out: [ebp+NTFS.frs_buffer] contains information |
; CF=1 - failed, in this case eax=0 => something with FS, eax nonzero => disk error |
; out: [ebp+NTFS.frs_buffer] = record data |
; 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] |
1080,9 → 1208,11 |
ret |
|
ntfs_find_lfn: |
; in: esi+[esp+4] -> name |
; out: CF=1 - file not found |
; else CF=0, [ebp+NTFS.ntfs_cur_iRecord] valid, eax->record in parent directory |
; in: [esi]+[esp+4] = name |
; out: |
; [ebp+NTFS.ntfs_cur_iRecord] = number of MFT fileRecord |
; eax = pointer in parent index node |
; CF=1 -> file not found (or just error) |
mov [ebp+NTFS.ntfs_cur_iRecord], 5 ; start parse from root cluster |
.doit2: |
mov [ebp+NTFS.ntfs_cur_attr], 0x90 ; $INDEX_ROOT |
1116,12 → 1246,10 |
@@: |
; reallocate |
push eax |
push [ebp+NTFS.cur_index_buf] |
call kernel_free |
stdcall kernel_free, [ebp+NTFS.cur_index_buf] |
pop eax |
mov [ebp+NTFS.cur_index_size], eax |
push eax |
call kernel_alloc |
stdcall kernel_alloc, eax |
test eax, eax |
jnz @f |
and [ebp+NTFS.cur_index_size], 0 |
1137,8 → 1265,7 |
cmp edx, [ebp+NTFS.cur_index_size] |
jbe .ok2 |
push esi edx |
push edx |
call kernel_alloc |
stdcall kernel_alloc, edx |
pop edx esi |
test eax, eax |
jz .stc_ret |
1149,8 → 1276,7 |
mov esi, eax |
mov [ebp+NTFS.cur_index_size], edx |
push esi edx |
push [ebp+NTFS.cur_index_buf] |
call kernel_free |
stdcall kernel_free, [ebp+NTFS.cur_index_buf] |
pop edx esi |
mov [ebp+NTFS.cur_index_buf], esi |
.ok2: |
1209,15 → 1335,19 |
mov eax, edx |
shl eax, 9 |
cmp [ebp+NTFS.ntfs_cur_read], eax |
jnz .notfound |
jnz .err |
cmp dword [esi], 'INDX' |
jnz .notfound |
jnz .err |
mov [ebp+NTFS.ntfs_cur_buf], esi |
mov ebx, esi |
call ntfs_restore_usa |
jc .notfound |
jc .err |
add esi, 0x18 |
jmp .scanloop |
.notfound: |
mov [ebp+NTFS.ntfsNotFound], 1 |
mov [esp+1Ch], esi |
.err: |
popad |
stc |
ret 4 |
1250,13 → 1380,7 |
ret 4 |
|
;---------------------------------------------------------------- |
; ntfs_Read - NTFS implementation of reading a file |
; in: ebp = pointer to NTFS structure |
; in: esi+[esp+4] = name |
; in: ebx = pointer to parameters from sysfunc 70 |
; out: eax, ebx = return values for sysfunc 70 |
;---------------------------------------------------------------- |
ntfs_Read: |
ntfs_ReadFile: |
cmp byte [esi], 0 |
jnz @f |
or ebx, -1 |
1290,8 → 1414,7 |
popad |
xor ebx, ebx |
.eof: |
movi eax, ERROR_END_OF_FILE |
push eax |
push ERROR_END_OF_FILE |
call ntfs_unlock |
pop eax |
ret |
1392,12 → 1515,6 |
ret |
|
;---------------------------------------------------------------- |
; ntfs_ReadFolder - NTFS implementation of reading a folder |
; in: ebp = pointer to NTFS structure |
; in: esi+[esp+4] = name |
; in: ebx = pointer to parameters from sysfunc 70 |
; out: eax, ebx = return values for sysfunc 70 |
;---------------------------------------------------------------- |
ntfs_ReadFolder: |
call ntfs_lock |
mov eax, 5 ; root cluster |
1458,12 → 1575,10 |
@@: |
; reallocate |
push eax |
push [ebp+NTFS.cur_index_buf] |
call kernel_free |
stdcall kernel_free, [ebp+NTFS.cur_index_buf] |
pop eax |
mov [ebp+NTFS.cur_index_size], eax |
push eax |
call kernel_alloc |
stdcall kernel_alloc, eax |
test eax, eax |
jnz @f |
and [ebp+NTFS.cur_index_size], 0 |
1485,8 → 1600,7 |
cmp edx, [ebp+NTFS.cur_index_size] |
jbe .ok2 |
push esi edx |
push edx |
call kernel_alloc |
stdcall kernel_alloc, edx |
pop edx esi |
test eax, eax |
jz .nomem |
1496,8 → 1610,7 |
rep movsd |
mov esi, eax |
mov [ebp+NTFS.cur_index_size], edx |
push [ebp+NTFS.cur_index_buf] |
call kernel_free |
stdcall kernel_free, [ebp+NTFS.cur_index_buf] |
mov [ebp+NTFS.cur_index_buf], esi |
.ok2: |
add esi, 10h |
1860,30 → 1973,698 |
ret |
|
;---------------------------------------------------------------- |
; ntfs_Rewrite - NTFS implementation of creating a new file |
; in: ebp = pointer to NTFS structure |
; in: esi+[esp+4] = name |
; in: ebx = pointer to parameters from sysfunc 70 |
; out: eax, ebx = return values for sysfunc 70 |
;---------------------------------------------------------------- |
ntfs_Rewrite: |
ntfs_CreateFolder: |
mov [ebp+NTFS.ntfsFolder], 1 |
jmp @f |
ntfs_CreateFile: |
mov [ebp+NTFS.ntfsFolder], 0 |
@@: |
cmp byte [esi], 0 |
jnz @f |
xor ebx, ebx |
mov eax, ERROR_UNSUPPORTED_FS |
movi eax, ERROR_ACCESS_DENIED ; root directory itself |
ret |
@@: ; 1. Search file |
call ntfs_lock |
mov [ebp+NTFS.ntfsNotFound], 0 |
stdcall ntfs_find_lfn, [esp+4] |
jnc @f ; found; rewrite |
cmp [ebp+NTFS.ntfsFragmentCount], 1 |
jnz @f ; record fragmented |
cmp [ebp+NTFS.ntfsNotFound], 1 |
jz .notFound |
push ERROR_FS_FAIL |
jmp ntfsError |
@@: |
push ERROR_UNSUPPORTED_FS |
jmp ntfsError |
.notFound: ; create; check name |
cmp dword [esp+4], 0 |
jnz .bad |
cmp byte [esi], 0 |
jnz @f |
.bad: ; path folder not found |
push ERROR_FILE_NOT_FOUND |
jmp ntfsError |
@@: ; 2. Prepair directory record |
mov ecx, esi |
@@: ; count characters |
inc ecx |
cmp byte [ecx], '/' |
jz .bad |
cmp byte [ecx], 0 |
jnz @b |
sub ecx, esi |
push ecx |
lea ecx, [ecx*2+52h] ; precalculate index length |
add ecx, 7 ; align 8 |
and ecx, not 7 |
mov edi, [ebp+NTFS.cur_index_buf] |
push esi |
push ecx |
cmp dword [edi], 'INDX' ; where are we? |
jz .indexRecord |
mov esi, [ebp+NTFS.frs_buffer] ; mftRecord |
mov edx, [esi+recordRealSize] |
add edx, ecx |
cmp [esi+recordAllocatedSize], edx |
jnc @f |
add esp, 12 |
push ERROR_UNSUPPORTED_FS ; indexAllocation required |
jmp ntfsError |
@@: ; index fits in the indexRoot |
mov [esi+recordRealSize], edx |
mov ecx, edx |
shr ecx, 2 |
rep movsd |
mov edi, [ebp+NTFS.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 cx, [edi+attributeOffset] |
add edi, ecx |
add [edi+16+nodeRealSize], esi |
add [edi+16+nodeAllocatedSize], esi |
sub eax, [ebp+NTFS.cur_index_buf] |
add eax, edi |
mov edi, [ebp+NTFS.cur_index_buf] |
add edi, edx |
sub edi, 4 |
jmp .common |
|
.indexRecord: |
mov edx, [edi+1ch] |
add edx, ecx |
cmp [edi+20h], edx |
jnc @f |
add esp, 12 |
push ERROR_UNSUPPORTED_FS ; new node required |
jmp ntfsError |
@@: ; index fits in the node |
mov [edi+1ch], edx |
lea edi, [edi+edx+14h] |
.common: |
mov esi, edi |
sub esi, [esp] |
mov ecx, esi |
sub ecx, eax ; eax = pointer in the node |
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 eax |
pop esi |
mov [edi+indexAllocatedSize], ax ; fill index with data |
mov eax, [esp] |
lea eax, [eax*2+42h] |
mov [edi+indexRawSize], ax |
mov eax, [ebp+NTFS.ntfs_attr_iRecord] |
mov [edi+directoryRecordReference], eax |
mov eax, [ebp+NTFS.frs_buffer] |
mov eax, [eax+reuseCounter] |
mov [edi+directoryReferenceReuse], ax |
mov eax, [ebx+12] |
mov [ebp+NTFS.fileRealSize], eax |
mov [edi+fileRealSize], 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, 52h |
@@: ; record filename |
lodsb |
call ansi2uni_char |
stosw |
dec ecx |
jnz @b |
mov eax, [ebp+NTFS.ntfsLastRead] |
mov [ebp+NTFS.nodeLastRead], eax |
cmp [ebp+NTFS.ntfsFolder], 0 |
jz @f |
mov edi, [ebp+NTFS.indexOffset] |
mov byte [edi+fileFlags+3], 16 |
jmp .mftBitmap |
|
@@: ; 3. File data |
cmp [ebp+NTFS.fileRealSize], 0 |
jz .mftBitmap |
; One piece free space bitmap search engine |
mov edi, [ebp+NTFS.BitmapBuffer] |
add edi, [ebp+NTFS.BitmapStart] |
mov eax, [ebp+NTFS.fileDataSize] |
shr eax, 5 |
jz .small |
push eax ; bitmap dwords |
add edi, 4 |
xor edx, edx |
.start: |
mov ecx, [ebp+NTFS.BitmapSize] |
mov eax, edi |
sub eax, [ebp+NTFS.BitmapBuffer] |
sub ecx, eax |
shr ecx, 2 |
@@: |
xor eax, eax |
repnz scasd ; search for empty dword |
jz @f |
call bitmapBuffering |
jmp @b |
@@: |
cmp ecx, [esp] |
jnc @f |
call bitmapBuffering |
jmp @b |
@@: |
sub edi, 4 |
mov ecx, [esp] |
mov esi, edi |
xor eax, eax |
repz scasd ; check following dwords |
jnz .start |
sub esi, 4 |
mov eax, [esi] |
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 ; last dword |
jz .done |
and edx, 31 |
cmp ecx, edx |
jnc .done |
add esp, 8 |
jmp .start |
|
.small: ; less than 32 clusters |
mov ecx, [ebp+NTFS.BitmapSize] |
sub ecx, [ebp+NTFS.BitmapStart] |
shr ecx, 2 |
.smStart: |
mov eax, -1 |
repz scasd ; search for zero bits |
push ecx |
test ecx, ecx |
jnz @f |
call bitmapBuffering |
pop eax |
jmp .smStart |
@@: |
sub edi, 4 |
mov eax, [edi] |
not eax |
@@: |
bsf ecx, eax ; first 0 |
jz .again |
not eax |
shr eax, cl |
shl eax, cl |
bsf edx, eax ; next 1 |
jz @f |
sub edx, ecx |
cmp edx, [ebp+NTFS.fileDataSize] |
jnc .got ; fits inside |
bsf ecx, 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, ecx |
cmp edx, [ebp+NTFS.fileDataSize] |
jnc .got ; share between dwords |
.again: |
add edi, 4 |
pop ecx |
jmp .smStart |
|
.got: |
push ecx ; 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] |
mov esi, [ebp+NTFS.fileDataSize] |
mov edx, [edi] |
ror edx, cl |
neg ecx |
add ecx, 32 |
mov eax, -1 |
sub esi, ecx |
jnc @f |
mov esi, ecx ; fits inside |
mov ecx, [ebp+NTFS.fileDataSize] |
shrd edx, eax, cl |
sub esi, ecx |
mov ecx, esi |
ror edx, cl |
mov [edi], edx |
jmp .writeData |
|
@@: |
shrd edx, eax, cl |
mov [edi], edx |
mov ecx, esi |
shr ecx, 5 |
add edi, 4 |
rep stosd |
mov ecx, esi |
and ecx, 31 |
mov edx, [edi] |
shr edx, cl |
shld edx, eax, cl |
mov [edi], edx |
.writeData: |
pop edx |
sub edx, [ebp+NTFS.BitmapBuffer] |
shl edx, 3 |
pop eax |
add eax, edx |
pop edx |
mov [ebp+NTFS.fileDataStart], eax |
mul [ebp+NTFS.sectors_per_cluster] |
mov ecx, [ebp+NTFS.fileRealSize] |
add ecx, 511 |
shr ecx, 9 |
mov ebx, [ebx+16] |
call fs_write64_app |
test eax, eax |
jz .mftBitmap |
push 11 |
jmp ntfsError |
|
; 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 |
jnz @f |
push ERROR_UNSUPPORTED_FS ; no free records |
jmp ntfsError |
@@: ; mark record |
mov al, [edi] |
bts eax, ecx |
mov [edi], al |
; 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.ntfs_cur_iRecord], 0 |
mov [ebp+NTFS.ntfs_cur_attr], 0x80 |
mov [ebp+NTFS.ntfs_cur_offs], eax |
mov [ebp+NTFS.ntfs_cur_size], 1 |
mov eax, [ebp+NTFS.frs_buffer] |
mov [ebp+NTFS.ntfs_cur_buf], eax |
call ntfs_read_attr |
cmp [ebp+NTFS.ntfs_cur_read], 0 |
jnz .mftRecord |
; extend MFT $DATA |
mov eax, [ebp+NTFS.mft_cluster] |
mul [ebp+NTFS.sectors_per_cluster] |
push ERROR_UNSUPPORTED_FS |
cmp eax, [ebp+NTFS.ntfsLastRead] |
jnz ntfsError ; auxiliary record |
mov edi, [ebp+NTFS.ntfs_attr_offs] |
mov ebx, [ebp+NTFS.sectors_per_cluster] |
shl ebx, 9+3 |
add dword [edi+lastVCN], 8 |
add [edi+attributeAllocatedSize], ebx |
add [edi+attributeRealSize], ebx |
add [edi+initialDataSize], ebx |
add edi, [edi+dataRunsOffset] |
movzx eax, byte [edi] |
inc edi |
shl eax, 4 |
shr al, 4 |
mov cl, 4 |
sub cl, al |
shl cl, 3 |
add ah, al |
shr eax, 8 |
cmp byte [edi+eax], 0 |
jnz ntfsError ; $MFT fragmented |
mov al, 8 |
mov edx, [edi] |
rol eax, cl |
rol edx, cl |
add eax, edx |
jc ntfsError |
ror eax, cl |
shr edx, cl |
mov [edi], eax |
add edx, [ebp+NTFS.mft_cluster] |
mov esi, edx |
mov ecx, edx |
and ecx, 7 |
shr edx, 3 |
add edx, [ebp+NTFS.BitmapBuffer] |
movzx eax, word [edx] |
shr eax, cl |
jnz ntfsError |
mov al, -1 |
xchg [edx], al |
mov [edx+1], al |
pop eax |
push 12 |
stdcall kernel_alloc, ebx |
test eax, eax |
jz ntfsError |
mov ecx, ebx |
shr ecx, 2 |
mov edi, eax |
push ebx |
mov ebx, eax |
xor eax, eax |
rep stosd |
mov eax, esi |
mul [ebp+NTFS.sectors_per_cluster] |
pop ecx |
shr ecx, 9 |
call fs_write64_sys ; clear new records |
stdcall kernel_free, ebx |
pop eax |
push 11 |
mov eax, esi |
shr eax, 3+9 |
mov ebx, eax |
shl ebx, 9 |
add ebx, [ebp+NTFS.BitmapBuffer] |
add eax, [ebp+NTFS.BitmapLocation] |
mov ecx, 1 |
xor edx, edx |
call fs_write64_app ; partition bitmap |
test eax, eax |
jnz ntfsError |
mov eax, [ebp+NTFS.frs_buffer] |
mov [ebp+NTFS.ntfs_cur_buf], eax |
call writeRecord ; $MFT |
test eax, eax |
jnz ntfsError |
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 |
test eax, eax |
jnz ntfsError |
pop eax |
mov eax, [ebp+NTFS.ntfs_cur_offs] |
add [ebp+NTFS.ntfsLastRead], eax |
.mftRecord: |
mov esi, [ebp+NTFS.indexOffset] |
mov edi, [ebp+NTFS.frs_buffer] |
xor eax, eax |
movzx ecx, word [esi+indexAllocatedSize] |
add ecx, 8+30h+48h+50h+8 |
push ecx |
shr ecx, 2 |
rep stosd |
mov edi, [ebp+NTFS.frs_buffer] |
; record header |
mov dword[edi], 'FILE' |
mov byte [edi+updateSequenceOffset], 2ah |
mov byte [edi+updateSequenceSize], 3 |
mov byte [edi+hardLinkCounter], 1 |
mov byte [edi+attributeOffset], 30h |
pop dword[edi+recordRealSize] |
mov word [edi+recordAllocatedSize], 1024 |
mov byte [edi+newAttributeID], 3 |
rdtsc |
mov [edi+2ah], ax |
add edi, 30h |
; $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 byte [edi+attributeType], 30h |
mov byte [edi+attributeID], 1 |
mov cx, [esi+indexRawSize] |
mov [edi+sizeWithoutHeader], ecx |
mov cx, [esi+indexAllocatedSize] |
add ecx, 8 |
mov [edi+sizeWithHeader], ecx |
mov byte [edi+attributeOffset], 18h |
mov byte [edi+attributeFlags], 1 |
add edi, 18h |
add esi, 16 |
sub ecx, 18h |
shr ecx, 2 |
rep movsd |
cmp [ebp+NTFS.ntfsFolder], 0 |
jnz @f |
; $Data |
mov byte [edi+attributeType], 80h |
cmp [ebp+NTFS.fileRealSize], 0 |
jz .zeroSize |
mov esi, [ebp+NTFS.indexOffset] |
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 |
mov byte [edi+40h], 44h |
mov eax, [ebp+NTFS.fileDataSize] |
mov [edi+41h], eax |
dec eax |
mov [edi+lastVCN], eax |
mov eax, [ebp+NTFS.fileDataStart] |
mov [edi+45h], eax |
mov al, 1 |
jmp .writeMftRecord |
|
.zeroSize: |
mov byte [edi+attributeOffset], 18h |
mov al, 1 |
jmp .writeMftRecord |
|
@@: ; $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 |
add edi, 20h |
mov byte [edi+attributeType], 30h |
mov byte [edi+collationRule], 1 |
mov eax, [ebp+NTFS.sectors_per_cluster] |
shl eax, 9 |
mov [edi+indexRecordSize], eax |
mov byte [edi+indexRecordSizeClus], 1 |
mov byte [edi+16+indexOffset], 16 |
mov byte [edi+16+nodeRealSize], 32 |
mov byte [edi+16+nodeAllocatedSize], 32 |
mov byte [edi+32+indexAllocatedSize], 16 |
mov byte [edi+32+indexFlags], 2 |
sub edi, 20h |
mov al, 3 |
.writeMftRecord: |
mov byte [edi+sizeWithHeader], 50h |
mov byte [edi+attributeID], 2 |
mov dword[edi+50h], -1 ; $End |
mov edi, [ebp+NTFS.frs_buffer] |
mov [edi+recordFlags], al |
mov [ebp+NTFS.ntfs_cur_buf], edi |
call writeRecord |
test eax, eax |
jz @f |
push 11 |
jmp ntfsError |
@@: |
mov esi, [ebp+PARTITION.Disk] |
call disk_sync |
; 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 |
test eax, eax |
jz @f |
push 11 |
jmp ntfsError |
@@: ; 5. Write partition bitmap |
cmp [ebp+NTFS.ntfsFolder], 0 |
jnz @f |
cmp [ebp+NTFS.fileRealSize], 0 |
jz @f |
mov ecx, [ebp+NTFS.fileDataStart] |
mov eax, ecx |
add ecx, [ebp+NTFS.fileDataSize] |
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 |
call fs_write64_app |
test eax, eax |
jz @f |
push 11 |
jmp ntfsError |
@@: |
mov esi, [ebp+PARTITION.Disk] |
call disk_sync |
mov edi, [ebp+NTFS.indexOffset] |
mov eax, [ebp+NTFS.newMftRecord] |
mov [edi+fileRecordReference], eax |
; 6. Write directory node |
mov eax, [ebp+NTFS.nodeLastRead] |
mov [ebp+NTFS.ntfsLastRead], eax |
mov eax, [ebp+NTFS.cur_index_buf] |
mov [ebp+NTFS.ntfs_cur_buf], eax |
call writeRecord |
push eax |
mov esi, [ebp+PARTITION.Disk] |
call disk_sync |
call ntfs_unlock |
pop eax |
mov ebx, [ebp+NTFS.fileRealSize] |
ret |
|
writeRecord: |
; in: |
; [ebp+NTFS.ntfs_cur_buf] = record |
; [ebp+NTFS.ntfsLastRead] = partition sector |
; making updateSequence |
mov esi, [ebp+NTFS.ntfs_cur_buf] |
mov edi, esi |
movzx ecx, word [esi+updateSequenceOffset] |
add edi, ecx |
mov ax, [edi] |
add edi, 2 |
mov cx, [esi+updateSequenceSize] |
dec ecx |
push ecx |
@@: |
add esi, 510 |
movsw |
mov [esi-2], ax |
dec ecx |
jnz @b |
; writing to disk |
mov eax, [ebp+NTFS.ntfsLastRead] |
mov ebx, [ebp+NTFS.ntfs_cur_buf] |
pop ecx |
xor edx, edx |
jmp fs_write64_sys |
|
bitmapBuffering: |
; Extend BitmapBuffer and read next 32kb of bitmap |
; Warning: $Bitmap fragmentation is not foreseen |
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 |
@@: |
mov ecx, [ebp+NTFS.BitmapSize] |
mov eax, edi |
sub eax, [ebp+NTFS.BitmapBuffer] |
sub ecx, eax |
shr ecx, 2 |
pop ebx |
ret |
|
.err: |
mov eax, [ebp+NTFS.BitmapBuffer] |
add eax, [ebp+NTFS.BitmapSize] |
mov ecx, 8 |
call release_pages |
.end: |
add esp, 12 ; double ret |
push ERROR_DISK_FULL |
jmp ntfsError |
|
;---------------------------------------------------------------- |
; ntfs_Write - NTFS implementation of writing to file |
; in: ebp = pointer to NTFS structure |
; in: esi+[esp+4] = name |
; in: ebx = pointer to parameters from sysfunc 70 |
; out: eax, ebx = return values for sysfunc 70 |
;---------------------------------------------------------------- |
ntfs_Write: |
xor ebx, ebx |
mov eax, ERROR_UNSUPPORTED_FS |
ret |
|
;---------------------------------------------------------------- |
ntfs_SetFileEnd: |
ntfs_SetFileInfo: |
ntfs_Delete: |
1891,12 → 2672,6 |
ret |
|
;---------------------------------------------------------------- |
; ntfs_GetFileInfo - NTFS implementation of getting file info |
; in: ebp = pointer to NTFS structure |
; in: esi+[esp+4] = name |
; in: ebx = pointer to parameters from sysfunc 70 |
; out: eax, ebx = return values for sysfunc 70 |
;---------------------------------------------------------------- |
ntfs_GetFileInfo: |
cmp byte [esi], 0 |
jnz @f |
1907,14 → 2682,11 |
stdcall ntfs_find_lfn, [esp+4] |
jnc .doit |
test eax, eax |
movi eax, ERROR_FILE_NOT_FOUND |
jz @f |
mov al, 11 |
@@: |
push eax |
call ntfs_unlock |
push ERROR_FILE_NOT_FOUND |
jz ntfsError |
pop eax |
ret |
push 11 |
jmp ntfsError |
.doit: |
push esi edi |
mov esi, eax |
1926,3 → 2698,8 |
xor eax, eax |
ret |
|
ntfsError: |
call ntfs_unlock |
xor ebx, ebx |
pop eax |
ret |