fat32 rewrite and storage stability fixes

This commit is contained in:
Aaron committed 2026-03-16 21:18:44 +00:00
1 parent 5d7aacc28f
commit aadc59f831
13 files changed
+9163 -4706

No files matched your search

+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 do_refresh_build(state: PFileBrowserState); forward;
procedure schedule_refresh(state: PFileBrowserState); forward; procedure schedule_refresh(state: PFileBrowserState); forward;
procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl; 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 fb_collect_cb(key: pchar; data: void; ud: void); forward;
procedure do_refresh(state: PFileBrowserState); forward; procedure do_refresh(state: PFileBrowserState); forward;
procedure navigate_to(state: PFileBrowserState; newDir: pchar); forward; procedure navigate_to(state: PFileBrowserState; newDir: pchar); forward;
@@ -582,6 +583,51 @@ begin
puint32(@state^.refresh_pending)^ := 1; puint32(@state^.refresh_pending)^ := 1;
end; 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 } { fb_refresh_poll_cb — 50ms LVGL poll timer; when rd_ready=1, build UI }
procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl; procedure fb_refresh_poll_cb(tmr: Plv_timer); cdecl;
var var
@@ -604,9 +650,12 @@ begin
case state^.wop_kind of case state^.wop_kind of
1: lv_msgbox_add_text(mbox, 'Could not create folder.'); 1: lv_msgbox_add_text(mbox, 'Could not create folder.');
2: lv_msgbox_add_text(mbox, 'Could not rename item.'); 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 else
lv_msgbox_add_text(mbox, 'Operation failed.'); lv_msgbox_add_text(mbox, 'Operation failed.');
end; end;
lv_msgbox_add_text(mbox, fb_error_name(state^.wop_result));
ok_b := lv_msgbox_add_footer_button(mbox, 'OK'); ok_b := lv_msgbox_add_footer_button(mbox, 'OK');
lv_obj_add_event_cb(ok_b, @fb_mbox_close_cb, LV_EVENT_CLICKED, mbox); lv_obj_add_event_cb(ok_b, @fb_mbox_close_cb, LV_EVENT_CLICKED, mbox);
end; end;
@@ -619,8 +668,8 @@ begin
kfree(void(state^.wop_path2)); kfree(void(state^.wop_path2));
state^.wop_path2 := nil; state^.wop_path2 := nil;
end; end;
{ For rename, clear selection } { For rename/delete, clear selection }
if state^.wop_kind = 2 then if (state^.wop_kind = 2) or (state^.wop_kind = 4) then
clear_selection(state); clear_selection(state);
schedule_refresh(state); schedule_refresh(state);
end; end;
@@ -1591,15 +1640,16 @@ begin
@fb_delete_done, state); @fb_delete_done, state);
end; 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); procedure fb_delete_done(error: TError; userdata: pointer);
var var
state: PFileBrowserState; state: PFileBrowserState;
begin begin
state := PFileBrowserState(userdata); state := PFileBrowserState(userdata);
if state = nil then exit; if state = nil then exit;
state^.op_err := error; state^.wop_kind := 4;
lv_timer_create(@fb_delete_done_timer, 1, state); state^.wop_result := error;
puint32(@state^.wop_done)^ := 1;
end; end;
{ Deferred UI update after delete } { Deferred UI update after delete }
@@ -1864,11 +1914,16 @@ begin
end else begin end else begin
if nfctx^.handle <> 0 then if nfctx^.handle <> 0 then
driver.storage.vfs.CloseFile(nfctx^.handle); 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;
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); procedure fb_newfile_written(error: TError; userdata: pointer);
var var
nfctx : PNewFileCtx; nfctx : PNewFileCtx;
@@ -1878,7 +1933,12 @@ begin
if error <> eNone then nfctx^.err := error; if error <> eNone then nfctx^.err := error;
if nfctx^.handle <> 0 then if nfctx^.handle <> 0 then
driver.storage.vfs.CloseFile(nfctx^.handle); 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;
procedure fb_newfile_done_timer(tmr: Plv_timer); cdecl; 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); procedure write_mbr(device : PStorage_Device; mbr : PMaster_Boot_Record);
var var
oldCache : pointer;
cache : puint32; cache : puint32;
buf : puint32; buf : puint32;
begin begin
push_trace('driver.storage.mgr.write_mbr'); push_trace('driver.storage.mgr.write_mbr');
if (device = nil) or (mbr = nil) or (not device^.writable) then exit; if (device = nil) or (mbr = nil) or (not device^.writable) then exit;
{ Update cache } { Update cache }
if device^.cachedMBR <> nil then oldCache := device^.cachedMBR;
kfree(device^.cachedMBR);
cache := puint32(kalloc(sizeof(TMaster_Boot_Record))); cache := puint32(kalloc(sizeof(TMaster_Boot_Record)));
if cache <> nil then if cache <> nil then
memcpy(uint32(mbr), uint32(cache), sizeof(TMaster_Boot_Record)); memcpy(uint32(mbr), uint32(cache), sizeof(TMaster_Boot_Record));
device^.cachedMBR := pointer(cache); device^.cachedMBR := pointer(cache);
if oldCache <> nil then
kfree(oldCache);
buf := alloc_mbr_io_buffer(device, mbr); buf := alloc_mbr_io_buffer(device, mbr);
if buf = nil then exit; if buf = nil then exit;
@@ -364,6 +366,7 @@ end;
procedure write_mbr_async(device : PStorage_Device; mbr : PMaster_Boot_Record; procedure write_mbr_async(device : PStorage_Device; mbr : PMaster_Boot_Record;
callback : TIOCallback; callbackData : pointer); callback : TIOCallback; callbackData : pointer);
var var
oldCache : pointer;
cache : puint32; cache : puint32;
buf : puint32; buf : puint32;
ctx : PMBRWriteAsyncData; ctx : PMBRWriteAsyncData;
@@ -379,12 +382,13 @@ begin
exit; exit;
end; end;
{ Update cache } { Update cache }
if device^.cachedMBR <> nil then oldCache := device^.cachedMBR;
kfree(device^.cachedMBR);
cache := puint32(kalloc(sizeof(TMaster_Boot_Record))); cache := puint32(kalloc(sizeof(TMaster_Boot_Record)));
if cache <> nil then if cache <> nil then
memcpy(uint32(mbr), uint32(cache), sizeof(TMaster_Boot_Record)); memcpy(uint32(mbr), uint32(cache), sizeof(TMaster_Boot_Record));
device^.cachedMBR := pointer(cache); device^.cachedMBR := pointer(cache);
if oldCache <> nil then
kfree(oldCache);
buf := alloc_mbr_io_buffer(device, mbr); buf := alloc_mbr_io_buffer(device, mbr);
if buf = nil then begin 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. { Volume invalidation — must be called before freeing a volume.
Closes all open FDs referencing the volume across all processes, Closes all open FDs referencing the volume across all processes,
evicts directory cache entries, and removes VFS mount points. } evicts directory cache entries, and removes VFS mount points. }
procedure InvalidateVolume(vol : PStorage_Volume); function InvalidateVolume(vol : PStorage_Volume) : boolean;
{ VFS Functions } { VFS Functions }
function newVirtualDirectory(Path : pchar) : TError; function newVirtualDirectory(Path : pchar) : TError;
@@ -149,6 +149,7 @@ implementation
uses uses
driver.storage.fs.mgr, driver.storage.fs.mgr,
driver.storage.fs.fat32,
proc.mgr, proc.mgr,
proc.types, proc.types,
io.stdio, io.stdio,
@@ -508,7 +509,36 @@ end;
function GetObjectFromPath(path : pchar) : PVFSObject; forward; function GetObjectFromPath(path : pchar) : PVFSObject; forward;
function GetObjectFromPathEx(path : pchar; var volRelPath : 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 var
pCount : uint32; pCount : uint32;
pi : uint32; pi : uint32;
@@ -522,7 +552,12 @@ var
child : PVFSObject; child : PVFSObject;
begin begin
debug.tracer.push_trace('driver.storage.vfs.InvalidateVolume.enter'); debug.tracer.push_trace('driver.storage.vfs.InvalidateVolume.enter');
InvalidateVolume := false;
if vol = nil then exit; 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 } { 1. Evict all directory cache entries for this volume }
DirCache_Invalidate(vol, nil); DirCache_Invalidate(vol, nil);
@@ -575,6 +610,10 @@ begin
kfree(void(bootObj)); kfree(void(bootObj));
end; 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'); debug.tracer.push_trace('driver.storage.vfs.InvalidateVolume.exit');
end; end;
@@ -1313,6 +1352,17 @@ begin
{ Cache per-file metadata and populate DataSize via FS open hook } { Cache per-file metadata and populate DataSize via FS open hook }
if (vol^.filesystem <> nil) and (vol^.filesystem^.openFileCallback <> nil) then begin if (vol^.filesystem <> nil) and (vol^.filesystem^.openFileCallback <> nil) then begin
fd^.FSPrivate := vol^.filesystem^.openFileCallback(vol, dir, fname, fd^.DataSize); 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 if vol^.filesystem^.closeFileCallback <> nil then
fd^.FSCloseHook := pointer(vol^.filesystem^.closeFileCallback); fd^.FSCloseHook := pointer(vol^.filesystem^.closeFileCallback);
if Error <> nil then Error^ := eNone; if Error <> nil then Error^ := eNone;
@@ -1599,6 +1649,19 @@ begin
if (vol^.filesystem <> nil) and (vol^.filesystem^.openFileCallback <> nil) then begin if (vol^.filesystem <> nil) and (vol^.filesystem^.openFileCallback <> nil) then begin
fd^.FSPrivate := vol^.filesystem^.openFileCallback(vol, dir, fname, fd^.DataSize); 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 if vol^.filesystem^.closeFileCallback <> nil then
fd^.FSCloseHook := pointer(vol^.filesystem^.closeFileCallback); fd^.FSCloseHook := pointer(vol^.filesystem^.closeFileCallback);
if Error <> nil then Error^ := eNone; 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, io.syslog,
debug.tracer, debug.tracer,
core.util, arch.x86.util, core.util, arch.x86.util,
driver.storage.vol.mbr,
driver.storage.vol.mgr; driver.storage.vol.mgr;
type type
@@ -763,13 +764,38 @@ end;
function identify_volume(volume : PStorage_Volume) : boolean; function identify_volume(volume : PStorage_Volume) : boolean;
var var
buffer : puint32; buffer : puint32;
bufSize : uint32; bufSize : uint32;
mbr : PMaster_Boot_Record;
freeMbr : boolean;
slot : sint32;
i : uint32;
begin begin
push_trace('driver.storage.fs.flatfs.identify_volume'); push_trace('driver.storage.fs.flatfs.identify_volume');
identify_volume := false; identify_volume := false;
if volume^.device^.dispatchRead = nil then exit; 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; bufSize := volume^.device^.sectorSize;
if bufSize < 512 then bufSize := 512; if bufSize < 512 then bufSize := 512;
buffer := puint32(kalloc(bufSize)); buffer := puint32(kalloc(bufSize));
@@ -783,6 +809,7 @@ begin
end; end;
kfree(buffer); kfree(buffer);
if freeMbr then kfree(void(mbr));
end; end;
function readDirectoryEntries(volume : PStorage_Volume; directory : pchar; status : PuInt32) : PLinkedListBase; 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