// 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->driver.storage.fs.flatfs super simple flat filesystem @author(Aaron Hance ah@aaronhance.me) } unit driver.storage.fs.flatfs; interface uses boot.mgr, driver.storage.fs.mgr, core.ds.lists, memory.heap, io.stdio, driver.storage.mgr, driver.storage.types, core.strings, io.syslog, debug.tracer, core.util, arch.x86.util, driver.storage.vol.mbr, driver.storage.vol.mgr; type TDisk_Info = bitpacked record jmp2boot: ubit24; OEMName: array[0..7] of char; version: uint16; sectorCount : uint16; fileCount : uint16; signature : uint32; end; PDisk_Info = ^TDisk_Info; TFile_Entry = bitpacked record attribues : uint8; // 0x00 = does not exsist, 0x01 = file, 0x02 = hidden, 0x04 = system/read only, 0x10 = directory, 0x20 = archive name: array[0..53] of char; //max file name length is 54 rsv : uint8; size: uint32; start : uint32; end; PFile_Entry = ^TFile_Entry; var filesystem : TFilesystem; procedure init; function create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : PFSFormatParams) : TError; procedure detect_volumes(disk : PStorage_Device); function read_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32) : PLinkedListBase; procedure write_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32); procedure write_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; size : uint32; status : PuInt32); procedure read_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; status : PuInt32); implementation function validate_format(volume : PStorage_Volume; sectors, start : uint32; config : PFSFormatParams) : TError; begin validate_format := eInvalidArgument; if (volume = nil) or (volume^.device = nil) then exit; if not volume^.device^.writable then begin validate_format := eReadOnly; exit; end; { Legacy FlatFS readers use 512-byte sectors and exactly 1000 entries. } if (volume^.device^.sectorSize <> 512) or (sectors > 65535) or (sectors <= 126) or (start <> volume^.sectorStart) or (sectors <> volume^.sectorCount) or (start >= volume^.device^.maxSectorCount) or (sectors > volume^.device^.maxSectorCount-start) then exit; if config <> nil then begin if (config^.Version <> FS_FORMAT_PARAMS_VERSION_1) or ((config^.Flags and (not FS_FORMAT_PARAM_FILE_COUNT)) <> 0) then exit; if ((config^.Flags and FS_FORMAT_PARAM_FILE_COUNT) <> 0) and (config^.FileCount <> 1000) then exit; end; validate_format := eNone; end; function create_volume(volume : PStorage_Volume; sectors : uint32; start : uint32; config : PFSFormatParams) : TError; var info : PDisk_Info; entries : PFile_Entry; err : TError; begin create_volume := validate_format(volume, sectors, start, config); if create_volume <> eNone then exit; info := PDisk_Info(kalloc(512)); entries := PFile_Entry(kalloc(125*512)); if (info = nil) or (entries = nil) then begin if info <> nil then kfree(void(info)); if entries <> nil then kfree(void(entries)); create_volume := eOutOfMemory; exit; end; memset(uint32(info), 0, 512); memset(uint32(entries), 0, 125*512); err := storage_write(volume^.device, start, 1, puint32(info)); if err = eNone then begin entries[0].attribues := $10; memcpy(uint32(pchar('/system')), uint32(@entries[0].name[0]), 7); entries[1].attribues := $10; memcpy(uint32(pchar('/programs')), uint32(@entries[1].name[0]), 9); entries[2].attribues := $10; memcpy(uint32(pchar('/user')), uint32(@entries[2].name[0]), 5); err := storage_write(volume^.device, start+1, 125, puint32(entries)); end; if err = eNone then begin memcpy(uint32(pchar('ASURO ')), uint32(@info^.OEMName[0]), 8); info^.version := 1; info^.sectorCount := sectors; info^.fileCount := 1000; info^.signature := $0B00B1E5; err := storage_write(volume^.device, start, 1, puint32(info)); end; kfree(void(entries)); kfree(void(info)); create_volume := err; end; procedure write_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32); var directories : PLinkedListBase; i : uint32; fileEntries : PFile_Entry; fileEntry : PFile_Entry; buffer : PuInt32; position : uint32; offset : uint32; const fileCount : uint32 = 1000; begin status^ := 0; //directories := read_directory(volume, directory, status); fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount + 512); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, (sizeof(TFile_Entry) * fileCount) div 512, PuInt32(fileEntries)); if status^ = 0 then begin for i := 0 to fileCount - 1 do begin push_trace('write_directory_2'); //fileEntry := PFile_Entry(STRLL_Get(directories, i)); fileEntry := @fileEntries[i]; if fileEntry^.attribues = 0 then begin fileEntry^.attribues := $10; fileEntry^.size := 0; fileEntry^.start := 0; memcpy(uint32(directory), uint32(@fileEntry^.name), stringSize(directory)); push_trace('write_directory_3'); //write file table buffer := PuInt32(Kalloc(513)); memset(uint32(buffer), 0, 512); push_trace('write_directory.b4_read'); //read file table cointaining entry of interest position := (i * sizeof(TFile_Entry)) div 512; driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1 + position, 1, buffer); push_trace('write_directory.after_read'); //calculate entry offset into sector offset := (i * sizeof(TFile_Entry)) mod 512; offset := offset div sizeof(TFile_Entry); //set entry in buffer PFile_Entry(buffer)[offset] := fileEntry^; //write updated file table sector driver.storage.mgr.storage_write(volume^.device, volume^.sectorStart + 1 + position, 1, buffer); Kfree(buffer); push_trace('write_directory.after_write'); break; end; end; end; if fileEntry^.attribues = 0 then begin status^ := 1; end; end; function read_directory(volume : PStorage_Volume; directory : pchar; status : PuInt32) : PLinkedListBase; var directories : PLinkedListBase; fileEntrys : PFile_Entry; i : uint32; compString : PChar; afterString : PChar; slash : PChar; compStringLength : uint32; fileCount : uint32 = 1000; slashArray : array[0..1] of char = '/'; begin compString := pchar(kalloc(60)); memset(uint32(compString), 0, 60); afterString := pchar(kalloc(60)); memset(uint32(afterString), 0, 60); slash := stringNew(2); memcpy(uint32(@slashArray), uint32(slash), 1); directories := STRLL_New(); fileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntrys)); for i := 0 to fileCount - 1 do begin if uint32(fileEntrys[i].name[0]) = 0 then begin break; end; compString := stringSub(fileEntrys[i].name, 0, stringSize(directory)); afterString := stringSub(fileEntrys[i].name, stringSize(directory), stringSize(fileEntrys[i].name)-1); push_trace('read_directory_2'); if stringEquals(compString, directory) and (not stringContains(afterString, slash)) then begin STRLL_Add(directories, afterString); end; end; push_trace('read_directory_3'); Kfree(puint32(fileEntrys)); read_directory := directories; end; procedure quicksort_start(list : PFile_Entry; begining : uint32; stop : uint32); var pivot : uint32; i : uint32; j : uint32; temp : PLinkedListBase; tempFileEntry : PFile_Entry; begin tempFileEntry := PFile_Entry(Kalloc(sizeof(TFile_Entry))); memset(uint32(tempFileEntry), 0, sizeof(TFile_Entry)); i:=begining; j:=stop; pivot := list[(begining + stop) div 2].start; while i <= j do begin while list[i].start < pivot do begin i := i + 1; end; while list[j].start > pivot do begin j := j - 1; end; if i <= j then begin tempFileEntry^ := list[i]; list[i] := list[j]; list[j] := tempFileEntry^; i := i + 1; j := j - 1; end; end; if begining < j then begin quicksort_start(list, begining, j); end; if i < stop then begin quicksort_start(list, i, stop); end; Kfree(puint32(tempFileEntry)); end; function find_free_space(volume : PStorage_Volume; size : uint32; status : PuInt32) : uint32; var fileEntrys : PFile_Entry; confirmedFileEntrys : PFile_Entry; confirmedFileEntrysCount : uint32 = 0; fileEntry : PFile_Entry; i : uint32; fileCount : uint32 = 1000; baseAddress : uint32; begin fileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); confirmedFileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); memset(uint32(fileEntrys), 0, sizeof(TFile_Entry) * fileCount + 512); memset(uint32(confirmedFileEntrys), 0, sizeof(TFile_Entry) * fileCount + 512); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntrys)); baseAddress := volume^.sectorStart + 2 + ( (fileCount * sizeof(TFile_Entry)) div 512); for i := 0 to fileCount - 1 do begin if fileEntrys[i].attribues <> 0 then begin confirmedFileEntrys[confirmedFileEntrysCount] := fileEntrys[i]; confirmedFileEntrysCount := confirmedFileEntrysCount + 1; end; end; //sort file table by start address using quicksort quicksort_start(confirmedFileEntrys, 0, confirmedFileEntrysCount - 1); //find free space big enough to fit size //TODO: make this work for i := 0 to confirmedFileEntrysCount - 1 do begin if confirmedFileEntrys[i+1].attribues = 0 then begin if confirmedFileEntrys[i].size = 0 then begin find_free_space := baseAddress; break; end else begin find_free_space := confirmedFileEntrys[i].start + confirmedFileEntrys[i].size; break; end; break; end; if confirmedFileEntrys[i].size <> 0 then begin if confirmedFileEntrys[i+1].start - (confirmedFileEntrys[i].start + confirmedFileEntrys[i].size) > size+1 then begin find_free_space := confirmedFileEntrys[i].start + confirmedFileEntrys[i].size; status^ := 0; break; end; end; end; Kfree(puint32(fileEntrys)); Kfree(puint32(confirmedFileEntrys)); end; procedure write_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; size : uint32; status : PuInt32); var directories : PLinkedListBase; i : uint32; p : uint32; fileEntries : PFile_Entry; fileEntry : PFile_Entry; buffer : PuInt32; position : uint32; offset : uint32; fileCount : uint32 = 1000; exsists : boolean; elm : puint32; begin push_trace('driver.storage.fs.flatfs.write_file'); //load file table fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); //check if file exists else find free entry for i := 0 to fileCount - 1 do begin if stringEquals(fileEntries[i].name, fileName) then begin exsists := true; p:= i; break; end; if fileEntries[i].attribues = 0 then begin exsists := false; p:= i; break; end; end; //if entry is directory then exit if fileEntries[p].attribues and $10 = $10 then begin status^ := 1; exit; end; // //read file table cointaining entry of interest position := (p * sizeof(TFile_Entry)) div 512; //if file exists update entry else create new entry if exsists then begin //check if size is greater than current size if size > fileEntries[p].size then begin //find free space fileEntries[p].start := find_free_space(volume, size, status); fileEntries[p].size := size; end else begin //update size fileEntries[p].size := size; end; end else begin //find free space fileEntries[p].start := find_free_space(volume, size, status); fileEntries[p].size := size; fileEntries[p].attribues := $01; //add file name memcpy(uint32(fileName), uint32(@fileEntries[p].name), 14); end; //write file table driver.storage.mgr.storage_write(volume^.device, volume^.sectorStart + 1 , 1, puint32(@fileEntries[position])); //write file driver.storage.mgr.storage_write(volume^.device, fileEntries[p].start, size, data); //free buffer Kfree(puint32(fileEntries)); end; procedure read_file(volume : PStorage_Volume; fileName : pchar; data : PuInt32; status : PuInt32); var directories : PLinkedListBase; i : uint32; p : uint32; fileEntries : PFile_Entry; fileEntry : PFile_Entry; fileCount : uint32 = 1000; exsists : boolean = false; size : uint32; begin //load file table fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); //check if file exists else exit for i := 0 to fileCount - 1 do begin if stringEquals(fileEntries[i].name, fileName) then begin exsists := true; p:= i; break; end; end; //if entry is directory then exit if fileEntries[p].attribues and $10 = $10 then begin status^ := 1; exit; end; //if entry is not found exit if fileEntries[i].attribues = 0 then begin exsists := false; p:= i; exit; end; //file size rounded up to nearest 512 size := (fileEntries[p].size + 511) and -512; //read file data := Kalloc(size); driver.storage.mgr.storage_read(volume^.device, fileEntries[p].start, size, data); //free buffer Kfree(puint32(fileEntries)); status^ := 0; end; { Count free sectors on a FlatFS volume by scanning file entries. } function countFreeFlatFSSectors(volume : PStorage_Volume) : uint32; var buffer : puint32; diskInfo : PDisk_Info; sectorCount : uint32; fileCount : uint32; tableSectors : uint32; fileEntries : PFile_Entry; usedSectors : uint32; overhead : uint32; i : uint32; begin countFreeFlatFSSectors := 0; buffer := puint32(kalloc(512)); memset(uint32(buffer), 0, 512); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart, 1, buffer); diskInfo := PDisk_Info(buffer); sectorCount := diskInfo^.sectorCount; fileCount := diskInfo^.fileCount; kfree(buffer); if sectorCount = 0 then exit; tableSectors := (fileCount * sizeof(TFile_Entry)) div 512; overhead := 2 + tableSectors; { header + boundary + file table } usedSectors := 0; if fileCount > 0 then begin fileEntries := PFile_Entry(kalloc(sizeof(TFile_Entry) * fileCount + 512)); memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, tableSectors, puint32(fileEntries)); for i := 0 to fileCount - 1 do begin if fileEntries[i].attribues <> 0 then usedSectors := usedSectors + fileEntries[i].size; end; kfree(puint32(fileEntries)); end; if sectorCount > (overhead + usedSectors) then countFreeFlatFSSectors := sectorCount - overhead - usedSectors else countFreeFlatFSSectors := 0; end; procedure detect_volumes(disk : PStorage_Device); var buffer : puint32; bufSize : uint32; volume : PStorage_volume; begin push_trace('driver.storage.fs.flatfs.detectVolumes()'); bufSize := disk^.sectorSize; if bufSize < 512 then bufSize := 512; buffer := puint32(kalloc(bufSize)); memset(uint32(buffer), 0, bufSize); if disk^.dispatchRead = nil then begin io.syslog.writestringln('FlatFS: detect_volumes: device has no read dispatch.'); kfree(buffer); exit; end; driver.storage.mgr.storage_read(disk, 2, 1, buffer); if (PDisk_Info(buffer)^.signature = $0B00B1E5) then begin io.syslog.writestringln('FlatFS: volume found!'); volume := PStorage_volume(kalloc(sizeof(TStorage_Volume))); memset(uint32(volume), 0, sizeof(TStorage_Volume)); volume^.device := disk; volume^.sectorStart := 2; volume^.sectorSize := 512; volume^.sectorCount := PDisk_Info(buffer)^.sectorCount; volume^.filesystem := @filesystem; volume^.freeSectors := countFreeFlatFSSectors(volume); volume^.isBootDrive := false; driver.storage.vol.mgr.register_volume(disk, volume); end; kfree(buffer); end; procedure delete_file(volume : PStorage_Volume; fileName : pchar; status : PuInt32); var directories : PLinkedListBase; i : uint32; p : uint32; fileEntries : PFile_Entry; fileEntry : PFile_Entry; zeroBuffer : PuInt32; position : uint32; offset : uint32; fileCount : uint32 = 1000; exsists : boolean; begin //load file table fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); //check if file exists else exit for i := 0 to fileCount - 1 do begin if stringEquals(fileEntries[i].name, fileName) then begin exsists := true; p:= i; break; end; end; //set position of entry position := (p * sizeof(TFile_Entry)) div 512; //if entry is directory then exit if fileEntries[p].attribues and $10 = $10 then begin status^ := 1; exit; end; //if entry is not found exit if fileEntries[i].attribues = 0 then begin exsists := false; p:= i; exit; end; //zero out file zeroBuffer := Kalloc(fileEntries[p].size); memset(uint32(zeroBuffer), 0, fileEntries[p].size); //write file driver.storage.mgr.storage_write(volume^.device, fileEntries[p].start, fileEntries[p].size, zeroBuffer); //free buffer Kfree(puint32(zeroBuffer)); //zero out file table memset(uint32(@fileEntries[p]), 0, sizeof(TFile_Entry)); //write file table driver.storage.mgr.storage_write(volume^.device, volume^.sectorStart + 1 , 1, puint32(@fileEntries[position])); //free buffer Kfree(puint32(fileEntries)); status^ := 0; end; procedure delete_directory(volume : PStorage_Volume; directoryName : pchar; status : PuInt32); var directories : PLinkedListBase; i : uint32; p : uint32; fileEntries : PFile_Entry; fileEntry : PFile_Entry; zeroBuffer : PuInt32; position : uint32; offset : uint32; fileCount : uint32 = 1000; exsists : boolean; begin //load file table fileEntries := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); //check if file exists else exit for i := 0 to fileCount - 1 do begin if stringEquals(fileEntries[i].name, directoryName) then begin exsists := true; p:= i; break; end; end; //if entry is not directory then exit if fileEntries[i].attribues and $10 <> $10 then begin status^ := 1; exit; end; //calculate position of directory in file table position := (p * sizeof(TFile_Entry)) div 512; //zero file table memset(uint32(@fileEntries[p]), 0, sizeof(TFile_Entry)); //write file table driver.storage.mgr.storage_write(volume^.device, volume^.sectorStart + 1 , 1, puint32(@fileEntries[position])); //free buffer Kfree(puint32(fileEntries)); status^ := 0; end; function identify_volume(volume : PStorage_Volume) : boolean; var 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)); memset(uint32(buffer), 0, bufSize); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart, 1, buffer); if (PDisk_Info(buffer)^.signature = $0B00B1E5) then begin identify_volume := true; volume^.freeSectors := countFreeFlatFSSectors(volume); end; kfree(buffer); if freeMbr then kfree(void(mbr)); end; function readDirectoryEntries(volume : PStorage_Volume; directory : pchar; status : PuInt32) : PLinkedListBase; var entries : PLinkedListBase; fileEntrys : PFile_Entry; i : uint32; compString : PChar; afterString : PChar; slash : PChar; fileCount : uint32; dirEntry : PDirectory_Entry; elm : void; slashArray : array[0..1] of char; begin push_trace('driver.storage.fs.flatfs.readDirectoryEntries'); fileCount := 1000; slashArray[0] := '/'; slashArray[1] := char(0); entries := LL_New(sizeof(TDirectory_Entry)); fileEntrys := PFile_Entry(Kalloc(sizeof(TFile_Entry) * fileCount + 512)); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntrys)); slash := stringNew(2); memcpy(uint32(@slashArray), uint32(slash), 1); for i := 0 to fileCount - 1 do begin if uint32(fileEntrys[i].name[0]) = 0 then break; compString := stringSub(fileEntrys[i].name, 0, stringSize(directory)); afterString := stringSub(fileEntrys[i].name, stringSize(directory), stringSize(fileEntrys[i].name) - 1); if stringEquals(compString, directory) and (not stringContains(afterString, slash)) then begin elm := LL_Add(entries); dirEntry := PDirectory_Entry(elm); dirEntry^.fileName := stringCopy(afterString); if fileEntrys[i].attribues and $10 = $10 then dirEntry^.entryType := TDirectory_Entry_Type.directoryEntry else dirEntry^.entryType := TDirectory_Entry_Type.fileEntry; dirEntry^.fileSize := fileEntrys[i].size; dirEntry^.modifiedDate := 0; dirEntry^.modifiedTime := 0; dirEntry^.attributes := fileEntrys[i].attribues; end; kfree(void(compString)); kfree(void(afterString)); end; Kfree(puint32(fileEntrys)); kfree(void(slash)); readDirectoryEntries := entries; end; function getFileSize(volume : PStorage_Volume; directory : pchar; fileName : pchar) : uint32; var fileEntries : PFile_Entry; fileCount : uint32; i : uint32; begin getFileSize := 0; fileCount := 1000; fileEntries := PFile_Entry(kalloc(sizeof(TFile_Entry) * fileCount + 512)); memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); for i := 0 to fileCount - 1 do begin if fileEntries[i].attribues = 0 then break; if stringEquals(fileEntries[i].name, fileName) then begin getFileSize := fileEntries[i].size; break; end; end; kfree(puint32(fileEntries)); end; function readFileAtOffset(volume : PStorage_Volume; directory : pchar; fileName : pchar; offset : uint32; buffer : puint32; byteCount : uint32; ctx : pointer) : uint32; var fileEntries : PFile_Entry; fileCount : uint32; i : uint32; fileStart : uint32; fileSize : uint32; remaining : uint32; secOff : uint32; curSec : uint32; destPos : uint32; bytesToCopy : uint32; secBuf : puint32; found : boolean; begin readFileAtOffset := 0; fileCount := 1000; found := false; fileEntries := PFile_Entry(kalloc(sizeof(TFile_Entry) * fileCount + 512)); memset(uint32(fileEntries), 0, sizeof(TFile_Entry) * fileCount); driver.storage.mgr.storage_read(volume^.device, volume^.sectorStart + 1, sizeof(TFile_Entry) * fileCount div 512, puint32(fileEntries)); for i := 0 to fileCount - 1 do begin if fileEntries[i].attribues = 0 then break; if stringEquals(fileEntries[i].name, fileName) then begin fileStart := fileEntries[i].start; fileSize := fileEntries[i].size; found := true; break; end; end; kfree(puint32(fileEntries)); if not found then exit; { Clamp to EOF } if offset >= fileSize then exit; remaining := fileSize - offset; if byteCount < remaining then remaining := byteCount; secBuf := puint32(kalloc(512)); if secBuf = nil then exit; destPos := 0; curSec := fileStart + (offset div 512); secOff := offset mod 512; while remaining > 0 do begin driver.storage.mgr.storage_read(volume^.device, curSec, 1, secBuf); bytesToCopy := 512 - secOff; if bytesToCopy > remaining then bytesToCopy := remaining; core.util.memcpy(uint32(secBuf) + secOff, uint32(buffer) + destPos, bytesToCopy); destPos := destPos + bytesToCopy; remaining := remaining - bytesToCopy; secOff := 0; curSec := curSec + 1; end; kfree(secBuf); readFileAtOffset := destPos; end; procedure init(); begin push_trace('driver.storage.fs.flatfs.init()'); filesystem.sName:= 'driver.storage.fs.flatfs'; filesystem.system_id:= $02; filesystem.readDirCallback:= @readDirectoryEntries; filesystem.fileSizeCallback:= @getFileSize; filesystem.readOffsetCallback:= @readFileAtOffset; // filesystem.createDirCallback:= @writeDirectoryGen; filesystem.createcallback:= @create_volume; filesystem.validateFormat := @validate_format; filesystem.detectcallback:= @detect_volumes; filesystem.identifyCallback:= @identify_volume; filesystem.formatParamFlags := 0; { fixed 1000-entry legacy format } // filesystem.writecallback:= @writeFile; // filesystem.readcallback := @readFile; driver.storage.fs.mgr.register_filesystem(@filesystem); end; Initialization boot.mgr.registerBoot('driver.storage.fs.flatfs', @init, 'FlatFS Filesystem', 'driver.storage.fs.mgr'); end.