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:
Aaron committed 2026-03-07 18:58:32 +00:00
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
+18 -12
View File
@@ -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
+1 -3
View File
@@ -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));
+93
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff