Hallo zusammen,
Ich hab das jetzt mal verifiziert, OA3 hat tatsächlich ein 32MB Limit, es dürfte sich hierbei wohl um einen Design/Programmierfehler handeln.
Ich würde es als Bug klassifizieren, nachdem keinerlei Sicherheitsüberprüfungen zu diesem Limit implementiert sind und der Fehler somit für die Datenintegrität gefährlich ist (u.A. zerschießt man sich bei der Reparatur das .IF file).
Bevor wir zur weiteren Analyse schreiten gleich vorweg: Dieser Fehler wurde in OA4 behoben, dort kann man also ungestört mit großen Dateien operieren (bei der Netzwerkversion aber meinen Heap-Patch nicht vergessen, dort ist nämlich wiederum ein Speicherüberschreiber bug drinnen, aber das haben wir eh schon in einem anderen Thread abgehandelt).
Aufgrund der Natur des Fehlers, ist es leider nicht möglich, das so einfach zu patchen, da hierfür umfangreiche Änderungen des Programmablaufs notwendig wären (Aufruf anderer Funktionen mit anderen Parametern, etc.).
Nun zur Fehleranalyse:
Schreibt man ein kleines Programm, welches in einer Schleife einfach versucht, die Datenbank zu befüllen und mit dem uns wohl bekannten TRACE-Utility mitprotokolliert, so erhält man kurz vor Abbruch Folgendes:
8 = Filehandle der .DF Datei
Code:
42 3A3B:135D lseek(8, 0, 2) = 33551361 ; Länge der Datei?
42 3A3B:135D lseek(8, 33551360, 0) = 33551360 ; Gehen wir zur letzen Seite
3f 3A3B:1310 read(8, 29C0:675A, 4096) = 1 ; Seite Lesen -> Ups, die ist nicht da, nur 1 Byte gelesen
42 3A3B:135D lseek(8, 0, 1) = 33551361 ; Wo sind wir? -> Am Ende der Datei
Offensichtlich konnte also die letzte Seite im File nicht geschrieben werden.
Hier kann man noch nicht ganz verstehen, was los ist, aber beim Reparieren der Datei offenbart sich das Ganze dann etwas detaillierter. Merken wir uns vorerst nur einmal die Adresse der hier zur Anwendung kommenden Funktion, welche lseek aufruft -> 3A3B:135D
Repariert man die .DF Datei also mit dem Reparaturtool, so kann man schön sehen, wie die .IF Datei sich kurz vor Abbruch der Reparatur mit einem Fehler selbst zerstört. Auch hier können wir wieder den Trace betrachten (7 ist hier das handle zur .DF Datei, 8 ist das zur .IF Datei):
Code:
26C4:24E7 lseek(8, 33550336, 0) = 33550336
26C4:251D read(8, 904A:0010, 2048) = 2048
3A3B:135D lseek(7, 0, 2) = 33551361
3A3B:135D lseek(7, 21398528, 0) = 21398528
3A3B:1310 read(7, 29C0:414A, 4096) = 4096
3A3B:135D lseek(7, 0, 1) = 21402624
26C4:24E7 lseek(8, 33550336, 0) = 33550336
26C4:251D write(8, 904A:0010, 2020) = 2020
3A3B:135D lseek(8, 2048, 0) = 2048
3A3B:1310 write(8, 29C0:FDB4, 0) = 0
3A3B:135D lseek(8, 0, 2) = 2048
Das ganze Log ist leider zu lang, aber würde man es druchforsten kann man anhand der Funktionsadresse des lseek-Aufrufs feststellen, dass die Funktion zum Seeken an 3A3B:135D normalerweise nur für Filehandle 7 (.DF Datei) genutzt wird, hier bei Überschreitung der 32MB-Grenze jedoch auch für Handle 8 (.IF Datei).
Der Seek auf Position 2048 sieht wie ein 16bit Integer Überlauf aus, denn unter 16bit Systemen wie DOS ist ein INT nunmal so lang. Die Bestätigung dieser Theorie findet sich, wenn man die Seek-Funktion an der angegebenen Adresse zerlegt, ich habe sie hier dokumentiert und erläutert.
Nachdem ich den Debugger in einer anderen Umgebung ausgeführt habe, als den Trace, bitte nicht darüber wundern, dass hier im Dump ein anderes Segment steht als im TRACE:
Code:
2393:133A 8BEC mov bp,sp
2393:133C 8B5E08 mov bx,[bp+08] ss:[FE86]=0006 ; File handle 6
2393:133F 8B460A mov ax,[bp+0A] ss:[FE88]=0042 ; 42h = lseek
2393:1342 8AE0 mov ah,al
2393:1344 3C3E cmp al,3E ; Close file?
2393:1346 7415 je 0000135D ($+15) (no jmp) ; \_ Then do it
2393:1348 8B4E04 mov cx,[bp+04] ss:[FE82]=0000 ; Origin of move (0,1,2)
2393:134B 8AC1 mov al,cl
2393:134D 8B5606 mov dx,[bp+06] ss:[FE84]=13AA ; Block in file (0-32MB)!
2393:1350 33C9 xor cx,cx
2393:1352 8ACE mov cl,dh
2393:1354 8AF2 mov dh,dl
2393:1356 32D2 xor dl,dl
2393:1358 F8 clc
2393:1359 D1D2 rcl dx,1
2393:135B D1D1 rcl cx,1 ; Offset = 512*Sector no
2393:135D CD21 int 21
Stack:
------
0-4 Return address
4-6 Origin of move (0,1,2)
6-8 Block to write
8-A File handle
A-C DOS INT21h Function (42h=lseek, 3E=close file)
Wie wir eindeutig erkennen können, wird dieser Funktion nur eine 16bit Blocknummer übergeben, und die läuft nach 65535 nunmal über, das entspricht auch ungefähr der von uns festgestellten Limitierung von 65535 * 512 = 33553920 Bytes = 32MB.
Jetzt stellt sich noch die Frage, wo diese Funktion aufgerufen wird. Sieht man sich die Returnadresse am Stack an und folgt dieser, landet man nach Ausführen der Funktion wieder im UCSD-Pascal PCODE-Interpreter. Anhand der gelesenen Bytes lässt sich feststellen, wo im PCODE wir uns befinden.
Es handelt sich um eine Stelle im PASCAL.IMAGE der OA3.SPI, also im PASCAL-Teil der Runtime. Sieht man sich die PCODE Opcodes dort an, so hat man zumindest eine Vermutung, was hier getan wird. Leider sind die PCODE Opcodes der Pascal Runtime für die hier verwendete DOS-Version scheinbar nirgends dokumentiert, sie dürften sich rein von der Logik her auch von der klassischen PCode-Machine für Apple II unterscheiden, leider ist es mir bis heute nicht gelungen heruaszufinden, welches UCSD Development-System hier verwendet wird. Eventuell ist das von SPI selbst gestrickt, oder sie haben es irgendwo zugekauft, es finden sich jedoch keine Spuren davon im Netz, man findet meist nur die alten Runtime-Systeme aus Ende der 70er Jahre.
Hier mein "Interpretationsversuch" der Opcodes (alles Hex):
Code:
-------------------------------------------------------
PASCAL.IMAGE
-------------------------------------------------------
6B 00 - ?
80 42 - Push 42h (Dos function code number for lseek)
27 - Push file handle
7C - Push block number
25 00 - Push 0 (Origin of move)
91 2A - Execute Function 2A (our block lseek funct)
-------------------------------------------------------
68 00 - ?
80 3F - Push 3Fh (Dos function code number for read)
27 - Push Buffer of data
7C - Push file handle
24 - Push number of bytes to read
91 29 - Execute Function 29 (Do INT21h with params)
-------------------------------------------------------
68 23 - ?
D4 - ?
09 - Return
-------------------------------------------------------
SS.IMG
-------------------------------------------------------
20 00 B3 F1 05 01 ...
Sieht man sich den Aufbau und Position der der Funktion innerhalb
von PASCAL.IMAGE an, so handelt es sich vermutlich um die UCSD Pascal
BlockRead-Funktion:
Code:
FUNCTION BLOCKREAD(FILEID: FILE, ARRAY, BLOCKS: INTEGER, [ RELBLOCK: INTEGER] ): INTEGER;
Wie man sehen kann, wird hier für die Blocknummer ein INTEGER gegeben. DOS ist ein 16bit System, damit ist auch die Größe eines INTs mit 16bit limitiert, wobei wir wieder bei unserem 32MB-Limit wären. Dieses Problem wird u.A. auch
hier beschrieben, und schon damals suchte man nach Lösungsansätzen.
Sieht man sich die Adresse der Schreiboperationen beim Datenfile über die 32MB-Grenze hinaus an, so wird man feststellen, dass beim .DF dort dieselbe BlockWrite-Funktion verwendet wird, womit man dort eben genauso in das Limit hineinläuft.
Sieht man sich nun einen Trace von OpenAccess IV an derselben Stelle an und überprüft mit einem Debugger auch noch die aufgerufenen Funktionen, so sieht man, dass die BlockRead / BlockWrite Funktionen hier scheinbar nicht mehr verwendet werden, sondern welche, die diese Limitierung nicht mehr haben:
Code:
26C4:23AE lseek(8, 33547588, 0) = 33547588
26C4:2257 lseek(8, 33550336, 0) = 33550336
26C4:239D read(8, 935F:0000, 2048) = 2048
26C4:23AE lseek(8, 33552384, 0) = 33552384
4656:148A lseek(7, 21398528, 0) = 21398528
4656:10B2 read(7, 29E6:3F82, 4096) = 4096
4656:148A lseek(7, 0, 1) = 21402624
26C4:2257 lseek(8, 33550336, 0) = 33550336
26C4:239D write(8, 935F:0000, 2020) = 2020
26C4:23AE lseek(8, 33552356, 0) = 33552356
4656:148A lseek(8, 33556480, 0) = 33556480
4656:10B2 write(8, 29E6:FCBA, 0) = 0
4656:148A lseek(8, 0, 2) = 33556480
Es sieht also so aus, als ob die Autoren von OA das Problem erkannt und in der Version 4 behoben hätten. Nachdem die PCode-Opcodes des UCSD-Systems nirgendwo dokumentiert sind und es sich auch nicht um einen einfachen Bug sondern um falsch verwendete Funktionen handelt, hilft also voraussichtlich nur ein Update auf OA4.
Lg.