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