// Copyright 2021 Aaron Hance // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. { Driver->Storage->VolumeManager Drive volume manager @author(Aaron Hance ah@aaronhance.me) } unit volumemanager; interface uses util, console, terminal, strings, lists, tracer, lmemorymanager, vfs, MBR, storagetypes; type TDirectory_Entry_Type = (directoryEntry, fileEntry, mountEntry); PDirectory_Entry = ^TDirectory_Entry; PFilesystem = ^TFilesystem; PStorage_Volume = ^TStorage_Volume; PPWriteHook = procedure(volume : PStorage_volume; directory : pchar; entry : PDirectory_Entry; byteCount : uint32; buffer : puint32; statusOut : puint32); PPReadHook = function(volume : PStorage_Volume; directory : pchar; fileName : pchar; fileExtension : pchar; buffer : puint32; bytecount : puint32) : uint32; PPHIOHook = procedure(volume : PStorage_device; addr : uint32; sectors : uint32; buffer : puint32); PPCreateHook = procedure(volume : PStorage_volume; start : uint32; size : uint32; config : puint32); 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; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error PPHIOHook_ = procedure; TFilesystem = record sName : pchar; system_id : uint8; writeCallback : PPWriteHook; readCallback : PPReadHook; createCallback : PPCreateHook; //create volume detectCallback : PPDetectHook; //detect volume createDirCallback : PPCreateDirHook; readDirCallback : PPReadDirHook; end; TStorage_Volume = record device : PStorage_device; sectorStart : uint32; sectorSize : uint32; sectorCount : uint32; freeSectors : uint32; filesystem : PFilesystem; { True if this drive contains the loaded OS } isBootDrive : boolean; end; { Generic directory entry } TDirectory_Entry = record { Contains filename and optionally file extension, seperated by the last period.} fileName : pchar; entryType : TDirectory_Entry_Type; // creationT : TDateTime; // modifiedT : TDateTime; end; var storageVolumes : PLinkedListBase; filesystems : PlinkedListBase; procedure init(); procedure register_filesystem(filesystem : PFilesystem); procedure check_for_volumes(drive : PStorage_device); procedure create_volume(disk : PStorage_device; filesystem : PChar; sectorStart : uint32; sectorCount : uint32); //IO functions implementation procedure format_volume_command(params : PParamList); var volume : PStorage_Volume; filesystem : PFilesystem; config : puint32; begin push_trace('VolumeManager.format_volume_command'); console.writestringlnWND('Not implimented, use disk format', getTerminalHWND()); exit; if paramCount(params) < 3 then begin console.writestringlnWND('Invalid arguments', getTerminalHWND()); end; push_trace('VolumeManager.format_volume_command.1'); volume := PStorage_volume(LL_get(storageVolumes, stringToInt(getParam(1, params)))); filesystem := PFilesystem(LL_get(filesystems, stringToInt(getParam(2, params)))); push_trace('VolumeManager.format_volume_command.2'); config := PuInt32(kalloc(sizeof(uInt32))); config^ := 4; push_trace('VolumeManager.format_volume_command.3'); //TODO check things exsist console.writestringlnWND('Staring volume formatting...', getTerminalHWND()); // filesystem^.formatVolumeCallback(volume^.device, volume^.sectorSize, volume^.sectorStart, config); console.writestringlnWND('Volume format finished.', getTerminalHWND()); end; procedure list_filesystems_command(); var i : uint32; filesystem : PFilesystem; begin push_trace('VolumeManager.list_filesystems_command'); if LL_Size(filesystems) < 1 then begin console.writestringlnWnd('ERROR: no filesystems found!', getTerminalHWND()); exit; end; console.writestringlnWnd('Filesystems: ', getTerminalHWND()); for i:=0 to LL_Size(filesystems) - 1 do begin filesystem := PFilesystem(LL_Get(filesystems, i)); console.writestringWnd(' ', getTerminalHWND()); console.writestringWnd(filesystem^.sName, getTerminalHWND()); console.writestringWnd(' - ', getTerminalHWND()); console.writehexlnWND(filesystem^.system_id, getTerminalHWND()); end; end; procedure list_volume_command(); var i : uint32; volume : PStorage_Volume; begin push_trace('VolumeManager.list_volume_command'); if LL_Size(storageVolumes) < 1 then begin console.writestringlnWnd('No volumes found.', getTerminalHWND()); exit; end; console.writestringlnWnd('Volumes: ', getTerminalHWND()); for i:=0 to LL_Size(storageVolumes) - 1 do begin volume := PStorage_Volume(LL_Get(storageVolumes, i)); console.writestringWnd(' ', getTerminalHWND()); console.writeintWnd(i, getTerminalHWND()); console.writestringWnd(' - Capacity: ', getTerminalHWND()); console.writeintWnd(uint32((volume^.sectorSize * volume^.sectorCount) DIV 1024 DIV 1024), getTerminalHWND()); console.writestringWnd('MB, Filesystem: ', getTerminalHWND()); if volume^.filesystem <> nil then begin console.writestringlnWnd(volume^.filesystem^.sName, getTerminalHWND()); end else begin console.writestringlnWnd('None', getTerminalHWND()); end; end; end; procedure volume_command(params : PParamList); var i : uint16; subcmd : pchar; begin push_trace('VolumeManager.volume_command'); subcmd:= getParam(0, params); if ((paramCount(params) = 0)) then begin console.writestringlnWnd('Please provide valid arguments.', getTerminalHWND()); console.writestringlnWnd(' ls - for listing all volumes', getTerminalHWND()); console.writestringlnWnd(' lsfs - for listing all filesystems', getTerminalHWND()); console.writestringlnWnd(' info [volume] - display formation for specified volume', getTerminalHWND()); console.writestringlnWnd(' format [volume] [filesystem] - destructive, formats a volume with a specified filesystem', getTerminalHWND()); exit; end; if( stringEquals(subcmd, 'ls') ) then begin list_volume_command(); exit; end; if( stringEquals(subcmd, 'lsfs') ) then begin list_filesystems_command(); exit; end; if( stringEquals(subcmd, 'format') ) then begin format_volume_command(params); exit; end; end; { Initialise volume manager } procedure init(); begin push_trace('VolumeManager.init'); { setup lists } storageVolumes:= ll_New(sizeof(TFilesystem)); filesystems:= ll_New(sizeof(TFilesystem)); terminal.registerCommand('volume', @volume_command, 'Volume utility'); end; { register a new type of filesystem } procedure register_filesystem(filesystem : PFilesystem); //TODO on init create null filesystem for empty partition var elm : void; begin //add filesystem elm := LL_add(filesystems); memcpy(uint32(filesystem), uInt32(elm), SizeOf(TFilesystem)); //check drives for volumes of new type end; { Check for volumes on a physical device } procedure check_for_volumes(drive : PStorage_device); var bootrecord : PMaster_Boot_Record; storageVolume : TStorage_Volume; i : uint32 = 0; elm : void; begin push_trace('VolumeManager.check_for_volumes'); bootrecord := PMaster_Boot_Record(kalloc(SizeOf(TMaster_Boot_Record))); drive^.readCallback(drive, 0, 1, puint32(bootrecord)); //TODO multiple drives //TODO multipe partition entries if bootrecord^.partition[0].LBA_start <> 0 then begin //set volume information storageVolume.device := drive; storageVolume.sectorStart := bootrecord^.partition[0].LBA_start; storageVolume.sectorCount := bootrecord^.partition[0].sector_count; storageVolume.sectorSize := drive^.sectorSize; storageVolume.freeSectors := 0; //TODO impliment storageVolume.filesystem := nil; if LL_Size(filesystems) < 1 then begin console.writestringln('Failed to initalise storage system, no filesystems found, stopping!'); exit; end; //check for filesystem type for i:=0 to LL_Size(filesystems) - 1 do begin if bootrecord^.partition[0].system_id = PFilesystem(LL_Get(filesystems, i))^.system_id then begin storageVolume.filesystem := PFilesystem(LL_Get(filesystems, i)); end; end; push_trace('VolumeManager.init3'); //add volume to list elm := LL_Add(storageVolumes); memcpy(uint32(@storageVolume), uint32(elm), SizeOf(TStorage_Volume)); push_trace('VolumeManager.init4'); elm := LL_Add(drive^.volumes); memcpy(uint32(@storageVolume), uint32(elm), SizeOf(TStorage_Volume)); end; end; procedure remove_volume(list : PLinkedListBase; volume : PStorage_Volume); var i : uint32; elm : void; begin for i:=0 to LL_Size(list) - 1 do begin elm := LL_Get(list, i); if (PStorage_Volume(elm)^.device = volume^.device) and (PStorage_Volume(elm)^.sectorStart = volume^.sectorStart) then begin LL_Delete(list, i); break; end; end; end; procedure create_volume(disk : PStorage_device; filesystem : PChar; sectorStart : uint32; sectorCount : uint32); var volume : PStorage_Volume; elm : void; i : uint16; config : PuInt32; begin push_trace('VolumeManager.create_volume'); volume := PStorage_Volume(kalloc(SizeOf(TStorage_Volume))); volume^.device := disk; volume^.sectorStart := sectorStart; volume^.sectorCount := sectorCount - 10; volume^.sectorSize := disk^.sectorSize; volume^.freeSectors := 0; //setup by filesystem //find filesystem for i:=0 to LL_Size(filesystems) - 1 do begin if stringEquals(filesystem, PFilesystem(LL_Get(filesystems, i))^.sName) then begin volume^.filesystem := PFilesystem(LL_Get(filesystems, i)); end; end; //TODO CHECK FILESYSTEM EXSISTS //format volume volume^.filesystem^.createCallback(volume, sectorCount, sectorStart, config); push_trace('VolumeManager.create_volume.2'); //remove volume from list of volumes remove_volume(storageVolumes, volume); //add volume to list elm := LL_Add(storageVolumes); memcpy(uint32(volume), uint32(elm), SizeOf(TStorage_Volume)); push_trace('VolumeManager.create_volume.3'); //remove volume from list of volumes on device remove_volume(disk^.volumes, volume); //add volume to device elm := LL_Add(disk^.volumes); memcpy(uint32(volume), uint32(elm), SizeOf(TStorage_Volume)); //free memory kfree(puint32(volume)); end; procedure register_volume(device : PStorage_Device; volume : PStorage_Volume); var elm : void; begin push_trace('storagemanagement.register_volume'); elm := LL_Add(device^.volumes); PStorage_volume(elm)^:= volume^; // if rootVolume = PStorage_Volume(0) then rootVolume:= volume; end; end.