771 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			771 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{ ************************************************
 | 
						|
  * Asuro
 | 
						|
  * Unit: Drivers/storage/fat32
 | 
						|
  * Description: fat32 file system driver
 | 
						|
  * 
 | 
						|
  ************************************************
 | 
						|
  * Author: Aaron Hance
 | 
						|
  * Contributors: 
 | 
						|
  ************************************************ }
 | 
						|
 | 
						|
  {
 | 
						|
      Todo in the future, optimise by prvoiding batch read/write commands
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
unit FAT32;
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
    console,
 | 
						|
    storagemanagement,
 | 
						|
    util, terminal,
 | 
						|
    lmemorymanager,
 | 
						|
    strings,
 | 
						|
    lists,
 | 
						|
    tracer,
 | 
						|
    serial,
 | 
						|
    rtc;
 | 
						|
 | 
						|
type 
 | 
						|
 | 
						|
    TBootRecord = bitpacked record
 | 
						|
        jmp2boot        : ubit24;
 | 
						|
        OEMName         : array[0..7] of char;
 | 
						|
        sectorSize      : uint16;
 | 
						|
        spc             : uint8;
 | 
						|
        rsvSectors      : uint16;
 | 
						|
        numFats         : uint8;
 | 
						|
        numDirEnt       : uint16;
 | 
						|
        numSectors      : uint16;
 | 
						|
        mediaDescp      : uint8;
 | 
						|
        sectorsPerFat   : uint16;
 | 
						|
        sectorsPerTrack : uint16;
 | 
						|
        heads           : uint16;
 | 
						|
        hiddenSectors   : uint32;
 | 
						|
        manySectors     : uint32;
 | 
						|
        FATSize       : uint32;
 | 
						|
        flags         : uint16;
 | 
						|
        signature     : uint8;
 | 
						|
        FATVersion    : uint16;
 | 
						|
        rootCluster   : uint32;
 | 
						|
        FSInfoCluster : uint16;
 | 
						|
        backupCluster : uint16;
 | 
						|
        reserved0     : array[0..11] of uint8;
 | 
						|
        driveNumber   : uint8;
 | 
						|
        reserved1     : uint8;
 | 
						|
        bsignature     : uint8;// = $28;
 | 
						|
        volumeID      : uint32;
 | 
						|
        volumeLabel   : array[0..10] of uint8;
 | 
						|
        identString   : array[0..7] of char;// = 'FAT32 ';
 | 
						|
    end;
 | 
						|
    PBootRecord = ^TBootRecord;
 | 
						|
 | 
						|
    TDirectory = packed record
 | 
						|
        fileName      : array[0..7] of char;
 | 
						|
        fileExtension : array[0..2] of char;
 | 
						|
        attributes    : uint8;
 | 
						|
        reserved0     : uint8;
 | 
						|
        timeFine      : uint8;
 | 
						|
        time          : uint16;
 | 
						|
        date          : uint16;
 | 
						|
        accessTime    : uint16;
 | 
						|
        clusterHigh   : uint16;
 | 
						|
        modifiedTime  : uint16;
 | 
						|
        modifiedDate  : uint16;
 | 
						|
        clusterLow    : uint16;
 | 
						|
        byteSize      : uint32;
 | 
						|
    end;
 | 
						|
    PDirectory = ^TDirectory;
 | 
						|
 | 
						|
    TFilesystemInfo = record
 | 
						|
        leadSignature   : uint32;
 | 
						|
        reserved0       : array[0..479] of uint8;
 | 
						|
        structSignature : uint32;
 | 
						|
        freeSectors     : uint32;
 | 
						|
        nextFreeSector  : uint32;
 | 
						|
        reserved1       : array[0..11] of uint8;
 | 
						|
        trailSignature  : uint32; 
 | 
						|
    end;
 | 
						|
 | 
						|
    TFatVolumeInfo = record
 | 
						|
        sectorsPerCluster : uint8; // must be power of 2 and mult by sectorsize to max 32k
 | 
						|
    end;
 | 
						|
    PFatVolumeInfo = ^TFatVolumeInfo;
 | 
						|
 | 
						|
var
 | 
						|
    filesystem : TFilesystem;
 | 
						|
 | 
						|
procedure init;
 | 
						|
procedure create_volume(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32);
 | 
						|
procedure detect_volumes(disk : PStorage_Device);
 | 
						|
//function writeDirectory(volume : PStorage_volume; directory : pchar; attributes : uint32) : uint8; // need to handle parent table cluster overflow, need to take attributes
 | 
						|
//function readDirectory(volume : PStorage_volume; directory : pchar; listPtr : PLinkedListBase) : uint8; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = error
 | 
						|
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
procedure STOS(str : PChar);
 | 
						|
var
 | 
						|
    i : uint32;
 | 
						|
 | 
						|
begin
 | 
						|
    for i:=0 to StringSize(str)-1 do begin
 | 
						|
        serial.send(COM1, uint8(str[i]), 100);
 | 
						|
    end;
 | 
						|
    serial.send(COM1, 13, 100);
 | 
						|
end;
 | 
						|
 | 
						|
function load(ptr : void) : boolean;
 | 
						|
begin
 | 
						|
    console.outputln('DUMMY DRIVER', 'LOADED.')
 | 
						|
end;
 | 
						|
 | 
						|
function cleanString(str : pchar; status : puint32) : byteArray8;
 | 
						|
var
 | 
						|
    i : uint32;
 | 
						|
    ii: uint32;
 | 
						|
begin
 | 
						|
    //cleanString:= pchar(kalloc(sizeof(10)));
 | 
						|
    for i:=0 to 7 do begin
 | 
						|
        if str[i] = char(0) then begin
 | 
						|
            for ii:=i to 7 do begin
 | 
						|
                cleanString[ii]:= ' ';
 | 
						|
            end;
 | 
						|
            break;
 | 
						|
        end else begin
 | 
						|
            // if (str[i] = '/') or (str[i] = ',') then begin
 | 
						|
            //     status:= 3;
 | 
						|
            // end;
 | 
						|
            cleanString[i]:= str[i];
 | 
						|
        end;
 | 
						|
    end;
 | 
						|
 | 
						|
end;    
 | 
						|
 | 
						|
function readBootRecord(volume : PStorage_volume) : PBootRecord; // need write functions for boot record!
 | 
						|
var
 | 
						|
    buffer : puint32;
 | 
						|
begin
 | 
						|
    buffer:= puint32(kalloc(512));
 | 
						|
    memset(uint32(buffer), 0, 512);
 | 
						|
    volume^.device^.readcallback(volume^.device, volume^.sectorStart + 1, 1, buffer);
 | 
						|
    readBootRecord:= PBootRecord(buffer);
 | 
						|
end;
 | 
						|
 | 
						|
//TODO fat starts after reserved secotrs
 | 
						|
 | 
						|
function readFat(volume : PStorage_volume; cluster : uint32; bootRecord : PBootRecord) : uint32;
 | 
						|
var
 | 
						|
    buffer              : puint32;
 | 
						|
    fatEntriesPerSector : uint32;
 | 
						|
    sectorLocation      : uint32;
 | 
						|
    dataStart : uint32;
 | 
						|
begin
 | 
						|
    push_trace('fat32.readFat');
 | 
						|
    buffer:= puint32(kalloc(bootRecord^.sectorsize));
 | 
						|
    memset(uint32(buffer), 0, bootRecord^.sectorsize);
 | 
						|
    fatEntriesPerSector:= bootRecord^.sectorsize div 4;
 | 
						|
    sectorLocation:= cluster div fatEntriesPerSector;
 | 
						|
    dataStart:= (volume^.sectorStart + 1 + bootRecord^.rsvSectors);
 | 
						|
 | 
						|
    volume^.device^.readcallback(volume^.device, datastart + sectorLocation, 1, buffer); 
 | 
						|
    readFat:= buffer[cluster - (sectorLocation * fatEntriesPerSector)];
 | 
						|
 | 
						|
    kfree(buffer);
 | 
						|
end;
 | 
						|
 | 
						|
procedure writeFat(volume : PStorage_volume; cluster : uint32; value : uint32; bootRecord : PBootRecord);
 | 
						|
var
 | 
						|
    buffer              : puint32;
 | 
						|
    fatEntriesPerSector : uint32;
 | 
						|
    sectorLocation      : uint32;
 | 
						|
    dataStart           : uint32;
 | 
						|
begin
 | 
						|
    push_trace('fat32.WriteFat');
 | 
						|
    buffer:= puint32(kalloc(bootRecord^.sectorsize));
 | 
						|
    memset(uint32(buffer), 0, bootRecord^.sectorsize);
 | 
						|
    fatEntriesPerSector:= bootRecord^.sectorsize div 4;
 | 
						|
    sectorLocation:= cluster div fatEntriesPerSector;
 | 
						|
    dataStart:= (volume^.sectorStart + 1 + bootRecord^.rsvSectors);
 | 
						|
 | 
						|
    volume^.device^.readcallback(volume^.device, dataStart + sectorLocation, 1, buffer);
 | 
						|
    buffer[cluster - (sectorLocation * fatEntriesPerSector)]:= value;
 | 
						|
    volume^.device^.writecallback(volume^.device, dataStart + sectorLocation, 1, buffer);
 | 
						|
 | 
						|
    kfree(buffer);
 | 
						|
end;
 | 
						|
 | 
						|
function getFatChain(volume : PStorage_volume; cluster : uint32; bootRecord : PBootRecord) : PLinkedListBase;
 | 
						|
var
 | 
						|
    currentCluster      : uint32;
 | 
						|
    currentClusterValue : uint32;
 | 
						|
    clusters            : PLinkedListBase;
 | 
						|
    dirElm              : puint32;
 | 
						|
begin
 | 
						|
    push_trace('fat32.getFatChain');
 | 
						|
    clusters:= LL_New(sizeof(uint32));
 | 
						|
    currentCluster:= cluster;
 | 
						|
    currentClusterValue:= cluster;
 | 
						|
 | 
						|
    while true do begin
 | 
						|
        currentClusterValue:= readFat(volume, currentClusterValue, bootRecord);
 | 
						|
 | 
						|
        if currentClusterValue = $FFFFFFF7 then begin
 | 
						|
            break;
 | 
						|
        end else if currentClusterValue = $FFFFFFF8 then begin
 | 
						|
            dirElm:= LL_add(clusters);
 | 
						|
            dirElm^:= currentCluster;
 | 
						|
            break;
 | 
						|
        end else if currentClusterValue = 0 then begin
 | 
						|
            break;
 | 
						|
        end else begin
 | 
						|
            dirElm:= LL_add(clusters);
 | 
						|
            dirElm^:= currentCluster;
 | 
						|
        end;
 | 
						|
 | 
						|
        currentCluster+=1;
 | 
						|
    end;
 | 
						|
 | 
						|
    getFatChain:= clusters;
 | 
						|
end;
 | 
						|
 | 
						|
//TODO improve with FSINFO
 | 
						|
function findFreeClusters(volume : PStorage_volume; amount : uint32; bootRecord : PBootRecord) : PLinkedListBase;
 | 
						|
var
 | 
						|
    i                   : uint32 = 2;
 | 
						|
    currentClusterValue : uint32;
 | 
						|
    currentAmount       : uint32 = 0;
 | 
						|
    clusters            : PLinkedListBase;
 | 
						|
    dirElm              : puint32;
 | 
						|
begin
 | 
						|
    push_trace('fat32.findFreeClusters');
 | 
						|
    clusters := LL_New(8);
 | 
						|
 | 
						|
    while true do begin
 | 
						|
 | 
						|
        if currentAmount = amount then break;
 | 
						|
 | 
						|
        currentClusterValue:= readFat(volume, i, bootRecord);
 | 
						|
 | 
						|
        if currentClusterValue = 0 then begin
 | 
						|
            dirElm:= LL_add(clusters);
 | 
						|
            dirElm^:= i;
 | 
						|
            currentAmount+=1;
 | 
						|
        end;
 | 
						|
 | 
						|
        i+=1;
 | 
						|
    end;
 | 
						|
 | 
						|
    findFreeClusters:= clusters;
 | 
						|
 | 
						|
end;
 | 
						|
 | 
						|
//TODO add optional attributes flag to refine what i return
 | 
						|
function getDirEntries(volume : PStorage_volume; cluster : uint32; bootRecord : PBootRecord) : PLinkedListBase;
 | 
						|
var
 | 
						|
    buffer : puint32;
 | 
						|
    bufferI : puint32;
 | 
						|
    clusters : PLinkedListBase;
 | 
						|
    directories : PLinkedListBase;
 | 
						|
    i : uint32 = 0;
 | 
						|
    datastart : uint32;
 | 
						|
    sectorLocation : uint32;
 | 
						|
    dirElm : puint32;
 | 
						|
begin
 | 
						|
    push_trace('fat32.getDirEntries');
 | 
						|
    directories:= LL_New(sizeof(TDirectory));
 | 
						|
 | 
						|
    clusters:= PLinkedListBase(getFatChain(volume, cluster, bootRecord));
 | 
						|
    buffer:= puint32(kalloc( (bootRecord^.sectorSize * bootRecord^.spc) * LL_size(clusters) ));
 | 
						|
    memset(uint32(buffer), 0, (bootRecord^.sectorSize * bootRecord^.spc) * LL_size(clusters) );
 | 
						|
 | 
						|
    dataStart:= volume^.sectorStart + 1 + bootRecord^.rsvSectors + bootRecord^.FATSize;
 | 
						|
 | 
						|
    for i:=0 to LL_size(clusters) - 1 do begin
 | 
						|
        sectorLocation:= bootRecord^.spc * (i + cluster);
 | 
						|
        bufferI:= @buffer[i * (bootRecord^.spc * bootRecord^.sectorSize)];
 | 
						|
        volume^.device^.readcallback(volume^.device, datastart + sectorLocation, bootRecord^.spc, bufferI); //datastart + spc(i + cluster)
 | 
						|
    end;
 | 
						|
 | 
						|
    i:=0;
 | 
						|
    while true do begin
 | 
						|
        if PDirectory(buffer)[i].fileName[0] = char(0) then break;
 | 
						|
 | 
						|
        dirElm:= LL_Add(directories);
 | 
						|
        PDirectory(dirElm)^:= PDirectory(buffer)[i];
 | 
						|
        i+=1;
 | 
						|
    end;
 | 
						|
 | 
						|
    getDirEntries:= directories; //get last .
 | 
						|
    LL_Free(clusters);
 | 
						|
    kfree(buffer);
 | 
						|
end;
 | 
						|
 | 
						|
function compareByteArray8(str1 : byteArray8; str2 : byteArray8) : boolean;
 | 
						|
var
 | 
						|
    i : uint32;
 | 
						|
begin
 | 
						|
    push_trace('fat32.compareArray');
 | 
						|
    compareByteArray8:= true;
 | 
						|
    for i:=0 to 7 do begin
 | 
						|
        if str1[i] <> str2[i] then begin
 | 
						|
            compareByteArray8:= false;
 | 
						|
            break;
 | 
						|
        end;
 | 
						|
    end;
 | 
						|
end;
 | 
						|
 | 
						|
function fat2GenericEntries(list : PLinkedListBase) : PLinkedListBase;
 | 
						|
var
 | 
						|
    i     : uint32;
 | 
						|
    entry : PDirectory_Entry;
 | 
						|
    dir   : PDirectory;
 | 
						|
    dirElm: puint32;
 | 
						|
begin
 | 
						|
    push_trace('fat32.fat2GenericEntries');
 | 
						|
    puint32(entry) := kalloc(sizeof(TDirectory_Entry));
 | 
						|
    fat2GenericEntries:= LL_New(sizeof(TDirectory_Entry));
 | 
						|
 | 
						|
    if LL_size(list) > 0 then begin //TODO
 | 
						|
 | 
						|
        for i:= 0 to LL_Size(list) - 1 do begin
 | 
						|
            dir := PDirectory(LL_get(list, i));
 | 
						|
            entry^.fileName:= pchar(dir^.fileName);
 | 
						|
            entry^.extension:= pchar(dir^.fileExtension);
 | 
						|
 | 
						|
            if dir^.attributes = $10 then begin
 | 
						|
                entry^.entryType:= TDirectory_Entry_Type.directoryEntry;
 | 
						|
            end else begin //TODO add mount type
 | 
						|
                entry^.entryType:= TDirectory_Entry_Type.fileEntry;
 | 
						|
            end;
 | 
						|
 | 
						|
            //add to list
 | 
						|
            dirElm:= LL_add(fat2GenericEntries);
 | 
						|
            PDirectory_Entry(dirElm)^:= entry^;
 | 
						|
        end;
 | 
						|
    end;
 | 
						|
 | 
						|
    kfree(puint32(list));
 | 
						|
    kfree(puint32(entry));
 | 
						|
end;
 | 
						|
 | 
						|
//need to find out why having multiple dir stings isn't working, maybe the ls command? did I fix this?
 | 
						|
function readDirectory(volume : PStorage_volume; directory : pchar; statusOut : puint32) : PLinkedListBase; //statusout: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = invalid name, 4= already exists
 | 
						|
var
 | 
						|
    bootRecord       : PBootRecord;
 | 
						|
    directoryStrings : PLinkedListBase;
 | 
						|
    directories      : PLinkedListBase;
 | 
						|
    cluster          : uint32;
 | 
						|
    i                : uint32;
 | 
						|
    ii               : uint32 = 0;
 | 
						|
    dirEntry         : PDirectory;
 | 
						|
    status : puint32;
 | 
						|
begin
 | 
						|
    status:= puint32(kalloc(sizeof(uint32)));
 | 
						|
    push_trace('fat32.readDirectory');
 | 
						|
    status^:= 0;
 | 
						|
    bootRecord:= readBootRecord(volume);
 | 
						|
    directoryStrings:= LL_fromString(directory, '/');
 | 
						|
    directories:= getDirEntries(volume, bootRecord^.rootCluster, bootRecord);
 | 
						|
 | 
						|
    if LL_size(directoryStrings) > 0 then begin
 | 
						|
        for i:=0 to (LL_Size(directoryStrings) ) do begin /// maybe -1 will work
 | 
						|
            ii:=0;
 | 
						|
 | 
						|
            while true do begin
 | 
						|
                if ii > LL_Size(directories) - 1 then begin
 | 
						|
                    status^:= 1;
 | 
						|
                    break; 
 | 
						|
                end;
 | 
						|
                dirEntry:= PDirectory(LL_Get(directories, ii));
 | 
						|
 | 
						|
                if compareByteArray8( dirEntry^.fileName, cleanString( pchar(puint32(LL_Get(directoryStrings, i))^), status)) then begin
 | 
						|
                    cluster:= uint32(dirEntry^.clusterLow);
 | 
						|
                    cluster:= uint32(cluster) or uint32(dirEntry^.clusterHigh shl 16);
 | 
						|
                    break;
 | 
						|
                end;
 | 
						|
                ii+=1;
 | 
						|
            end;
 | 
						|
 | 
						|
            if status^ <> 0 then break;
 | 
						|
            
 | 
						|
            LL_Free(directories); 
 | 
						|
            directories:= getDirEntries(volume, cluster, bootRecord);
 | 
						|
 | 
						|
            if i = LL_Size(directoryStrings) - 1 then break;
 | 
						|
        end;
 | 
						|
    end else begin
 | 
						|
        while true do begin
 | 
						|
            if ii > LL_Size(directories) - 1 then break;
 | 
						|
            dirEntry:= PDirectory(LL_Get(directories, ii));
 | 
						|
            ii+=1;
 | 
						|
        end;
 | 
						|
    end;
 | 
						|
 | 
						|
    readDirectory:= directories;
 | 
						|
 | 
						|
    statusOut^:= status^;
 | 
						|
    LL_Free(directoryStrings);
 | 
						|
    kfree(puint32(bootRecord));
 | 
						|
end;
 | 
						|
 | 
						|
function readDirectoryGen(volume : PStorage_volume; directory : pchar; status : puint32) : PLinkedListBase; //returns: 0 = success, 1 = dir not exsist, 2 = not directory, 3 = invalid name, 4= already exists
 | 
						|
begin
 | 
						|
    readDirectoryGen:= fat2GenericEntries(readDirectory(volume, directory, status));
 | 
						|
end;
 | 
						|
 | 
						|
//need to allow for setting file extension
 | 
						|
function writeDirectory(volume : PStorage_volume; directory : pchar; dirName : pchar; attributes : uint32; statusOut : puint32; fileExtension : pchar) : uint32; // need to handle parent table cluster overflow, need to take attributes
 | 
						|
var
 | 
						|
    directories     : PLinkedListBase;
 | 
						|
    parentDirectory : PDirectory;
 | 
						|
    parentCluster   : uint32;
 | 
						|
    clusters        : PLinkedListBase;
 | 
						|
    cluster         : uint32;
 | 
						|
    bootRecord      : PBootRecord;
 | 
						|
    buffer          : puint32;
 | 
						|
    bufferPointer   : PDirectory;
 | 
						|
    dataStart       : uint32;
 | 
						|
    EntriesPerSector : uint32;
 | 
						|
    sectorLocation  : uint32;
 | 
						|
    dataOffset      : uint32;
 | 
						|
    i : uint32;
 | 
						|
 | 
						|
    thisArray     : byteArray8 = ('.',' ',' ',' ',' ',' ',' ',' ');
 | 
						|
    parentArray   : byteArray8 = ('.','.',' ',' ',' ',' ',' ',' ');
 | 
						|
    status : puint32;
 | 
						|
begin
 | 
						|
    push_trace('fat32.writeDirectory');
 | 
						|
    status:= puint32(kalloc(sizeof(uint32)));
 | 
						|
    status^:= 0;
 | 
						|
 | 
						|
    directories:= readDirectory(volume, directory, status); 
 | 
						|
 | 
						|
    if(LL_size(directories) > 1) then begin
 | 
						|
        for i:=0 to LL_Size(directories) - 1 do begin
 | 
						|
            if compareByteArray8( pchar(PDirectory(LL_get(directories, i))^.fileName), cleanString( dirName , status)) then begin
 | 
						|
                status^:= 4;
 | 
						|
            end;
 | 
						|
        end;
 | 
						|
    end;
 | 
						|
 | 
						|
    bootRecord:= readBootRecord(volume);
 | 
						|
    datastart:= volume^.sectorStart + 1 + bootRecord^.FATSize + bootRecord^.rsvSectors;
 | 
						|
 | 
						|
    if status^ = 0 then begin
 | 
						|
 | 
						|
        parentDirectory:= PDirectory(LL_Get(directories, 0));
 | 
						|
        parentCluster:= uint32(parentDirectory^.clusterlow) or uint32(parentDirectory^.clusterhigh shl 16);
 | 
						|
        //TODO check if this cluster is full, if so allocate new cluster
 | 
						|
 | 
						|
        clusters:= findFreeClusters(volume, 1, bootRecord); 
 | 
						|
        cluster:= uint32(LL_Get(clusters, 0)^);
 | 
						|
        LL_Free(clusters);
 | 
						|
        buffer:= puint32(kalloc(bootRecord^.sectorSize));
 | 
						|
 | 
						|
        if attributes = $10 then begin // if directory
 | 
						|
 | 
						|
            memset(uint32(buffer), 0, bootRecord^.sectorSize);
 | 
						|
 | 
						|
            bufferPointer:= @PDirectory(buffer)[0];
 | 
						|
            bufferPointer^.fileName:= thisArray; //TODO implement time
 | 
						|
            bufferPointer^.attributes:= attributes;
 | 
						|
            bufferPointer^.clusterLow:= cluster;
 | 
						|
            bufferPointer^.clusterHigh:= uint16((cluster shr 16) and $0000FFFF);
 | 
						|
            
 | 
						|
            bufferPointer:= @PDirectory(buffer)[1];
 | 
						|
            bufferPointer^.fileName:= parentArray; //TODO implement time
 | 
						|
            bufferPointer^.attributes:= attributes;
 | 
						|
            bufferPointer^.clusterLow:= parentCluster;
 | 
						|
            bufferPointer^.clusterHigh:= uint16((parentCluster shr 16) and $0000FFFF);
 | 
						|
 | 
						|
            //write to disk
 | 
						|
            volume^.device^.writecallback(volume^.device, dataStart + (cluster * bootRecord^.spc), 1, buffer);
 | 
						|
 | 
						|
            //write fat
 | 
						|
            writeFat(volume, cluster, $FFFFFFF8, bootRecord);
 | 
						|
        end;
 | 
						|
        memset(uint32(buffer), 0, bootRecord^.sectorSize);
 | 
						|
        //calculate write cluster using directories and parentCluster
 | 
						|
        sectorLocation:= LL_size(directories) * sizeof(TDirectory) div bootRecord^.sectorSize;
 | 
						|
        sectorLocation:= sectorLocation + (parentCluster * bootRecord^.spc);
 | 
						|
 | 
						|
        //dataOffset:= datastart + ( (LL_size(directories) * sizeof(PDirectory)) - (sizeUsed * bootRecord^.sectorSize));
 | 
						|
        volume^.device^.readcallback(volume^.device, dataStart + sectorLocation, 1, buffer);
 | 
						|
 | 
						|
        //construct my dir entry
 | 
						|
        bufferPointer:= @PDirectory(buffer)[LL_size(directories)];
 | 
						|
        bufferPointer^.fileName:= cleanString(dirName, status);
 | 
						|
        bufferPointer^.attributes:= attributes;
 | 
						|
        //if attributes = 0 then bufferPointer^.fileExtension:= fileExtension;
 | 
						|
        bufferPointer^.clusterLow:= cluster;
 | 
						|
        bufferPointer^.clusterHigh:= uint16((cluster shr 16) and $0000FFFF);
 | 
						|
 | 
						|
        writeDirectory:= cluster;
 | 
						|
 | 
						|
        //write to disk
 | 
						|
        volume^.device^.writecallback(volume^.device, dataStart + sectorLocation, 1, buffer);
 | 
						|
        kfree(buffer);
 | 
						|
    end;
 | 
						|
 | 
						|
    statusOut^:= status^;
 | 
						|
    kfree(puint32(bootRecord));
 | 
						|
    //LL_Free(directories); // page fault on free, possible memory leak now?
 | 
						|
    push_trace('writedirectory.end');
 | 
						|
 | 
						|
end;
 | 
						|
 | 
						|
procedure writeDirectoryGen(volume : PStorage_volume; directory : pchar; dirName : pchar; attributes : uint32; statusOut : puint32); // need to handle parent table cluster overflow, need to take attributes
 | 
						|
begin
 | 
						|
    writeDirectory(volume, directory, dirName, attributes, statusOut, '');
 | 
						|
end;
 | 
						|
 | 
						|
procedure writeFile(volume : PStorage_volume; directory : pchar; entry : PDirectory_Entry; byteCount : uint32; buffer : puint32; statusOut : puint32);
 | 
						|
var
 | 
						|
    bootRecord : PBootRecord;
 | 
						|
    directories : PLinkedListBase;
 | 
						|
    clusters : PLinkedListBase;
 | 
						|
    startCluster: uint32;
 | 
						|
    device : PStorage_Device;
 | 
						|
    dir : PDirectory;
 | 
						|
    exists : boolean = false;
 | 
						|
    sectorCount : uint32;
 | 
						|
    clusterCount : uint32;
 | 
						|
    clusterDifference : uint32;
 | 
						|
    dataStart : uint32;
 | 
						|
    iterations : uint32;
 | 
						|
    bufferPointer : puint32;
 | 
						|
 | 
						|
    i : uint32;
 | 
						|
    status : puint32;
 | 
						|
begin
 | 
						|
    push_trace('fat32.writefile');
 | 
						|
    status:= kalloc(4);
 | 
						|
    bootRecord:= readBootRecord(volume);
 | 
						|
    device:= volume^.device;
 | 
						|
    directories:= readDirectory(volume, directory, statusOut);
 | 
						|
    sectorCount:= (byteCount div bootRecord^.sectorSize) + 1;
 | 
						|
    datastart:= volume^.sectorStart + 1 + bootRecord^.FATSize + bootRecord^.rsvSectors;
 | 
						|
 | 
						|
 | 
						|
    for i:=0 to LL_size(directories) - 1 do begin
 | 
						|
        dir:= PDirectory(LL_get(directories, i));
 | 
						|
        if (dir^.fileName = entry^.fileName) and (dir^.fileExtension = entry^.extension) then begin
 | 
						|
            exists:= true;
 | 
						|
            break;
 | 
						|
        end;
 | 
						|
    end;
 | 
						|
 | 
						|
    push_trace('writefile.1');
 | 
						|
 | 
						|
    if exists then begin
 | 
						|
        startCluster:= uint32(dir^.clusterlow) or uint32(dir^.clusterhigh shl 16);
 | 
						|
        clusters:= getFatChain(volume, startCluster, bootRecord); //check no clusters and check if needs to be more or less, add/remove clusters
 | 
						|
        clusterCount := LL_size(clusters);
 | 
						|
 | 
						|
        if (clusterCount * bootRecord^.spc) > sectorCount then begin //shrink
 | 
						|
            clusterDifference:= clusterCount - (sectorCount div bootRecord^.spc);
 | 
						|
            for i:= (clusterCount - clusterDifference) + 1 to clusterCount do begin //free unused clusters
 | 
						|
                writeFat(volume, startCluster + i, 0, bootRecord);
 | 
						|
            end;
 | 
						|
            writeFat(volume, startCluster + (clusterCount - clusterDifference), $FFFFFFF8, bootRecord); // add new cluster terminator
 | 
						|
            //change last entry to terminator.
 | 
						|
        end else if (clusterCount * bootRecord^.spc) < sectorCount then begin //expand
 | 
						|
            clusterDifference:= (sectorCount div bootRecord^.spc) - clusterCount;
 | 
						|
            LL_Free(clusters);
 | 
						|
            clusters:= findFreeClusters(volume, clusterDifference, bootRecord);
 | 
						|
            for i:= clusterCount to clusterCount + clusterDifference - 1 do begin
 | 
						|
                writeFat(volume, startCluster + i, startCluster + i + 1, bootRecord);
 | 
						|
            end;
 | 
						|
                writeFat(volume, startcluster + clusterCount + clusterDifference, $FFFFFFF8, bootRecord);
 | 
						|
        end else begin //nothing
 | 
						|
            clusterDifference:= 0;
 | 
						|
        end;
 | 
						|
 | 
						|
    end else begin
 | 
						|
        push_trace('writefile.1.2.1');
 | 
						|
 | 
						|
        startCluster:= writeDirectory(volume, directory, entry^.fileName, 0, status, entry^.extension);
 | 
						|
        clusterDifference:= (byteCount div bootRecord^.sectorsize) - 1;
 | 
						|
            push_trace('writefile.1.2.2');
 | 
						|
 | 
						|
        for i:= startcluster to startCluster + clusterDifference - 1 do begin
 | 
						|
            writeFat(volume, i, i + 1, bootRecord);
 | 
						|
        end;
 | 
						|
                push_trace('writefile.1.2.3');
 | 
						|
 | 
						|
        writeFat(volume, startcluster + clusterDifference, $FFFFFFF8, bootRecord);
 | 
						|
        //setup fat chain 
 | 
						|
    end;
 | 
						|
 | 
						|
    push_trace('writefile.2');
 | 
						|
 | 
						|
    iterations:= (bytecount div bootRecord^.sectorSize) div 4;
 | 
						|
 | 
						|
    for i:=0 to byteCount div bootRecord^.sectorSize do begin
 | 
						|
        bufferPointer:= @buffer[i * uint32(bootRecord^.sectorsize * 4)];
 | 
						|
        volume^.device^.writecallback(volume^.device, dataStart + startCluster, 4, bufferPointer);
 | 
						|
    end;
 | 
						|
 | 
						|
    kfree(puint32(bootRecord));
 | 
						|
end;
 | 
						|
 | 
						|
procedure create_volume(disk : PStorage_Device; sectors : uint32; start : uint32; config : puint32);
 | 
						|
var
 | 
						|
    buffer     : puint32;
 | 
						|
    zeroBuffer : puint32;
 | 
						|
    bootRecord : PBootRecord;
 | 
						|
    dataStart  : uint32;
 | 
						|
    fatStart   : uint32;
 | 
						|
    FATSize    : uint32;
 | 
						|
    i          : uint32 = 0;
 | 
						|
 | 
						|
    asuroArray    : byteArray8 = ('A','S','U','R','O',' ','V','1');
 | 
						|
    fatArray      : byteArray8 = ('F','A','T','3','2',' ',' ',' ');
 | 
						|
    thisArray     : byteArray8 = ('.',' ',' ',' ',' ',' ',' ',' ');
 | 
						|
    parentArray   : byteArray8 = ('.','.',' ',' ',' ',' ',' ',' ');
 | 
						|
 | 
						|
    asuroFileArray     : byteArray8 = ('A','S','U','R','O',' ',' ',' ');
 | 
						|
    mountFileArray     : byteArray8 = ('M','O','U','N','T',' ',' ',' ');
 | 
						|
    programFileArray   : byteArray8 = ('P','R','O','G','R','A','M','S');
 | 
						|
    rootCluster   : uint32 = 1;
 | 
						|
begin
 | 
						|
    push_trace('fat32.create_volume()');
 | 
						|
 | 
						|
    // zeroBuffer:= puint32(kalloc( disk^.sectorSize * 4 ));
 | 
						|
    // memset(uint32(zeroBuffer), 0, disk^.sectorSize * 4);
 | 
						|
 | 
						|
    // while true do begin
 | 
						|
    //     if i > sectors then break;
 | 
						|
    //     disk^.writecallback(disk, 1 + i, 1, zeroBuffer);
 | 
						|
    //     i+=1;
 | 
						|
    // end;
 | 
						|
    // kfree(zeroBuffer);
 | 
						|
 | 
						|
    //fat32 structure
 | 
						|
    (* BootRecord            *)
 | 
						|
    (* reserved sectors      *)
 | 
						|
    (* File Allocation Table *)
 | 
						|
    (* Data Area             *)
 | 
						|
    
 | 
						|
    buffer:= puint32(kalloc(sizeof(TBootRecord)));
 | 
						|
    memset(uint32(buffer), 0, sizeof(TBootRecord));
 | 
						|
    bootRecord:= PBootRecord(buffer);
 | 
						|
 | 
						|
    FATSize:= ((sectors div config^) * 4) div disk^.sectorsize;
 | 
						|
 | 
						|
    bootRecord^.jmp2boot        := $0; //TODO impliment boot jump
 | 
						|
    bootRecord^.OEMName         := asuroArray;
 | 
						|
    bootRecord^.sectorSize      := disk^.sectorsize;
 | 
						|
    bootRecord^.spc             := config^;
 | 
						|
    bootRecord^.rsvSectors      := 32; //32 is standard
 | 
						|
    bootRecord^.numFats         := 1;
 | 
						|
    bootRecord^.mediaDescp      := $F8;
 | 
						|
    bootRecord^.hiddenSectors   := start;
 | 
						|
    bootRecord^.manySectors     := sectors;
 | 
						|
    bootRecord^.FATSize         := FATSize;
 | 
						|
    bootRecord^.rootCluster     := rootCluster;
 | 
						|
    bootRecord^.FSInfoCluster   := 0;
 | 
						|
    bootRecord^.driveNumber     := $80;
 | 
						|
    bootRecord^.volumeID        := 62; //+ puint32(@rtc.getDateTime())^;
 | 
						|
    bootRecord^.bsignature      := $29;
 | 
						|
    bootRecord^.identString     := fatArray;
 | 
						|
 | 
						|
    puint32(buffer)[127]:= $55AA;
 | 
						|
 | 
						|
    disk^.writecallback(disk, start + 1, 1, buffer);
 | 
						|
 | 
						|
    fatStart:= start + 1 + bootRecord^.rsvSectors;
 | 
						|
    dataStart:= fatStart + bootRecord^.FATSize;
 | 
						|
 | 
						|
    zeroBuffer:= puint32(kalloc( disk^.sectorSize * 4 ));
 | 
						|
    memset(uint32(zeroBuffer), 0, disk^.sectorSize * 4);
 | 
						|
 | 
						|
    while true do begin
 | 
						|
        if i > FATSize DIV 4 then break;
 | 
						|
        disk^.writecallback(disk, fatStart + i, 4, zeroBuffer);
 | 
						|
        i+=4;
 | 
						|
    end;
 | 
						|
 | 
						|
    kfree(buffer);
 | 
						|
    kfree(zeroBuffer);
 | 
						|
 | 
						|
    buffer:= puint32(kalloc(disk^.sectorSize));
 | 
						|
    memset(uint32(buffer), 0, disk^.sectorSize);
 | 
						|
 | 
						|
    puint32(buffer)[0]:= $FFFFFFF8; //fsinfo
 | 
						|
    puint32(buffer)[1]:= $FFFFFFF8; //root cluster
 | 
						|
 | 
						|
    disk^.writecallback(disk, fatStart, 1, buffer);
 | 
						|
 | 
						|
    kfree(buffer);
 | 
						|
 | 
						|
    buffer:= puint32(kalloc(disk^.sectorsize));
 | 
						|
    memset(uint32(buffer), 0, disk^.sectorsize);
 | 
						|
 | 
						|
    PDirectory(buffer)[0].fileName   := thisArray;
 | 
						|
    PDirectory(buffer)[0].attributes := $08;
 | 
						|
    PDirectory(buffer)[0].clusterLow := 1;
 | 
						|
 | 
						|
    PDirectory(buffer)[1].fileName   := parentArray;
 | 
						|
    PDirectory(buffer)[1].attributes := $10;
 | 
						|
    PDirectory(buffer)[1].clusterLow := 1;
 | 
						|
    
 | 
						|
    disk^.writecallback(disk, dataStart + (config^ * rootCluster), 1, buffer);
 | 
						|
 | 
						|
    kfree(buffer);
 | 
						|
 | 
						|
end;
 | 
						|
 | 
						|
procedure detect_volumes(disk : PStorage_Device);
 | 
						|
var
 | 
						|
    buffer : puint32;
 | 
						|
    i : uint8;
 | 
						|
    volume : PStorage_volume;
 | 
						|
 | 
						|
    dir : PDirectory;
 | 
						|
    dirs : PLinkedListBase;
 | 
						|
begin
 | 
						|
    push_trace('fat32.detectVolumes()');
 | 
						|
    console.writeintln(2);
 | 
						|
    redrawWindows();
 | 
						|
 | 
						|
    volume:= PStorage_volume(kalloc(sizeof(TStorage_Volume)));
 | 
						|
    //check first address for MBR
 | 
						|
    //if found then add volume and use info to see if there is another volume
 | 
						|
    buffer := puint32(kalloc(512));
 | 
						|
    memset(uint32(buffer), 0, 512);
 | 
						|
    disk^.readcallback(disk, 2, 1, buffer);
 | 
						|
 | 
						|
        console.writeintln(3);
 | 
						|
    redrawWindows();
 | 
						|
 | 
						|
    if (puint32(buffer)[127] = $55AA) and (PBootRecord(buffer)^.bsignature = $29) then begin //TODO partition table
 | 
						|
        console.writestringln('FAT32: volume found!');
 | 
						|
        volume^.device:= disk;
 | 
						|
        volume^.sectorStart:= 1;
 | 
						|
        volume^.sectorSize:= PBootRecord(buffer)^.sectorSize;
 | 
						|
        volume^.freeSectors:= 1000000; //TODO implement get free sectors need FSINFO implemented first
 | 
						|
        volume^.filesystem := @filesystem;
 | 
						|
        storagemanagement.register_volume(disk, volume);
 | 
						|
    end;
 | 
						|
 | 
						|
    kfree(buffer);
 | 
						|
end;
 | 
						|
 | 
						|
procedure init();
 | 
						|
begin
 | 
						|
    push_trace('fat32.init()');
 | 
						|
    filesystem.sName:= 'FAT32'; 
 | 
						|
    filesystem.readDirCallback:= @readDirectoryGen;
 | 
						|
    filesystem.createDirCallback:= @writeDirectoryGen;
 | 
						|
    filesystem.createcallback:= @create_volume;
 | 
						|
    filesystem.detectcallback:= @detect_volumes;
 | 
						|
    filesystem.writecallback:= @writeFile;
 | 
						|
 | 
						|
    storagemanagement.register_filesystem(@filesystem);
 | 
						|
end;
 | 
						|
 | 
						|
end. |