int21h
Jak na diskové oddíly aneb FDisk v pascalu
Předpokládám, že čtenáři vědí, co je to
diskový oddíl. Typický příklad - v počítači je jeden harddisk, ale systém hlásí, že máme nainstalované dvě jednotky pevných disků: jednotky C: a D:
Takový pevný disk je rozdělen na dva oddíly (anglicky partition). Dokonce i když máme v počítači jen jednotku C: tak i v takovém připadě existuje tabulka rozdělení disku, ale oddíl je definován jen jeden.
Tabulka rozdělení disku je povinná pro harddisky a mají ji téměř všechny flešky. Nemají ji diskety a optické disky.
Programové prostředky
Když chceme v pascalu (a samozřejmě i jiných programovacích jazycích) pracovat s diskovými oddíly, tak v první řadě musíme vyřešit, jakým způsobem přistupovat k disku. Zabudované funkce pascalu nám nepomohou. Nepomůže nám ani DOS. Funkce DOSu přistupují k disku na vyšší úrovni - tzn. na úrovni souborů, maximálně na úrovni souborového systému. Přímo na nejnižší možnou úroveň přístupu na disk - na jednotlivé sektory - se nedostaneme.
Je tedy pravda, že existují dvě výjimky - DOSové funkce pro přístup k jednotlivým sektorům (INT25h, CX=FFFFh a INT21h, AX=7305h). Jenže ty vidí jen vnitřky DOSových oblastí. Na tabulku rozdělení disku se nedostaneme a na neDOSové oblasti tuplem ne.
musíme tedy použít funkce BIOSového přerušení INT13h.
Všechen další text se opírá o moji jednotku Disk.pas, ve které jsou použity všehny uvedené fragmenty zdrojáků.
Praktické odzkoušení jednotky Disk ukáže prográmek DiskTest. Oba dva zdrojáky jsou obojživelné, tzn. je možné je přeložit i ve Freepascalu i v Turbo pascalu.
S přístupem přes BIOS souvisí jeden problém. Existují totiž dva způsoby adresování pevných disků. Existuje trojrozměrná adresace CYLINDR, HLAVA, SEKTOR (dále
CHS adresace) které určují konkrétní sektor na disku a novější adresace pomocí "Logical block addressing (dále
LBA adresace).
CHS adresace neumí obsloužit disky větší než 8,4GB - na takové musíme použít LBA adresaci. LBA adresace obsáhne jakkoliv velké disky, ale musí ji podporovat BIOS a řadič disku.
Takže než budeme pomocí BIOSu přistupovat k disku, tak si zjistíme, podporuje-li LBA adresaci:
Function IsExtdskFunc(dsk: Byte):byte; assembler;
asm
mov dl, dsk
mov bx, 55AAh
mov ah, 41h
int 13h
mov al,0
jc @1
cmp bx, $AA55
jne @1
test cl, 1
je @1
mov al,cl
@1:
end;
Pokud ano, tedy pokud
IsExtdskFunc>0 tak všechny další přístupy budeme vykonávat pomocí funkcí používajících LBA.
Takhle vypadá přečtení sektoru v CHS módu
function ReadSector_CHS(dsk: Byte; Cylinder, Head, Sector: Word; num:byte; var Buffer):Byte;
var r:registers;
c:word;
begin
asm
mov ax,cylinder
mov cx,ax
shl ax,8
shr cx,10
and cx,192
or cx,ax
or cx,sector
mov c,cx
end;
r.cx:=c;
r.dh:=head;
r.dl:=dsk;
r.ah:=$02;
r.al:=num;
r.es:=tb_segment;
r.bx:=tb_offset;
r.es:=seg(buffer);
r.bx:=ofs(buffer);
FillChar(buffer,sizeof(TSector),0);
Intr($13,r);
CopyFromDOS(buffer,sizeof(TSector));
ReadSector_CHS:=r.ah;
end;
a takhle v LBA módu.
function ReadSector_LBA(dsk: Byte; LBASector: dword; num:byte; var Buffer): Byte;
var r:registers;
zadanka:TExtdskFuncPack;
LowMemPtr:dword;
begin
FillChar(zadanka,SizeOf(zadanka),0);
with zadanka do begin
xfsize:=$10;
xfreserved:=0;
xfsectornum:=num;
LowMemPtr:=Global_DOS_Alloc(sizeof(TSector));
xfDataBuffer_seg:=Word(LowMemPtr shr 16);
xfDataBuffer_ofs:=0;
xfDataBuffer_seg:=seg(buffer);
xfDataBuffer_ofs:=ofs(buffer);
xfLBAsector[0]:=LBAsector;
end;
r.dl:=dsk;
r.ax:=$4200;
CopyToDOS(zadanka,sizeof(zadanka));
r.ds:=tb_segment;
r.si:=tb_offset;
r.ds:=seg(zadanka);
r.si:=ofs(zadanka);
Intr($13,r);
DOSmemGet(Word(LowMemPtr shr 16),0,buffer,sizeof(TSector));
Global_DOS_Free(Word(LowMemPtr));
ReadSector_LBA:=r.ah;
end;
Rozbor MBR záznamu
Dalším krokem je rozbor tabulky rozdělení disku (dále ji budu nazývat MBR).
Pozor - měli byste být připraveni na to, že disk MBR nemá a bez jakéhokoliv zaobalení obsahuje vnitřek jednoho oddílu.
Takhle to mají diskety a velmi vzácně i některé flešky.
U flešek se zastavíme. Moderní BIOSy umožňují namapovat USB disky přítomné v momentu zapnutí počítače jako pevné disky. Takže bez jakýchkoliv USB ovladačů DOS vidí USB flešky jako pevné disky C:, D:, E: a tak dále. Nevýhodou je, že takto mapované flešky nemůžeme po dobu běhu počítače vyjmout.
Kromě toho existují DOSové ovladače USB disků. Některé mapují USB disky jen na úrovni DOSu, takže je BIOS nevidí, ale minimálně ovladač od Breta Johnsona umí USB disky namapovat i na úrovni BIOSu. Ovladač umožňuje vyndavání flešek i zapojování za běhu, takže to funguje jako ve windows.
Závěr z tohoto odstavce je ten, že je žádoucí, aby vaše diskové rutiny počítaly s existencí USB disků (především flešek), a to, že tato věc se týká (zaplať pánbůh) i DOSu.
Na tomto místě je ještě třeba poznamenat, že v poslední době (tzn. cca od roku 2009 se pomalu začíná objevovat alternativní schéma rozdělení disku, a to
GPR.
GPR je reakcí na to, že stávající schéma neobsáhne disky větší než 2TB (pokud bude zachována stávající praxe 512 bajtových sektorů) a že podpora více než čtyř sektorů je pomocí MBR schématu poněkud kostrbatá. Novinkou jsou také určité vlastnosti související s vlastnictvím a ochranou dat. GPR dělení disku každopádně podporují pouze 64-bitové verze Windows Vista a Windows 7 a některé Linuxy. Pravdou je také to, že při značném zvýšení složitosti je reálný přínos jen velmi malý. Navíc GPR schéma v sobě z důvodu zpětné kompatibility zahrnuje i klasický MBR record.
Takže zpět k MBR.
MBR záznam se nachází vždy v prvním sektoru disku. Tedy v LBA=0 nebo v (C=0, H=0, S=1 (pozor, sektory se číslují od 1))
Pomocí LBA nebo CHS čtení načteme sektor a provedeme analýzu, je-li to MBR záznam, nebo se jedná o začátek diskového oddílu.
MBR má takovouto strukturu.
PartitionEntry = packed record
Active: Byte;
StartHead: Byte;
StartCylSec: Word;
Typ: Byte;
EndHead: Byte;
EndCylSec: Word;
StartRelSec: dword;
Size: dword;
end;
TMBR_Record = packed record
bootcode: array[0..445] of byte;
PartTable: array [0..3] of PartitionEntry;
BootSignature: Word;
end;
Pro pevné disky by mělo být dostatečné pohlédnout u všech čtyř slotů pole "Active". Právě jeden by měl být roven 80h a všechny ostatní by měly být 0. Nepříjemné je, že to nemusí fungovat u flešek, protože ve všech slotech mohou mít "Active" rovny nule. Nevím, jak moc je to košer, ale takové flešky jsem viděl a byly normálně funkční, takže se to asi připouští.
Znamená to ale, že je třeba provést dodatečné testy. Já jsem detekci napsal takhle:
Function TDskInfo.DetekceMBR(var buf:TSector):boolean;
var a,b,c:byte;
pcyl,phla,psek:dword;
bf:PMBR_record;
i:dword;
begin
bf:=@buf;
a:=0;
for b:=0 to 3 do
if bf^.PartTable[b].active=$80 then a:=a+1
else if bf^.PartTable[b].active<>0 then a:=a+10;
if a=1 then DetekceMBR:=true else
if a>1 then DetekceMBR:=false else
begin
c:=255;
for b:=0 to 3 do if bf^.PartTable[b].typ<>0 then begin c:=b;Break;end;
if c=255 then DetekceMBR:=false
else begin
phla:=bf^.PartTable[c].StartHead;
psek:=bf^.PartTable[c].StartCylSec and 63;
pcyl:=(bf^.PartTable[c].StartCylSec shr 8) or ((bf^.PartTable[c].StartCylSec and 192) shl 2);
DetekceMBR:=bf^.PartTable[c].StartRelSec=i;
end;
end;
end;
Jestliže disk má MBR record, tak ho budeme zkoumat. Prvních 416 bajtů je kód prvotního zavaděče. Standardně je to kód vyhledání prvního aktivního oddílu a spuštění jeho zavaděče. (také se tu ale mohou skrývat viry)
Nejprve se podíváme na pole "typ".
Je-li tam nula, není v daném slotu definován žádný oddíl. Je-li tam 5 nebo 0Fh, tak je to opičárna zvaná Extended Master Boot Record (EMBR), o které se rozepíšu dále. V jiném případě je to kód souborového systému, který daný oddíl obsahuje. V kódech souborového systému je neskutečný zmatek. Nejčastěji používané kódy jsou v této tabulce:
1, 5, 6, 0Bh, 0Ch    | FAT |
7 | NTFS, HPFS, exFAT |
83h | všechny Linuxové formáty: ext2/3/4, RaiserFS,... |
82h | swapovací oblast pro Linux |
Diskové oddíly definované v MBR se nazývají
primární oddíly. Kromě primárních oddílů existují ještě
rozšířené oddíly a
logické oddíly. Tyto pojmy souvisí s EMBR a vysvětlím je dále.
V polích
StartCylSec a
StartHead je zapsán začátek oddílu v CHS adresaci.
V poli
StartRelSec je tzv. relativní poloha sektoru, která má využití převážně v LBA adresaci. V případě MBR je relativní vzhledem k sektoru LBA 0, takže není problém, v případě EMBR jde o relativní polohu k EMBR, což má několik háčků, které popíšu za chvilku.
Znovu pozor - v případě, že disk podporuje LBA, pole vstahující se k CHS ignorujte - neobsáhnou více než 8,4GB. Uvnitř této hranice by měly korespondovat se StartRelSec, ale v rámci odolnosti vůči chybám MBR je rozumné na to nespoléhat.
A ještě jedna věc. Nikdo netvrdí, že sloty jsou zaplňované postupně, tedy od nultého do třetího. Vůbec ne - nesmí vás rozhodit, když jsou 0.-2. slot prázdné (respektive obsahují záznam s kódem oblasti
0) a až na 3. pozici je definován oddíl se souborovým systémem.
Rozbor boot recordu
Další krok je načtení sektoru specifikovaného pomocí CHS či LBA schématu v MBR.
V tomto textu tento sektor nazývám
boot record. Pozor - literatura v terminologii není jednotná a pod pojmem "boot record" často chápe i MBR záznam.
Já se ale držím toho pojetí, že jde o první sektor vnitřku oddílu.
V souborových systémech od Microsoftu jsou zde uvedeny nejdůležitější parametry souborového systému a také začátek zavaděče operačního systému.
Jinak to ale není pravidlem a třeba Linuxový systém ext2 nemá prvních 1024 bajtů (dva sektory) oddílu konkrétně definovaných a parametry souborového systému jsou až od 3. sektoru dále. A ReiserFS má údaje týkající se souborového systému až 64KB daleko. Všechno před tím je nedefinovaná oblast, kde může volně operovat zavaděč Linuxu.
V systémech, kolem kterých se mihl Microsoft je to ale tak, že první tři bajty boot sektoru jsou instrukce
JMP někam
, dalších 8 bajtů je
popiska filesystému a pak jsou základní data filesystému.
Pozor - popiska filesystému nijak nespecifikuje použitý souborový systém a je hrubá chyba ho podle něho detekovat. Je sice zvykem, že NTFS oddíly zde mají "NTFS ", ale je to jen úzus a nic závazného.
Nyní je nejvyšší čas zastavit se u pojmu sektor. V předchozím textu jsem toto slovo často zmiňoval, ale pořádně nevysvětlil. Nuže, sektor je nejmenší samostatně adresovatelná jednotka disku. Stejně, jako operační pamět neumí adresovat jednotlivé bity a nejmenší jednotka je osmibitový bajt, tak podobně disky nemají rozlišení ani na bit, ani na bajt, ale na N bajtů velké sektory.
Již odedávna dokumentace varovaly, že N je třeba si napřed zjistit, ale zatím téměř všechna záznamová zařízení pro PC vyrobená od dávnověku pro současnost používala velikost sektoru 512 bajtů. Až v poslední době se začínají objevovat disky, které mají velikost sektoru 4096 bajtů. Zatím naštěstí dělají interní přepočty a zvnějšku se chovají jako klasické 512 bajtové, ale je třeba mít se na pozoru.
Extended Master Boot Record (EMBR)
EMBR je rozšíření MBR, které umožňuje definovat více než 4 oblasti. Tuto věc vymysleli v Microsoftu, tudíž se nelze divit, že je to hrozná opičárna.
Podle poznámky o více než 4 oblastech by se mohlo zdát, že se s EMBR setkáme jen zřídka. Velký omyl. Staré verze MS-DOSu totiž nedokážou nabootovat čí správně vidět všechny diskové oddíly, pokud je definovaný více než jeden primární oddíl. Takže když pod takovou verzí MS-DOSu chcete mír harddisk rozdělený na C: a D:, tak ho musíte rozdělit na
primární oddíl C: a
rozšířený oddíl (extended partition) a až uvnitř extended partition definovat
logický oddíl D:
Dokonce microsoftí
FDisk vám ani neumožní vytvořit více než jeden primární oddíl.
Naštěstí FreeDOS a také další odrůdy DOSu s vícenásobnými primárními oddíly nemají problém a jiné programy na tvorbu diskových oddílů je vás nechají vytvořit.
Žádný DOS a nejspíše ani žádný jiný operační systém ale nemůže z rozšířeného oddílu nabootovat (alespoň bez pomoci boot managerů)
Také je dobré vědět, že podle specifikace smí být v MBR záznamu definovaný maximálně jeden rozšířený oddíl - tedy jeden EMBR záznam. Doporučuju, aby váš program při vytváření oddílů toto pravidlo respektoval, ale aby současně uměl přečíst nestandardní MBR záznamy, který definuje více EMBR záznamů.
Struktura EMBR
Pokud je ve slotu MBR kód
05h nebo
0Fh, jde o příznak, že jde o odkaz na takzvanou "extended partition". Ta má všechna pole jako
StartRelSec,
Size atd. definované jako normální primární oblast, ale na místě boot sectoru a začátku souborového systému obsahuje tabulku, která vypadá skoro stejně jako MBR, akorát že obsahuje jenom dva sloty.
TEMBR_Record = packed record
bootcode:packed array[0..445] of byte;
PartTable:packed array[0..1] of PartitionEntry;
unused:packed array[0..31] of byte;
BootSignature:word;
end;
Jeden ze slotů je odkaz na oddíl souborového systému jako FAT, NTFS atd. Struktura PartitionEntry má stejný formát jako v MBR záznamu. V CHS notaci oblasti je vše zcela bez problému, ale zádrhel může nastat u LBA notace, která se určuje jako relativní sektor (položka
StartRelSec) vůči MBR nebo EMBR. Takže při analýze diskových oddílů si vždy musíme pamatovat, z jakého sektoru jsme načetli EMBR, protože všechny logické oddíly z něho vycházející se odpočítávají vzhledem k tomotu sektoru.
Druhý ze slotů může obsahovat záznam s kódem oblasti 0. To znamená, že další oblast není definovaná a EMBR dále nepokračuje.
Také ale může obsahovat záznam s kódem opět
05h nebo
0Fh což je odkaz na další EMBR záznam, v kterém je zase odkaz na oddíl se souborovým systémem a pak buďto 0 nebo další EMBR.
EMBR záznamy tedy tvoří jednosměrný spojový seznam, který v každé etáži odkazuje na oddíl se souborovým systémem.
Ale pozor! Je tam zrada. Zádrhel spočívá v poli
StartRelSec. Navzdory intuici, která by říkala, že záznam je relativní vůči aktuální etáži EMBR, je to jinak. Záznam je totiž relativní vůči nejvyšší etáži EMBR, tedy vůči prvotnímu, primárnímu EMBR.
To by bylo z teorie všechno. Pro praktickou ukázku si spusťte
DiskTest a prohlédněte si zdroják
Disk.pas
Datum: 15.5.2010 18:27
Od: RayeR
Titulek: Aktivni flag
Pokud vim, tak disk musi mit 1 oddil oznaceny jako aktivni (80h) jen v pripade, ze se z nej ma bootovat. U flashek, z kerych se bezne s bootovanim nepocita, tedy neni na skodu, kdyz zadny oddil neni aktivni. Stejne tak pokud sou v PC 2 a vic fyzickych HDD, tak aktivni oddil musi byt jen na bootovacim disku.
p.s. dobra stranka, pridam k sobe odkaz :)