// IfsBackupJob2.bcpl -- Main backup process, part 2
// Copyright Xerox Corporation 1979, 1980, 1981, 1982
// Last modified October 3, 1982 3:50 PM by Taft
get "Ifs.decl"
get "IfsSystemInfo.decl"
get "IfsFiles.decl"
get "Disks.d"
get "IfsBackup.decl"
get "IfsDirs.decl"
external
[
// outgoing procedures
BackupDriver
// incoming procedures
BackupIFSDir; CopyFile; OkToDoBackup
CreateFD; DestroyFD; LookupFD; NextFD; UnlockDirFD; LockFile; UnlockFile
GetDIFRec; DIFRecFromDR; CreateDirectoryEntry; TransferLeaderPage
CreateIFSFile; GetDiskFromFD; GetBufferForFD
EmptiestFreePages; InstallDR
OpenIFSStream; CloseIFSStream; KsBufferAddress; WriteBlock
MakeKPMTemplate; VFileWritePage
MoveBlock; Zero; SysFree; FreePointer; IFSError; MultEq; ReadCalendar
DoubleUsc; DoubleAdd; Dismiss; TruePredicate
// incoming statics
infoVMD
]
//---------------------------------------------------------------------------
structure DUR: // Disk Usage Record -- accumulates correction for DIFRec.diskPageUsage
//---------------------------------------------------------------------------
[
difFD word // -> FD for directory's DIF (0 if abandoned correction attempt)
diskPageUsage word 2 // accumulated actual disk usage
timeStart word 2 // time at which DIF was encountered
]
manifest lenDUR = size DUR/16
//---------------------------------------------------------------------------
let BackupDriver(fs, backupFS, WantBackup, arg, startingName) = valof
//---------------------------------------------------------------------------
// Issues calls to do the backup. Returns code saying why it stopped.
// First, backs up IFS.Dir to capture state at start of backup,
// then incrementally backs up all files in the file system,
// then backs up IFS.Dir again (if possible) to capture ending state.
// startingName is the name of the file to start at.
[
let ec = nil
let fd = CreateFD(startingName, lcVExplicit, lv ec, fs)
if fd eq 0 then IFSError(ecBackupCreateFD, ec)
fd>>FD.template = MakeKPMTemplate("**")
UpdateBackupInfo(fd)
unless BackupIFSDir(fs, backupFS) do
[ DestroyFD(fd); resultis backupDiskFull ]
FreePointer(lv fd>>FD.pathStk)
fd>>FD.version = fd>>FD.version-1
unless NextFD(fd) do
[ DestroyFD(fd); resultis backupDone ]
let dur = vec lenDUR; Zero(dur, lenDUR)
ec = valof
[
for i = 1 to 10 do
[
if fd>>FD.dr>>DR.type eq drTypeDIF then RepairDiskUsage(dur, fd)
let res = BackupFile(fd, backupFS, WantBackup, arg, dur)
if res eq backupDiskFull resultis backupDiskFull
unless res eq backupDone % res eq backupNotNeeded do
// abandon accumulating diskPageUsage
if dur>>DUR.difFD ne 0 then dur>>DUR.difFD = DestroyFD(dur>>DUR.difFD)
unless NextFD(fd) do
[
RepairDiskUsage(dur, 0)
resultis backupDone //no more files
]
]
UpdateBackupInfo(fd) //record progress after every 10 files
unless OkToDoBackup() resultis backupHalted
] repeat
if dur>>DUR.difFD ne 0 then DestroyFD(dur>>DUR.difFD)
UpdateBackupInfo(fd)
DestroyFD(fd)
unless ec eq backupDiskFull do
unless BackupIFSDir(fs, backupFS) do ec = backupDiskFull
resultis ec
]
//---------------------------------------------------------------------------
and UpdateBackupInfo(fd) be
//---------------------------------------------------------------------------
[
let bi = VFileWritePage(infoVMD, biPage)
MoveBlock(lv bi>>BI.pathName, lv fd>>FD.dr>>DR.pathName, lenPathName)
]
//---------------------------------------------------------------------------
and RepairDiskUsage(dur, fd) be
//---------------------------------------------------------------------------
// Checks and corrects if necessary the disk page usage described by dur. Then,
// if fd ne 0, assumes it is the DIF for a new directory and begins accumulating
// disk usage information for it.
// The way the disk usage correction works in the face of concurrent directory
// activity is as follows:
// 1. When a DIF is encountered, make a copy of the FD in the DUR, and record the
// current time.
// 2. For all files in the directory, accumulate the page count in the DUR.
// If a busy file or a file with write time ge starting time is encountered,
// abandon trying to correct the disk usage for this directory (by forgetting the FD).
// 3. When the next DIF is encountered, if the actual accumulated page count
// differs from the one in the remembered FD, AND the actual DIF page count has
// not changed in the meantime, then update the DIF from the accumulated page count.
[
let difFD = dur>>DUR.difFD
if difFD ne 0 then
[
unless MultEq(lv dur>>DUR.diskPageUsage,
lv (DIFRecFromDR(difFD>>FD.dr))>>DIFRec.diskPageUsage) do
[ // accumulated total is different from remembered value
let oldUsage = vec 1
MoveBlock(oldUsage, lv (DIFRecFromDR(difFD>>FD.dr))>>DIFRec.diskPageUsage, 2)
if LookupFD(difFD, lockWrite) eq 0 &
MultEq(oldUsage, lv (DIFRecFromDR(difFD>>FD.dr))>>DIFRec.diskPageUsage) then
[ // remembered value hasn't changed, so fix it
MoveBlock(lv (DIFRecFromDR(difFD>>FD.dr))>>DIFRec.diskPageUsage,
lv dur>>DUR.diskPageUsage, 2)
CreateDirectoryEntry(difFD)
]
UnlockDirFD(difFD)
]
DestroyFD(dur>>DUR.difFD)
]
Zero(dur, lenDUR)
if fd ne 0 then dur>>DUR.difFD = GetDIFRec(fd)
ReadCalendar(lv dur>>DUR.timeStart)
]
//---------------------------------------------------------------------------
and BackupFile(fd, backupFS, WantBackup, arg, dur) = valof
//---------------------------------------------------------------------------
// Checks to see whether the file described by FD needs to be backed up, by
// calling WantBackup(ild, arg) with ild pointing to the file's leader page.
// If so, backs up the file onto the backup file system backupFS
// and marks the file as having been backed up.
// Invalidates dur if a file with newer write date is encountered (see
// RepairDiskUsage in IfsBackupJob.bcpl).
// Should be called with the directory unlocked and returns with it unlocked.
// Returns one of the backup result codes (see IfsBackup.decl).
[
if LookupFD(fd) ne 0 resultis backupCantAccess
// If this is a DIF, restore the cached directory information to the file
if fd>>FD.dr>>DR.type eq drTypeDIF then
[
let stream = OpenIFSStream(fd, 0, modeReadWrite)
if stream ne 0 then
[
// Know the first page of the file is in the buffer now
let difRec = DIFRecFromDR(fd>>FD.dr)
unless MultEq(difRec, KsBufferAddress(stream), lenDIFRec) do
WriteBlock(stream, difRec, lenDIFRec)
CloseIFSStream(stream)
]
]
// Read leader page and check dates
let buf = GetBufferForFD(fd)
let ec = valof
[
if LookupFD(fd, lockRead) ne 0 resultis backupCantAccess
TransferLeaderPage(fd, buf)
// accumulate disk pages used in this directory
if dur ne 0 then
[
if DoubleUsc(lv buf>>ILD.written, lv dur>>DUR.timeStart) ge 0 then
// abandon accumulating diskPageUsage
if dur>>DUR.difFD ne 0 then dur>>DUR.difFD = DestroyFD(dur>>DUR.difFD)
let pageCount = vec 1
pageCount!0, pageCount!1 = 0, buf>>LD.hintLastPageFa.pageNumber+1
DoubleAdd(lv dur>>DUR.diskPageUsage, pageCount)
]
unless WantBackup(buf, arg) resultis backupNotNeeded
// Make sure there is enough room in the backup file system.
// This test is not right in the case of overwriting an existing
// file, but it errs on the side of being too conservative.
if EmptiestFreePages(backupFS) uls
buf>>LD.hintLastPageFa.pageNumber+100 resultis backupDiskFull
// Want to back up this file; attempt to lock it for reading.
// If file is busy, wait up to 10 seconds for it to become free.
for try = 1 to 10 do
[
if LockFile(fd, modeRead) resultis 0
UnlockDirFD(fd)
Dismiss(100)
if LookupFD(fd, lockRead) ne 0 resultis backupCantAccess
]
resultis backupFileBusy
]
// BackupFile (cont'd)
UnlockDirFD(fd)
if ec ne 0 then
[ SysFree(buf); resultis ec ]
// Make FD for file in backup system
let backupFD = CreateFD(lv fd>>FD.dr>>DR.pathName, lcCreate+lcVExplicit,
lv ec, backupFS)
if backupFD eq 0 then IFSError(ecBackupCreateFD, ec)
// Make backup DR be an exact copy of primary DR (in case DIF, etc.)
InstallDR(backupFD, fd>>FD.dr)
// See whether file already exists in backup system.
// If there is no such directory in the backup file system and we are not
// now trying to back up a DIF, then make a recursive call to back up the
// DIF first.
ec = LookupFD(backupFD, lockWrite)
if ec eq ecDirNotFound then
[
ec = 0
if fd>>FD.dr>>DR.type ne drTypeDIF then
[
UnlockDirFD(backupFD)
let difFD = GetDIFRec(fd)
if difFD eq 0 then IFSError(ecCantFindDIF)
let res = BackupFile(difFD, backupFS, TruePredicate, nil, 0)
DestroyFD(difFD)
if res ne backupDone then
[
UnlockFile(fd)
DestroyFD(backupFD)
SysFree(buf)
resultis res
]
ec = LookupFD(backupFD, lockWrite)
]
]
unless ec eq 0 do IFSError(ecBackupLookupFD, ec)
// BackupFile (cont'd)
test backupFD>>FD.lookupStatus eq lsExists
ifso
[ // file already exists.
// If it is a DIF, ensure stuff in dir entry is up-to-date
if fd>>FD.dr>>DR.type eq drTypeDIF then CreateDirectoryEntry(backupFD)
]
ifnot
[ // file doesn't exist, create it
ec = CreateIFSFile(backupFD, buf)
if ec ne 0 then IFSError(ecBackupCreateFile, ec)
]
SysFree(buf) // don't hold on to buf across CopyFile call
UnlockDirFD(backupFD)
// Actually copy the file to the backup disk
CopyFile(GetDiskFromFD(backupFD), lv backupFD>>FD.dr>>DR.fp,
GetDiskFromFD(fd), lv fd>>FD.dr>>DR.fp, SetBackupDate)
// Mark file as having been backed up
buf = GetBufferForFD(fd)
TransferLeaderPage(fd, buf)
SetBackupDate(buf)
TransferLeaderPage(fd, buf, true)
SysFree(buf)
// Clean up and leave
UnlockFile(fd)
DestroyFD(backupFD)
resultis backupDone
]
//---------------------------------------------------------------------------
and SetBackupDate(buf) be ReadCalendar(lv buf>>ILD.backedUp)
//---------------------------------------------------------------------------