Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 5926 → Rev 5954

/kernel/trunk/fs/ntfs.inc
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