NFSTestImpl.mesa
Copyright Ó 1989 by Xerox Corporation. All rights reserved.
Bob Hagmann March 22, 1989 8:13:58 am PST
DIRECTORY
Basics,
BasicTime,
Camelot,
Commander,
CommandTool,
Convert,
File,
FS,
IO,
Mach,
PBasics,
Process,
Random,
RefText,
Rope,
SunMount,
SunNFS,
YggDID,
YggDIDPrivate,
YggDIDMapPrivate,
YggdrasilInit,
YggLock,
YggFixedNames,
YggEnvironment,
YggFile,
YggFileStream,
YggIndexMaint,
YggInternal,
YggMonitoringLog,
YggNFS,
YggTransaction;
NFSTestImpl: CEDAR PROGRAM
IMPORTS Basics, BasicTime, Commander, CommandTool, Convert, IO, Process, Random, RefText, Rope, YggDID, YggdrasilInit, YggFile, YggNFS
EXPORTS YggDID, YggInternal
= BEGIN
ROPE: TYPE = Rope.ROPE;
junk
DID: PUBLIC TYPE ~ REF DIDRep;
DIDRep: PUBLIC TYPE ~ YggDIDPrivate.DIDRep;
Document: TYPE = REF DocumentRep;
DocumentRep: PUBLIC TYPE = YggDIDMapPrivate.DocumentRep;
Global data
sunEpoch: BasicTime.GMT ← BasicTime.Pack[
[year~1970, month~January, day~1, hour~0, minute~0, second~0, zone~0, dst~no]
];
randomStream: Random.RandomStream;
RandomSequenceSize: INT = 20;
1) make a new object with int contents of 77
1a) bug: now it looks it up
2) add one attribute to it: "two" with two values: (NIL with a "foo", and field2 with a did of 1234 and a string of bar)
RandomSequence: ARRAY [0..RandomSequenceSize) OF INT ← [3, 1, 77, 16, 0, 2, 2, 1, 3, 0, 2, 6, 1234, 3, 1, 13, 0, 52, 4, 77];
KnownDirectoryINodes: LIST OF REF DirectoryINode ← NIL;
NumberOfKnownDirectoryINodess: INT ← 0;
NumberOfRMDirectories: INT ← 0;
FailedToRMDirCount: INT ← 0;
DirectoryStuff: TYPE = RECORD [
directoryName: REF TEXT,
dirINode: REF DirectoryINode
];
DirectoryINode: TYPE = RECORD [
fHandle: SunNFS.FHandle,
inDirectory: LIST OF REF DirectoryINode,
filesInDirectory: LIST OF FileStuff,
directoriesInDirectory: LIST OF DirectoryStuff,
isRoot: BOOLFALSE,
rmOnly: BOOLFALSE
];
KnownFiles: LIST OF FileStuff ← NIL;
NumberOfKnownFiles: INT ← 0;
FileStuff: TYPE = RECORD [
fileName: REF TEXT,
inode: REF INodeStuff
];
INodeStuff: TYPE = RECORD [
fHandle: SunNFS.FHandle,
inDirectory: LIST OF REF DirectoryINode,
contents: REF TEXT,
mode: CARD,
uid: CARD,
gid: CARD
];
MaxFileSize: INT ← 1234;
ReadBuffer: REF TEXT;
VolatilizeTest Command
NFSTestProc: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
randomIndex: INT ← 0;
replyLookup: SunNFS.DirOpRes;
CheckAFile: PROC [lofs: LIST OF FileStuff] ~ {
reply: SunNFS.AttrStat;
gotAHit: BOOLFALSE;
FOR dirs: LIST OF REF DirectoryINode ← lofs.first.inode.inDirectory, dirs.rest UNTIL dirs = NIL DO
matchedOK: BOOL ← FALSE;
FOR files: LIST OF FileStuff ← dirs.first.filesInDirectory, files.rest UNTIL files = NIL DO
IF files.first.inode = lofs.first.inode AND RefText.Equal[files.first.fileName, lofs.first.fileName] THEN {matchedOK ← TRUE; EXIT;};
REPEAT FINISHED => ERROR;
ENDLOOP;
IF ~matchedOK THEN LOOP;
gotAHit ← TRUE;
replyLookup ← YggNFS.Lookup[[dir: dirs.first.fHandle, name: lofs.first.fileName]];
IF replyLookup.status # ok THEN ERROR;
IF ~RefText.Equal[replyLookup.file, lofs.first.inode.fHandle] THEN ERROR;
ENDLOOP;
IF ~gotAHit THEN ERROR;
reply ← YggNFS.Getattr[lofs.first.inode.fHandle];
IF reply.status # ok THEN ERROR;
IF reply.attributes.mode # lofs.first.inode.mode THEN ERROR;
IF reply.attributes.uid # lofs.first.inode.uid THEN ERROR;
IF reply.attributes.gid # lofs.first.inode.gid THEN ERROR;
IF reply.attributes.size # RefText.Length[lofs.first.inode.contents] THEN ERROR;
SELECT CARD[Basics.BITAND[reply.attributes.mode, YggNFS.typeBits]] FROM
YggNFS.regularModeBits => {
reply ← YggNFS.Read[file: lofs.first.inode.fHandle, offset: 0, count: reply.attributes.size, block: ReadBuffer];
IF reply.status # ok THEN ERROR;
IF ~RefText.Equal[ReadBuffer, lofs.first.inode.contents] THEN ERROR;
};
YggNFS.symbolicLinkModeBits => {
status: SunNFS.Stat;
path: SunNFS.Path;
[status: status, data: path] ← YggNFS.Readlink[file: lofs.first.inode.fHandle];
IF status # ok THEN ERROR;
IF ~RefText.Equal[path, lofs.first.inode.contents] THEN ERROR;
};
ENDCASE => ERROR;
reply ← YggNFS.Read[file: lofs.first.inode.fHandle, offset: 0, count: reply.attributes.size, block: ReadBuffer];
IF reply.status # ok THEN ERROR;
IF ~RefText.Equal[ReadBuffer, lofs.first.inode.contents] THEN ERROR;
};
CheckADirectory: PROC [lodin: LIST OF REF DirectoryINode] ~ {
cookie: SunNFS.Cookie ← NIL;
fileNameList: LIST OF REF TEXTNIL;
files: LIST OF Rope.ROPENIL;
sawPointPoint: BOOLFALSE;
allAtOnceFiles: LIST OF Rope.ROPENIL;
allAtOnceSawPointPoint: BOOLFALSE;
allAtOnceNoOfNFSItems: INT ← 0;
noOfFiles: INT ← 0;
noOfDirs: INT ← 0;
noOfNFSItems: INT ← 0;
FOR files: LIST OF FileStuff ← lodin.first.filesInDirectory, files.rest UNTIL files = NIL DO
CheckAFile[files];
noOfFiles ← noOfFiles + 1;
ENDLOOP;
FOR dirsInDir: LIST OF DirectoryStuff ← lodin.first.directoriesInDirectory, dirsInDir.rest UNTIL dirsInDir = NIL DO
noOfDirs ← noOfDirs + 1;
FOR parentDirs: LIST OF REF DirectoryINode ← dirsInDir.first.dirINode.inDirectory, parentDirs.rest UNTIL parentDirs = NIL DO
IF parentDirs.first = lodin.first THEN EXIT;
REPEAT FINISHED => ERROR;
ENDLOOP;
ENDLOOP;
{
XeachDir: YggNFS.EachDirEntryProc ~ {
PROC [fileid: CARD, filename: -- ephemeral -- FileName]
RETURNS [accept: BOOL, continue: BOOLTRUE];
IF RefText.Equal[s1: filename, s2: "..", case: FALSE] THEN {
allAtOnceSawPointPoint ← TRUE;
RETURN;
};
allAtOnceFiles ← CONS[Rope.FromRefText[filename], allAtOnceFiles];
allAtOnceNoOfNFSItems ← allAtOnceNoOfNFSItems + 1;
};
status: SunNFS.Stat;
eof: BOOLFALSE;
[status: status, eof: eof] ← YggNFS.Readdir[dir: lodin.first.fHandle, cookie: NIL, count: 1000, eachDirEntry: XeachDir];
IF status # ok THEN ERROR;
IF ~eof THEN ERROR;
};
DO
eachDir: YggNFS.EachDirEntryProc ~ {
PROC [fileid: CARD, filename: -- ephemeral -- FileName]
RETURNS [accept: BOOL, continue: BOOLTRUE];
IF RefText.Equal[s1: filename, s2: "..", case: FALSE] THEN {
sawPointPoint ← TRUE;
RETURN;
};
fileNameList ← CONS[filename, fileNameList];
files ← CONS[Rope.FromRefText[filename], files];
noOfNFSItems ← noOfNFSItems + 1;
};
status: SunNFS.Stat;
eof: BOOLFALSE;
[status: status, eof: eof, newCookie: cookie] ← YggNFS.Readdir[dir: lodin.first.fHandle, cookie: cookie, count: RandomChooseInt[randomStream, 1, 5], eachDirEntry: eachDir];
IF status # ok THEN ERROR;
IF eof THEN EXIT;
ENDLOOP;
IF noOfNFSItems # (noOfFiles + noOfDirs) THEN ERROR;
FOR fnl: LIST OF REF TEXT ← fileNameList, fnl.rest UNTIL fnl = NIL DO
foundIt: BOOLFALSE;
FOR files: LIST OF FileStuff ← lodin.first.filesInDirectory, files.rest UNTIL files = NIL DO
IF RefText.Equal[fnl.first, files.first.fileName] THEN {foundIt ← TRUE; EXIT};
ENDLOOP;
IF foundIt THEN LOOP;
FOR dirs: LIST OF DirectoryStuff ← lodin.first.directoriesInDirectory, dirs.rest UNTIL dirs = NIL DO
IF RefText.Equal[fnl.first, dirs.first.directoryName] THEN {foundIt ← TRUE; EXIT};
ENDLOOP;
IF ~foundIt THEN ERROR;
ENDLOOP;
FOR files: LIST OF FileStuff ← lodin.first.filesInDirectory, files.rest UNTIL files = NIL DO
CheckAFile[files];
ENDLOOP;
};
argv: CommandTool.ArgumentVector;
out: IO.STREAM;
noisy: BOOLFALSE;
fixedRandom: BOOLFALSE;
offset: INT ← 0;
noTests: INT;
mntStatus: SunMount.FHStatus;
mountINode: REF DirectoryINode;
startTime: BasicTime.GMT ← BasicTime.Now[];
out ← cmd.out;
argv ← CommandTool.Parse[cmd: cmd ! CommandTool.Failed => {msg ← errorMsg; GO TO failed}];
IF argv.argc < 2 THEN GOTO failed;
DO
IF Rope.InlineFetch[argv[offset+1], 0] = '- THEN {
c: CHAR;
IF argv[offset+1].Length[] # 2 THEN GOTO failed;
c ← argv[offset+1].InlineFetch[1] ;
SELECT c FROM
'n => noisy ← TRUE;
'f => fixedRandom ← TRUE;
ENDCASE;
offset ← offset + 1;
}
ELSE EXIT;
ENDLOOP;
noTests ← Convert.IntFromRope[argv[1+offset] ! Convert.Error => GOTO failed];
KnownDirectoryINodes ← NIL;
NumberOfKnownDirectoryINodess ← 0;
NumberOfRMDirectories ← 0;
FailedToRMDirCount ← 0;
KnownFiles ← NIL;
NumberOfKnownFiles ← 0;
IF noTests <= 0 THEN RETURN;
YggdrasilInit.Initialize["bogus", "pooz"];
randomStream ← Random.Create[range: 16384, seed: -1] ;
IF YggNFS.MakeFileSystem[] THEN ERROR;
mntStatus ← YggNFS.Mnt["/"];
IF mntStatus.status # 0 THEN ERROR;
mountINode ← NEW[DirectoryINode ← [mntStatus.directory, NIL, NIL, NIL, TRUE]];
KnownDirectoryINodes ← LIST[mountINode];
FOR loopCount: INT IN (0..noTests] DO
rand: INT ← RandomChooseInt[randomStream, 0, 40];
Process.CheckForAbort[];
IF noisy AND (loopCount MOD 100) = 0 THEN {
freePages: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
out.PutF["..."];
PrintTimeOnOut[startTime, out];
out.PutF["(loopCount %g, number of files %g, free pages %g, at %g)...\n", IO.int[loopCount], IO.int[NumberOfKnownFiles], IO.int[freePages], IO.time[]];
};
IF loopCount = noTests THEN rand ← 0;
SELECT rand FROM
IN [0..1) => { -- look up all old files
lofs: LIST OF FileStuff;
lodin: LIST OF REF DirectoryINode;
skipMax: INT ← 1;
skipCnt: INT ← 0;
IF NumberOfKnownFiles <= 0 THEN LOOP;
SELECT loopCount FROM
IN [250..500) => skipMax ← RandomChooseInt[randomStream, 1, 3];
IN [500..1000) => skipMax ← RandomChooseInt[randomStream, 1, 7];
IN [1000..2000) => skipMax ← RandomChooseInt[randomStream, 1, 15];
IN [2000..4000) => skipMax ← RandomChooseInt[randomStream, 1, 33];
IN [4000..8000) => skipMax ← RandomChooseInt[randomStream, 1, 67];
IN [8000..16000) => skipMax ← RandomChooseInt[randomStream, 1, 155];
ENDCASE;
IF loopCount = noTests THEN skipMax ← 1;
IF noisy THEN {
out.PutF["\n"];
PrintTimeOnOut[startTime, out];
out.PutF["Start Look Up All Old files/directories, loopCount= %g, skipMax= %g\n", IO.int[loopCount], IO.int[skipMax]];
};
FOR lofs ← KnownFiles, lofs.rest UNTIL lofs = NIL DO
skipCnt ← skipCnt + 1;
IF skipCnt >= skipMax THEN skipCnt ← 0 ELSE LOOP;
CheckAFile[lofs];
ENDLOOP;
FOR lodin ← KnownDirectoryINodes, lodin.rest UNTIL lodin = NIL DO
skipCnt ← skipCnt + 1;
IF skipCnt >= skipMax THEN skipCnt ← 0 ELSE LOOP;
CheckADirectory[lodin];
ENDLOOP;
IF noisy THEN {
PrintTimeOnOut[startTime, out];
out.PutF["Look Up All Old files/directories Done\n\n"];
};
};
IN [1..13) => { -- make new file with a contents; sometimes the file is a symbolic link
rand2: INT;
lodin: LIST OF REF DirectoryINode;
now: SunNFS.TimeVal;
symbolicLink: BOOLFALSE;
mode: CARD;
uid: CARD;
gid: CARD;
oldFree: INT;
newFileName: REF TEXT;
contents: REF TEXT;
reply: SunNFS.DirOpRes;
replyAttr: SunNFS.AttrStat;
status: SunNFS.Stat;
now ← SunTimeFromGMT[BasicTime.Now[]];
rand2 ← RandomChooseInt[randomStream, 0, NumberOfKnownDirectoryINodess];
FOR lodin ← KnownDirectoryINodes, lodin.rest UNTIL lodin = NIL DO
rand2 ← rand2 - 1;
IF rand2 <= 0 AND ~lodin.first.rmOnly THEN EXIT;
ENDLOOP;
IF lodin = NIL THEN LOOP;
IF lodin.first.isRoot AND NumberOfKnownDirectoryINodess # 0 THEN LOOP; -- root directory, and there are others
newFileName ← PickAName[lodin];
uid ← PickUID[];
gid ← PickGID[];
oldFree ← YggFile.ServerInfo[].secondaryBlocksFree;
IF RandomChooseInt[randomStream, 0, 6] = 3 THEN {
lookupStat: SunNFS.DirOpRes;
symbolicLink ← TRUE;
mode ← PickMode[] + YggNFS.symbolicLinkModeBits;
contents ← PickAName[NIL];
status ← YggNFS.Symlink[from: [dir: lodin.first.fHandle, name: newFileName], to: contents, attributes: [mode: mode, uid: uid, gid: gid, size: RefText.Length[contents], atime: now, mtime: now]];
IF status # ok THEN ERROR;
lookupStat ← YggNFS.Lookup[which: [dir: lodin.first.fHandle, name: newFileName]];
IF lookupStat.status # ok THEN ERROR;
reply.file ← lookupStat.file;
}
ELSE {
contents ← MakeUpContents[];
mode ← PickMode[] + YggNFS.regularModeBits;
reply ← YggNFS.Create[where: [dir: lodin.first.fHandle, name: newFileName], attributes: [mode: mode, uid: uid, gid: gid, size: RefText.Length[contents], atime: now, mtime: now]];
IF reply.status # ok THEN ERROR;
replyAttr ← YggNFS.Write[file: reply.file, offset: 0, count: RefText.Length[contents], block: contents];
IF replyAttr.status # ok THEN ERROR;
};
NumberOfKnownFiles ← NumberOfKnownFiles + 1;
lodin.first.filesInDirectory ← CONS[[newFileName, NEW[INodeStuff ← [fHandle: reply.file, inDirectory: LIST[lodin.first], contents: contents, mode: mode, uid: uid, gid: gid]]], lodin.first.filesInDirectory];
KnownFiles ← CONS[lodin.first.filesInDirectory.first, KnownFiles];
IF noisy THEN {
newFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
newDID: DID ← DIDFromFHandle[reply.file];
preferedPrintName: Rope.ROPE;
preferedPrintName ← MakePreferedPrintName[lodin.first.filesInDirectory];
IF symbolicLink THEN out.PutF["Make new symbolic link: name= %g, did: %g, took %g pages\n", IO.rope[preferedPrintName], IO.card[newDID.didLow], IO.int[oldFree - newFree] ]
ELSE out.PutF["Make new file: name= %g, did: %g, took %g pages\n", IO.rope[preferedPrintName], IO.card[newDID.didLow], IO.int[oldFree - newFree] ];
};
};
IN [13..19) => { -- delete a file
rand2: INT;
fileCount: INT ← 0;
lodin: LIST OF REF DirectoryINode;
lofiles: LIST OF FileStuff;
allKnownFiles: LIST OF FileStuff ← NIL;
prevAllKnownFiles: LIST OF FileStuff ← NIL;
prevFiles: LIST OF FileStuff ← NIL;
lordin: LIST OF REF DirectoryINode;
prevDir: LIST OF REF DirectoryINode;
dirINode: REF DirectoryINode;
status: SunNFS.Stat;
oldFree: INT;
IF NumberOfKnownFiles = 0 THEN LOOP;
rand2 ← RandomChooseInt[randomStream, 0, NumberOfKnownDirectoryINodess];
FOR lodin ← KnownDirectoryINodes, lodin.rest UNTIL lodin = NIL DO
rand2 ← rand2 - 1;
IF rand2 <= 0 THEN EXIT;
ENDLOOP;
IF lodin = NIL THEN LOOP;
dirINode ← lodin.first;
FOR lofs: LIST OF FileStuff ← dirINode.filesInDirectory, lofs.rest UNTIL lofs = NIL DO
fileCount ← fileCount + 1;
ENDLOOP;
IF fileCount = 0 THEN LOOP;
rand2 ← RandomChooseInt[randomStream, 0, fileCount-1];
FOR lofiles ← dirINode.filesInDirectory, lofiles.rest UNTIL lofiles = NIL DO
rand2 ← rand2 -1;
IF rand2 <= 0 THEN EXIT;
prevFiles ← lofiles;
ENDLOOP;
IF lofiles = NIL THEN LOOP;
oldFree ← YggFile.ServerInfo[].secondaryBlocksFree;
status ← YggNFS.Remove[which: [dir: dirINode.fHandle, name: lofiles.first.fileName]];
IF status # ok THEN ERROR;
NumberOfKnownFiles ← NumberOfKnownFiles - 1;
FOR allKnownFiles ← KnownFiles, allKnownFiles.rest UNTIL allKnownFiles = NIL DO
IF RefText.Equal[lofiles.first.fileName, allKnownFiles.first.fileName] AND (lofiles.first.inode = allKnownFiles.first.inode) THEN EXIT;
prevAllKnownFiles ← allKnownFiles;
REPEAT FINISHED => ERROR;
ENDLOOP;
IF prevAllKnownFiles = NIL THEN KnownFiles ← allKnownFiles.rest
ELSE prevAllKnownFiles.rest ← allKnownFiles.rest;
IF prevFiles = NIL THEN dirINode.filesInDirectory ← lofiles.rest
ELSE prevFiles.rest ← lofiles.rest;
FOR lordin ← lofiles.first.inode.inDirectory, lordin.rest UNTIL lordin = NIL DO
IF lordin.first = dirINode THEN {
EXIT;
};
prevDir ← lordin;
REPEAT FINISHED => ERROR;
ENDLOOP;
IF prevDir = NIL THEN lofiles.first.inode.inDirectory ← lordin.rest
ELSE prevDir.rest ← lordin.rest;
IF lofiles.first.inode.inDirectory # NIL THEN { -- multi-hard link file has had one of it's links deleted. Does the other one still work?
FOR files: LIST OF FileStuff ← lofiles.first.inode.inDirectory.first.filesInDirectory, files.rest UNTIL files = NIL DO
IF files.first.inode = lofiles.first.inode THEN {
CheckAFile[files];
EXIT;
};
REPEAT FINISHED => ERROR;
ENDLOOP;
CheckADirectory[lofiles.first.inode.inDirectory]; -- check the directory too
};
IF noisy THEN {
newFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
out.PutF["Delete file: name= %g in directory name= %g, got %g pages\n", IO.rope[RefText.TrustTextAsRope[lofiles.first.fileName]], IO.rope[MakePreferedDirPrintName[dirINode]], IO.int[newFree - oldFree] ];
};
};
IN [19..21) => { -- make new directory
rand2: INT;
lodin: LIST OF REF DirectoryINode;
now: SunNFS.TimeVal;
mode: CARD;
uid: CARD;
gid: CARD;
oldFree: INT;
newDirName: REF TEXT;
reply: SunNFS.DirOpRes;
now ← SunTimeFromGMT[BasicTime.Now[]];
rand2 ← RandomChooseInt[randomStream, 0, NumberOfKnownDirectoryINodess];
FOR lodin ← KnownDirectoryINodes, lodin.rest UNTIL lodin = NIL DO
rand2 ← rand2 - 1;
IF rand2 <= 0 AND ~lodin.first.rmOnly THEN EXIT;
ENDLOOP;
IF lodin = NIL THEN LOOP;
IF lodin.first.isRoot AND NumberOfKnownDirectoryINodess # 0 THEN LOOP; -- root directory, and there are others
newDirName ← PickAName[lodin];
mode ← PickMode[];
uid ← PickUID[];
gid ← PickGID[];
oldFree ← YggFile.ServerInfo[].secondaryBlocksFree;
reply ← YggNFS.Mkdir[where: [dir: lodin.first.fHandle, name: newDirName], attributes: [mode: mode, uid: uid, gid: gid, size: 0, atime: now, mtime: now]];
IF reply.status # ok THEN ERROR;
NumberOfKnownDirectoryINodess ← NumberOfKnownDirectoryINodess + 1;
lodin.first.directoriesInDirectory ← CONS[[newDirName, NEW[DirectoryINode ← [fHandle: reply.file, inDirectory: LIST[lodin.first], filesInDirectory: NIL, directoriesInDirectory: NIL, isRoot: FALSE]]], lodin.first.directoriesInDirectory];
KnownDirectoryINodes ← CONS[lodin.first.directoriesInDirectory.first.dirINode, KnownDirectoryINodes];
IF noisy THEN {
newFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
newDID: DID ← DIDFromFHandle[reply.file];
preferedPrintName: Rope.ROPE;
preferedPrintName ← MakePreferedDirPrintName[lodin.first.directoriesInDirectory.first.dirINode];
out.PutF["Make new directory: name= %g, did: %g, took %g pages\n", IO.rope[preferedPrintName], IO.card[newDID.didLow], IO.int[oldFree - newFree] ];
};
};
IN [21..22) => { -- remove a directory
lodin: LIST OF REF DirectoryINode;
See if dirs are empty
FOR lodin ← KnownDirectoryINodes, lodin.rest UNTIL lodin = NIL DO
IF lodin.first.isRoot THEN LOOP;
IF ~lodin.first.rmOnly THEN LOOP;
IF lodin.first.filesInDirectory = NIL AND lodin.first.directoriesInDirectory = NIL THEN {
FailedToRMDirCount ← 0;
RemoveDirectory[lodin, noisy, out];
EXIT;
};
ENDLOOP;
IF lodin # NIL THEN {
LOOP;
};
If it looks like we need another
IF NumberOfRMDirectories * 5 < NumberOfKnownDirectoryINodess OR FailedToRMDirCount > 5 THEN {
FailedToRMDirCount ← 0;
FOR lodin ← KnownDirectoryINodes, lodin.rest UNTIL lodin = NIL DO
IF lodin.first.isRoot THEN LOOP;
IF lodin.first.rmOnly THEN LOOP;
lodin.first.rmOnly ← TRUE;
IF noisy THEN {
preferedPrintName: Rope.ROPE;
preferedPrintName ← MakePreferedDirPrintName[lodin.first];
out.PutF["Flag directory for RmDir: name= %g\n", IO.rope[preferedPrintName] ];
};
NumberOfRMDirectories ← NumberOfRMDirectories + 1;
EXIT;
ENDLOOP;
}
ELSE {
FailedToRMDirCount ← FailedToRMDirCount + 1;
};
};
IN [22..24) => { -- change a file's attrs
rand2: INT;
dirINode: REF DirectoryINode;
prevFiles: LIST OF FileStuff ← NIL;
fileCount: INT ← 0;
lofiles: LIST OF FileStuff;
lodin: LIST OF REF DirectoryINode;
mode: CARD;
uid: CARD;
gid: CARD;
oldFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
reply: SunNFS.AttrStat;
IF NumberOfKnownFiles = 0 THEN LOOP;
rand2 ← RandomChooseInt[randomStream, 0, NumberOfKnownDirectoryINodess];
FOR lodin ← KnownDirectoryINodes, lodin.rest UNTIL lodin = NIL DO
rand2 ← rand2 - 1;
IF rand2 <= 0 THEN EXIT;
ENDLOOP;
IF lodin = NIL THEN LOOP;
dirINode ← lodin.first;
FOR lofs: LIST OF FileStuff ← dirINode.filesInDirectory, lofs.rest UNTIL lofs = NIL DO
fileCount ← fileCount + 1;
ENDLOOP;
IF fileCount = 0 THEN LOOP;
rand2 ← RandomChooseInt[randomStream, 0, fileCount-1];
FOR lofiles ← dirINode.filesInDirectory, lofiles.rest UNTIL lofiles = NIL DO
rand2 ← rand2 -1;
IF rand2 <= 0 THEN EXIT;
prevFiles ← lofiles;
ENDLOOP;
IF lofiles = NIL THEN LOOP;
mode ← PickMode[] + CARD[Basics.BITAND[lofiles.first.inode.mode, YggNFS.typeBits]];
uid ← PickUID[];
gid ← PickGID[];
reply ← YggNFS.Setattr[file: lofiles.first.inode.fHandle, attributes: [mode: mode, uid: uid, gid: gid, size: CARD.LAST, atime: [CARD.LAST, CARD.LAST], mtime: [CARD.LAST, CARD.LAST]]];
IF reply.status # ok THEN ERROR;
lofiles.first.inode.mode ← mode;
lofiles.first.inode.uid ← uid;
lofiles.first.inode.gid ← gid;
IF noisy THEN {
newFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
preferedPrintName: Rope.ROPE;
preferedPrintName ← MakePreferedPrintName[lofiles];
out.PutF["Changed attributes on file: name= %g\n", IO.rope[preferedPrintName] ];
IF newFree # oldFree THEN out.PutF[" ---- Change attributes took %g pages\n", IO.int[newFree - oldFree] ];
};
};
IN [24..29) => { -- make a hard link
rand2: INT;
lodin: LIST OF REF DirectoryINode;
loallFileStuff: LIST OF FileStuff;
mode: CARD;
uid: CARD;
gid: CARD;
oldFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
newFileName: REF TEXT;
oldPreferedPrintName: Rope.ROPE;
fileFHandle: SunNFS.FHandle;
status: SunNFS.Stat;
IF NumberOfKnownFiles = 0 THEN LOOP;
rand2 ← RandomChooseInt[randomStream, 0, NumberOfKnownDirectoryINodess];
FOR lodin ← KnownDirectoryINodes, lodin.rest UNTIL lodin = NIL DO
rand2 ← rand2 - 1;
IF rand2 <= 0 AND ~lodin.first.rmOnly THEN EXIT;
ENDLOOP;
IF lodin = NIL THEN LOOP;
IF lodin.first.isRoot AND NumberOfKnownDirectoryINodess # 0 THEN LOOP; -- root directory, and there are others
rand2 ← RandomChooseInt[randomStream, 0, NumberOfKnownFiles-1];
FOR loallFileStuff ← KnownFiles, loallFileStuff.rest UNTIL loallFileStuff = NIL DO
rand2 ← rand2 - 1;
IF rand2 <= 0 AND CARD[Basics.BITAND[loallFileStuff.first.inode.mode, YggNFS.typeBits]] # YggNFS.symbolicLinkModeBits THEN EXIT;
ENDLOOP;
IF loallFileStuff = NIL THEN LOOP;
oldPreferedPrintName ← MakePreferedPrintName[loallFileStuff];
fileFHandle ← loallFileStuff.first.inode.fHandle;
newFileName ← PickAName[lodin];
mode ← PickMode[];
uid ← PickUID[];
gid ← PickGID[];
status ← YggNFS.Link[to: fileFHandle, as: [dir: lodin.first.fHandle, name: newFileName]];
lodin.first.filesInDirectory ← CONS[[newFileName, loallFileStuff.first.inode], lodin.first.filesInDirectory];
loallFileStuff.first.inode.inDirectory ← CONS[lodin.first, loallFileStuff.first.inode.inDirectory];
KnownFiles ← CONS[lodin.first.filesInDirectory.first, KnownFiles];
NumberOfKnownFiles ← NumberOfKnownFiles + 1;
IF noisy THEN {
newFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
preferedPrintName: Rope.ROPE;
preferedPrintName ← MakePreferedPrintName[lodin.first.filesInDirectory];
out.PutF["Make new hard link: name= %g (one other name is %g)\n", IO.rope[preferedPrintName], IO.rope[oldPreferedPrintName] ];
IF newFree # oldFree THEN out.PutF[" ---- New hard link took %g pages\n", IO.int[newFree - oldFree] ];
};
};
IN [29..35) => { -- rename file
rand2: INT;
fileCount: INT ← 0;
fromDir: LIST OF REF DirectoryINode;
lofiles: LIST OF FileStuff;
allKnownFiles: LIST OF FileStuff ← NIL;
prevAllKnownFiles: LIST OF FileStuff ← NIL;
prevFiles: LIST OF FileStuff ← NIL;
lordin: LIST OF REF DirectoryINode;
prevDir: LIST OF REF DirectoryINode;
dirINode: REF DirectoryINode;
status: SunNFS.Stat;
toDir: LIST OF REF DirectoryINode;
now: SunNFS.TimeVal;
symbolicLink: BOOLFALSE;
newFileName: REF TEXT;
oldPreferedPrintName: Rope.ROPE;
oldFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
IF NumberOfKnownFiles = 0 THEN LOOP;
rand2 ← RandomChooseInt[randomStream, 0, NumberOfKnownDirectoryINodess];
FOR fromDir ← KnownDirectoryINodes, fromDir.rest UNTIL fromDir = NIL DO
rand2 ← rand2 - 1;
IF rand2 <= 0 THEN EXIT;
ENDLOOP;
IF fromDir = NIL THEN LOOP;
dirINode ← fromDir.first;
FOR lofs: LIST OF FileStuff ← dirINode.filesInDirectory, lofs.rest UNTIL lofs = NIL DO
fileCount ← fileCount + 1;
ENDLOOP;
IF fileCount = 0 THEN LOOP;
rand2 ← RandomChooseInt[randomStream, 0, fileCount-1];
FOR lofiles ← dirINode.filesInDirectory, lofiles.rest UNTIL lofiles = NIL DO
rand2 ← rand2 -1;
IF rand2 <= 0 THEN EXIT;
prevFiles ← lofiles;
ENDLOOP;
IF lofiles = NIL THEN LOOP;
oldPreferedPrintName ← MakePreferedPrintName[lofiles];
lofiles.first is the file to rename
now ← SunTimeFromGMT[BasicTime.Now[]];
rand2 ← RandomChooseInt[randomStream, 0, NumberOfKnownDirectoryINodess];
FOR toDir ← KnownDirectoryINodes, toDir.rest UNTIL toDir = NIL DO
rand2 ← rand2 - 1;
IF rand2 <= 0 AND ~toDir.first.rmOnly THEN EXIT;
ENDLOOP;
IF toDir = NIL THEN LOOP;
IF toDir.first.isRoot AND NumberOfKnownDirectoryINodess # 0 THEN LOOP; -- root directory, and there are others
newFileName ← PickAName[toDir];
status ← YggNFS.Rename[from: [dir: fromDir.first.fHandle, name: lofiles.first.fileName], to: [dir: toDir.first.fHandle, name: newFileName]];
IF status # ok THEN ERROR;
IF prevFiles = NIL THEN dirINode.filesInDirectory ← lofiles.rest
ELSE prevFiles.rest ← lofiles.rest;
FOR lordin ← lofiles.first.inode.inDirectory, lordin.rest UNTIL lordin = NIL DO
IF lordin.first = dirINode THEN {
EXIT;
};
prevDir ← lordin;
REPEAT FINISHED => ERROR;
ENDLOOP;
IF prevDir = NIL THEN lofiles.first.inode.inDirectory ← lordin.rest
ELSE prevDir.rest ← lordin.rest;
toDir.first.filesInDirectory ← CONS[[newFileName, lofiles.first.inode], toDir.first.filesInDirectory];
lofiles.first.inode.inDirectory ← CONS[toDir.first, lofiles.first.inode.inDirectory];
FOR allKnownFiles ← KnownFiles, allKnownFiles.rest UNTIL allKnownFiles = NIL DO
IF RefText.Equal[lofiles.first.fileName, allKnownFiles.first.fileName] AND (lofiles.first.inode = allKnownFiles.first.inode) THEN {
allKnownFiles.first.fileName ← newFileName;
EXIT;
};
REPEAT FINISHED => ERROR;
ENDLOOP;
IF noisy THEN {
newFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
newPreferedPrintName: Rope.ROPE;
newPreferedPrintName ← MakePreferedPrintName[toDir.first.filesInDirectory];
out.PutF["Rename: from = %g to = %g\n", IO.rope[oldPreferedPrintName], IO.rope[newPreferedPrintName] ];
IF newFree # oldFree THEN out.PutF[" ---- Rename took %g pages\n", IO.int[newFree - oldFree] ];
};
};
ENDCASE;
ENDLOOP;
EXITS
failed => {result ← $Failure};
};
Utilities
PrintTimeOnOut: PROC [startTime: BasicTime.GMT, out: IO.STREAM] ~ {
now: BasicTime.GMT ← BasicTime.Now[];
delta: INT;
delta ← BasicTime.Period[from: startTime, to: now];
out.PutF["%g seconds: ", IO.int[delta]];
};
RemoveDirectory: PROC [lodin: LIST OF REF DirectoryINode, noisy: BOOL, out: IO.STREAM] ~ {
status: SunNFS.Stat;
oldFree: INT;
preferedPrintName: Rope.ROPE;
prevDir: LIST OF REF DirectoryINode ← NIL;
IF lodin.first.isRoot THEN ERROR;
FOR ll: LIST OF REF DirectoryINode ← KnownDirectoryINodes, ll.rest UNTIL ll = NIL DO
IF ll = lodin THEN EXIT;
prevDir ← ll;
REPEAT FINISHED => ERROR;
ENDLOOP;
oldFree ← YggFile.ServerInfo[].secondaryBlocksFree;
preferedPrintName ← MakePreferedDirPrintName[lodin.first];
IF prevDir = NIL THEN KnownDirectoryINodes ← lodin.rest
ELSE prevDir.rest ← lodin.rest;
FOR parentDirs: LIST OF REF DirectoryINode ← lodin.first.inDirectory, parentDirs.rest UNTIL parentDirs = NIL DO
prevDirInParent: LIST OF DirectoryStuff ← NIL;
FOR nowDirs: LIST OF DirectoryStuff ← parentDirs.first.directoriesInDirectory, nowDirs.rest UNTIL nowDirs = NIL DO
IF nowDirs.first.dirINode = lodin.first THEN {
status ← YggNFS.Rmdir[which: [dir: parentDirs.first.fHandle, name: nowDirs.first.directoryName]];
IF status # ok THEN ERROR;
IF prevDirInParent = NIL THEN parentDirs.first.directoriesInDirectory ← nowDirs.rest
ELSE prevDirInParent.rest ← nowDirs.rest;
EXIT;
};
prevDirInParent ← nowDirs;
REPEAT FINISHED => ERROR;
ENDLOOP;
ENDLOOP;
NumberOfKnownDirectoryINodess ← NumberOfKnownDirectoryINodess - 1;
IF noisy THEN {
newFree: INT ← YggFile.ServerInfo[].secondaryBlocksFree;
out.PutF["Directory RmDired: name= %g, got %g pages\n", IO.rope[preferedPrintName], IO.int[newFree - oldFree] ];
};
};
RandomChooseInt: PROC [rs: Random.RandomStream ← NIL, min: INT ← 0, max: INT] RETURNS [int: INT] ~ {
IF fixedRandom THEN {
v: INT;
v ← RandomSequence[randomIndex];
IF v >= min AND v <= max THEN int ← v
ELSE {
v ← v MOD (max-min);
v ← v + min;
};
IF v < min OR v > max THEN ERROR;
int ← v;
randomIndex ← (randomIndex + 1) MOD RandomSequenceSize;
}
ELSE
RETURN[Random.ChooseInt[rs, min, max]];
};
PickAName: PROC [lodin: LIST OF REF DirectoryINode] RETURNS [newFileName: REF TEXT] ~ {
DO
nameLength: INT;
itsADup: BOOLFALSE;
nameLength ← RandomChooseInt[randomStream, 3, 10];
newFileName ← RefText.New[nameLength];
FOR inx: INT IN [0..nameLength) DO
char: CHAR;
char ← VAL[BYTE[ORD['a] + RandomChooseInt[randomStream, 0, 25]]];
[] ← RefText.AppendChar[newFileName, char];
ENDLOOP;
IF lodin # NIL THEN {
FOR lof: LIST OF FileStuff ← lodin.first.filesInDirectory, lof.rest UNTIL lof = NIL DO
IF RefText.Equal[newFileName, lof.first.fileName] THEN {
itsADup ← TRUE;
EXIT;
};
ENDLOOP;
IF itsADup THEN LOOP;
FOR dirs: LIST OF DirectoryStuff ← lodin.first.directoriesInDirectory, dirs.rest UNTIL dirs = NIL DO
IF RefText.Equal[newFileName, dirs.first.directoryName] THEN {
itsADup ← TRUE;
EXIT;
};
ENDLOOP;
IF itsADup THEN LOOP;
};
EXIT;
ENDLOOP;
};
MakePreferedPrintName: PROC [filesInDirectory: LIST OF FileStuff] RETURNS [preferedPrintName: Rope.ROPE] ~ {
preferedPrintName ← Rope.Concat[MakePreferedDirPrintName[filesInDirectory.first.inode.inDirectory.first], RefText.TrustTextAsRope[filesInDirectory.first.fileName]];
};
MakePreferedDirPrintName: PROC [dirINode: REF DirectoryINode] RETURNS [preferedPrintName: Rope.ROPE ← NIL] ~ {
DO
dirStuff: REF DirectoryINode;
IF dirINode.isRoot THEN EXIT;
dirStuff ← dirINode.inDirectory.first;
FOR lodirs: LIST OF DirectoryStuff ← dirStuff.directoriesInDirectory, lodirs.rest UNTIL lodirs = NIL DO
IF lodirs.first.dirINode = dirINode THEN {
preferedPrintName ← Rope.Cat[RefText.TrustTextAsRope[lodirs.first.directoryName], "/", preferedPrintName];
EXIT;
};
REPEAT FINISHED => ERROR;
ENDLOOP;
dirINode ← dirStuff;
ENDLOOP;
preferedPrintName ← Rope.Concat["/", preferedPrintName];
};
MakeUpContents: PROC RETURNS [contents: REF TEXT] ~ {
size: INT;
size ← RandomChooseInt[randomStream, 0, MaxFileSize-1];
contents ← RefText.New[size];
FOR inx: INT IN [0..size) DO
char: CHAR;
char ← VAL[BYTE[ORD['a] + RandomChooseInt[randomStream, 0, 25]]];
[] ← RefText.InlineAppendChar[contents, char];
ENDLOOP;
};
PickUID: PROC RETURNS [uid: CARD] ~ {
uid ← RandomChooseInt[randomStream, 1, 321];
};
PickGID: PROC RETURNS [gid: CARD] ~ {
gid ← RandomChooseInt[randomStream, 1, 42];
};
PickMode: PROC RETURNS [theBits: CARD] ~ {
theBits ← 0;
FOR i: INT IN [0..4) DO
theBits ← theBits * 8;
theBits ← theBits + RandomChooseInt[randomStream, 0, 7];
ENDLOOP;
};
PointerFromRefText: UNSAFE PROC [block: REF READONLY TEXT] RETURNS [LONG POINTER] ~ TRUSTED INLINE {
RETURN[ LOOPHOLE[block, LONG POINTER] + UNITS[TEXT[0]] ] };
DIDFromFHandle: PROC [file: SunNFS.FHandle] RETURNS [did: YggDID.DID] ~ TRUSTED {
did ← YggDID.VolatilizeDID[PointerFromRefText[file]];
};
FHandleFromDID: PROC [did: YggDID.DID] RETURNS [file: SunNFS.FHandle] ~ TRUSTED {
file ← NEW[TEXT[SunNFS.fhSize]];
YggDID.StabilizeDID[did, PointerFromRefText[file]];
};
GMTFromSunTime: PROC [sunTime: SunNFS.TimeVal] RETURNS [gmt: BasicTime.GMT] ~ {
RETURN [BasicTime.Update[sunEpoch, INT[sunTime.seconds]]];
};
SunTimeFromGMT: PROC [gmt: BasicTime.GMT] RETURNS [sunTime: SunNFS.TimeVal] ~ {
RETURN [ [seconds~BasicTime.Period[from~sunEpoch, to~gmt], useconds~0] ];
};
CompareSunTimes: PROC [t1, t2: SunNFS.TimeVal] RETURNS [Basics.Comparison] ~ {
RETURN [SELECT t1.seconds FROM
< t2.seconds => less,
> t2.seconds => greater,
ENDCASE => Basics.CompareCard[t1.useconds, t2.useconds]];
};
Initialization
Init: PROC = {
Commander.Register["NFSTest", NFSTestProc, "NFSTest noTests"];
ReadBuffer ← RefText.New[MaxFileSize];
};
Init[];
END.