fix(storage): harden disk operations, add volume labels, and stabilize AHCI discovery

This commit is contained in:
Aaron committed 2026-10-31 16:30:40 +00:00
1 parent 7e99bc2db1
commit d1ecd3fc41
18 files changed
+1563 -1493

No files matched your search

+325 -175
View File
File diff suppressed because it is too large. Load diff
+17 -8
View File
@@ -288,7 +288,9 @@ begin
if bootrecord <> nil then begin
if bootrecord^.boot_sector <> $AA55 then begin
println('No valid mbr found, initialising disk...');
driver.storage.vol.mgr.init_disk(device);
if driver.storage.vol.mgr.init_disk(device) <> eNone then begin
println('Error: disk initialization failed.'); exit;
end;
end;
kfree(puint32(bootrecord));
end;
@@ -335,7 +337,9 @@ begin
driver.storage.vol.mbr.setup_partition(@part, lba, sectors);
part.system_id := 0;
driver.storage.vol.mgr.add_partition(device, slot, part);
if driver.storage.vol.mgr.add_partition(device, slot, part) <> eNone then begin
println('Error: partition creation failed.'); exit;
end;
print('Partition added in slot ');
printint(slot);
@@ -407,7 +411,9 @@ begin
exit;
end;
driver.storage.vol.mgr.remove_partition(device, slot);
if driver.storage.vol.mgr.remove_partition(device, slot) <> eNone then begin
println('Error: partition removal failed.'); exit;
end;
print('Partition slot ');
printint(slot);
@@ -523,11 +529,14 @@ begin
exit;
end;
vol^.filesystem := fs;
fs^.createCallback(vol, vol^.sectorCount, vol^.sectorStart, nil);
{ Re-probe filesystem so identify picks up the new signature }
driver.storage.fs.mgr.probe_volume(vol);
i := 0;
while i < driver.storage.vol.mgr.get_volume_count() do begin
if driver.storage.vol.mgr.get_volume(i) = vol then break;
Inc(i);
end;
if not driver.storage.vol.mgr.format_volume(device, i, fsName, nil) then begin
println('Error: format failed; the volume was not reported ready.'); exit;
end;
print('Partition ');
printint(slot);
+13
View File
@@ -271,6 +271,7 @@ procedure fpc_shortstr_assign(len: longint; sstr, dstr: Pointer); compilerproc;
any int64/uint64 arithmetic occurs (e.g. sint32 * uint32 promotion).
Must live in the system unit so the compiler can resolve it. }
function fpc_mul_int64(f1, f2: int64): int64; compilerproc;
function fpc_mul_dword_to_qword(f1, f2: uint32): qword; compilerproc;
function fpc_div_int64(d, n: int64): int64; compilerproc;
function fpc_mod_int64(d, n: int64): int64; compilerproc;
function fpc_mod_qword(d, n: qword): qword; compilerproc;
@@ -523,6 +524,18 @@ begin
fpc_mul_int64 := res;
end;
function fpc_mul_dword_to_qword(f1, f2: uint32): qword; [public, alias: 'FPC_MUL_DWORD_TO_QWORD']; compilerproc;
var res: qword;
begin
asm
MOV EAX, f1
MUL f2
MOV DWORD [res], EAX
MOV DWORD [res+4], EDX
end;
fpc_mul_dword_to_qword := res;
end;
{ ---------- 64-bit division / modulo ---------- }
{ Helper: unsigned 64÷64→64 using shift-subtract (binary long division).
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
+13 -2
View File
@@ -154,7 +154,8 @@ type
sleVolumeInvalidated,
sleVolumeRemoved,
sleDeviceAdded,
sleDeviceRemoved
sleDeviceRemoved,
sleVolumeLabelChanged
);
TStorageLifecycleCallback = procedure(event : TStorageLifecycleEvent;
@@ -163,6 +164,8 @@ type
error : TError;
userdata : pointer);
TVolumeLabel = array[0..32] of char; { cached, NUL-terminated display name }
TFSFormatParams = record
Version : uint16;
Flags : uint16;
@@ -186,13 +189,15 @@ type
TIOCallback = procedure(error : TError; userdata : pointer);
{ Filesystem callback core.types }
PPCreateHook = procedure(volume : PStorage_volume; start : uint32; size : uint32; config : PFSFormatParams);
PPCreateHook = function(volume : PStorage_volume; sectors : uint32; start : uint32; config : PFSFormatParams) : TError;
PPCreateAsyncHook = procedure(volume : PStorage_volume; start : uint32; size : uint32; config : PFSFormatParams; callback : TIOCallback; callbackData : pointer);
PPDetectHook = procedure(disk : PStorage_Device);
PPCreateDirHook = procedure(volume : PStorage_volume; directory : pchar; dirname : pchar; attributes : uint32; status : puint32);
PPReadDirHook = function(volume : PStorage_volume; directory : pchar; status : puint32) : PLinkedListBase;
PPDeleteFileHook = procedure(volume : PStorage_Volume; filePath : pchar; status : puint32);
PPDeleteDirHook = procedure(volume : PStorage_Volume; path : pchar; status : puint32);
PPValidateLabelHook = function(text : pchar; var normalized : TVolumeLabel) : TError;
PPSetLabelHook = function(volume : PStorage_Volume; text : pchar) : TError;
PPIdentifyHook = function(volume : PStorage_Volume) : boolean;
PPRenameFileHook = procedure(volume : PStorage_Volume; filePath : pchar; newName : pchar; status : puint32);
{ Offset-based read: reads byteCount bytes starting at byte offset into the file.
@@ -271,6 +276,8 @@ type
maxSectorCount : uint32;
sectorSize : uint32; //in bytes
start : uint32; //start of device in sectors
metadataBusy : boolean; { volume/partition operation owns device }
mbrWriteBusy : boolean; { serializes staged sector-zero writes }
cachedMBR : pointer; { cached mbr data, nil if not yet read }
isBootDevice : boolean; { true if this is the device GRUB booted from }
end;
@@ -280,6 +287,7 @@ type
sName : pchar;
system_id : uint8;
createCallback : PPCreateHook;
validateFormat : PPCreateHook; { pure preflight, before invalidation }
detectCallback : PPDetectHook;
createDirCallback : PPCreateDirHook;
readDirCallback : PPReadDirHook;
@@ -304,6 +312,8 @@ type
openFileCallback : PPOpenFileHook;
closeFileCallback : PPCloseFileHook;
formatParamFlags : uint32;
validateLabelCallback : PPValidateLabelHook;
setLabelCallback : PPSetLabelHook;
end;
{ Generic storage volume }
@@ -317,6 +327,7 @@ type
filesystem : PFilesystem;
isBootDrive : boolean;
lifecycleFlags : uint32;
labelText : TVolumeLabel;
fsPrivate : pointer; { FS-driver private data (e.g. PFATCache for FAT32) }
end;
+25 -5
View File
@@ -134,6 +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. }
function VolumeHasOpenHandles(vol : PStorage_Volume) : boolean;
function InvalidateVolume(vol : PStorage_Volume) : boolean;
{ VFS Functions }
@@ -509,7 +510,7 @@ end;
function GetObjectFromPath(path : pchar) : PVFSObject; forward;
function GetObjectFromPathEx(path : pchar; var volRelPath : pchar) : PVFSObject; forward;
function volumeHasAsyncHandles(vol : PStorage_Volume) : boolean;
function volumeHasHandles(vol : PStorage_Volume; asyncOnly : boolean) : boolean;
var
pCount : uint32;
pi : uint32;
@@ -517,7 +518,7 @@ var
pCtx : proc.types.PProcessContext;
tbl : PFDTable;
begin
volumeHasAsyncHandles := false;
volumeHasHandles := false;
if vol = nil then exit;
pCount := proc.mgr.processCount();
@@ -530,14 +531,17 @@ begin
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;
and ((not asyncOnly) or (tbl^.Entries[fi].AsyncRefs > 0)) then begin
volumeHasHandles := true;
exit;
end;
end;
end;
end;
function VolumeHasOpenHandles(vol : PStorage_Volume) : boolean;
begin VolumeHasOpenHandles := volumeHasHandles(vol,false); end;
function InvalidateVolume(vol : PStorage_Volume) : boolean;
var
pCount : uint32;
@@ -554,7 +558,7 @@ begin
debug.tracer.push_trace('driver.storage.vfs.InvalidateVolume.enter');
InvalidateVolume := false;
if vol = nil then exit;
if volumeHasAsyncHandles(vol) then begin
if volumeHasHandles(vol,true) then begin
debug.tracer.push_trace('driver.storage.vfs.InvalidateVolume.busy');
exit;
end;
@@ -986,6 +990,7 @@ begin
volumeGetDirectories := nil;
if vol = nil then exit;
if (vol^.device = nil) or vol^.device^.metadataBusy or driver.storage.vol.mgr.volume_is_busy(vol) then exit;
if vol^.filesystem = nil then exit;
if vol^.filesystem^.readDirCallback = nil then exit;
@@ -1153,6 +1158,15 @@ begin
exit;
end;
mountObj := PVFSObject(core.ds.hashmap.get(ht, mountName));
if mountObj <> nil then begin
if (mountObj^.ObjectType = otDRIVE) and (mountObj^.Reference = void(volume)) then begin
mountVolume := pvRegistered;
driver.storage.vol.mgr.volume_set_mounted(volume, true);
end;
kfree(void(parentPath)); STRLL_Free(splitPath); exit;
end;
{ Create the mount object storing the volume pointer }
mountObj := PVFSObject(kalloc(sizeof(TVFSObject)));
@@ -1219,6 +1233,12 @@ begin
end;
volOut^ := PStorage_Volume(Obj^.Reference);
if (volOut^ = nil) or (volOut^^.device = nil) then begin
if volRel <> nil then kfree(void(volRel)); exit;
end;
if volOut^^.device^.metadataBusy or driver.storage.vol.mgr.volume_is_busy(volOut^) then begin
if volRel <> nil then kfree(void(volRel)); volOut^ := nil; exit;
end;
{ volRel is the path within the volume, e.g. '/mydir/myfile.txt' }
if (volRel = nil) or StringEquals(volRel, '/') then begin
File diff suppressed because it is too large. Load diff
@@ -392,6 +392,15 @@ end;
{ ==================== Filesystem callbacks ==================== }
procedure cacheVolumeLabel(volume : PStorage_Volume; pvd : PPVD);
var n, i : uint32;
begin
memset(uint32(@volume^.labelText[0]),0,sizeof(volume^.labelText));
n := 32;
while (n > 0) and ((pvd^.volumeId[n-1] = ' ') or (pvd^.volumeId[n-1] = #0)) do Dec(n);
i := 0; while i < n do begin volume^.labelText[i] := pvd^.volumeId[i]; Inc(i); end;
end;
{ Identify: check for 'CD001' at sector 16 }
function identify_volume(volume : PStorage_Volume) : boolean;
var
@@ -405,6 +414,7 @@ begin
buf := readSectors(volume^.device, volume^.sectorStart + PVD_SECTOR, 1);
if buf = nil then exit;
identify_volume := isPVD(buf);
if identify_volume then cacheVolumeLabel(volume,PPVD(buf));
kfree(buf);
end;
@@ -437,6 +447,7 @@ begin
volume^.filesystem := @filesystem;
volume^.isBootDrive := disk^.isBootDevice;
cacheVolumeLabel(volume,pvd);
driver.storage.vol.mgr.register_volume(disk, volume);
kfree(buf);
@@ -94,10 +94,7 @@ end;
function fatMaxClusterCount(bootRecord : PBootRecord) : uint32;
begin
if (bootRecord = nil) or (bootRecord^.sectorSize = 0) then
fatMaxClusterCount := 0
else
fatMaxClusterCount := (bootRecord^.FATSize * bootRecord^.sectorSize) div 4;
fatMaxClusterCount := FAT32ClusterLimit(bootRecord);
end;
function fatClustersForBytes(byteCount : uint32; bytesPerCluster : uint32) : uint32;
@@ -391,7 +388,9 @@ begin
sectorBuf := puint32(kalloc(bufSize));
if sectorBuf = nil then exit;
memset(uint32(sectorBuf), 0, bufSize);
driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart, 1, sectorBuf);
if driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart, 1, sectorBuf) <> eNone then begin
kfree(sectorBuf); exit;
end;
bootRecord := PBootRecord(sectorBuf)^;
kfree(sectorBuf);
readBootRecordRaw := true;
@@ -699,9 +698,13 @@ begin
{ Keep runtime addressing compatible with the legacy formatter and metadata paths. }
ctx^.Info.DataStart := volume^.sectorStart + ctx^.Info.BootRecord.rsvSectors +
ctx^.Info.BootRecord.FATSize;
uint32(ctx^.Info.BootRecord.numFats) * ctx^.Info.BootRecord.FATSize;
ctx^.Info.BytesPerCluster := uint32(ctx^.Info.BootRecord.spc) * uint32(ctx^.Info.BootRecord.sectorSize);
ctx^.Info.MaxCluster := fatMaxClusterCount(@ctx^.Info.BootRecord);
if (ctx^.Info.MaxCluster <= 2) or
(ctx^.Info.BootRecord.manySectors > volume^.sectorCount) then begin
fatCtxFree(ctx); exit;
end;
ctx^.InfoValid := true;
ctx^.Cache.Data := puint32(kalloc(FAT_CACHE_LINES * 512));
@@ -1135,6 +1138,7 @@ begin
end;
if dir^.fileName[0] = char($E5) then
continue;
if (dir^.attributes and $08) <> 0 then continue; { volume labels and LFN entries are not files }
if compareByteArray8(dir^.fileName, cleanName) and matchExtension(dir^.fileExtension, ext) then begin
findEntryInSector := integer(idx);
exit;
@@ -2048,7 +2052,7 @@ begin
end;
if dir^.fileName[0] = char($E5) then
continue;
if dir^.attributes = $0F then
if (dir^.attributes and $08) <> 0 then
continue;
if fatIsDotEntry(dir) then
continue;
File diff suppressed because it is too large. Load diff
@@ -16,6 +16,7 @@ uses
io.syslog,
core.util,
driver.storage.fs.fat32.vol,
driver.storage.fs.fat32.labels,
driver.storage.fs.fat32.core,
driver.storage.fs.fat32.transfer;
@@ -36,7 +37,8 @@ begin
filesystem.readDirCallback := @FAT32ReadDirectory;
filesystem.createDirCallback := @FAT32CreateDirectory;
filesystem.createCallback := @create_volume;
filesystem.createAsyncCallback := @create_volume_async;
filesystem.validateFormat := @validate_format;
filesystem.createAsyncCallback := nil; { volume manager runs checked formatter in a worker }
filesystem.detectCallback := @detect_volumes;
filesystem.readOffsetCallback := @FAT32ReadFileAtOffset;
filesystem.writeOffsetCallback := @FAT32WriteFileAtOffset;
@@ -51,7 +53,9 @@ begin
filesystem.closeFileCallback := @FAT32CloseFile;
filesystem.createDirAsyncCallback := nil;
filesystem.readDirAsyncCallback := nil;
filesystem.formatParamFlags := FS_FORMAT_PARAM_CLUSTER_SIZE;
filesystem.formatParamFlags := FS_FORMAT_PARAM_CLUSTER_SIZE or FS_FORMAT_PARAM_LABEL;
filesystem.validateLabelCallback := @normalize_label;
filesystem.setLabelCallback := @write_label;
FAT32VolInit(@filesystem);
driver.storage.fs.mgr.register_filesystem(@filesystem);
@@ -224,6 +224,28 @@ type
WriteRefs : array[0..FAT_OPEN_HANDLE_SLOTS - 1] of TFATWriteRef;
end;
{ Exclusive cluster bound, constrained by both data area and FAT capacity. }
function FAT32ClusterLimit(br : PBootRecord) : uint32;
implementation
function FAT32ClusterLimit(br : PBootRecord) : uint32;
var overhead, total, capacity, limit : uint64;
begin
FAT32ClusterLimit := 0;
if br = nil then exit;
if (br^.sectorSize <> 512) or (br^.spc = 0) or (br^.spc > 128) or
((br^.spc and (br^.spc - 1)) <> 0) or (br^.rsvSectors = 0) or
(br^.numFats = 0) or (br^.FATSize = 0) then exit;
total := br^.manySectors;
if total = 0 then total := br^.numSectors;
overhead := uint64(br^.rsvSectors) + uint64(br^.numFats) * br^.FATSize;
if overhead >= total then exit;
limit := (total - overhead) div br^.spc + 2;
capacity := uint64(br^.FATSize) * (br^.sectorSize div 4);
if (limit > capacity) or (limit > $0FFFFFF0) then exit;
FAT32ClusterLimit := uint32(limit);
end;
end.
File diff suppressed because it is too large. Load diff
@@ -86,7 +86,7 @@ procedure set_bootable(partition_table : PPartition_table);
begin
push_trace('driver.storage.vol.mbr.set_bootable');
//set the bootble bit in attributes
partition_table^.attributes := (partition_table^.attributes and $80);
partition_table^.attributes := (partition_table^.attributes or $80);
end;
{ Check a partitions bootable bit }
@@ -105,7 +105,7 @@ begin
partition_table^.LBA_start := address;
partition_table^.sector_count := sectorSize;
partition_table^.CHS_start := LBA_2_CHS(address);
partition_table^.CHS_start := LBA_2_CHS(address + sectorSize);
partition_table^.CHS_end := LBA_2_CHS(address + sectorSize - 1);
push_trace('driver.storage.vol.mbr.setup_partition.end');
end;
File diff suppressed because it is too large. Load diff
+4
View File
@@ -12,6 +12,7 @@ implementation
uses
core.strings,
app.diskutil,
driver.bus.usb.types,
driver.bus.usb.core,
driver.bus.usb.uhci,
@@ -32,6 +33,7 @@ uses
core.ds.prio,
driver.storage.vfs,
driver.storage.test,
driver.storage.metatest,
core.enc.fnv1a,
core.enc.djb2,
core.ds.bloom,
@@ -46,6 +48,7 @@ procedure runAllTests();
begin
{ Run unit tests }
core.strings.UnitTest;
app.diskutil.UnitTest;
driver.bus.usb.types.UnitTest;
driver.bus.usb.core.UnitTest;
driver.bus.usb.uhci.UnitTest;
@@ -66,6 +69,7 @@ begin
core.ds.prio.UnitTest;
driver.storage.vfs.UnitTest;
driver.storage.test.UnitTest;
driver.storage.metatest.UnitTest;
core.enc.fnv1a.UnitTest;
core.enc.djb2.UnitTest;
core.ds.bloom.UnitTest;