unit ubixfs; interface uses device, lists; const INODE_NO_FLAG = $00000000; const INODE_IN_USE = $00000001; const ATTR_INODE = $00000004; const INODE_LOGGED = $00000008; const INODE_DELETED = $00000010; const PERMANENT_FLAGS = $0000ffff; const INODE_NO_CACHE = $00010000; const INODE_WAS_WRITTEN = $00020000; const S_IFDIR = $00040000; // POSIX const NO_TRANSACTION = $00040000; const NUM_DIRECT_BLOCKS = 16; const UBIXFS_MAGIC_1 = $A0A0A0A; const UBIXFS_MAGIC_2 = $B0B0B0B; const UBIXFS_MAGIC_3 = $C0C0C0C; const UBIXFS_INODE_MAGIC = $3BBE0AD9; const UBIXFS_CLEAN = $434C454E; const UBIXFS_DIRTY = $44495254; const SECTOR_SHIFT = 9; const SECTOR_SIZE = 1 shl SECTOR_SHIFT; type int8 = shortInt; int16 = smallInt; int32 = longInt; uInt8 = byte; uInt16 = word; uInt32 = dWord; { Common Integer array definitions } type int8ArrayPtr = ^int8Array; int8Array = array[ 0..0 ] of int8; int16ArrayPtr = ^int16Array; int16Array = array[ 0..0 ] of int16; int32ArrayPtr = ^int32Array; int32Array = array[ 0..0 ] of int32; { Common Unsigned Integer array definitions } type uInt8ArrayPtr = ^uInt8Array; uInt8Array = array[ 0..0 ] of uInt8; uInt16ArrayPtr= ^uInt16Array; uInt16Array = array[ 0..0 ] of uInt16; uInt32ArrayPtr= ^uInt32Array; uInt32Array = array[ 0..0 ] of uInt32; type (* TBlockRun = packed record AG : int32; start : uInt16; len : uInt16; end; inodeAddr = TblockRun; uPtr = packed record case integer of 0: (i : int32); 1: (u : uInt32); 2: (s : single); 3: (d : double); 4: (p : pointer); 5: (offset : int64); 6: (iAddr : inodeAddr); end; *) PdiskSuperBlock = ^diskSuperBlock; diskSuperBlock = packed record name : array[0..31] of char; magic1 : int32; fsByteOrder : int32; // blockSize on disk blockSize : uInt32; // number of bits needed to shift a block number to get a byte address blockShift : uInt32; numBlocks : int64; usedBlocks : int64; inodeSize : uInt32; magic2 : uInt32; blocksPerAG : uInt32; AGShift : uInt32; numAGs : uInt32; flags : uInt32; logBlocks : TBlockRun; logStart : int64; logEnd : int64; magic3 : int32; BAT : inodeAddr; rootDir : inodeAddr; indicies : inodeAddr; pad : array[0..371] of byte; end; // diskSuperBlock PDataStream = ^TDataStream; TDataStream = packed record direct : array[0..NUM_DIRECT_BLOCKS-1] of TBlockRun; maxDirect : uInt32; maxDirectU : uInt32; // upper 32 bits indirect : TBlockRun; maxIndirect : uInt32; maxIndirectU : uInt32; // upper 32 bits doubleIndirect: TBlockrun; maxDIndirect : uInt32; maxDIndirectU : uInt32; // upper 32 bits size : uInt32; sizeU : uInt32; // upper 32 bits end; // TDataStream PubixfsInode = ^ubixfsInode; ubixfsInode = packed record magic : int32; inodeNum : inodeAddr; uid : uInt32; gid : uInt32; mode : int32; flags : int32; createTime : int64; modifiedTime : int64; parent : inodeAddr; attributes : inodeAddr; iType : uInt32; inodeSize : uInt32; data : uPtr; {this was the etc field in bfs} refCount : uInt32; blocks : TDataStream; blockCount : uInt32; smallData : array[0..0] of byte; end; // ubixfsInode PfileDescriptor = ^TfileDescriptor; TfileDescriptor = packed record magic : uInt32; inode : pointer; offset : int64; size : int64; end; // fileDescriptor vfs_abstract = object private prev : ^vfs_abstract; next : ^vfs_abstract; dev : PDevice; public constructor init; destructor done; virtual; end; // vfs_abstract PUbixFS = ^TUbixFS; TUbixFS = object(vfs_abstract) private superBlock : ^diskSuperBlock; freeBlockList : PfileDescriptor; InodeCache : PbTree; procedure assert(x:boolean); function allocBRun(blockRun:TBlockRun):TBlockRun; function allocInode(parentIAddr:inodeAddr):PUbixfsInode; function extendFile(inode:PubixfsInode; numBlocks:uInt32):integer; function findBRun(dataStream:PDataStream; blockNum:uInt32):TBlockRun; function findBRunDirect(dataStream:PDataStream; blockNum:uInt32):TBlockRun; function findBRunIndirect(dataStream:PDataStream; blockNum:uInt32):TBlockRun; function findBRunDIndirect(dataStream:PDataStream; blockNum:uInt32):TBlockRun; function freeUsedBlocks(blockRun:TBlockRun):integer; function getFileSize(inode:PUbixfsInode):uInt32; // limits to 4GB function getFileSpace(inode:PUbixfsInode):uInt32; // limits to 4GB function iAddrToStr(iAddr:inodeAddr):string; function isValidIAddr(iAddr:inodeAddr):boolean; function loadInode(iaddr:inodeAddr):PUbixfsInode; function markUsedBlocks(blockRun:TBlockRun):integer; function nextAG(AG:uInt32):uInt32; function pathToIAddr(path:PChar):inodeAddr; function readDataStream(inode:PubixfsInode; position:uInt32; buf:pointer; length:uInt32):integer; // position limits files to 4GB function saveInode(inode:PUbixfsInode):integer; procedure setBlockRun(var blockRun:TBlockRun; AG:uInt32; start, len:uInt16); procedure setIAddr(var inode:inodeAddr; AG:uInt32; start, len:uInt16); function strToIAddr(s:string):inodeAddr; function unloadInode(inode:PubixfsInode):integer; function writeDataStream(inode:PubixfsInode; position:uInt32; buf:pointer; length:uInt32):integer; // position limits files to 4GB public constructor init(device:Pdevice); procedure printSuperBlock; function vfs_fCreate(filename:PChar):int32; function vfs_fExist(path:PChar):integer; function vfs_format(device:PDevice; fsBlockSize:uInt32; quick:boolean):integer; function vfs_init:integer; function vfs_mkdir(path:PChar):integer; function vfs_read(filedes:int64; position:uInt32; buffer:pointer; size:uInt32):uInt32; // limits to 4GB function vfs_write(filedes:int64; position:uInt32; buffer:pointer; size:uInt32):uInt32; // limits to 4GB procedure examine; destructor done; virtual; end; // TUbixFS implementation uses strings, dos, math, lists; type PUbixBTreeVFS = ^TUbixBTreeVFS; TUbixBTreeVFS = object(TBTreeVFS) private fs : PUbixFS; inode : PUbixFSInode; position : uInt32; // limits files to 4GB inodeSize : uInt32; public constructor init(_fs:PUbixFS); function fClose:boolean; virtual; function fCreate(const filename:string):boolean; virtual; function fExist(const filename:string):boolean; virtual; function fOpen(const filename:string):boolean; virtual; function fRead(var buf; size:uInt32):boolean; virtual; function fSeek(offset:uInt32):boolean; virtual; function fWrite(var buf; size:uInt32):boolean; virtual; destructor done; virtual; end; // TUbixBTreeVFS constructor TUbixBTreeVFS.init; begin // writeln('TUbixBTreeVFS.init()'); fs := _fs; inode := NIL; inodeSize := 0; position := 0; end; // TUbixBTreeVFS.init; function TUbixBTreeVFS.fClose; begin if (fs = NIL) or (inode = NIL) then exit; fs^.unloadInode(inode); inode := NIL; inodeSize := 0; position := 0; end; // TUbixBTreeVFS.fClose function TUbixBTreeVFS.fCreate; begin // I *think* that this code is only called for the // B^Tree. Therefore, the file technically exists, but just // has nothing in it when we call fCreate. result := fOpen(filename); end; // TUbixBTreeVFS.fCreate function TUbixBTreeVFS.fExist; var tmpIAddr:inodeAddr; tmpInode:PUbixfsInode; begin result := FALSE; assert(fs <> NIL); if (fs = NIL) then exit; assert(fs^.superBlock <> NIL); if (fs^.superBlock = NIL) then exit; tmpIAddr := fs^.strToIAddr(filename); if not fs^.isValidIAddr(tmpIAddr) then exit; // If we've created an inode, but haven't written any data to it, then // the B^Tree hasn't written out a superblock, so technically the file doesn't // exist, and this is a really long run-on sentence. tmpInode := fs^.loadInode(tmpIAddr); result := (tmpInode <> NIL) and (tmpInode^.blocks.size <> 0); fs^.unloadInode(tmpInode); end; // TUbixBTreeVFS.fExist function TUbixBTreeVFS.fOpen; var iAddr : inodeAddr; begin result := FALSE; if (fs = NIL) then exit; if (fs^.superBlock = NIL) then exit; iAddr := fs^.strToIAddr(filename); position := 0; inode := fs^.loadInode(iAddr); result := (inode <> NIL); end; // TUbixBTreeVFS.fOpen function TUbixBTreeVFS.fRead; begin result := FALSE; if (fs = NIL) or (inode = NIL) then exit; // with inode^.inodeNum do // write('fRead(', AG, ':', start, ':', len, ', '); // write(position, ', '); // write('@buf, '); result := (fs^.readDataStream(inode, position, @buf, size) = 0); inc(position, size); end; // TUbixBTreeVFS.fRead function TUbixBTreeVFS.fSeek; begin // uhm.. if the desired offset is beyond the end of the file, // should we not extend out the file? Or does that occur in writeDataStream ... position := offset; result := TRUE; end; // TUbixBTreeVFS.fSeek function TUbixBTreeVFS.fWrite; begin result := FALSE; if (fs = NIL) or (inode = NIL) then exit; // with inode^.inodeNum do // write('fWrite(', AG, ':', start, ':', len, ', '); // write(position, ', '); // write('@buf, '); result := (fs^.writeDataStream(inode, position, @buf, size) = 0); inc(position, size); end; // TUbixBTreeVFS.fWrite; destructor TUbixBTreeVFS.done; begin if ((inode <> NIL) and (inodeSize <> 0)) then FreeMem(inode, inodeSize); inodeSize := 0; inode := NIL; fs := NIL; // don't destruct it since we didn't allocate it end; // TUbixBTreeVFS.done constructor vfs_abstract.init; begin next := NIL; prev := NIL; dev := NIL; end; // vfs_abstract.init destructor vfs_abstract.done; begin end; // vfs_abstract.done constructor TUbixFS.init; begin inherited init; freeBlockList := NIL; inodeCache := NIL; superBlock := NIL; dev := device; end; // TUbixFS.init procedure TUbixFS.assert(x:boolean); begin if not x then runerror(1000); end; // TUbixFS.assert function TUbixFS.allocBRun; var inode:PUbixfsInode; FBL:int8ArrayPtr; FBLSize:uInt32; bitOffset:uInt32; byteOffset:uInt32; curBlock, baseBlock:uInt32; freeCount:uInt32; begin setBlockRun(result, 0, 0, 0); if (superBlock = NIL) then exit; if (blockRun.AG > superBlock^.numAGs) then exit; if (superBlock^.usedBlocks + blockRun.len > superBlock^.numBlocks) then exit; // should probably give a louder warning inode := loadInode(superBlock^.BAT); assert(inode <> NIL); // should probably check for failure // now that the BAT inode is loaded, we can scan through it looking for // a free bit // we read in one block at a time FBLSize := superBlock^.blockSize; GetMem(FBL, FBLSize); assert(FBL <> NIL); fillchar(FBL^, FBLSize, 0); // better safe than sorry assert(readDataStream(inode, blockRun.AG shl (superblock^.AGShift shr 3), FBL, FBLSize) = 0); byteOffset := blockRun.start shr 3; bitOffset := 7-(blockRun.start and 7); baseBlock := (blockRun.AG shl superBlock^.AGShift) + blockRun.start; curBlock := baseBlock; freeCount := 0; // an optimization would be to check to see if the byte is -1, thus skipping 8 at a time // This will need to be implemented (but can be later) repeat inc(curBlock); if (FBL^[byteOffset] and (1 shl bitOffset) = 0) then inc(freeCount) else freeCount := 0; if (freeCount = blockRun.len) then break; if (bitOffset = 0) then begin bitOffset := 7; inc(byteOffset); end else dec(bitOffset); if (byteOffset = superBlock^.blocksPerAG) then begin byteOffset := 0; curBlock := 0; inc(blockRun.AG); if (blockRun.AG = superBlock^.numAGs) then blockRun.AG := 0; assert(readDataStream(inode, blockRun.AG shl (superblock^.AGShift shr 3), FBL, FBLSize) = 0); bitOffset := 7; end; until (curBlock = baseBlock); if (freeCount = blockRun.len) then // we found a run begin blockRun.AG := (curBlock-blockRun.len) shr superBlock^.AGShift; blockRun.start := (curBlock-blockRun.len) and (superBlock^.blocksPerAG-1); markUsedBlocks(blockRun); result := blockRun; end else writeln('Failure in allocBRun()'); FreeMem(FBL, FBLSize); assert(unloadInode(inode) = 0); end; // TUbixFS.allocBRun function TUbixFS.allocInode; var inode:PUbixfsInode; inodeSize:uInt32; blockRun:TBlockRun; begin // XXX ToDo: // need to fill in the rest of the fields in the inode record result := NIL; if (superBlock = NIL) then exit; assert(parentIAddr.AG < superBlock^.numAGs); if (parentIAddr.AG >= superBlock^.numAGs) then exit; // parent addr is outside out block count? // ask for a single block somewhere around the beginning of the requested AG setBlockRun(blockRun, parentIAddr.AG, 0, 1); blockRun := allocBRun(blockRun); assert(blockRun.len <> 0); if (blockRun.len = 0) then exit; // this would be bad inodeSize := superBlock^.inodeSize; GetMem(inode, inodeSize); assert(inode <> NIL); if (inode = NIL) then exit; fillchar(inode^, inodeSize, 0); with inode^ do begin magic := UBIXFS_INODE_MAGIC; // inodes point to themselves inodeNum := blockRun; uid := 0; // this doesn't mean much here gid := 0; // mode := ?? flags := INODE_NO_FLAG; // createTime := // modifiedTime := parent := parentIAddr; setIAddr(attributes, 0, 0, 0); inodeSize := superBlock^.inodeSize; // data := 0; refCount := 0; // maybe 1? // there's no data yet in the file, so don't // set blocks to anything (cleared above) blockCount := 0; end; // with result := inode; end; // TUbixFS.allocInode function TUbixFS.extendFile; var blockRun, exBlockRun:TBlockRun; i:integer; found:boolean; begin // XXX ToDo: // Need to add support for indirect and double-indirect blocks // Fix 4GB issue // Check to see if the block extension is the last one in direct blocks result := -1; // failure by default // basic sanity checks if (inode = NIL) or (superBlock = NIL) then exit; // if the file size is 0 then this is the first extension. if (inode^.blocks.size = 0) then // first extension begin // a file's data is stored in the next allocation group from it's parent // inode. This will help distribute the data across the disk. setBlockRun(exBlockRun, nextAG(inode^.inodeNum.AG), 0, numBlocks); exBlockRun := allocBRun(exBlockRun); // try to allocate a blockrun assert(exBlockRun.len <> 0); if (exBlockRun.len = 0) then exit; // erp. Couldn't extend the file inode^.blocks.direct[0] := exBlockRun; // Set the first direct run // The space allocated for the file can and may be different than the // actual size. Set the maxDirect to the size (in bytes) of how much space // we allocated inode^.blocks.maxDirect := numBlocks shl superBlock^.blockShift; end // size = 0 else begin // the size of the file wasn't 0 (handled above). We need to extend the // file out. Find where the last blocks of the file are // inode^.blocks.size is now a 32-bit integer because the compiler doesn't // properly support 64-bit ints. Therefore, we are limited to 4GB blockRun := findBRun(@inode^.blocks, inode^.blocks.size shr superBlock^.blockShift); assert(blockRun.len <> 0); // hm.. was exBlockRun if (blockRun.len = 0) then exit; // This would be odd exBlockRun := blockRun; // copy the last run to exBlockRun exBlockRun.len := numBlocks; // set the requested number of blocks exBlockRun := allocBRun(exBlockRun); // allocate a block run near the previous one if (exBlockRun.len = 0) then exit; // should probably yell louder about this // blockRun points to the last blockRun structure associated with this inode if (exBlockRun.AG = blockRun.AG) and (exBlockRun.start = blockRun.start+1) then begin // the extended block run continues a previous run. // scan through to find what the last run was with inode^, blocks do begin // XXX only works for direct block runs for now // I'm going to have to put a special check in to see if // we're the last entry in the direct field if (direct[high(direct)].len <> 0) then assert(false); for i := low(direct) to high(direct) do if (direct[i].len = 0) then break; assert(i <> low(direct)); // big problemo // make sure we don't overflow the length if (dword(direct[i-1].len) + numBlocks < 65535) then begin inc(direct[i-1].len, numBlocks); // update the previous entry end else // oops, we overflowed the length begin // need to check to see if we were the last direct entry. If we were // then we need to allocate an indirect block and store it in there if (i = high(direct)) then assert(false); direct[i] := exBlockRun; end; // else end; // with end else // extended blockRun isn't contiguous to the previous one begin with inode^, blocks do begin // XXX only works for direct block runs for now // I'm going to have to put a special check in to see if // we're the last entry in the direct field if (direct[high(direct)].len <> 0) then assert(false); for i := low(direct) to high(direct) do if (direct[i].len = 0) then break; // i points to the entry in the direct[] block runs to the first free slot direct[i] := exBlockRun; // set this entry to the extended block run // now update the maxDirect size maxDirect := maxDirect + numBlocks shl superBlock^.blockShift; end; // with end; // else end; // else size <> 0 inc(inode^.blockCount, numBlocks); // update the blockCount in the inode // no matter where we stored the extended blockRun, we have updated the inode assert(saveInode(inode) = 0); // 0 result means success result := 0; // no error end; // TUbixFS.extendFile function TUbixFS.findBRun; var position:uInt32; // limits filesize to 4gb begin setBlockRun(result, 0, 0, 0); if (dataStream = NIL) then exit; position := blockNum shl superBlock^.blockShift; if (position < dataStream^.maxDirect) then result := findBRunDirect(dataStream, blockNum) else if (position < dataStream^.maxIndirect) then result := findBRunIndirect(dataStream, blockNum) else if (position < dataStream^.maxDIndirect) then result := findBRunDIndirect(dataStream, blockNum) else writeln('Block position is not mapped by dataStream'); end; // TUbixFS.findBRun function TUbixFS.findBRunDirect; var maxBlock:uInt32; sum, i, offset:uInt32; begin // set the result to a nil blockrun setBlockRun(result, 0, 0, 0); // maxDirect is in bytes, so shift it down to blocks maxBlock := dword(dataStream^.maxDirect) shr superblock^.blockShift; if (blockNum > maxBlock) then begin writeln('Requested block outside direct block run range'); exit; end; sum := 0; with dataStream^ do for i := low(direct) to high(direct) do with direct[i] do begin if (blockNum < sum + len) then // found it begin offset := blockNum - sum; setBlockRun(result, AG, start + offset, len - offset); exit end; inc(sum, len); end; // for end; // TUbixFS.findBRunDirect function TUbixFS.findBRunIndirect; begin // XXX ToDo: // load and scan the indirect block for the corresponding block run setBlockRun(result, 0, 0, 0); end; // TUbixFS.findBRunIndirect function TUbixFS.findBRunDIndirect; begin // XXX ToDo: // find the position in the double-indirect range // load the appropiate double-indirect block and get the blockRun setBlockRun(result, 0, 0, 0); end; // TUbixFS.findBRunDIndirect function TUbixFS.freeUsedBlocks; var inode:PUbixfsInode; FBL:int8ArrayPtr; FBLSize:uInt32; startByte, endByte:uInt32; bitCount, bitOffset:uInt32; startBlock:uInt32; byteOffset:uInt32; begin result := -1; // basic sanity checks if ((superBlock = NIL) or (blockRun.len = 0)) then exit; // first thing to do is load the BAT inode inode := loadInode(superBlock^.BAT); assert(inode <> NIL); // Okay.. inode is the BAT inode. We need this to read from/write to the BAT // Compute how much of the BAT we have to read in. with blockRun, superBlock^ do begin startBlock := (AG shl AGShift) + start; startByte := startBlock shr 3; // divide by 8 since it's a bitfield endByte := (startBlock + len) shr 3; // divide by 8 since it's a bitfield FBLSize := endByte - startByte + 1; GetMem(FBL, FBLSize); assert(readDataStream(inode, startByte, FBL, FBLSize) = 0); bitOffset := 7-(startBlock and 7); byteOffset := 0; for bitCount := 1 to len do begin FBL^[byteOffset] := FBL^[byteOffset] and not (1 shl bitOffset); if (bitOffset = 0) then begin bitOffset := 7; inc(byteOffset); end else dec(bitOffset); end; // for bitCount // save back out the modified block assert(writeDataStream(inode, startByte, FBL, FBLSize) = 0); FreeMem(FBL, FBLSize); assert(unloadInode(inode) = 0); usedBlocks := usedBlocks - len; end; // with result := 0; end; // TUbixFS.freeUsedBlocks function TUbixFS.getFileSize; begin // XXX ToDo: // Fix to support sizes > 4GB result := 0; if (inode = NIL) then exit; // inode^.blocks.size is now a 32-bit integer. // This limits the filesize to 4GB result := inode^.blocks.size; end; // TUbixFS.getFileSize function TUbixFS.getFileSpace; begin // getFileSpace() will return the size of the file's allocated space // XXX ToDo: // Fix to support sizes > 4GB result := 0; if (inode = NIL) or (superBlock = NIL) then exit; result := inode^.blockCount shl superBlock^.blockShift; end; // TUbixFS.getFileSpace function TUbixFS.iAddrToStr; var s:string; begin // Note: this won't work in C/C++ because strings are really null // terminated character arrays. So, the B^Tree won't hold a string // with nulls in it properly length(s) := sizeof(iAddr); move(iAddr, s[1], sizeof(iAddr)); result := s; end; // TUbixFS.iAddrToStr; function TUbixFS.loadInode; var u:uPtr; inode:PUbixfsInode; begin // XXX ToDo: // Read through file cache // Do more sanity checks on the inode result := NIL; if (superBlock = NIL) or (dev = NIL) or (inodeCache = NIL) then exit; if inodeCache^.Find(@iAddr, u) then // inode was in cache begin inode := u.p; assert(inode <> NIL); end else begin // allocate space for the inode GetMem(inode, superBlock^.inodeSize); assert(inode <> NIL); // clear it (this isn't absolutely necessary, but better // safe than sorry). Also, it's only done the first time // the inode is loaded, so speed isn't as much a concern fillchar(inode^, superBlock^.inodeSize, 0); with iAddr do writeln('inode: ', AG, ':', start, ':', len); with iAddr, superBlock^ do dev^.read(dev, inode, ((AG shl AGShift) + start) * (inodeSize shr SECTOR_SHIFT), inodeSize shr SECTOR_SHIFT ); // dev^.read // should probably check more than this if (inode^.magic <> UBIXFS_INODE_MAGIC) then begin FreeMem(inode, superBlock^.inodeSize); writeln('TUbixFS.loadInode() failed to load an inode!'); exit; end; // now insert the pointer into the InodeCache u.p := inode; assert(inodeCache^.Insert(@iAddr, u)); end; inc(inode^.refCount); // increment the reference count result := inode; end; // TUbixFS.loadInode function TUbixFS.markUsedBlocks; var inode:PUbixfsInode; FBL:uInt8ArrayPtr; FBLSize:uInt32; startByte, endByte:uInt32; bitCount, bitOffset:uInt32; startBlock:uInt32; byteOffset:uInt32; begin result := -1; // basic sanity checks if ((superBlock = NIL) or (blockRun.len = 0)) then exit; with blockRun do writeln('---- marking used blocks: ', AG, ':', start, ':', len); // first thing to do is load the BAT inode inode := loadInode(superBlock^.BAT); assert(inode <> NIL); // Okay.. inode is the BAT inode. We need this to read from/write to the BAT // Compute how much of the BAT we have to read in. with blockRun, superBlock^ do begin startBlock := (AG shl AGShift) + start; startByte := startBlock shr 3; endByte := (startBlock + len) shr 3; FBLSize := endByte - startByte + 1; GetMem(FBL, FBLSize); assert(readDataStream(inode, startByte, FBL, FBLSize) = 0); fillchar(FBL^, FBLSize, 0); // better safe than sorry bitOffset := 7-(startBlock and 7); byteOffset := 0; for bitCount := 1 to len do begin FBL^[byteOffset] := FBL^[byteOffset] or (1 shl bitOffset); if (bitOffset = 0) then begin bitOffset := 7; inc(byteOffset); end else dec(bitOffset); end; // for bitCount assert(writeDataStream(inode, startByte, FBL, FBLSize) = 0); FreeMem(FBL, FBLSize); assert(unloadInode(inode) = 0); usedBlocks := usedBlocks + len; end; // with result := 0; end; // TUbixFS.markUsedBlocks function TUbixFS.nextAG; begin // there's no error we can throw from here if superblock is NIL, // so don't bother to check. Just hope whomever calls this // function has done some basic sanity checks result := (AG+1) mod superBlock^.numAGs end; // TUbixFS.nextAG function TUbixFS.pathToIAddr; function recursePath(path:PChar; len:integer; iAddr:inodeAddr):inodeAddr; var dirTree:PBTree; u:uPtr; nameSize:integer; begin // if the string length is 0 then we have found the inode in the previous // recurse, so return what we found. if (len = 0) then begin result := iAddr; exit; end else setIAddr(result, 0, 0, 0); // hm. If we're looking for something like ``/.'' then the ``/'' is stripped // off in the main call leaving only ``.'', which means that this malformed // path check would trigger. Same with /foo/bar if bar is a dir. //if uInt8ArrayPtr(path)^[0] = ord('/') then exit; // malformed path if not (isValidIAddr(iAddr)) then exit; // sanity check nameSize := 0; while (len > 0) and (uInt8ArrayPtr(path)^[nameSize] <> ord('/')) do begin inc(nameSize); dec(len); end; uInt8ArrayPtr(path)^[nameSize+1] := 0; if (len <> 0) then dec(len); new(dirTree, open(IAddrToStr(iAddr), new(PUbixBTreeVFS, init(@self)))); if (dirTree = NIL) then exit; // hm.. what happens to the PUbixBTreeVFS is this is NIL? // note that the dirTree is a tree of BT_PCHAR, and searches are case sensitive. // if the tree doesn't exist, Find() should return false. if dirTree^.Find(path, u) then result := recursePath(path + nameSize + 1, len, u.iAddr); dispose(dirTree, done); end; // recursePath var len:integer; begin setIAddr(result, 0, 0, 0); if (path = NIL) or (superBlock = NIL) then exit; len := strlen(path); if (len = 0) then exit; // empty string if uInt8ArrayPtr(path)^[0] <> ord('/') then exit; // malformed path result := recursePath(path+1, len-1, superBlock^.rootDir); end; // TUbixFS.pathToIAddr procedure TUbixFS.printSuperBlock; begin if (superBlock = NIL) then exit; with superBlock^ do begin writeln('superBlock^.name........... ', name); writeln('superBlock^.magic1......... ', hex(magic1)); writeln('superBlock^.fsByteOrder.... ', fsByteOrder); writeln('superBlock^.blockSize...... ', blockSize); writeln('superBlock^.blockShift..... ', blockShift); writeln('superBlock^.numBlocks...... ', numBlocks); writeln('superBlock^.usedBlocks..... ', usedBlocks); writeln('superBlock^.magic2......... ', hex(magic2)); writeln('superBlock^.blocksPerAG.... ', blocksPerAG); writeln('superBlock^.AGShift........ ', AGShift); writeln('superBlock^.numAGs......... ', numAGs); writeln('superBlock^.flags.......... ', hex(flags)); writeln('superBlock^.magic3......... ', hex(magic3)); with superBlock^.BAT do writeln('superBlock^.BAT............ ', AG, ':', start, ':', len); with superBlock^.rootDir do writeln('superBlock^.rootDir........ ', AG, ':', start, ':', len); end; end; // TUbixFS.printSuperBlock function TUbixFS.saveInode; begin // XXX ToDo: // Write through cache result := -1; if (superBlock = NIL) or (dev = NIL) or (inode = NIL) then exit; with inode^.inodeNum, superBlock^ do dev^.write(dev, inode, ((AG shl AGShift) + start) * (inodeSize shr SECTOR_SHIFT), inodeSize shr SECTOR_SHIFT ); // device^.write result := 0; end; // TUbixFS.saveInode function TUbixFS.isValidIAddr; begin result := (iAddr.len <> 0) and (iAddr.AG < superBlock^.numAGs); end; // TUbixFS.isValidIAddr function TUbixFS.readDataStream; var tmpBlock:uInt8ArrayPtr; blockRun:TBlockRun; size, remainder:uInt32; blockStart:uInt32; blocksToRead:uInt32; bytesToRead:uInt32; begin // XXX ToDo: // Read from cache result := -1; if ((inode = NIL) or (buf = NIL)) then exit; tmpBlock := NIL; size := length; remainder := position mod superBlock^.blockSize; if (remainder <> 0) then begin // the position we've been asked to read from doesn't align to // a block size. We need to read in the previous block and pad // the data blockStart := position shr superBlock^.blockShift; blockRun := findBRun(@inode^.blocks, blockStart); assert(blockRun.len > 0); GetMem(tmpBlock, superBlock^.blockSize); assert(tmpBlock <> NIL); if (tmpBlock = NIL) then exit; with superBlock^, blockRun do dev^.read(dev, tmpBlock, ((AG shl AGShift) + start) * (blockSize shr SECTOR_SHIFT), blockSize shr SECTOR_SHIFT ); // dev^.write bytesToRead := min(size, (superBlock^.blockSize - remainder)); move(tmpBlock^[remainder], buf^, bytesToRead); inc(buf, bytesToRead); dec(size, bytesToRead); inc(position, bytesToRead); end; // if remainder != 0 remainder := size mod superBlock^.blockSize; dec(size, remainder); // Size is currently in bytes. The remainder variable above holds the number // of remaining bytes (if any) outside full blocks we have to write. // convert the size into blocks size := size shr superBlock^.blockShift; while (size > 0) do begin blockRun := findBRun(@inode^.blocks, position shr superBlock^.blockShift); assert(blockRun.len > 0); blocksToRead := min(blockRun.len, size); with inode^, superBlock^, blockRun do dev^.read(dev, buf, ((AG shl AGShift) + start) * (blockSize shr SECTOR_SHIFT), blockSize shr SECTOR_SHIFT ); // dev^.read dec(size, blocksToRead); inc(buf, blocksToRead shl superBlock^.blockShift); end; // while if (remainder <> 0) then begin // There is less than a block of data left to read. blockStart := (position+length-remainder) shr superBlock^.blockShift; blockRun := findBRun(@inode^.blocks, blockStart); assert(blockRun.len > 0); if (tmpBlock = NIL) then GetMem(tmpBlock, superBlock^.blockSize); assert(tmpBlock <> NIL); with inode^, superBlock^, blockRun do dev^.read(dev, tmpBlock, ((AG shl AGShift) + start) * (blockSize shr SECTOR_SHIFT), blockSize shr SECTOR_SHIFT ); // dev^.read move(tmpBlock^, buf^, remainder); end; if (tmpBlock <> NIL) then FreeMem(tmpBlock, superBlock^.blockSize); result := 0; end; // TUbixFS.readDataStream procedure TUbixFS.setBlockRun; begin blockRun.AG := AG; blockRun.start := start; blockRun.len := len; end; // TUbixFS.setBlockRun procedure TUbixFS.setIAddr; begin inode.AG := AG; inode.start := start; inode.len := len; end; // TUbixFS.setIAddr function TUbixFS.strToIAddr; begin // Note: this won't work in C/C++ because strings are really null // terminated character arrays. So, the B^Tree won't hold a string // with nulls in it properly // begin fiendishly clever hack move(s[1], result, sizeof(inodeAddr)); // end fiendishly clever hack end; // TUbixFS.strToIAddr function TUbixFS.unloadInode; var u:uPtr; begin // XXX ToDo: // I guess this would be a good place to determine if the // file has been deleted, and free all resources associated // with it after the ref count drops to 0 result := -1; if (inode = NIL) or (inodeCache = NIL) then exit; dec(inode^.refCount); // decrese the reference count if (inode^.refCount = 0) then begin // the last handle to the inode was closed, so de-allocate // the memory for the inode and delete it out of the cache u.p := inode; inodeCache^.Delete(@inode^.inodeNum, u); // we have inodes in the cache, but they might have been modified // The proper solution to this is to not have an inode cache and // instead use a real block cache and write out modified blocks. // For the time being, save out this inode to the device saveInode(inode); { if (dev <> NIL) then with inode^.inodeNum, superBlock^ do dev^.write(dev, inode, ((AG shl AGShift) + start) * (inodeSize shr SECTOR_SHIFT), inodeSize shr SECTOR_SHIFT ); // device^.write } FreeMem(inode, inode^.inodeSize); end; result := 0; end; // TUbixFS.unloadInode function TUbixFS.vfs_fCreate; var inode:PUbixFSInode; dirTree:PBTree; dirIAddr, fileIAddr:inodeAddr; filedes:int64; u:uPtr; searchRec:BTreeSearchRec; len : integer; path:PChar; filenamePtr:PChar; curPos:integer; begin result := -1; if (filename = NIL) or (superBlock = NIL) then exit; // we need to split the filename into it's component path and filename pieces. // From there we need to see if the file exists. If it does then I guess we // should truncate the file and load the inode. Otherwise allocate a new inode len := strlen(filename); if (len = 0) then exit; if uInt8ArrayPtr(filename)^[0] <> ord('/') then exit; // malformed path GetMem(path, len+1); strCopy(path, filename); curPos := len; setIAddr(dirIAddr, 0, 0, 0); // zero out the inode address for the directory setIAddr(fileIAddr, 0, 0, 0); // zero out the inode address for the file while (curPos > 0) and (uInt8ArrayPtr(path)^[curPos] <> ord('/')) do dec(curPos); fileNamePtr := path + curPos + 1; if (curPos = 0) then begin // file is in the root dir writeln('path: /'); writeln('filename: ', fileNamePtr); dirIAddr := pathToIAddr('/'); end else begin uInt8ArrayPtr(path)^[curPos] := 0; // null out the separator writeln('Path: ', path); writeln('Filename: ', fileNamePtr); dirIAddr := pathToIAddr(path); end; with dirIAddr do writeln('iaddr to path: ', AG, ':', start, ':', len); assert(isValidIAddr(dirIAddr)); if not isValidIAddr(dirIAddr) then exit; // should probably check to make sure that we're opening a directory and not a file write('creating new dir tree...'); new(dirTree, open(IAddrToStr(dirIAddr), new(PUbixBTreeVFS, init(@self)))); writeln('done'); if (dirTree = NIL) then exit; if dirTree^.Find(fileNamePtr, u) then begin writeln(fileNamePtr, ' already exists'); end else begin writeln('creating new file: ', fileNamePtr); writeln('allocating new inode'); inode := allocInode(dirIAddr); // alloc a new inode (pass in the parent iaddr) assert(inode <> NIL); // make sure it isn't nil if (inode = NIL) then exit; saveInode(inode); // save the new inode u.iAddr := inode^.inodeNum; // copy the inode addr to a uPtr // now add the file to the directory writeln('Inserting ', fileNamePtr, ' into directory returned ', dirTree^.Insert(fileNamePtr, u)); end; dispose(dirTree, done); FreeMem(path, len+1); result := 0; end; // TUbixFS.vfs_fCreate function TUbixFS.vfs_fExist; begin // XXX ToDo: // Find out why fExist('/.') fails result := -1; if ((superBlock = NIL) or (path = NIL)) then exit; if (not isValidIAddr(superBlock^.rootDir)) then exit; if isValidIAddr(pathToIAddr(path)) then result := 0; end; // TUbixFS.vfs_fExist function TUbixFS.vfs_format; var sector:array[0..511] of byte; fs:PUbixFS; logicalBlocks, fsBATSize, fsBATBlocks:uInt32; sb:PdiskSuperBlock; inode:PubixfsInode; i, fsBlockShift, lBlockShift, nodeSize, keyLength:integer; blockRun:TBlockRun; remainderBlocks:uInt32; blockBit:shortint; begin result := -1; if (device = NIL) then exit; if (fsBlockSize < 1024) then begin writeln('Blocksize must be >= 1024'); exit; end; // zero out the sector fillchar(sector, sizeof(sector), 0); writeln('device^.sectors: ', device^.sectors); if not quick then begin system.write('clearing device... '); for i := 0 to device^.sectors-1 do device^.write(device, @sector, i, 1); writeln('done'); end; // if !quick fsBlockShift := 10; {minimum is 1024} while (1 shl fsBlockShift < fsBlockSize) do inc(fsBlockShift); // allocate a new superBlock and clear it new(sb); if (sb = NIL) then exit; fillchar(sb^, sizeof(diskSuperBlock), 0); // device^.sectors is the number of 512 byte sectors lBlockShift := fsBlockSize shr 9; writeln('lBlockShift: ', lBlockShift); logicalBlocks := device^.sectors div lBlockShift; // logical blocks writeln('logical blocks: ', logicalBlocks); with sb^ do begin strcopy(name, 'UbixFS'); magic1 := UBIXFS_MAGIC_1; fsByteOrder := 0; blockSize := fsBlockSize; blockShift := fsBlockShift; numBlocks := logicalBlocks; usedBlocks := 0; inodeSize := fsBlockSize; magic2 := UBIXFS_MAGIC_2; blocksPerAG := min(65536, blockSize shl 3); AGShift := 10; // realistically it should be a min of 13 while (1 shl AGShift <> blocksPerAG) do inc(AGShift); numAGs := (dword(numBlocks)+(blocksPerAG-1)) div blocksPerAG; flags := UBIXFS_CLEAN; setBlockRun(logBlocks, 0, 0, 0); logStart := 0; logEnd := 0; magic3 := UBIXFS_MAGIC_3; setIAddr(indicies, 0, 0, 0); setIAddr(rootDir, 0, 0, 0); end; // with // create the free block list inode getmem(inode, fsBlockSize); // make the inode the same size as a block if (inode = NIL) then exit; fillchar(inode^, sb^.inodeSize, 0); with inode^ do begin magic := UBIXFS_INODE_MAGIC; // inodes point to themselves setIAddr(inodeNum, 0, sb^.logBlocks.len+1, 1); // the +1 for the start might be wrong setIAddr(parent, 0, 0, 0); // If the partition is larger than 550GB we will need to allocate more than // one blockRun for the BAT. fsBATSize := logicalBlocks shr 3; // compute how many bytes the BAT takes up fsBATBlocks := (fsBATSize + fsBlockSize-1) div fsBlockSize; writeln('fsBATSize: ', fsBATSize); writeln('fsBATBlocks: ', fsBATBlocks); blockCount := fsBATBlocks; setBlockRun(blocks.direct[0], 0, inodeNum.start+1, fsBATBlocks); blocks.size := (fsBATBlocks * fsBlockSize); // round the file size up to the nearest block size blocks.maxDirect := blocks.size-1; uid := 0; gid := 0; mode := S_IFDIR; flags := INODE_NO_FLAG; setIAddr(attributes, 0, 0, 0); inodeSize := sb^.inodeSize; end; {with} sb^.BAT := inode^.inodeNum; writeln('writing out superblock to sector 1'); device^.write(device, sb, 1, 1); // note that the superblock does not contain proper inode addresses for the // BAT and rootDir. Those are sync'd to disk in the destructor after they've been // created below. system.write('Writing out BAT inode... '); // write out the Block Allocation Table inode with inode^.inodeNum, sb^ do device^.write(device, inode, ((AG shl AGShift) + start) * (inodeSize shr SECTOR_SHIFT), inodeSize shr SECTOR_SHIFT ); // device^.write writeln('done'); // Okay, the superblock is the only piece of information needed // to mount an UbixFS partition new(fs, init(device)); assert(fs <> NIL); assert(fs^.vfs_init() = 0); // The freshly formatted parition is now mounted. // From here we need to create: // free block list file contents // journal // root dir // inode still points to the BAT. Clear out the file in case people decided to // use quick formatting for i := 0 to (dword(inode^.blocks.size) div sizeof(sector))-1 do fs^.writeDataStream(inode, i*sizeof(sector), @sector, sizeof(sector)); setBlockRun(blockRun, 0, 0, 1); fs^.markusedBlocks(blockRun); // mark superBlock block as used // mark the journal as used here fs^.markUsedBlocks(inode^.inodeNum); // mark the BAT inode as used // now mark the BAT file blocks as used. Note that if the BAT takes up // more than one blockRun (unlikely, but not impossible) then this code // will have to check for that. writeln('BAT inode blocks.direct[0].len: ', inode^.blocks.direct[0].len); fs^.markUsedBlocks(inode^.blocks.direct[0]); // If the number of blocksPerAG doesn't divide evenly into the // number of logical blocks, mark out all the "remainder" blocks // We need to mark the blocks as used in the FBL manually because // if we do it through markUsedBlocks() it will change the usedBlocks // count. remainderBlocks := (fsBATBlocks shl sb^.blockShift) - fsBATSize; writeln('RemainderBlocks: ', remainderBlocks); if (remainderBlocks <> 0) then begin // check if the remainder blocks are evenly divisible by 8 if (remainderBlocks and 7 <> 0) then begin blockBit := uInt8(1 shl (remainderBlocks and 7)) -1; fs^.writeDataStream(inode, fsBatSize, @blockBit, 1); blockBit := -1; for i := fsBatSize+1 to (fsBATBlocks shl sb^.blockShift)-1 do fs^.writeDataStream(inode, i, @blockBit, 1); end else begin blockBit := -1; for i := fsBatSize to (fsBATBlocks shl sb^.blockShift)-1 do fs^.writeDataStream(inode, i, @blockBit, 1); end; end; fs^.vfs_mkdir('/'); //writeln(fs^.vfs_fExist('/.')); // <- this blows up!? fs^.printSuperBlock; dispose(fs, done); writeln('Format complete!'); dispose(sb); freemem(inode, fsBlockSize); end; // TUbixfS.vfs_format function TUbixFS.vfs_init; begin // This function should probably not be called more than once without // calling the appropiate shutdown code writeln('TUbixFS.vfs_init()'); result := -1; if (dev = NIL) then exit; new(superBlock); assert(superBlock <> NIL); if (superBlock = NIL) then exit; fillchar(superBlock^, sizeof(superBlock^), 0); // better safe than sorry // read in the superBlock. It's always the 1st block on the partition (counting from 0) system.write('reading in superBlock... '); dev^.read(dev, superBlock, 1, 1); writeln('done'); assert(superBlock^.magic1 = UBIXFS_MAGIC_1); assert(superBlock^.magic2 = UBIXFS_MAGIC_2); assert(superBlock^.magic3 = UBIXFS_MAGIC_3); assert(strcomp(superBlock^.name, 'UbixFS') = 0); assert((1 shl superBlock^.blockShift) = superBlock^.blockSize); assert((1 shl superBlock^.AGShift) = superBlock^.blocksPerAG); assert(superBlock^.flags = UBIXFS_CLEAN); result := 0; // success // create the file decriptor tree new(inodeCache, init('', 256, BT_INT64, NIL)); end; // TUbixFS.vfs_init function TUbixFS.vfs_mkdir; const dot:array[0..1] of char = '.'#0; dotdot:array[0..2] of char = '..'#0; var inode:PUbixFSInode; iAddr:inodeAddr; bRun:TBlockRun; len:integer; dirTree:PbTree; u:uPtr; begin // XXX ToDo: // Ability to add sub directories result := -1; assert(path <> NIL); assert(superBlock <> NIL); if (path = NIL) or (superBlock = NIL) then exit; len := strlen(path); assert(len <> 0); if (len = 0) then exit; if (len = 1) and (uInt8ArrayPtr(path)^[0] = ord('/')) then begin writeln('Creating root directory'); // wanting to create the root. Check to see if the root dir inode is empty assert(not isValidIAddr(superBlock^.rootDir)); if isValidIAddr(superBlock^.rootDir) then exit; // should set a proper error code here setIAddr(iAddr, 0, 0, 0); // technically the "parent" iaddr inode := allocInode(iAddr); // allocate an inode (parent points to la-la land) assert(inode <> NIL); // make sure it isn't nil if (inode = NIL) then exit; superBlock^.rootDir := inode^.inodeNum; // assign the root dir saveInode(inode); // save the inode u.iAddr := inode^.inodeNum; // copy the inode addr to a uPtr // open the root dir with superBlock^.rootDir do writeln('Root dir inode: ', AG, ':', start, ':', len); writeln('Opening root dir tree'); new(dirTree, init(iAddrToStr(superBlock^.rootDir), 1024, BT_PCHAR, new(PUbixBTreeVFS, init(@self)))); writeln('Inserting ''.'''); // Get ready to insert the '.' :: inode^.inodeNum key :: value pair writeln('dirTree^.Insert(@dot, u) returned ', dirTree^.Insert(@dot, u)); // '.' points to ourself dispose(dirTree, done); // close and save the directory // saveInode(inode); // save the inode freemem(inode, inode^.inodeSize); // free the memory associate with the inode (saved above) end else begin // subdir support goes here end; result := 0; end; // TUbixFS.vfs_mkdir function TUbixFS.vfs_read; var inode:PUbixFSInode; u:uPtr; begin result := -1; if (buffer = NIL) or (superBlock = NIL) or (inodeCache = NIL) then exit; if not inodeCache^.Find(@filedes, u) then exit; inode := u.p; if (inode = NIL) then exit; assert(readDataStream(inode, position, buffer, size) = 0); result := size; end; // TUbixFS.vfs_read function TUbixFS.vfs_write; var inode:PUbixFSInode; u:uPtr; begin result := -1; if (buffer = NIL) or (inodeCache = NIL) then exit; if not inodeCache^.Find(@filedes, u) then exit; inode := u.p; if (inode = NIL) then exit; assert(writeDataStream(inode, position, buffer, size) = 0); result := size; end; // TUbixFS.vfs_write function TUbixFS.writeDataStream; var tmpBlock:uInt8ArrayPtr; blockRun:TBlockRun; size, remainder:uInt32; blockStart:uInt32; blocksToWrite:uInt32; bytesToWrite:uInt32; inodeChanged:boolean; begin // XXX ToDo: // Read/Write through cache // fix block extension size result := -1; if ((inode = NIL) or (buf = NIL) or (superBlock = NIL)) then exit; tmpBlock := NIL; inodeChanged := FALSE; if (position + length > getFileSpace(inode)) then begin // the next line is slightly wrong. We extend 4 blocks, // but we may be writing out more than 4 blocks worth of // data. extendFile(inode, 4); // XXX pick a reasonable amount to extend by inode^.blocks.size := position+length; // update the blocks writeln('inode^.blocks.size = ', inode^.blocks.size); inodeChanged := TRUE; end else if (position + length > inode^.blocks.size) then begin // The above case checks to see if we're writing out past // the end of the allocated space on the device. If we do, we extend // out the file. However, if we're just writing past the end of // the file, we have to update the size. inodeChanged := TRUE; inode^.blocks.size := position+length; end; size := length; remainder := position mod superBlock^.blockSize; if (remainder <> 0) then begin // the position we've been asked to write to doesn't align to // a block size. We need to read in the previous block and pad // the data blockStart := position shr superBlock^.blockShift; blockRun := findBRun(@inode^.blocks, blockStart); assert(blockRun.len > 0); GetMem(tmpBlock, superBlock^.blockSize); assert(tmpBlock <> NIL); if (tmpBlock = NIL) then exit; // read in the block readDataStream(inode, blockStart, tmpBlock, superBlock^.blockSize); bytesToWrite := min(size, (superBlock^.blockSize - remainder)); move(buf^, tmpBlock^[remainder], bytesToWrite); with inode^, superBlock^, blockRun do dev^.write(dev, tmpBlock, ((AG shl AGShift) + start) * (blockSize shr SECTOR_SHIFT), blockSize shr SECTOR_SHIFT ); // dev^.write inc(buf, bytesToWrite); dec(size, bytesToWrite); inc(position, bytesToWrite); end; // if remainder != 0 remainder := size mod superBlock^.blockSize; dec(size, remainder); // Size is currently in bytes. The remainder variable above holds the number // of remaining bytes (if any) outside full blocks we have to write. // convert the size into blocks size := size shr superBlock^.blockShift; while (size > 0) do begin blockRun := findBRun(@inode^.blocks, position shr superBlock^.blockShift); assert(blockRun.len > 0); blocksToWrite := min(blockRun.len, size); with inode^, superBlock^, blockRun do dev^.write(dev, buf, ((AG shl AGShift) + start) * (blockSize shr SECTOR_SHIFT), blockSize shr SECTOR_SHIFT ); // dev^.write dec(size, blocksToWrite); inc(buf, blocksToWrite shl superBlock^.blockShift); end; // while if (remainder <> 0) then begin // There is less than a block of data left to write. Read in the old block blockStart := (position+length-remainder) shr superBlock^.blockShift; blockRun := findBRun(@inode^.blocks, blockStart); assert(blockRun.len > 0); if (tmpBlock = NIL) then GetMem(tmpBlock, superBlock^.blockSize); assert(tmpBlock <> NIL); if (tmpBlock = NIL) then exit; // read in the block readDataStream(inode, blockStart, tmpBlock, superBlock^.blockSize); move(buf^, tmpBlock^[0], remainder); with inode^, superBlock^, blockRun do dev^.write(dev, tmpBlock, ((AG shl AGShift) + start) * (blockSize shr SECTOR_SHIFT), blockSize shr SECTOR_SHIFT ); // dev^.write end; if (tmpBlock <> NIL) then FreeMem(tmpBlock, superBlock^.blockSize); if (inodeChanged) then saveInode(inode); result := 0; end; // TUbixFS.writeDataStream destructor TUbixFS.done; var searchRec:BTreeSearchRec; inode:PUbixfsInode; begin inherited done; if (InodeCache <> NIL) then begin if (InodeCache^.getFirstKey(searchRec)) then repeat inode := searchRec.value.p; if (inode <> NIL) then unloadInode(inode); //if (inode <> NIL) then freemem(inode, inode^.inodeSize); until not InodeCache^.findNext(searchRec); writeln('inodes in InodeCache: ', InodeCache^.GetKeyCount()); dispose(InodeCache, done); InodeCache := NIL; end; // we need to sync the superblock back out to disk // Doing this should probably be done in sync() instead of here. // We should also probably honour the clean/dirty flags and only // write it out when needed. if (dev <> NIL) then dev^.write(dev, superBlock, 1, 1); if (superBlock <> NIL) then dispose(superBlock); end; // TUbixFS.done procedure TUbixFS.examine; var dirTree:PBTree; searchRec:BTreeSearchRec; u:uPtr; begin writeln('------------- examining FS -----------------'); write('Opening root dir...'); new(dirTree, open(IAddrToStr(superblock^.rootDir), new(PUbixBTreeVFS, init(@self)))); //dirTree^.PrintList('rootDir.txt'); writeln('done'); writeln('Root directory has ', dirTree^.getKeyCount(), ' entrie(s)'); write('Getting first key...'); dirTree^.getFirstKey(searchRec); writeln('done'); writeln('key: ', pchar(searchRec.key)^); with searchRec.value.iAddr do writeln('value: ', AG, ':', start, ':', len); dispose(dirTree, done); writeln('------------------ done --------------------'); end; end.