storage: fix VFS corruption, ISO9660 hang, OOM; add notepad, file picker, fdtable, GPT, async I/O
VFS fixes: - GetDirectoryListing(otVDIRECTORY) now returns a heap-allocated snapshot instead of a raw live-tree pointer; FreeDirectoryListing no longer corrupts the VFS tree (fixes page fault on repeated navigation) - volumeGetDirectories: guard LL_Size > 0 before loop to prevent uint32 underflow (0-1 = 0xFFFFFFFF) causing 4B kalloc iterations -> OOM - Free entry^.fileName and dirList in all branches (memory leak fix) iso9660: - equalsIgnoreCase: guard la=0 before loop (same uint32 underflow bug) - readDirectoryEntries/resolvePath: add 65536/256-iter safety caps storagemanager: - Strip debug syslog calls that were allocating 6+ strings per I/O (hundreds of I/Os x 6 allocs exhausted the heap) Process manager: - Per-process FDTable and Cwd initialised on create, freed on reap - proc_await: use pointer-indirection spin per lesson #6 (FPC caches non-volatile reads; raw while flag=0 returns immediately) - graphicsrefresh: move LVGL handler from timer ISR (IF=0) to a dedicated process so LVGL callbacks can call blocking I/O New units: - fdtable.pas: per-process file descriptor table (fd_alloc/get/close) - iorequest.pas: heap allocation helpers for TIORequest - gpt.pas: GPT header/entry read + CRC validation - filepicker.pas: reusable OS-level open/save file picker dialog - notepad.pas: GUI text editor with selection, indent, save-as Shell commands (diskcmd, partcmd, volcmd, diskutil): - Route output to stdout_buf instead of syslog (lesson #4) - diskutil: async add/remove/format partition; cache-based device display Add Apache 2.0 license headers to storagemanager.pas, iso9660.pas, fat32.pas, storagetest.pas Lessons learnt: #9 VFS listing must return snapshot (not live pointer)
This commit is contained in:
1 parent
789fb7971a
commit
deb513c925
32 files changed
+5100
-1854
No files matched your search
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -23,15 +23,15 @@ unit flatfs;
|
||||
interface
|
||||
|
||||
uses
|
||||
tracer,
|
||||
strings,
|
||||
filesystemmanager,
|
||||
lists,
|
||||
syslog,
|
||||
lmemorymanager,
|
||||
stdio,
|
||||
storagemanager,
|
||||
storagetypes,
|
||||
lmemorymanager,
|
||||
strings,
|
||||
syslog,
|
||||
tracer,
|
||||
util,
|
||||
volumemanager;
|
||||
|
||||
@@ -592,15 +592,18 @@ end;
|
||||
procedure detect_volumes(disk : PStorage_Device);
|
||||
var
|
||||
buffer : puint32;
|
||||
bufSize : uint32;
|
||||
volume : PStorage_volume;
|
||||
begin
|
||||
push_trace('flatfs.detectVolumes()');
|
||||
|
||||
buffer := puint32(kalloc(512));
|
||||
memset(uint32(buffer), 0, 512);
|
||||
bufSize := disk^.sectorSize;
|
||||
if bufSize < 512 then bufSize := 512;
|
||||
buffer := puint32(kalloc(bufSize));
|
||||
memset(uint32(buffer), 0, bufSize);
|
||||
|
||||
if (disk^.readCallback = nil) and (disk^.readCallbackAsync = nil) then begin
|
||||
syslog.writestringln('FlatFS: detect_volumes: device has no read callback.');
|
||||
if disk^.dispatchRead = nil then begin
|
||||
syslog.writestringln('FlatFS: detect_volumes: device has no read dispatch.');
|
||||
kfree(buffer);
|
||||
exit;
|
||||
end;
|
||||
@@ -759,14 +762,17 @@ end;
|
||||
|
||||
function identify_volume(volume : PStorage_Volume) : boolean;
|
||||
var
|
||||
buffer : puint32;
|
||||
buffer : puint32;
|
||||
bufSize : uint32;
|
||||
begin
|
||||
push_trace('flatfs.identify_volume');
|
||||
identify_volume := false;
|
||||
if (volume^.device^.readCallback = nil) and (volume^.device^.readCallbackAsync = nil) then exit;
|
||||
if volume^.device^.dispatchRead = nil then exit;
|
||||
|
||||
buffer := puint32(kalloc(512));
|
||||
memset(uint32(buffer), 0, 512);
|
||||
bufSize := volume^.device^.sectorSize;
|
||||
if bufSize < 512 then bufSize := 512;
|
||||
buffer := puint32(kalloc(bufSize));
|
||||
memset(uint32(buffer), 0, bufSize);
|
||||
|
||||
storagemanager.storage_read(volume^.device, volume^.sectorStart, 1, buffer);
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -38,12 +38,10 @@ begin
|
||||
if size = 0 then exit;
|
||||
|
||||
{ Round up to a whole number of sectors so DMA / block I/O is safe }
|
||||
sectorSize := volume^.device^.sectorSize; //TODO need fileystsem function to get thing size, like cluster size, for file-level IO buffer allocation. For now we just assume sector size.
|
||||
sectorSize := volume^.device^.sectorSize;
|
||||
if sectorSize = 0 then
|
||||
sectorSize := 512;
|
||||
|
||||
sectorSize := 512*4; { safe default } //TODO
|
||||
|
||||
alignedSize := ((size + sectorSize - 1) div sectorSize) * sectorSize;
|
||||
|
||||
buf := puint32(kalloc(alignedSize));
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
{
|
||||
Driver->Storage->IORequest - I/O request allocation and lifecycle helpers.
|
||||
|
||||
Provides heap allocation/free for TIORequest records used by the
|
||||
submit_io / complete_io path in storagemanager.pas.
|
||||
|
||||
@author(Aaron Hance <[email protected]>)
|
||||
}
|
||||
unit iorequest;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
lmemorymanager,
|
||||
storagetypes;
|
||||
|
||||
{ Allocate a new TIORequest on the heap and initialise its core fields.
|
||||
Caller, State, Error and ByteCount are zeroed (caller fills as needed). }
|
||||
function ioreq_alloc(reqType : TIORequestType;
|
||||
device : PStorage_Device;
|
||||
lba : uint32;
|
||||
sectors : uint32;
|
||||
buf : pointer) : PIORequest;
|
||||
|
||||
{ Free a heap-allocated TIORequest. Safe to call with nil. }
|
||||
procedure ioreq_free(req : PIORequest);
|
||||
|
||||
{ Copy an existing TIORequest into a new heap allocation (deep copy of the
|
||||
record, NOT the buffer it points to). Used by dispatch_next to create an
|
||||
ISR-safe copy from the CFIFO value-element. }
|
||||
function ioreq_copy(src : PIORequest) : PIORequest;
|
||||
|
||||
implementation
|
||||
|
||||
function ioreq_alloc(reqType : TIORequestType;
|
||||
device : PStorage_Device;
|
||||
lba : uint32;
|
||||
sectors : uint32;
|
||||
buf : pointer) : PIORequest;
|
||||
var
|
||||
req : PIORequest;
|
||||
begin
|
||||
req := PIORequest(kalloc(SizeOf(TIORequest)));
|
||||
if req = nil then begin
|
||||
ioreq_alloc := nil;
|
||||
exit;
|
||||
end;
|
||||
req^.RequestType := reqType;
|
||||
req^.State := iosPending;
|
||||
req^.Device := device;
|
||||
req^.LBA := lba;
|
||||
req^.SectorCount := sectors;
|
||||
req^.Buffer := buf;
|
||||
req^.ByteCount := 0;
|
||||
req^.Error := eNone;
|
||||
req^.Caller := nil;
|
||||
req^.UserData := nil;
|
||||
req^.Callback := nil;
|
||||
req^.CallbackData := nil;
|
||||
ioreq_alloc := req;
|
||||
end;
|
||||
|
||||
procedure ioreq_free(req : PIORequest);
|
||||
begin
|
||||
if req <> nil then
|
||||
kfree(void(req));
|
||||
end;
|
||||
|
||||
function ioreq_copy(src : PIORequest) : PIORequest;
|
||||
var
|
||||
dst : PIORequest;
|
||||
begin
|
||||
dst := PIORequest(kalloc(SizeOf(TIORequest)));
|
||||
if dst = nil then begin
|
||||
ioreq_copy := nil;
|
||||
exit;
|
||||
end;
|
||||
dst^.RequestType := src^.RequestType;
|
||||
dst^.State := src^.State;
|
||||
dst^.Device := src^.Device;
|
||||
dst^.LBA := src^.LBA;
|
||||
dst^.SectorCount := src^.SectorCount;
|
||||
dst^.Buffer := src^.Buffer;
|
||||
dst^.ByteCount := src^.ByteCount;
|
||||
dst^.Error := src^.Error;
|
||||
dst^.Caller := src^.Caller;
|
||||
dst^.UserData := src^.UserData;
|
||||
dst^.Callback := src^.Callback;
|
||||
dst^.CallbackData := src^.CallbackData;
|
||||
ioreq_copy := dst;
|
||||
end;
|
||||
|
||||
end.
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+638
-234
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -5,6 +5,7 @@
|
||||
bindings used by processmanager.pas and contextswitcher.pas.
|
||||
|
||||
@author(Kieron Morris <[email protected]>)
|
||||
@author(Aaron Hance <[email protected]>)
|
||||
}
|
||||
unit proctypes;
|
||||
|
||||
@@ -105,6 +106,12 @@ type
|
||||
{ Resource bindings (PDList of TResourceBinding) }
|
||||
Resources : void;
|
||||
|
||||
{ Per-process file descriptor table (PFDTable from fdtable.pas) }
|
||||
FDTable : void;
|
||||
|
||||
{ Per-process working directory — heap-allocated, defaults to '/' }
|
||||
Cwd : pchar;
|
||||
|
||||
{ User-defined state }
|
||||
Local : void;
|
||||
end;
|
||||
|
||||
+37
-10
@@ -16,6 +16,7 @@
|
||||
Include->Util - Data Manipulation Utlities.
|
||||
|
||||
@author(Kieron Morris <[email protected]>)
|
||||
@author(Aaron Hance <[email protected]>)
|
||||
}
|
||||
unit util;
|
||||
|
||||
@@ -24,7 +25,8 @@ unit util;
|
||||
interface
|
||||
|
||||
uses
|
||||
bios_data_area, tracer;
|
||||
bios_data_area,
|
||||
tracer;
|
||||
|
||||
function INTE : boolean;
|
||||
procedure CLI();
|
||||
@@ -87,7 +89,12 @@ var
|
||||
implementation
|
||||
|
||||
uses
|
||||
syslog, RTC, cpu, serial, strings, isr_types;
|
||||
cpu,
|
||||
isr_types,
|
||||
RTC,
|
||||
serial,
|
||||
strings,
|
||||
syslog;
|
||||
|
||||
function abs(x : sint32) : uint32;
|
||||
var
|
||||
@@ -274,14 +281,35 @@ end;
|
||||
procedure psleep(t : uint16);
|
||||
var
|
||||
t1, t2 : uint16;
|
||||
|
||||
spin : uint32;
|
||||
begin
|
||||
t1:= BDA^.Ticks;
|
||||
t2:= BDA^.Ticks;
|
||||
while t2-t1 < t do begin
|
||||
break;
|
||||
t2:= BDA^.Ticks;
|
||||
if t2 < t1 then break;
|
||||
t1 := BDA^.Ticks;
|
||||
|
||||
{ Busy-spin briefly to give the tick counter a chance to advance.
|
||||
If after ~50 000 iterations the counter hasn't moved, the timer
|
||||
ISR isn't installed yet — fall back to a rough CPU busy-wait
|
||||
(~1 ms per requested tick at typical Bochs/VBox speed). }
|
||||
spin := 0;
|
||||
t2 := t1;
|
||||
while t2 = t1 do begin
|
||||
spin := spin + 1;
|
||||
if spin > 50000 then begin
|
||||
{ Timer not running — approximate delay with busy loop.
|
||||
Each outer iteration ≈ a few µs; 50000 * t gives a
|
||||
very rough ms-scale delay that is good enough for the
|
||||
hardware settle times that call psleep. }
|
||||
for spin := 1 to uint32(t) * 50000 do
|
||||
asm nop end;
|
||||
exit;
|
||||
end;
|
||||
t2 := BDA^.Ticks;
|
||||
end;
|
||||
|
||||
{ Timer is running — use real ticks }
|
||||
t1 := BDA^.Ticks;
|
||||
while t2 - t1 < t do begin
|
||||
t2 := BDA^.Ticks;
|
||||
if t2 < t1 then break; { tick counter wrapped }
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -324,7 +352,6 @@ begin
|
||||
//serial.sendString('[outb]');
|
||||
//serial.sendHex(port);
|
||||
//serial.sendHex(val);
|
||||
psleep(1);
|
||||
asm
|
||||
PUSH EAX
|
||||
PUSH EDX
|
||||
|
||||
+11
-3
@@ -65,6 +65,7 @@ uses
|
||||
mouse,
|
||||
multiboot,
|
||||
net,
|
||||
notepad,
|
||||
OHCI,
|
||||
partcmd,
|
||||
PCI,
|
||||
@@ -79,6 +80,7 @@ uses
|
||||
serial,
|
||||
stdio,
|
||||
storagemanager,
|
||||
storagetest,
|
||||
strings,
|
||||
syslog,
|
||||
testdriver,
|
||||
@@ -221,6 +223,7 @@ begin
|
||||
|
||||
{ VFS Init }
|
||||
vfs.init();
|
||||
storagetest.init;
|
||||
|
||||
{ Management Interfaces }
|
||||
tracer.push_trace('kmain.DRVMGMT');
|
||||
@@ -240,6 +243,9 @@ begin
|
||||
flatfs.init();
|
||||
iso9660.init();
|
||||
|
||||
{ Init process manager (must be before device drivers — submit_io needs CurrentProcess) }
|
||||
processmanager.init;
|
||||
|
||||
{ Device Drivers }
|
||||
tracer.push_trace('kmain.DEVDRV');
|
||||
syslog.logln('KERNEL', 'DEVICE DRIVERS: INIT BEGIN.');
|
||||
@@ -270,9 +276,6 @@ begin
|
||||
{ Init Progs }
|
||||
progmanager.init();
|
||||
|
||||
{ Init process manager }
|
||||
processmanager.init;
|
||||
|
||||
{ Seed RNG }
|
||||
rand.srand((getDateTime.Seconds SHL 24) OR (getDateTime.Minutes SHL 16) OR (getDateTime.Hours SHL 8) OR (getDateTime.Day));
|
||||
|
||||
@@ -292,6 +295,9 @@ begin
|
||||
{ Initialize disk utility (registers with desktop search) }
|
||||
diskutil.init;
|
||||
|
||||
{ Initialize notepad (registers with desktop search) }
|
||||
notepad.init;
|
||||
|
||||
{ Run unit tests }
|
||||
strings.UnitTest;
|
||||
usbtypes.UnitTest;
|
||||
@@ -311,6 +317,8 @@ begin
|
||||
minh.UnitTest;
|
||||
maxh.UnitTest;
|
||||
prio.UnitTest;
|
||||
vfs.UnitTest;
|
||||
storagetest.UnitTest;
|
||||
|
||||
{ Register timer-driven tasks }
|
||||
graphicsrefresh.init;
|
||||
|
||||
@@ -76,7 +76,6 @@ var
|
||||
|
||||
procedure init;
|
||||
function kalloc(size : uint32) : void;
|
||||
function kalloc(size : uint32; isPersistent : boolean) : void;
|
||||
function klalloc(size : uint32) : void;
|
||||
procedure klfree(address : uint32);
|
||||
function kpalloc(address : uint32) : void;
|
||||
|
||||
+30
-12
@@ -6,22 +6,24 @@
|
||||
the round-robin scheduler called from the context switch ISR.
|
||||
|
||||
@author(Kieron Morris <[email protected]>)
|
||||
@author(Aaron Hance <[email protected]>)
|
||||
}
|
||||
unit processmanager;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
proctypes,
|
||||
stdio,
|
||||
bios_data_area,
|
||||
fdtable,
|
||||
lists,
|
||||
lmemorymanager,
|
||||
util,
|
||||
proctypes,
|
||||
rand,
|
||||
stdio,
|
||||
strings,
|
||||
tracer,
|
||||
syslog,
|
||||
bios_data_area;
|
||||
tracer,
|
||||
util;
|
||||
|
||||
{ Core API }
|
||||
function create(name : pchar;
|
||||
@@ -152,6 +154,12 @@ begin
|
||||
{ Resources }
|
||||
ctx^.Resources := void(DL_New(SizeOf(TResourceBinding)));
|
||||
|
||||
{ Per-process file descriptor table }
|
||||
ctx^.FDTable := void(fd_table_new);
|
||||
|
||||
{ Per-process working directory }
|
||||
ctx^.Cwd := stringCopy('/');
|
||||
|
||||
{ Allocate per-process kernel stack }
|
||||
stack := kalloc(PROCESS_STACK_SIZE);
|
||||
ctx^.StackBase := stack;
|
||||
@@ -355,14 +363,12 @@ end;
|
||||
|
||||
procedure proc_await;
|
||||
begin
|
||||
{ Caller must set CurrentProcess^.State := psAwaiting before calling.
|
||||
This procedure just spin-waits until an ISR sets the state back
|
||||
to something other than psAwaiting. }
|
||||
if CurrentProcess <> nil then begin
|
||||
asm pushf; cli end;
|
||||
CurrentProcess^.State := psAwaiting;
|
||||
asm popf end;
|
||||
{ Wait for scheduler to switch us out, then back in when woken }
|
||||
asm
|
||||
hlt
|
||||
end;
|
||||
while CurrentProcess^.State = psAwaiting do
|
||||
asm hlt end;
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -543,6 +549,16 @@ begin
|
||||
if parent <> nil then
|
||||
sendMessage(parent^.ProcessID, smChildExited, void(ctx^.ProcessID));
|
||||
end;
|
||||
{ Clean up file descriptors }
|
||||
if ctx^.FDTable <> nil then begin
|
||||
fd_table_free(PFDTable(ctx^.FDTable));
|
||||
ctx^.FDTable := nil;
|
||||
end;
|
||||
{ Free per-process CWD }
|
||||
if ctx^.Cwd <> nil then begin
|
||||
kfree(void(ctx^.Cwd));
|
||||
ctx^.Cwd := nil;
|
||||
end;
|
||||
{ Clean up resources }
|
||||
unbindAllResources(ctx);
|
||||
{ Free stack }
|
||||
@@ -748,6 +764,8 @@ begin
|
||||
IdleProcess^.Quantum := 1; { Yield to real processes ASAP }
|
||||
IdleProcess^.PendingMsg := smNone;
|
||||
IdleProcess^.Resources := void(DL_New(SizeOf(TResourceBinding)));
|
||||
IdleProcess^.FDTable := void(fd_table_new);
|
||||
IdleProcess^.Cwd := stringCopy('/');
|
||||
{ No stack allocation — idle runs on the kernel's original stack }
|
||||
slotPtr := DL_Add(Processes);
|
||||
slotPtr^ := uint32(IdleProcess);
|
||||
|
||||
@@ -18,7 +18,6 @@ procedure init();
|
||||
implementation
|
||||
|
||||
uses
|
||||
syslog,
|
||||
lists,
|
||||
lmemorymanager,
|
||||
storagemanager,
|
||||
@@ -29,6 +28,9 @@ uses
|
||||
util,
|
||||
volumemanager;
|
||||
|
||||
var
|
||||
g_out : POutBuf;
|
||||
|
||||
{ ---------- helpers ---------- }
|
||||
|
||||
function wnd : uint32;
|
||||
@@ -57,22 +59,22 @@ end;
|
||||
|
||||
procedure print(s : pchar);
|
||||
begin
|
||||
syslog.writestring(s);
|
||||
stdio.bufWriteStr(g_out, s);
|
||||
end;
|
||||
|
||||
procedure println(s : pchar);
|
||||
begin
|
||||
syslog.writestringln(s);
|
||||
stdio.bufWriteStrLn(g_out, s);
|
||||
end;
|
||||
|
||||
procedure printint(v : uint32);
|
||||
begin
|
||||
syslog.writeint(v);
|
||||
stdio.bufWriteInt(g_out, integer(v));
|
||||
end;
|
||||
|
||||
procedure printhex(v : uint32);
|
||||
begin
|
||||
syslog.writehex(v);
|
||||
stdio.bufWriteHex(g_out, v);
|
||||
end;
|
||||
|
||||
procedure printsize(bytes : uint32);
|
||||
@@ -230,7 +232,7 @@ begin
|
||||
exit;
|
||||
end;
|
||||
|
||||
if (device^.writeCallback = nil) and (device^.writeCallbackAsync = nil) then begin
|
||||
if device^.dispatchWrite = nil then begin
|
||||
println('Error: device has no write support.');
|
||||
exit;
|
||||
end;
|
||||
@@ -274,6 +276,7 @@ procedure command_disk(params : PParamList; stdin_buf, stdout_buf, stderr_buf: P
|
||||
var
|
||||
subcmd : pchar;
|
||||
begin
|
||||
g_out := stdout_buf;
|
||||
push_trace('DiskCmd.command_disk');
|
||||
|
||||
if paramCount(params) = 0 then begin
|
||||
|
||||
Loaded 20 of 32 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user