MODULE SSFS;
IMPORT
SYSTEM,
KernelLog, Commands, Plugins, Dates, Strings, Disks, Files;
CONST
Ok* = 0;
InvalidBlockNumber* = 1000;
InvalidFilePosition* = 1001;
BlockSizeNotSupported* = 2000;
NotFormatted* = 2001;
WrongVersion* = 2002;
VolumeFull* = 3000;
DeviceNotFound* = 4000;
DeviceError* = 4001;
PartitionTooSmall* = 5000;
SSFS_MagicNumber = LONGINT(99887766H);
SSFS_Version = 01H;
BlockSize = 4096;
DirectoryEntrySize = 256;
DirectoryEntriesPerBlock = BlockSize DIV DirectoryEntrySize;
Offset_SuperBlock = 0;
BlockNotAllocated = 0;
MinVolumeSize = 5;
BitsPerSET = SYSTEM.SIZEOF(SET) * 8;
Trace = FALSE;
TYPE
Block = ARRAY BlockSize OF CHAR;
Volume = OBJECT
VAR
device : Disks.Device; partition : LONGINT;
nofBlocks : LONGINT;
sectorsPerBlock : LONGINT;
PROCEDURE ReadBlock(blockNumber : LONGINT; VAR block : Block; VAR res : LONGINT);
BEGIN
IF (0 <= blockNumber) & (blockNumber < nofBlocks) THEN
device.Transfer(Disks.Read, device.table[partition].start + blockNumber * sectorsPerBlock, sectorsPerBlock, block, 0, res);
ELSE
res := InvalidBlockNumber;
END;
END ReadBlock;
PROCEDURE WriteBlock(blockNumber : LONGINT; VAR block : Block; VAR res : LONGINT);
BEGIN
IF (0 <= blockNumber) & (blockNumber < nofBlocks) THEN
device.Transfer(Disks.Write, device.table[partition].start + blockNumber * sectorsPerBlock, sectorsPerBlock, block, 0, res);
ELSE
res := InvalidBlockNumber;
END;
END WriteBlock;
PROCEDURE Finalize;
VAR ignore : LONGINT;
BEGIN
device.Close(ignore);
END Finalize;
PROCEDURE &Init*(device : Disks.Device; partition : LONGINT; VAR res : LONGINT);
BEGIN
ASSERT((device # NIL) & (device.table # NIL) & (partition < LEN(device.table)));
SELF.device := device; SELF.partition := partition;
IF (BlockSize MOD device.blockSize = 0) THEN
nofBlocks := device.table[partition].size * device.blockSize DIV BlockSize;
IF (nofBlocks >= MinVolumeSize) THEN
sectorsPerBlock := BlockSize DIV device.blockSize;
res := Ok;
IF Trace THEN
KernelLog.String("Volume created on partition "); KernelLog.String(device.name); KernelLog.String("#"); KernelLog.Int(partition, 0);
KernelLog.String(", size: "); KernelLog.Int(nofBlocks, 0); KernelLog.String(" blocks a "); KernelLog.Int(BlockSize, 0);
KernelLog.String(" Bytes"); KernelLog.Ln;
END;
ELSE
res := PartitionTooSmall;
END;
ELSE
res := BlockSizeNotSupported;
END;
END Init;
END Volume;
TYPE
BlockBitmap = OBJECT
VAR
bitmap : POINTER TO ARRAY OF SET;
hint : LONGINT;
fileSystem : FileSystem;
PROCEDURE FreeBlock(blockNumber : LONGINT; VAR res : LONGINT);
BEGIN {EXCLUSIVE}
IF (0 <= blockNumber) & (blockNumber < fileSystem.volume.nofBlocks) THEN
SetUsed(blockNumber, FALSE);
WriteBack(blockNumber, res);
ELSE
res := InvalidBlockNumber;
END;
END FreeBlock;
PROCEDURE AllocateBlock(VAR res : LONGINT) : LONGINT;
VAR blockNumber : LONGINT;
BEGIN {EXCLUSIVE}
blockNumber := FindFreeBlock(res, TRUE);
IF (res = Ok) THEN
SetUsed(blockNumber, TRUE);
WriteBack(blockNumber, res);
END;
RETURN blockNumber;
END AllocateBlock;
PROCEDURE FindFreeBlock(VAR res : LONGINT; useHint : BOOLEAN) : LONGINT;
VAR blockNumber : LONGINT;
BEGIN
IF useHint & (hint >= fileSystem.superBlock.firstDataBlock) THEN
blockNumber := hint;
ELSE
blockNumber := fileSystem.superBlock.firstDataBlock;
END;
ASSERT(blockNumber >= fileSystem.superBlock.firstDataBlock);
WHILE (blockNumber < fileSystem.volume.nofBlocks) & IsUsed(blockNumber) DO INC(blockNumber); END;
IF (blockNumber < fileSystem.volume.nofBlocks) THEN
hint := blockNumber + 1;
res := Ok;
ELSE
IF useHint THEN
blockNumber := FindFreeBlock(res, FALSE);
ELSE
res := VolumeFull;
END;
END;
RETURN blockNumber;
END FindFreeBlock;
PROCEDURE IsUsed(blockNumber : LONGINT) : BOOLEAN;
BEGIN
ASSERT((0 <= blockNumber) & (blockNumber < fileSystem.volume.nofBlocks));
RETURN (blockNumber MOD BitsPerSET) IN bitmap[blockNumber DIV BitsPerSET];
END IsUsed;
PROCEDURE SetUsed(blockNumber : LONGINT; used : BOOLEAN);
BEGIN
ASSERT((0 <= blockNumber) & (blockNumber < fileSystem.volume.nofBlocks));
IF used THEN
ASSERT(~IsUsed(blockNumber));
INCL(bitmap[blockNumber DIV BitsPerSET], blockNumber MOD BitsPerSET);
ELSE
ASSERT(IsUsed(blockNumber));
EXCL(bitmap[blockNumber DIV BitsPerSET], blockNumber MOD BitsPerSET);
END;
END SetUsed;
PROCEDURE WriteBack(blockNumber : LONGINT; VAR res : LONGINT);
VAR data : Block; blockIdx, index : LONGINT;
BEGIN
ClearBlock(data);
blockIdx := 0;
index := blockNumber DIV BitsPerSET;
index := index - (index MOD (BlockSize DIV BitsPerSET));
ASSERT((0 <= index) & (index < LEN(bitmap)));
WHILE (blockIdx < BlockSize) & (index < LEN(bitmap)) DO
SYSTEM.PUT32(SYSTEM.ADR(data[blockIdx]), bitmap[index]);
INC(index); INC(blockIdx, SYSTEM.SIZEOF(SET));
END;
ASSERT((blockNumber DIV BlockSize) < fileSystem.superBlock.freeBlockBitmapSize);
fileSystem.volume.WriteBlock(fileSystem.superBlock.freeBlockBitmapFirst + (blockNumber DIV BlockSize), data, res);
END WriteBack;
PROCEDURE LoadFromDisk(VAR res : LONGINT);
VAR data : Block; blockNumber, blockIdx, index : LONGINT;
BEGIN
ASSERT(fileSystem.superBlock.freeBlockBitmapSize # 0);
index := 0;
FOR blockNumber := 0 TO fileSystem.superBlock.freeBlockBitmapSize-1 DO
fileSystem.volume.ReadBlock(fileSystem.superBlock.freeBlockBitmapFirst + blockNumber, data, res);
IF (res = Ok) THEN
blockIdx := 0;
WHILE (blockIdx < BlockSize) & (index < LEN(bitmap)) DO
bitmap[index] := SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.ADR(data[blockIdx])));
INC(index); INC(blockIdx, SYSTEM.SIZEOF(SET));
END;
ELSE
RETURN;
END;
END;
IF Trace THEN
KernelLog.String("Loaded bitmap from disk: "); KernelLog.Ln;
Show;
END;
END LoadFromDisk;
PROCEDURE Show;
VAR i : LONGINT;
BEGIN
FOR i := 0 TO 7 DO
IF (i MOD 2 = 0) THEN KernelLog.Ln; END;
KernelLog.Bits(bitmap[i], 0, 32); KernelLog.String(" ");
END;
KernelLog.Ln;
END Show;
PROCEDURE &Init*(fileSystem : FileSystem);
VAR bitmapSize : LONGINT; i : LONGINT;
BEGIN
ASSERT((fileSystem # NIL) & (fileSystem.volume # NIL));
ASSERT(BlockSize MOD BitsPerSET = 0);
SELF.fileSystem := fileSystem;
bitmapSize := (fileSystem.volume.nofBlocks + BitsPerSET-1) DIV BitsPerSET;
NEW(bitmap, bitmapSize);
FOR i := 0 TO LEN(bitmap)-1 DO bitmap[i] := {}; END;
IF Trace THEN
KernelLog.String("Bitmap start: "); KernelLog.Int(fileSystem.superBlock.freeBlockBitmapFirst, 0); KernelLog.Ln;
KernelLog.String("BlockBitmap created, size: "); KernelLog.Int(bitmapSize, 0); KernelLog.String(" entries");
KernelLog.Ln;
Show;
END;
END Init;
END BlockBitmap;
TYPE
DirectoryEntry = RECORD
name : ARRAY 252 OF CHAR;
inode : LONGINT;
END;
DirectoryBlock = ARRAY DirectoryEntriesPerBlock OF DirectoryEntry;
SuperBlock = RECORD
magicNumber : LONGINT;
version : LONGINT;
rootDirectory : LONGINT;
freeBlockBitmapFirst : LONGINT;
freeBlockBitmapSize : LONGINT;
firstDataBlock : LONGINT;
filler : ARRAY BlockSize - 6 * 4 OF CHAR;
END;
Inode = RECORD
size : LONGINT;
attributes : SET;
date, time : LONGINT;
unused : LONGINT;
direct : ARRAY (BlockSize - 5 * 4) DIV 4 OF LONGINT;
END;
Dnode = Block;
TYPE
FileSystem = OBJECT(Files.FileSystem)
VAR
volume : Volume;
superBlock : SuperBlock;
rootDirectory : DirectoryBlock;
bitmap : BlockBitmap;
PROCEDURE &Init*(volume : Volume; VAR res : LONGINT);
VAR block : Block;
BEGIN
ASSERT(volume # NIL);
SELF.volume := volume;
COPY("SSFS", desc);
flags := {}; vol := NIL;
volume.ReadBlock(Offset_SuperBlock, block, res);
IF (res = Ok) THEN
superBlock := SYSTEM.VAL(SuperBlock, block);
IF (superBlock.magicNumber = SSFS_MagicNumber) THEN
IF (superBlock.version = SSFS_Version) THEN
volume.ReadBlock(superBlock.rootDirectory, block, res);
IF (res = Ok) THEN
rootDirectory := SYSTEM.VAL(DirectoryBlock, block);
IF (superBlock.freeBlockBitmapFirst # BlockNotAllocated) & (superBlock.freeBlockBitmapSize <= volume.nofBlocks) THEN
NEW(bitmap, SELF);
bitmap.LoadFromDisk(res);
END;
END;
ELSE
res := WrongVersion;
END;
ELSE
res := NotFormatted;
END;
END;
END Init;
PROCEDURE New0*(name: ARRAY OF CHAR): Files.File;
VAR
file : File; filename : Files.FileName; fileExists : BOOLEAN;
inodeAdr, index, i, res : LONGINT; dateTime : Dates.DateTime;
inode : Inode; block : Block;
BEGIN {EXCLUSIVE}
IF Trace THEN KernelLog.String("New: "); KernelLog.String(name); KernelLog.Ln; END;
file := NIL; fileExists := FALSE;
IF GetFilename(name, filename) THEN
index := FindEntry(filename);
IF (index = -1) THEN
index := FindEntry("");
ELSE
fileExists := TRUE;
END;
IF (index >= 0) THEN
IF fileExists THEN
ASSERT(rootDirectory[index].inode # 0);
inodeAdr := rootDirectory[index].inode;
volume.ReadBlock(inodeAdr, block, res);
inode := SYSTEM.VAL(Inode, block);
DeleteDnodes(inode, res);
FOR i := 0 TO LEN(inode.direct)-1 DO inode.direct[i] := BlockNotAllocated; END;
ELSE
inodeAdr := bitmap.AllocateBlock(res);
ClearInode(inode);
END;
IF (res = Ok) THEN
dateTime := Dates.Now();
Dates.DateTimeToOberon(dateTime, inode.date, inode.time);
volume.WriteBlock(inodeAdr, SYSTEM.VAL(Block, inode), res);
IF (res = Ok) THEN
COPY(filename, rootDirectory[index].name);
rootDirectory[index].inode := inodeAdr;
volume.WriteBlock(superBlock.rootDirectory, SYSTEM.VAL(Block, rootDirectory), res);
IF (res = Ok) THEN
NEW(file, filename, inode, inodeAdr, SELF);
ELSE
KernelLog.String("Could not write back root directory, res: "); KernelLog.Int(res, 0); KernelLog.Ln;
END;
ELSE
KernelLog.String("Could not write Inode, res: "); KernelLog.Int(res, 0); KernelLog.Ln;
END;
ELSE
KernelLog.String("Could not allocate Inode for file "); KernelLog.String(filename);
KernelLog.String(", res: "); KernelLog.Int(res, 0); KernelLog.Ln;
END;
ELSE
KernelLog.String("Cannot create file "); KernelLog.String(filename); KernelLog.String(": root directory is full.");
KernelLog.Ln;
END;
ELSE
KernelLog.String("Invalid filename: "); KernelLog.String(filename); KernelLog.Ln;
END;
RETURN file;
END New0;
PROCEDURE Old0*(name: ARRAY OF CHAR): Files.File;
VAR file : File; filename : Files.FileName; block : Block; inode : Inode; index, res : LONGINT;
BEGIN {EXCLUSIVE}
IF Trace THEN KernelLog.String("Old: "); KernelLog.String(name); KernelLog.Ln; END;
file := NIL;
IF GetFilename(name, filename) THEN
index := FindEntry(filename);
IF (index >= 0) THEN
ASSERT(rootDirectory[index].inode # 0);
volume.ReadBlock(rootDirectory[index].inode, block, res);
IF (res = Ok) THEN
inode := SYSTEM.VAL(Inode, block);
NEW(file, filename, inode, rootDirectory[index].inode, SELF);
ELSE
KernelLog.String("Could not read Inode for file "); KernelLog.String(filename); KernelLog.String(", res: ");
KernelLog.Int(res, 0); KernelLog.Ln;
END;
END;
ELSE
KernelLog.String("Invalid filename: "); KernelLog.String(filename); KernelLog.Ln;
END;
RETURN file;
END Old0;
PROCEDURE UpdateInode(inode : Inode; inodeAdr : LONGINT);
VAR res : LONGINT;
BEGIN {EXCLUSIVE}
ASSERT(inodeAdr >= superBlock.firstDataBlock);
volume.WriteBlock(inodeAdr, SYSTEM.VAL(Block, inode), res);
IF (res # Ok) THEN
KernelLog.String("Error when writing back Inode of file, res: "); KernelLog.Int(res, 0); KernelLog.Ln;
END;
END UpdateInode;
PROCEDURE GetFilename(name : ARRAY OF CHAR; VAR filename : ARRAY OF CHAR) : BOOLEAN;
VAR ch : CHAR; i, j : LONGINT;
BEGIN
Strings.TrimWS(name);
ch := name[0];
i := 0; j := 0;
IF (ch = Files.PathDelimiter) THEN
INC(i);
END;
WHILE (i < LEN(name)) & (j < LEN(filename)-1) & (name[i] # 0X) DO
filename[j] := name[i];
INC(i); INC(j);
END;
filename[j] := 0X;
RETURN (filename # "");
END GetFilename;
PROCEDURE FindEntry(CONST name : ARRAY OF CHAR) : LONGINT;
VAR index : LONGINT;
BEGIN
index := 0;
WHILE (index < LEN(rootDirectory)) & (rootDirectory[index].name # name) DO INC(index); END;
IF (index >= LEN(rootDirectory)) THEN index := -1; END;
ASSERT((index = -1) OR ((0 <= index) & (index < LEN(rootDirectory))));
RETURN index;
END FindEntry;
PROCEDURE Delete0*(name: ARRAY OF CHAR; VAR key, res: LONGINT);
VAR filename : Files.FileName; index : LONGINT; block : Block;
BEGIN {EXCLUSIVE}
IF Trace THEN KernelLog.String("Delete: "); KernelLog.String(name); KernelLog.Ln; END;
IF GetFilename(name, filename) THEN
index := FindEntry(filename);
IF (index >= 0) THEN
ASSERT(rootDirectory[index].inode # 0);
volume.ReadBlock(rootDirectory[index].inode, block, res);
IF (res = Ok) THEN
DeleteFile(SYSTEM.VAL(Inode, block), rootDirectory[index].inode, res);
IF (res # Ok) THEN
KernelLog.String("Could not delete Inode or Dnodes"); KernelLog.Ln;
res := -99;
END;
rootDirectory[index].name := "";
rootDirectory[index].inode := 0;
volume.WriteBlock(superBlock.rootDirectory, SYSTEM.VAL(Block, rootDirectory), res);
END;
ELSE
res := Files.FileNotFound;
END;
ELSE
KernelLog.String("Invalid filename: "); KernelLog.String(name); KernelLog.Ln;
res := Files.FileNotFound;
END;
END Delete0;
PROCEDURE DeleteFile(inode : Inode; inodeAdr : LONGINT; VAR res : LONGINT);
BEGIN
DeleteDnodes(inode, res);
bitmap.FreeBlock(inodeAdr, res);
END DeleteFile;
PROCEDURE DeleteDnodes(inode : Inode; VAR res : LONGINT);
VAR i : LONGINT; finished : BOOLEAN;
BEGIN
finished := FALSE;
i := 0;
WHILE ~finished & (i < LEN(inode.direct)) DO
IF (inode.direct[i] # BlockNotAllocated) THEN
bitmap.FreeBlock(inode.direct[i], res);
IF (res # Ok) THEN RETURN; END;
ELSE
finished := TRUE;
END;
INC(i);
END;
END DeleteDnodes;
PROCEDURE Rename0*(old, new: ARRAY OF CHAR; f: Files.File; VAR res: LONGINT);
BEGIN {EXCLUSIVE}
res := -1;
END Rename0;
PROCEDURE Enumerate0*(mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator);
VAR i : LONGINT; block : Block; inode : Inode; name : Files.FileName; attributes : SET; time, date, size, res : LONGINT;
BEGIN {EXCLUSIVE}
IF( mask # "") THEN
FOR i := 0 TO LEN(rootDirectory)-1 DO
IF Strings.Match(mask, rootDirectory[i].name) THEN
IF (rootDirectory[i].inode # BlockNotAllocated) THEN
IF (flags # {}) THEN
volume.ReadBlock(rootDirectory[i].inode, block, res);
IF (res = Ok) THEN
inode := SYSTEM.VAL(Inode, block);
attributes := inode.attributes;
time := inode.time; date := inode.date;
size := inode.size;
ELSE
KernelLog.String("Enumerate0: Could not read block, res: "); KernelLog.Int(res, 0);
KernelLog.Ln;
END;
END;
Files.JoinName(prefix, rootDirectory[i].name, name);
enum.PutEntry(name, attributes, time, date, size);
ELSIF rootDirectory[i].name # "" THEN
KernelLog.String("Enumerate0: entry for file "); KernelLog.String(rootDirectory[i].name);
KernelLog.String(" but file seems to be unallocated."); KernelLog.Ln;
END;
END;
END;
END;
END Enumerate0;
PROCEDURE FileKey*(name: ARRAY OF CHAR): LONGINT;
VAR key, index : LONGINT;
BEGIN {EXCLUSIVE}
key := 0;
IF (name # "") THEN
index := FindEntry(name);
IF (index >= 0) THEN
key := rootDirectory[index].inode;
END;
END;
RETURN key;
END FileKey;
PROCEDURE CreateDirectory0*(name: ARRAY OF CHAR; VAR res: LONGINT);
BEGIN {EXCLUSIVE}
res := -1;
END CreateDirectory0;
PROCEDURE RemoveDirectory0*(name: ARRAY OF CHAR; force: BOOLEAN; VAR key, res: LONGINT);
BEGIN {EXCLUSIVE}
res := -1;
END RemoveDirectory0;
PROCEDURE Format(VAR res : LONGINT);
VAR block : Block; i : LONGINT;
BEGIN {EXCLUSIVE}
superBlock.magicNumber := SSFS_MagicNumber;
superBlock.version := SSFS_Version;
superBlock.rootDirectory := 1;
superBlock.freeBlockBitmapFirst := 2;
superBlock.freeBlockBitmapSize := (volume.nofBlocks + BlockSize-1) DIV BlockSize;
superBlock.firstDataBlock := superBlock.freeBlockBitmapFirst + superBlock.freeBlockBitmapSize;
FOR i := 0 TO LEN(superBlock.filler)-1 DO superBlock.filler[i] := 0X; END;
volume.WriteBlock(Offset_SuperBlock, SYSTEM.VAL(Block, superBlock), res);
IF Trace THEN
KernelLog.String("Fomat information: "); KernelLog.Ln;
KernelLog.String("SSFS Version: "); KernelLog.Int(superBlock.version, 0); KernelLog.Ln;
KernelLog.String("Root Directory Block: "); KernelLog.Int(superBlock.rootDirectory, 0); KernelLog.Ln;
KernelLog.String("Free Block Bitmap Start: "); KernelLog.Int(superBlock.freeBlockBitmapFirst, 0); KernelLog.Ln;
KernelLog.String("Free Block Bitmap Size: "); KernelLog.Int(superBlock.freeBlockBitmapSize, 0); KernelLog.Ln;
KernelLog.String("First Data Block: "); KernelLog.Int(superBlock.firstDataBlock, 0); KernelLog.Ln;
END;
IF (res = Ok) THEN
ClearBlock(block);
volume.WriteBlock(superBlock.rootDirectory, block, res);
FOR i := 0 TO superBlock.freeBlockBitmapSize-1 DO
volume.WriteBlock(superBlock.freeBlockBitmapFirst + i, block, res);
IF (res # Ok) THEN
RETURN;
END;
END;
END;
END Format;
PROCEDURE Finalize;
BEGIN
Finalize^;
volume.Finalize;
END Finalize;
END FileSystem;
TYPE
File = OBJECT(Files.File)
VAR
inode : Inode;
inodeModified : BOOLEAN;
fileSystem : FileSystem;
name : Files.FileName;
PROCEDURE &Init*(CONST name : ARRAY OF CHAR; inode : Inode; inodeAddress : LONGINT; fileSystem : FileSystem);
BEGIN
ASSERT((name # "") & (fileSystem # NIL) & (fileSystem.volume # NIL));
COPY(name, SELF.name);
SELF.inode := inode;
key := inodeAddress;
SELF.fileSystem := fileSystem;
SELF.fs := fileSystem;
END Init;
PROCEDURE Set*(VAR r: Files.Rider; pos: LONGINT);
BEGIN {EXCLUSIVE}
r.res := Ok; r.eof := FALSE; r.fs := fs; r.file := SELF;
IF (pos < 0) THEN
pos := 0;
ELSIF (pos < inode.size) THEN
r.apos := pos MOD BlockSize; r.bpos := pos DIV BlockSize;
ELSE
r.apos := inode.size MOD BlockSize; r.bpos := inode.size DIV BlockSize;
END;
END Set;
PROCEDURE Pos*(VAR r: Files.Rider): LONGINT;
BEGIN {EXCLUSIVE}
ASSERT(r.file = SELF);
RETURN r.apos + BlockSize * r.bpos;
END Pos;
PROCEDURE Read*(VAR r: Files.Rider; VAR x: CHAR);
VAR a: ARRAY 1 OF CHAR;
BEGIN
ReadBytes(r, a, 0, 1); x := a[0];
END Read;
PROCEDURE ReadBytes*(VAR r: Files.Rider; VAR x: ARRAY OF CHAR; ofs, len: LONGINT);
VAR dnode : Dnode; dataLeft, nofBytes, pos, res : LONGINT; eof : BOOLEAN;
BEGIN {EXCLUSIVE}
ASSERT(r.file = SELF);
ASSERT(LEN(x) >= ofs + len);
eof := FALSE;
LOOP
IF (len = 0) THEN
EXIT;
ELSIF (r.bpos < LEN(inode.direct)) & (inode.direct[r.bpos] # BlockNotAllocated) THEN
fileSystem.volume.ReadBlock(inode.direct[r.bpos], dnode, res);
IF (res = Ok) THEN
dataLeft := BlockSize - r.apos;
IF (len < dataLeft) THEN
nofBytes := len;
ELSE
nofBytes := dataLeft;
END;
pos := r.bpos * BlockSize + r.apos;
IF (pos + nofBytes > inode.size) THEN
nofBytes := inode.size - pos;
IF (nofBytes < 0) THEN nofBytes := 0; END;
eof := TRUE;
END;
SYSTEM.MOVE(SYSTEM.ADR(dnode[r.apos]), SYSTEM.ADR(x[ofs]), nofBytes);
len := len - nofBytes;
ofs := ofs + len;
r.apos := (r.apos + nofBytes) MOD BlockSize;
IF (nofBytes = dataLeft) THEN
r.bpos := r.bpos + 1;
END;
IF eof THEN
r.eof := TRUE;
EXIT;
END;
ELSE
r.res := res;
EXIT;
END;
ELSE
r.eof := TRUE;
EXIT;
END;
END;
r.res := len;
END ReadBytes;
PROCEDURE Write*(VAR r: Files.Rider; x: CHAR);
VAR a: ARRAY 1 OF CHAR;
BEGIN
a[0] := x; WriteBytes(r, a, 0, 1);
END Write;
PROCEDURE WriteBytes*(VAR r: Files.Rider; CONST x: ARRAY OF CHAR; ofs, len: LONGINT);
VAR dnode : Dnode; blockNumber, spaceLeft, nofBytes, pos, res : LONGINT;
BEGIN {EXCLUSIVE}
ASSERT(r.file = SELF);
ASSERT(r.bpos * BlockSize + r.apos <= inode.size);
LOOP
IF (len = 0) THEN
EXIT;
ELSE
ASSERT(r.bpos < LEN(inode.direct));
IF (inode.direct[r.bpos] = BlockNotAllocated) THEN
blockNumber := fileSystem.bitmap.AllocateBlock(res);
IF (res = Ok) THEN
ClearBlock(dnode);
inode.direct[r.bpos] := blockNumber;
inodeModified := TRUE;
ELSE
r.res := res;
EXIT;
END;
ELSE
blockNumber := inode.direct[r.bpos];
fileSystem.volume.ReadBlock(blockNumber, dnode, res);
IF (res # Ok) THEN
r.res := res;
EXIT;
END;
END;
ASSERT(blockNumber >= fileSystem.superBlock.firstDataBlock);
spaceLeft := BlockSize - r.apos;
IF (len < spaceLeft) THEN
nofBytes := len;
ELSE
nofBytes := spaceLeft;
END;
SYSTEM.MOVE(SYSTEM.ADR(x[ofs]), SYSTEM.ADR(dnode[r.apos]), nofBytes);
fileSystem.volume.WriteBlock(blockNumber, dnode, res);
IF (res = Ok) THEN
len := len - nofBytes;
ofs := ofs + nofBytes;
r.apos := (r.apos + nofBytes) MOD BlockSize;
IF (r.apos = 0) THEN
INC(r.bpos);
IF (r.bpos >= LEN(inode.direct)) THEN
DEC(r.bpos);
r.eof := TRUE;
KernelLog.String("Maximum file length reached."); KernelLog.Ln;
EXIT;
END;
END;
ELSE
r.res := res;
EXIT;
END;
END;
END;
pos := r.bpos * BlockSize + r.apos;
IF (pos > inode.size) THEN
inode.size := pos;
inodeModified := TRUE;
END;
r.res := len;
END WriteBytes;
PROCEDURE Length*(): LONGINT;
BEGIN {EXCLUSIVE}
RETURN inode.size;
END Length;
PROCEDURE GetDate*(VAR t, d: LONGINT);
BEGIN {EXCLUSIVE}
t := inode.time; d := inode.date;
END GetDate;
PROCEDURE SetDate*(t, d: LONGINT);
BEGIN {EXCLUSIVE}
inode.time := t; inode.date := d;
inodeModified := TRUE;
END SetDate;
PROCEDURE GetName*(VAR name: ARRAY OF CHAR);
BEGIN {EXCLUSIVE}
Files.JoinName(fileSystem.prefix, SELF.name, name)
END GetName;
PROCEDURE Register0*(VAR res: LONGINT);
BEGIN
Update;
END Register0;
PROCEDURE Update*;
BEGIN {EXCLUSIVE}
IF inodeModified THEN
ASSERT(key >= fileSystem.superBlock.firstDataBlock);
fileSystem.UpdateInode(inode, key);
END;
END Update;
END File;
PROCEDURE ClearBlock(VAR block : Block);
VAR i : LONGINT;
BEGIN
FOR i := 0 TO LEN(block)-1 DO
block[i] := 0X;
END;
END ClearBlock;
PROCEDURE ClearInode(VAR inode : Inode);
VAR i : LONGINT;
BEGIN
inode.size := 0;
inode.attributes := {};
inode.date := 0; inode.time := 0;
FOR i := 0 TO LEN(inode.direct)-1 DO inode.direct[i] := 0; END;
END ClearInode;
PROCEDURE GetFileSystem(context : Commands.Context; VAR res : LONGINT) : FileSystem;
VAR
devPart, devicename : ARRAY 64 OF CHAR; partition : LONGINT;
device : Disks.Device;
plugin : Plugins.Plugin;
volume : Volume;
fileSystem : FileSystem;
PROCEDURE ParseDevPart(CONST devPart : ARRAY OF CHAR; VAR devicename : ARRAY OF CHAR; VAR partition : LONGINT) : BOOLEAN;
VAR stringArray : Strings.StringArray;
BEGIN
stringArray := Strings.Split(devPart, "#");
IF (LEN(stringArray) = 2) THEN
COPY(stringArray[0]^, devicename);
Strings.StrToInt(stringArray[1]^, partition);
RETURN TRUE;
ELSE
RETURN FALSE;
END;
END ParseDevPart;
BEGIN
fileSystem := NIL;
IF context.arg.GetString(devPart) & ParseDevPart(devPart, devicename, partition) THEN
plugin := Disks.registry.Get(devicename);
IF (plugin # NIL) & (plugin IS Disks.Device) THEN
device := plugin (Disks.Device);
device.Open(res);
IF (res = Disks.Ok) THEN
IF (device.table # NIL) & (partition < LEN(device.table)) THEN
IF ~(Disks.Mounted IN device.table[partition].flags) THEN
NEW(volume, device, partition, res);
IF (res = Ok) THEN
NEW(fileSystem, volume, res);
IF (res # Ok) & (res # NotFormatted) THEN
fileSystem := NIL;
context.error.String("Could not mount file system, res: "); context.error.Ln;
END;
ELSE
context.error.String("Could not create volume, res: "); context.error.Int(res, 0); context.error.Ln;
END;
ELSE
res := DeviceError;
context.error.String("Partition is already mounted."); context.error.Ln;
END;
ELSE
res := DeviceError;
context.error.String("Partition "); context.error.Int(partition, 0); context.error.String(" not available on device ");
context.error.String(devicename); context.error.Ln;
END;
ELSE
context.error.String("Could not open device "); context.error.String(devicename); context.error.String(", res: ");
context.error.Int(res, 0); context.error.Ln;
END;
ELSE
res := DeviceNotFound;
context.error.String("Device "); context.error.String(devicename); context.error.String(" not found.");
context.error.Ln;
END;
ELSE
res := DeviceNotFound;
context.error.String("Expected device#partition argument."); context.error.Ln;
END;
RETURN fileSystem;
END GetFileSystem;
PROCEDURE Format*(context : Commands.Context);
VAR fileSystem : FileSystem; res : LONGINT;
BEGIN
fileSystem := GetFileSystem(context, res);
IF (res = Ok) OR (res = NotFormatted) THEN
fileSystem.Format(res);
fileSystem.Finalize;
IF (res = Ok) THEN
context.out.String("Disk formatted."); context.out.Ln;
ELSE
context.error.String("Formatting disk failed, res: "); context.out.Int(res, 0); context.error.Ln;
END;
END;
END Format;
PROCEDURE Mount*(context : Commands.Context);
VAR prefix : Files.Prefix; fileSystem : FileSystem; res : LONGINT;
BEGIN
IF context.arg.GetString(prefix) THEN
IF (Files.This(prefix) = NIL) THEN
fileSystem := GetFileSystem(context, res);
IF (res = Ok) THEN
Files.Add(fileSystem, prefix);
context.out.String(prefix); context.out.String(" mounted."); context.out.Ln;
ELSE
END;
ELSE
context.error.String("Prefix "); context.error.String(prefix); context.error.String(" is already used.");
context.error.Ln;
END;
ELSE
context.error.String("Usage: SSFS.Mount prefix device#partition ~"); context.error.Ln;
END;
END Mount;
PROCEDURE Unmount*(context : Commands.Context);
VAR prefix : Files.Prefix; filesystem : Files.FileSystem;
BEGIN
context.arg.SkipWhitespace; context.arg.String(prefix);
filesystem := Files.This(prefix);
IF (filesystem # NIL) THEN
IF (filesystem IS FileSystem) THEN
IF Trace THEN filesystem(FileSystem).bitmap.Show; END;
Files.Remove(filesystem);
context.out.String(prefix); context.out.String(" ummounted."); context.out.Ln;
ELSE
context.error.String(prefix); context.error.String(" is not a SSFS file system."); context.error.Ln;
END;
ELSE
context.error.String(prefix); context.error.String(" not found"); context.error.Ln;
END
END Unmount;
BEGIN
ASSERT(BlockSize MOD DirectoryEntrySize = 0);
END SSFS.
SystemTools.Free SSFS ~
SSFS.Format Test0#0 ~
SSFS.Mount SSFS Test0#0 ~
SSFS.Unmount SSFS ~
VirtualDisks.Create SSFS.Dsk 8000 512 ~
FSTools.DeleteFiles SSFS.Dsk ~
VirtualDisks.Install Test0 Test.Dsk ~
VirtualDisks.Uninstall Test0 ~