fat32 rewrite and storage stability fixes

This commit is contained in:
2026-03-16 21:18:44 +00:00
parent 5d7aacc28f
commit aadc59f831
13 changed files with 9163 additions and 4706 deletions
+500
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+68 -8
View File
@@ -195,6 +195,7 @@ procedure do_refresh_load(state: PFileBrowserState); forward;
procedure do_refresh_build(state: PFileBrowserState); forward;
procedure schedule_refresh(state: PFileBrowserState); forward;
procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl; forward;
function fb_error_name(err : TError) : pchar; forward;
procedure fb_collect_cb(key: pchar; data: void; ud: void); forward;
procedure do_refresh(state: PFileBrowserState); forward;
procedure navigate_to(state: PFileBrowserState; newDir: pchar); forward;
@@ -582,6 +583,51 @@ begin
puint32(@state^.refresh_pending)^ := 1;
end;
function fb_error_name(err : TError) : pchar;
begin
case err of
eNone: fb_error_name := 'eNone';
eUnknown: fb_error_name := 'eUnknown';
eNotSupported: fb_error_name := 'eNotSupported';
eOutOfMemory: fb_error_name := 'eOutOfMemory';
eInvalidArgument: fb_error_name := 'eInvalidArgument';
eFileInUse: fb_error_name := 'eFileInUse';
eFileDoesNotExist: fb_error_name := 'eFileDoesNotExist';
eInvalidFileName: fb_error_name := 'eInvalidFileName';
eInvalidFileExtension: fb_error_name := 'eInvalidFileExtension';
eFilenameTooLong: fb_error_name := 'eFilenameTooLong';
eDirectoryDoesNotExist: fb_error_name := 'eDirectoryDoesNotExist';
eDirectoryAlreadyExists: fb_error_name := 'eDirectoryAlreadyExists';
eDirectoryNotEmpty: fb_error_name := 'eDirectoryNotEmpty';
eDirectoryFull: fb_error_name := 'eDirectoryFull';
eNotADirectory: fb_error_name := 'eNotADirectory';
eWriteOnly: fb_error_name := 'eWriteOnly';
eReadOnly: fb_error_name := 'eReadOnly';
ePermissionDenied: fb_error_name := 'ePermissionDenied';
eInvalidPath: fb_error_name := 'eInvalidPath';
eTooManyOpenFiles: fb_error_name := 'eTooManyOpenFiles';
eInvalidHandle: fb_error_name := 'eInvalidHandle';
eFileNotLoaded: fb_error_name := 'eFileNotLoaded';
eAlreadyExists: fb_error_name := 'eAlreadyExists';
eDiskFull: fb_error_name := 'eDiskFull';
eIOError: fb_error_name := 'eIOError';
eIOTimeout: fb_error_name := 'eIOTimeout';
eIOCancelled: fb_error_name := 'eIOCancelled';
eDeviceNotReady: fb_error_name := 'eDeviceNotReady';
eDeviceRemoved: fb_error_name := 'eDeviceRemoved';
eDeviceNotFound: fb_error_name := 'eDeviceNotFound';
eQueueFull: fb_error_name := 'eQueueFull';
eNoFreeSlot: fb_error_name := 'eNoFreeSlot';
eCorruptFilesystem: fb_error_name := 'eCorruptFilesystem';
eBadSector: fb_error_name := 'eBadSector';
eAlreadyMounted: fb_error_name := 'eAlreadyMounted';
eNotMounted: fb_error_name := 'eNotMounted';
eUnsupportedFilesystem: fb_error_name := 'eUnsupportedFilesystem';
eInvalidPartitionTable: fb_error_name := 'eInvalidPartitionTable';
eVolumeNotFound: fb_error_name := 'eVolumeNotFound';
end;
end;
{ fb_refresh_poll_cb — 50ms LVGL poll timer; when rd_ready=1, build UI }
procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl;
var
@@ -604,9 +650,12 @@ begin
case state^.wop_kind of
1: lv_msgbox_add_text(mbox, 'Could not create folder.');
2: lv_msgbox_add_text(mbox, 'Could not rename item.');
3: lv_msgbox_add_text(mbox, 'Could not create file.');
4: lv_msgbox_add_text(mbox, 'Could not delete item.');
else
lv_msgbox_add_text(mbox, 'Operation failed.');
end;
lv_msgbox_add_text(mbox, fb_error_name(state^.wop_result));
ok_b := lv_msgbox_add_footer_button(mbox, 'OK');
lv_obj_add_event_cb(ok_b, @fb_mbox_close_cb, LV_EVENT_CLICKED, mbox);
end;
@@ -619,8 +668,8 @@ begin
kfree(void(state^.wop_path2));
state^.wop_path2 := nil;
end;
{ For rename, clear selection }
if state^.wop_kind = 2 then
{ For rename/delete, clear selection }
if (state^.wop_kind = 2) or (state^.wop_kind = 4) then
clear_selection(state);
schedule_refresh(state);
end;
@@ -1591,15 +1640,16 @@ begin
@fb_delete_done, state);
end;
{ Async completion callback — runs in ISR/worker context, defer to LVGL }
{ Async completion callback — runs outside the LVGL thread, so only set flags. }
procedure fb_delete_done(error: TError; userdata: pointer);
var
state: PFileBrowserState;
begin
state := PFileBrowserState(userdata);
if state = nil then exit;
state^.op_err := error;
lv_timer_create(@fb_delete_done_timer, 1, state);
state^.wop_kind := 4;
state^.wop_result := error;
puint32(@state^.wop_done)^ := 1;
end;
{ Deferred UI update after delete }
@@ -1864,11 +1914,16 @@ begin
end else begin
if nfctx^.handle <> 0 then
driver.storage.vfs.CloseFile(nfctx^.handle);
lv_timer_create(@fb_newfile_done_timer, 1, nfctx);
if nfctx^.state <> nil then begin
nfctx^.state^.wop_kind := 3;
nfctx^.state^.wop_result := nfctx^.err;
puint32(@nfctx^.state^.wop_done)^ := 1;
end;
kfree(void(nfctx));
end;
end;
{ Async step 2: write complete — close handle and schedule UI refresh }
{ Async step 2: write complete — close handle and defer UI work via poll flags }
procedure fb_newfile_written(error: TError; userdata: pointer);
var
nfctx : PNewFileCtx;
@@ -1878,7 +1933,12 @@ begin
if error <> eNone then nfctx^.err := error;
if nfctx^.handle <> 0 then
driver.storage.vfs.CloseFile(nfctx^.handle);
lv_timer_create(@fb_newfile_done_timer, 1, nfctx);
if nfctx^.state <> nil then begin
nfctx^.state^.wop_kind := 3;
nfctx^.state^.wop_result := nfctx^.err;
puint32(@nfctx^.state^.wop_done)^ := 1;
end;
kfree(void(nfctx));
end;
procedure fb_newfile_done_timer(tmr: Plv_timer); cdecl;
+433 -77
View File
File diff suppressed because it is too large Load Diff
+8 -4
View File
@@ -336,18 +336,20 @@ end;
procedure write_mbr(device : PStorage_Device; mbr : PMaster_Boot_Record);
var
oldCache : pointer;
cache : puint32;
buf : puint32;
begin
push_trace('driver.storage.mgr.write_mbr');
if (device = nil) or (mbr = nil) or (not device^.writable) then exit;
{ Update cache }
if device^.cachedMBR <> nil then
kfree(device^.cachedMBR);
oldCache := device^.cachedMBR;
cache := puint32(kalloc(sizeof(TMaster_Boot_Record)));
if cache <> nil then
memcpy(uint32(mbr), uint32(cache), sizeof(TMaster_Boot_Record));
device^.cachedMBR := pointer(cache);
if oldCache <> nil then
kfree(oldCache);
buf := alloc_mbr_io_buffer(device, mbr);
if buf = nil then exit;
@@ -364,6 +366,7 @@ end;
procedure write_mbr_async(device : PStorage_Device; mbr : PMaster_Boot_Record;
callback : TIOCallback; callbackData : pointer);
var
oldCache : pointer;
cache : puint32;
buf : puint32;
ctx : PMBRWriteAsyncData;
@@ -379,12 +382,13 @@ begin
exit;
end;
{ Update cache }
if device^.cachedMBR <> nil then
kfree(device^.cachedMBR);
oldCache := device^.cachedMBR;
cache := puint32(kalloc(sizeof(TMaster_Boot_Record)));
if cache <> nil then
memcpy(uint32(mbr), uint32(cache), sizeof(TMaster_Boot_Record));
device^.cachedMBR := pointer(cache);
if oldCache <> nil then
kfree(oldCache);
buf := alloc_mbr_io_buffer(device, mbr);
if buf = nil then begin
+65 -2
View File
@@ -134,7 +134,7 @@ procedure UnwatchDirectory(WatchID : uint32);
{ Volume invalidation — must be called before freeing a volume.
Closes all open FDs referencing the volume across all processes,
evicts directory cache entries, and removes VFS mount points. }
procedure InvalidateVolume(vol : PStorage_Volume);
function InvalidateVolume(vol : PStorage_Volume) : boolean;
{ VFS Functions }
function newVirtualDirectory(Path : pchar) : TError;
@@ -149,6 +149,7 @@ implementation
uses
driver.storage.fs.mgr,
driver.storage.fs.fat32,
proc.mgr,
proc.types,
io.stdio,
@@ -508,7 +509,36 @@ end;
function GetObjectFromPath(path : pchar) : PVFSObject; forward;
function GetObjectFromPathEx(path : pchar; var volRelPath : pchar) : PVFSObject; forward;
procedure InvalidateVolume(vol : PStorage_Volume);
function volumeHasAsyncHandles(vol : PStorage_Volume) : boolean;
var
pCount : uint32;
pi : uint32;
fi : uint32;
pCtx : proc.types.PProcessContext;
tbl : PFDTable;
begin
volumeHasAsyncHandles := false;
if vol = nil then exit;
pCount := proc.mgr.processCount();
if pCount = 0 then exit;
for pi := 0 to pCount - 1 do begin
pCtx := proc.mgr.getProcessByIndex(pi);
if pCtx = nil then continue;
tbl := PFDTable(pCtx^.FDTable);
if tbl = nil then continue;
for fi := 0 to MAX_FDS - 1 do begin
if tbl^.Entries[fi].InUse and (tbl^.Entries[fi].Volume = vol)
and (tbl^.Entries[fi].AsyncRefs > 0) then begin
volumeHasAsyncHandles := true;
exit;
end;
end;
end;
end;
function InvalidateVolume(vol : PStorage_Volume) : boolean;
var
pCount : uint32;
pi : uint32;
@@ -522,7 +552,12 @@ var
child : PVFSObject;
begin
debug.tracer.push_trace('driver.storage.vfs.InvalidateVolume.enter');
InvalidateVolume := false;
if vol = nil then exit;
if volumeHasAsyncHandles(vol) then begin
debug.tracer.push_trace('driver.storage.vfs.InvalidateVolume.busy');
exit;
end;
{ 1. Evict all directory cache entries for this volume }
DirCache_Invalidate(vol, nil);
@@ -575,6 +610,10 @@ begin
kfree(void(bootObj));
end;
if (vol^.filesystem <> nil) and (stringCompare(vol^.filesystem^.sName, 'FAT32') = 0) then
FAT32ReleaseVolumeState(vol);
InvalidateVolume := true;
debug.tracer.push_trace('driver.storage.vfs.InvalidateVolume.exit');
end;
@@ -1313,6 +1352,17 @@ begin
{ Cache per-file metadata and populate DataSize via FS open hook }
if (vol^.filesystem <> nil) and (vol^.filesystem^.openFileCallback <> nil) then begin
fd^.FSPrivate := vol^.filesystem^.openFileCallback(vol, dir, fname, fd^.DataSize);
if fd^.FSPrivate = nil then begin
if Error <> nil then begin
if (OpenMode = omCreate) or (OpenMode = omWrite) then
Error^ := eInvalidFileName
else
Error^ := eFileDoesNotExist;
end;
fd_close(tbl, slot);
debug.tracer.push_trace('driver.storage.vfs.OpenFile.exit');
exit;
end;
if vol^.filesystem^.closeFileCallback <> nil then
fd^.FSCloseHook := pointer(vol^.filesystem^.closeFileCallback);
if Error <> nil then Error^ := eNone;
@@ -1599,6 +1649,19 @@ begin
if (vol^.filesystem <> nil) and (vol^.filesystem^.openFileCallback <> nil) then begin
fd^.FSPrivate := vol^.filesystem^.openFileCallback(vol, dir, fname, fd^.DataSize);
if fd^.FSPrivate = nil then begin
if Error <> nil then begin
if (OpenMode = omCreate) or (OpenMode = omWrite) then
Error^ := eInvalidFileName
else
Error^ := eFileDoesNotExist;
end;
fd_close(tbl, slot);
OutHandle := 0;
if Callback <> nil then Callback(Error^, CallbackData);
debug.tracer.push_trace('driver.storage.vfs.OpenFileAsync.exit');
exit;
end;
if vol^.filesystem^.closeFileCallback <> nil then
fd^.FSCloseHook := pointer(vol^.filesystem^.closeFileCallback);
if Error <> nil then Error^ := eNone;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -34,6 +34,7 @@ uses
io.syslog,
debug.tracer,
core.util, arch.x86.util,
driver.storage.vol.mbr,
driver.storage.vol.mgr;
type
@@ -763,13 +764,38 @@ end;
function identify_volume(volume : PStorage_Volume) : boolean;
var
buffer : puint32;
bufSize : uint32;
buffer : puint32;
bufSize : uint32;
mbr : PMaster_Boot_Record;
freeMbr : boolean;
slot : sint32;
i : uint32;
begin
push_trace('driver.storage.fs.flatfs.identify_volume');
identify_volume := false;
if volume^.device^.dispatchRead = nil then exit;
mbr := driver.storage.mgr.get_cached_mbr(volume^.device);
freeMbr := false;
if mbr = nil then begin
mbr := driver.storage.mgr.read_mbr(volume^.device);
freeMbr := mbr <> nil;
end;
slot := -1;
if mbr <> nil then begin
for i := 0 to 3 do begin
if mbr^.partition[i].LBA_start = volume^.sectorStart then begin
slot := sint32(i);
break;
end;
end;
if (slot >= 0) and (mbr^.partition[slot].system_id <> filesystem.system_id) then begin
if freeMbr then kfree(void(mbr));
exit;
end;
end;
bufSize := volume^.device^.sectorSize;
if bufSize < 512 then bufSize := 512;
buffer := puint32(kalloc(bufSize));
@@ -783,6 +809,7 @@ begin
end;
kfree(buffer);
if freeMbr then kfree(void(mbr));
end;
function readDirectoryEntries(volume : PStorage_Volume; directory : pchar; status : PuInt32) : PLinkedListBase;
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