// IfsBackupRestore.bcpl -- restore files from backup
// Copyright Xerox Corporation 1979, 1980, 1981, 1982
// Last modified May 11, 1982 10:30 AM by Taft
get "Ifs.decl"
get "IfsFiles.decl"
get "IfsDirs.decl"
get "IfsBackup.decl"
external
[
// outgoing procedures
RestoreLoop; RestoreFile
// incoming procedures
CreateFD; LookupFD; NextFD; DestroyFD; InstallDR; LookupIFSFile
CreateDirectoryEntry; CreateIFSFile; CloseIFSFile; GetDiskFromFD
TransferLeaderPage; DIFRecFromDR; LockFile; LockDirFD; UnlockDirFD
GetBufferForFD
ReadRecLE; DirCompareKey; StringCompare; PurgeVMem
CopyFile; DoubleUsc; IFSError; TelnetAborting
InstallSysParams; InitCachedDIF; InitGroupName
SysFree; FreePointer; MoveBlock; Zero
DefaultArgs; ReturnFrom; CallersFrame; PutTemplate; Ws; IFSPrintError
// incoming statics
dsp; infoVMD
]
//----------------------------------------------------------------------------
let RestoreLoop(name, backupFS, fs, tree; numargs na) = valof
//----------------------------------------------------------------------------
// Restores all files matching name from backupFS onto fs.
// If tree is provided, a file is restored only if it is present in the tree.
// Returns zero normally, an error code if the initial lookup of name failed.
[
DefaultArgs(lv na, -2, 0)
let ec = nil
let backupFD = LookupIFSFile(name, lcMultiple+lcVAll, lv ec, backupFS)
if backupFD eq 0 then resultis ec
until TelnetAborting() do
[
ec = 0
if tree ne 0 then
[
let record = ReadRecLE(tree, backupFD)
if record eq 0 % DirCompareKey(backupFD, record) ne 0 then ec = -1
FreePointer(lv record)
]
if ec eq 0 then ec = RestoreFile(backupFD, fs)
PutTemplate(dsp, "*n $S ", lv backupFD>>FD.dr>>DR.pathName)
if ec ne 0 then
[
Ws("-- not restored: ")
switchon ec into
[
case ecRestoreNoBackup:
[ Ws("no-backup"); endcase ]
case ecRestoreObsolete:
[ Ws("already exists"); endcase ]
case -1:
[ Ws("deleted"); endcase ]
default:
[ Ws("*n "); IFSPrintError(dsp, ec) ]
]
]
unless NextFD(backupFD) break
]
DestroyFD(backupFD)
resultis 0
]
//----------------------------------------------------------------------------
and RestoreFile(backupFD, fs) = valof
//----------------------------------------------------------------------------
// Restores the file described by backupFD onto the file system fs, returning
// zero if successful and an error code if unsuccessful. Both directories must
// be unlocked at the time of the call and are unlocked upon return.
[
let buf = GetBufferForFD(backupFD)
let fd = 0
let ec = valof
[
let ec = LookupFD(backupFD, lockRead)
if ec ne 0 resultis ec
TransferLeaderPage(backupFD, buf)
if buf>>ILD.noBackup resultis ecRestoreNoBackup
// Build FD for file in primary fs and see whether it exists
fd = CreateFD(lv backupFD>>FD.dr>>DR.pathName, lcVExplicit+lcCreate, lv ec, fs)
if fd eq 0 then IFSError(ecRestoreCreateFD, ec)
InstallDR(fd, backupFD>>FD.dr) // in case creating DIF
ec = LookupFD(fd, lockWrite)
unless ec eq 0 % ec eq ecDirNotFound do IFSError(ecRestoreLookupFD, ec)
let isSystemInfo =
StringCompare(lv fd>>FD.dr>>DR.pathName, "<System>Info!1") eq 0
let dPages = buf>>ILD.hintLastPageFa.pageNumber+1
test fd>>FD.lookupStatus eq lsExists
ifso
[ // presently exists, compare write dates and check for no-backup
let backupWrite = vec 1; MoveBlock(backupWrite, lv buf>>ILD.written, 2)
let backupDamaged = buf>>ILD.damaged
TransferLeaderPage(fd, buf) // leader page of primary file
if buf>>ILD.noBackup resultis ecRestoreNoBackup
// Normally restore file only if the backup copy was written more
// recently. But in the case of <System>Info!1, restore if the
// primary copy has never been backed up. This is because when a
// new file system is created, a <System>Info!1 is created from
// whole cloth and has a more recent write date.
// Also restore if the primary copy is damaged, the backup copy is not,
// and the write dates are the same.
unless isSystemInfo & buf>>ILD.backedUp.h eq 0 do
switchon DoubleUsc(backupWrite, lv buf>>ILD.written) into
[
case -1: // backup copy has older write date than primary
resultis ecRestoreObsolete
case 0: // backup copy has same write date as primary
unless buf>>ILD.damaged & not backupDamaged do
resultis ecRestoreObsolete
// case 1: // backup copy has newer write date than primary
]
dPages = dPages-(buf>>ILD.hintLastPageFa.pageNumber+1)
if fd>>FD.dr>>DR.type eq drTypeDIF then
[ // update information cached in DIF record
let difRec = DIFRecFromDR(fd>>FD.dr)
let backupDIFRec = DIFRecFromDR(backupFD>>FD.dr)
MoveBlock(lv backupDIFRec>>DIFRec.diskPageUsage,
lv difRec>>DIFRec.diskPageUsage, 2)
MoveBlock(difRec, backupDIFRec, lenDIFRec)
CreateDirectoryEntry(fd) //special call to update entry
]
]
ifnot
[ //doesn't exist, create it
if fd>>FD.dr>>DR.type eq drTypeDIF then
Zero(lv (DIFRecFromDR(fd>>FD.dr)>>DIFRec.diskPageUsage), 2)
ec = CreateIFSFile(fd, buf)
if ec ne 0 resultis ec
]
FreePointer(lv buf)
// RestoreFile (cont'd)
// Write-lock the file (in primary FS) and restore it.
// No need to lock backup file, because we are the only client of
// the backup FS.
unless LockFile(fd, modeWrite) resultis ecFileBusy
UnlockDirFD(fd)
UnlockDirFD(backupFD)
// if about to restore <System>Info!1, flush in-core pages first.
if isSystemInfo then PurgeVMem(infoVMD)
CopyFile(GetDiskFromFD(fd), lv fd>>FD.dr>>DR.fp,
GetDiskFromFD(backupFD), lv backupFD>>FD.dr>>DR.fp)
CloseIFSFile(fd, dPages)
fd = DestroyFD(fd)
if isSystemInfo then
// Just restored <System>Info -- make system aware of new contents.
// The timing of this is a bit dicey, but is the best we can do without
// tighter coupling between the backup system and VMem.
[ InstallSysParams(true); InitCachedDIF(); InitGroupName() ]
resultis 0
]
// Upon exit from the above block, both directories are locked if the
// restore failed, but neither is locked if it succeeded.
if ec ne 0 then
[
UnlockDirFD(backupFD)
if fd ne 0 then [ UnlockDirFD(fd); DestroyFD(fd) ]
]
FreePointer(lv buf)
resultis ec
]