-- Salvager.mesa;
--Maurice Herlihy August 29, 1982
--Mike Schroeder, October 6, 1982 9:17 am
--Roy Levin, November 2, 1982 4:35 pm
DIRECTORY
CedarInitOps
USING [salvageLSD],
CIFS
USING [Error, ErrorCode],
ConvertUnsafe
USING [ToRope],
Directory
USING [DeleteFile, Error, GetProperty, PropertyType],
File
USING [Capability, Delete, nullCapability],
FileStream
USING [GetLeaderPropertiesForCapability],
FT
USING [GetBaseFreeSpace],
FtpMan
USING [GetFileInfo],
KernelFile
USING [GetNextFile],
LSD
USING [Create, Entry, Lookup, SetDirty, Unlock],
PupDefs
USING [PupPackageMake],
Rope
USING [Find,
ROPE, Substr],
Volume
USING [
ID, SystemID];
Salvager:
MONITOR
IMPORTS
CedarInitOps,
CIFS, ConvertUnsafe, Directory, File, FileStream, FT, FtpMan, KernelFile,
LSD, PupDefs, Rope, Volume
= {
-- Remote Name Property from Eric's code
RemoteNameProperty: Directory.PropertyType = LOOPHOLE[213B];
ROPE: TYPE = Rope.ROPE;
Salvage: PROC[] = {
-- Strategy:
-- Delete the LSD.
-- Iterate through each file on the disk,
-- putting remote files in the lsd with create date 0.
-- If duplicates are encountered, keep the most recently written.
-- Build up a list of files to be deleted at the end.
VictimList: TYPE = REF VictimRec;
VictimRec: TYPE = RECORD[
file: File.Capability,
next: VictimList
];
volume: Volume.ID ← Volume.SystemID[];
file: File.Capability ← File.nullCapability;
victim: File.Capability ← File.nullCapability;
fileName: STRING ← [125];
name: ROPE;
entry: LSD.Entry;
victims: VictimList ← NIL;
remoteState: {exists, doesNotExist, noResponse};
localCreate, remoteCreate: LONG CARDINAL;
-- Crank up the pup package.
PupDefs.PupPackageMake[];
-- Delete existing lsd, if any.
Directory.DeleteFile["lsd.dir" ! Directory.Error => CONTINUE];
-- Krock to start FT so it will initialize session start time
[] ← FT.GetBaseFreeSpace[];
-- Iterate through the files on the volume
WHILE (file ← KernelFile.GetNextFile[volume, file]) # File.nullCapability DO
remoteState ← exists;
remoteCreate ← 0;
fileName.length ← 0;
Directory.GetProperty[file: file, property: RemoteNameProperty,
propertyValue: DESCRIPTOR[fileName, SIZE[StringBody[fileName.maxlength]]]
-- in case of error, charge!
! Directory.Error => LOOP];
-- If the name doesn't start with '/, skip it.
IF fileName.length = 0 OR fileName[0] # '/ THEN LOOP;
name ← ConvertUnsafe.ToRope[fileName];
localCreate ← FileStream.GetLeaderPropertiesForCapability[file].create;
entry ← LSD.Lookup[name];
IF entry # NIL THEN { -- Found a duplicate.
IF FileStream.GetLeaderPropertiesForCapability[entry.fc].create >= localCreate
THEN { -- One in lsd is more current.
victims ← NEW [VictimRec ← [file: file, next: victims]];
LOOP; -- nothing more to do.
}
-- Mark lsd entry for deletion and insert new entry below.
ELSE victims ← NEW [VictimRec ← [file: entry.fc, next: victims]];
};
[remoteCreate, ] ← APPLY [FtpMan.GetFileInfo, Split[name]
! CIFS.Error => CHECKED {remoteState ← IF code = CIFS.ErrorCode[noSuchFile]
THEN doesNotExist ELSE noResponse; CONTINUE} ];
SELECT remoteState FROM
noResponse => -- it may be dirty --
MakeLSDEntry[name, file, 0, dirty];
doesNotExist =>
IF localCreate = 0
THEN -- probably still clean --
MakeLSDEntry[name, file, 0, clean]
ELSE -- a new dirty file --
MakeLSDEntry[name, file, localCreate - 1, dirty];
exists =>
SELECT localCreate FROM
< remoteCreate => -- local file has been superceded --
MakeLSDEntry[name, file, localCreate, recheck];
= remoteCreate => -- same as file on remote server --
MakeLSDEntry[name, file, localCreate, clean];
> remoteCreate => -- local file is more recent --
MakeLSDEntry[name, file, remoteCreate, dirty];
ENDCASE => ERROR;
ENDCASE => ERROR;
ENDLOOP;
-- Delete unwanted files.
WHILE victims # NIL DO
File.Delete[victims.file];
victims ← victims.next;
ENDLOOP;
};
LSDFull: ERROR = CODE;
MakeLSDEntry: PROC[n: ROPE, f: File.Capability, cr: LONG CARDINAL,
state: {clean, recheck, dirty} ]
-- make new LSD entry.
={
e: LSD.Entry = LSD.Create [name: n, fc: f, create: cr];
IF e = NIL THEN ERROR LSDFull;
SELECT state FROM
clean => NULL;
recheck => e.checked ← 0;
dirty => LSD.SetDirty[e];
ENDCASE => ERROR;
LSD.Unlock[e];
};
Split: PROC[file: ROPE] RETURNS [server, name: ROPE]
-- Take in CIFS name, split off server name.
={
fileNameStart: INT ← Rope.Find[file, "/", 1]; -- second slash in name
name ← file.Substr[start: fileNameStart+1];
server ← file.Substr[start: 1, len: fileNameStart - 1 ];
}; --Split--