Egy rövid bevezetőt szeretnék közreadni, hogy mire lehet pl. jó eddigi kínlódásunk. A minap történt velem:
Egyik ismerősöm adott egy winchestert, hogy segítsek rajta, mert eltűnt róla minden. Ez majd egy éves teljes számlázást, könyvelést meg hasonlókat tartalmazott. Első kérdésem mentés (backup) mikor volt utoljára ? Hááát ..., szóval nincs ... Ez egy 40MB-os, régi winyedli volt, s kb. 4 MB nélkülözhetetlen DBase adatbázis volt rajta. Hogy mi is a hibajelenség: Rövid kutakodás után kiderült, hogy a partíciós tábla, ill. a FAT mindkettő teljes példánya "eltűnt". Ezek után egy átlag felhasználó feladná, persze feltételezve, hogy eljutott odáig, hogy mi a hiba. De az informatIQus próbál valamit tenni. (Remélem az előző cikkek alapján nem kell magyaráznom, hogy FAT nélkül, lehetetlen kiolvasni a winyedliről, hogy hol – mi volt, hisz a töredezett file-ok össze-vissza vannak.) Visszatérnék arra, hogy eltűnt a FAT, meg a partíciós tábla: Hogy tűnhet el ??? Feltételezésem, ill. a tények:
  1. Első próbálkozásom, hogy dátum szerinti sorrendben fűzöm össze a DBF-rekordjait tartalmazó cluster-eket, csak a ciki az, hogy a DBF-en belűl nem rendezetten voltak.
  2. Ezután jött a briliáns ötlet:

  3. Tudjuk, hogy meg van a DBF minden cluster-e, csak a sorrend a kérdéses.
    Készítsünk egy olyan rekurzív, backtack-es algoritmust mely létrehozza a helyes összecsatolási sorrendet. De hogy ? Lássuk, hogyan helyezkedik el a DBF állomány a winchester-en:

Láthatjuk, hogy egy cluster-be nem fér éppen annyi rekord, hogy tele legyen, így az utolsó rekordnak csak az eleje van az X. cluster-ben, míg egy másik cluster-ben (amit egyenlőre nem ismerünk) van a rekord vége. Mivel a rekord mindig ugyanúgy épül fel kiszámíthatjuk, egy mezőből, pl. a számla dátumából, hogy a cluster-en belűl, hol is kezdődik az utolsó rekord.
Kitudjuk azt is számolni, hogy mennyi byte van az X. cluster-ben és mivel ismerjük a rekord byte-ban mért hosszát tudjuk mennyi byte kell a teljes rekordhoz. Olyan cluster-eket keresünk, - igen helyes a többes szám mivel többet fogunk találni - , amelyeknek az elején éppen a szükséges töredék hosszú rekord található. (Ezt hasonló módon nézzük meg: a Y. az X. hez illeszkedő cluster darab, töredék DBF rekordja utáni első rekordjának címe – 1 egyenlő lesz az Y. cluster-ben a töredék rekord hosszávan.) Ha X. töredéke és Y. elején a töredék byte hossza = egy rekord hosszával, akkor az a cluster az előzőhöz illeszkedik.

DE persze nem biztos, hogy csak az jó oda, hisz több hasonlóan jól illeszkedő cluster van. Ahogy fent említettm sajnos nem járható út két cluster összeillesztésénél, - feltéve, hogy összeillőek -, hogy az X. cluster dátumát hasonlítom össze az Y.-éval.

Ekkor az ötlet, hogy bízzuk a gépre az összeállítás sorrendjének kidolgozását, egy rekurzív - back-track algoritmussal.

Mert tudjuk, hogy CSAK EGY helyes összeállítási sorrend létezik, akár egy törött vázánál. Hisz, ha rossz helyre illesztjük az egyik darabot akkor végül lesz olyan darab melyhez már nem találunk hozzá illeszthetőt.

Az algoritmus: Van egy rutinunk mely ellenőrzi hogyha az X. cluster-hez, az Y-t illeszteném (csak akkor próbálja egyáltalán ha illeszkedik hozzá, a rutin a több illeszkedő közül az egy helyeset keresi), szóval ha X-hez Y-t illesztem akkor végigpróbálgatva az összes többit lesz –e minden cluster-nek illeszkedő párja.

Ha nem akkor van másik illeszkedő, sőt oda illő cluster is.

Ha igen akkor rutinunk megtalálta az oda illő darabot, s folytathatja a Y. törött rekordjához illő Z passzoló cluster-t. (Ha gondolkodunk lényegében csak egyszer fut le a rutin de mivel a felépítgetés, annyi szintű ahány cluster közül lehet választani marha nagy memória, ill. CPU igényű, hisz sokáig tarthat. Igy nem ajánlatos sima DOS módban, hanem inkább védett módban az ilyen algoritmust megírni.)

Első hallásra nehéz lehet megérteni a rutin teljes mibenlétét, de olyan veszettül nehéz. Pár apróság azoknak, akik megértették legalább a rutin alapjait.

Konklúzió, tanácsok:
  1. Ha valami rejtélyes módon nem tudsz boot-olni gépedről kezd gondolkodni, mi is lehet az oka ? Vírus ? Esetleg valami program letörölte a partíciós táblát ? Vagy netán WIn95-öt installáltál ? Vagy te törölhetted le nagy programozásban Int 13h-mal ?
  2. Ha tudod a kezdeti félelmeidet legyőzve, használd az eszed s ne vaktába össze-vissza csinálj mindent.
  3. Ha minden a HDD-n lévő fontos dologról van mentésed akkor legegyszerűbb egyet formázni.
  4. Ha volna mit lementeni, próbáld meg Norton Utilities DiskEditorral rendbe hozni a partíciós táblát, vagy a boot-rekordot ha az a hibás. ÉS NE NDD-t futtas, mert az általában csak tönkreteszi a FAT-ot, vagy a könyvtárstruktúrát nagyobb sérülés után. (A fenti példa csak részint bizonyítja ezt, de már sok esettel találkoztam amikor az NDD tette reménytelenné a visszaállítás lehetőségét.) Ha nem vagy ismerős a DOS file-kezlésének mélységeiben, keres szakembert (pl. eMail-ezz nekünk, megpróbálunk segíteni.). Ha mindenképpen NDD-zni akarsz akkor meg, minden alkalomnál amikor az NDD felkínálja készíts NDD Undo file-t.
  5. A NU DiskEdit-ben van egy hiba javító lehetőség, az úgyn. Advanced Recovery. Ez akkor jó ha teljesen ismeretlenek a partíciós tábla adatai.
  6. Mielőtt magad a DE-vel munkához látnál fog papírt, cerzát, s mindig írd fel mit, hogyan változtattál. Olyan dolgot NE csinálj amiben NEM vagy BIZTOS !
  7. Jótanács: gyakrak speed-disk-el a HDD-det, pl. havonta egyszer (persze lehet többször is: J ), ill. egy ARJ-ben tárold a HDD-den a legfontosabb dolgokat. Ez azért előnyös, mert ha egy ARJ-ben van és elszáll a FAT akkor az ARJ egy darabban található (a speed-disk miatt) és ezt a FAT kézi feltöltésével visszanyerhetjük.
  8. Persze az esetek 80%-ban marad a HDD-n olyan dolog ami nincs elmentve, vagy régi a mentés, és akkor jöhet a kínlódás: pl. a fent említett módszer ami meglehetősen sok türelmet, és jártasságot igényel.
  9. Vannak cégek akik a hibás HDD-kről való adatvisszanyeréssel foglalkoznak (Ők, a fizikailag hibás, pl. leejtett, megégett, stb. HDD-kkel is foglalkoznak, én csak a logikai hibákról írtam.). Szóval ezen cégek visszanyerik nekünk adatainkat csak a kérdés, hogy mennyiért ?

  10. Általában horribilis összegeket kérnek a csak logikailag hibás HDD-kért is.
    Ha meg fizikailag hibás a HDD akkor csak egy BANK, stb. számára megfizethető, lévén, hogy speciális a technika mellyel visszanyerik. (Ne írjon senki, nem mondok cégnevet.)
  11. Tizedik pont, az eszmefuttatásból elég, jöjjön a mai anyag ! De előtte remélem elrémítettelek annyira, hogy gyorsan készítesz mentést a legfontosabbakról.
 

Handle-s file-kezelés

Ugorjunk a közepébe ! Mivel kezdjük a file-műveleteket ? File-nyitással ! Lássuk:

function TR4sFileDOS.OpenFile(Name: TASCIIZ; AccessMode: Byte; var Handle: Word): Boolean;

 function GetFirstFreeHandle: Word;
 var i: Word;
 begin
   for i:=6 to 55 do
     if Not IsUsedHandle[i] then begin GetFirstFreeHandle:=i; Break; end;
 end;

var
  FFDataBlock: PFindFileDataType;
  NameS : String;
  begin {first handle 0006h; AccessMode = FileMode}
    if FindFirstFile(Name, AnyFile and ((Not Directory) and (Not VolumeID)))     then begin
      NameS:=ConvertASCIIZToString(Name); NameS:=UCaseString(UTrim(NameS)); FFDataBlock:=DTA;
      if NameS[2] = ':' then Delete(NameS, 1, 2);
      repeat
        Delete(NameS, Length(NameS), 1);
      until NameS[Length(NameS)] = '\';
      if Length(NameS) > 2 then Delete(NameS, Length(NameS), 1);
      NameS:=Slasher(NameS) + FFDataBlock^.FileName;   repeat
    Delete(NameS, Length(NameS), 1);
  until NameS[Length(NameS)] <> #0;   Handle:=GetFirstFreeHandle;   Handles[Handle].Path:=NameS;   Handles[Handle].Entry:=AEntry;   Handles[Handle].Position:=0;   Handles[Handle].OwnerDIR:=FFDataBlock^.OwnerDIRStartClus;   IsUsedHandle[Handle]:=True;
  OpenFile:=True;   end
  else
  begin
    Handle:=0;
    OpenFile:=False;     end;
  end;

Hogy ne a levegőbe beszéljek elmondom – igaz pár számmal régebben már elmondtam -, hogy a handle-s file-kezelés részint azért jobb, mert nekünk csak a megnyitott file sorszámával kell dolgoznunk, s DOS tárolja a megnyitott file-hoz tartozó információkat. Mivel saját file-kezelést írunk ezért most mi tároljuk a file-handle-höz tartozó információkat, egy rekord-tömbben.

{TR4sFileDOS = object}
private
  Handles : Array[6..55] of THandle;
end

type
  PHandle = ^THandle;
  THandle = record
    Path : String[80];
    Entry : TDirectoryEntry;
    Position: Longint;
    PosZero : Boolean;
    OwnerDIR: Word;
  end;

Nos láthatjuk, hogy 6..55-ig 50 szabad handle van. (0-5-ig DOS által foglalt, s mi is alkalmazkodunk ehhez.) A THandle rekord meg mindent tartalmaz ami kellhet.
Lássuk a file-lezérását:

function TR4sFileDOS.CloseFile(Handle: Word): Boolean;
begin
  if IsUsedHandle[Handle] then
  begin
    IsUsedHandle[Handle]:=False;
    FillChar(Handles[Handle], SizeOf(Handles[Handle]), 0);
    CloseFile:=True;
  end  else begin Error:=R4sFDOS_InvalidHandle; CloseFile:=False; end;
end;

Bármely megnyitott file lezárható.Viszont ha rossz handle-számot adunk meg, és az épp nem megnyitott file-hoz tartozik akkor hibát kell jeleznünk.
Lássuk a handle-ös file-olvasást. Remélem nem leszek nagyon érthetetlen:

function TR4sFileDOS.ReadFromFile(Handle: Word; var ByteToRead: Word; var Buf; BufSize: Word): Boolean;

var
  {... fölösleges a változóneveket itt az újságban is leírni}
begin
  if IsUsedHandle[Handle] then
  begin

ErrProc:=False;
if BufSize < 4096 then NeedLocalBuf:=True
else NeedLocalBuf:=False; if BufSize < ByteToRead then begin ByteToRead:=BufSize; ErrProc:=True; end; if NeedLocalBuf then
begin
  New(LocalBuf);
  FillChar(LocalBuf^, SizeOf(LocalBuf^), 0);
  ToBuf:=LocalBuf;
end else begin
  FillChar(Buf, BufSize, 0);
  ToBuf:=@Buf;
end; MustRead:=ByteToRead; WasPosZero:=False; if (Handles[Handle].Position <> 0) or Handles[Handle].PosZero then
begin
  if Handles[Handle].PosZero then
  begin
    Handles[Handle].PosZero:=False;
    WasPosZero:=True;
  end;
  PosDiv:=(Handles[Handle].Position + 1) div (BootInf.Byte_Sector * BootInf.Sector_Cluster);
  PosMod:=(Handles[Handle].Position + 1) mod (BootInf.Byte_Sector * BootInf.Sector_Cluster); end else begin PosDiv:=0; PosMod:=0; end;
  {PosDiv, and PosMod are Cluster "orianteted" variables}
  MaxCluster:=(Handles[Handle].Entry.FileSize div (BootInf.Byte_Sector * BootInf.Sector_Cluster));
  if Handles[Handle].Entry.FileSize mod (BootInf.Byte_Sector * BootInf.Sector_Cluster) <> 0 then Inc(MaxCluster); Cluster:=Handles[Handle].Entry.ClusterNumber; if PosDiv <> 0 then
for i:=Handles[Handle].Entry.ClusterNumber to Handles[Handle].Entry.ClusterNumber+PosDiv-1 do
  Cluster:=GetIndexFromFAT(FAT, Cluster); OK:=False; Res:=0;
repeat
  ClusToRead:=0;
  StartCluster:=Cluster;
  repeat
    Inc(ClusToRead);
    if GetIndexFromFAT(FAT, Cluster) >= Cluster12_EOF then Break;
    if Longint(ByteToRead) <= Longint(Longint(ClusToRead + Res) *
       Longint(BootInf.Byte_Sector) * Longint(BootInf.Sector_Cluster) ) then Break;
    NotINC:=False;
    if GetIndexFromFAT(FAT, Cluster) = Cluster + 1 then Cluster:=GetIndexFromFAT(FAT, Cluster)
    else NotINC:=True;
  until (GetIndexFromFAT(FAT, Cluster) <> Cluster + 1); TempW:=GetIndexFromFAT(FAT, Cluster);
if ((TempW >= Cluster12_EOF) or (TempW <> Cluster + 1)) and (Not NotINC)
then Inc(ClusToRead)
else NotINC:=False; Res:=Res + ClusToRead; ReadnLogigalSector(ConvertClusterNumberToLogicalSector(StartCluster), ClusToRead, ToBuf); ToBuf:=Ptr(Seg(ToBuf^), Ofs(ToBuf^) + Word(ClusToRead * BootInf.Byte_Sector * BootInf.Sector_Cluster)); Cluster:=GetIndexFromFAT(FAT, Cluster); until Longint(ByteToRead) <= Longint( Longint(Res) *
Longint(BootInf.Byte_Sector) * Longint(BootInf.Sector_Cluster) ); ByteToRead:=Res * Word(BootInf.Byte_Sector * BootInf.Sector_Cluster);
if ByteToRead > MustRead then ByteToRead:=MustRead; if PosMod <> 0 then
  if NeedLocalBuf then
   BringDecedXByte(LocalBuf^, Seg(LocalBuf^), Ofs(LocalBuf^), Word(PosMod), SizeOf(LocalBuf^))
  else BringDecedXByte(Buf, Seg(Buf), Ofs(Buf), Word(PosMod), BufSize); if NeedLocalBuf then begin Move(LocalBuf^, Buf, ByteToRead); Dispose(LocalBuf); end; if (Handles[Handle].Position = 0) and (Not Handles[Handle].PosZero) then Handles[Handle].PosZero:=True;
if (Handles[Handle].Position = 0) and (Not WasPosZero) then Handles[Handle].Position:=ByteToRead - 1
else Handles[Handle].Position:=Handles[Handle].Position + ByteToRead; if ErrProc then ReadFromFile:=False else ReadFromFile:=True; end else
begin FillChar(Buf, ByteToRead, 0); ByteToRead:=0; ReadFromFile:=False; end; end;

Nos ennyi lett volna maga a beolvasó rutin, nem olyan ördöngös, de azért nem mondom, hogy könnyű volt mikor lassan két éve írtam.
Lássuk a file-ban történő seek-elést. 3 féle seek-elés van:

function TR4sFileDOS.SeekFile(Handle: Word; SeekType: Byte; var Pos: Longint): Boolean;
begin
  if IsUsedHandle[Handle] then
  case SeekType of
  $00: begin Handles[Handle].Position:=Pos; SeekFile:=True; end;
  $01: begin Handles[Handle].Position:=Handles[Handle].Position + Pos; SeekFile:=True; end;
  $02: begin Handles[Handle].Position:=Handles[Handle].Entry.FileSize - Abs(Pos); SeekFile:=True; end;
  else begin Handles[Handle].Position:=Pos; SeekFile:=True; end;
  end else begin Pos:=0; SeekFile:=False; end;
end;

Ez a rutin pedig az aktuális file-pozíciót kérdezi le:

function TR4sFileDOS.GetFilePos(Handle: Word): Longint;
begin
  if IsUsedHandle[Handle] then GetFilePos:=Handles[Handle].Position
                          else GetFilePos:=$FFFFFFFF;
end;

Nos a Tippek – Trükkök rovat mai száma zárul. S ha minden igaz a DOS file-kezelését is lezárom. (Hacsak olvasó kérdések tömkelege nem érkezik.) Úgy gondolom elég aprólékosan sikerül e témán áthaladnunk, s elég sok forráskódot is közzé tettem. Így most is felteszem a mai cikk forráskódját, ill. a :\PC-XUSER.18\TT\R4sFDOS\ könyvtárban található ZIP egy terjeszthető változat az R4sFDOS-ról, és a gyors FDD-olvasóról az: R4sQuickFloppy-ról.

Bárkinek bármi kérdése intézze azt az alábbi eMail címre.

Bérczi László
eMail: PC-XUser@FREEMAIL.C3.HU, Subject: "T&T Rovat"