// 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 ]