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;
{Zjisti, jestli dsk podporuje rozsirene funkce INT13h
 dsk = BIOSove urceni disku (0 = 1.FDD, 1 = 2.FDD, 80h = 1.HDD, 81h = 2.HDD
 0 = nepodporuje nic, jinak:
     0.bit = podpora pevnych disku: AH=42h-44h,47h,48h
     1.bit = funkce vymenitelnych jednotek
     2.bit = diskova rozsireni (enhanced disk drive support) (???)
     3.bit = 64.bitova rozsireni}

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;
{Precte sektor v CHS modu.
 dsk = BIOSove urceni disku (0 = 1.FDD, 1 = 2.FDD, 80h = 1.HDD, 81h = 2.HDD
 Cylinder, Head, Sector: urceni sektoru, ktery presist

 Buffer: bude zaplnen daty ze sektoru
 Navrat funkce: 0 = vsechno v poradku
              <>0 = cislo chyby}

var r:registers;
    c:word;
begin

 asm                   {napisu to v asm, abych nemusel resit chyby z }
 mov ax,cylinder       {preteceni}
 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;
 {$IFDEF FPC}
 r.es:=tb_segment;
 r.bx:=tb_offset;
 {$ELSE}
 r.es:=seg(buffer);
 r.bx:=ofs(buffer);
 {$ENDIF}
 FillChar(buffer,sizeof(TSector),0);
 Intr($13,r);
 {$IFDEF FPC}
 CopyFromDOS(buffer,sizeof(TSector));
 {$ENDIF}
 ReadSector_CHS:=r.ah;
end;


a takhle v LBA módu.

function ReadSector_LBA(dsk: Byte; LBASector: dword; num:byte; var Buffer): Byte;
{Precte sektor v LBA modu.
 dsk = BIOSove urceni disku (0 = 1.FDD, 1 = 2.FDD, 80h = 1.HDD, 81h = 2.HDD
 Cylinder, Head, Sector: urceni sektoru, ktery presist

 Buffer: bude zaplnen daty ze sektoru
 Navrat funkce: 0 = vsechno v poradku
              <>0 = cislo chyby}
var r:registers;
    zadanka:TExtdskFuncPack;
    LowMemPtr:dword;

begin
FillChar(zadanka,SizeOf(zadanka),0);
with zadanka do begin
   xfsize:=$10; { $18}  {Type(ExtdskFuncPack)}
   xfreserved:=0;
   xfsectornum:=num;
   {$IFDEF FPC}
   LowMemPtr:=Global_DOS_Alloc(sizeof(TSector));
   xfDataBuffer_seg:=Word(LowMemPtr shr 16);
   xfDataBuffer_ofs:=0;
   {$ELSE}
   xfDataBuffer_seg:=seg(buffer);
   xfDataBuffer_ofs:=ofs(buffer);
   {$ENDIF}
   xfLBAsector[0]:=LBAsector;
   end;
r.dl:=dsk;
r.ax:=$4200;
{$IFDEF FPC}
CopyToDOS(zadanka,sizeof(zadanka));
r.ds:=tb_segment;
r.si:=tb_offset;
{$ELSE}
r.ds:=seg(zadanka);
r.si:=ofs(zadanka);
{$ENDIF}
Intr($13,r);
{$IFDEF FPC}
DOSmemGet(Word(LowMemPtr shr 16),0,buffer,sizeof(TSector));
Global_DOS_Free(Word(LowMemPtr));
{$ENDIF}
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
7NTFS, HPFS, exFAT
83hvšechny Linuxové formáty: ext2/3/4, RaiserFS,...
82hswapovací 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
    { $00 }bootcode:packed array[0..445] of byte;  {posl. 9 bajtu teto oblasti si}
                                            {nekdy pouzivaji bootmanagery}
    { $1be}PartTable:packed array[0..1] of PartitionEntry;
    { $1de}unused:packed array[0..31] of byte;
    { $1fe}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
2010-04-23 | Laaca
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 :)
Reklamy: