// IfsBackupCopy.bcpl -- Copy file using TFS-level operations only // Copyright Xerox Corporation 1979, 1981, 1982 // Last modified October 3, 1982 12:27 PM by Taft get "AltoFileSys.d" get "Disks.d" get "Tfs.d" external [ // outgoing procedures CopyFile // incoming procedures ActOnDiskPages; WriteDiskPages; DeleteDiskPages DefaultArgs; Allocate; SysAllocate; SysFree; MultEq Umin; SetBlock; MoveBlock; Noop // incoming statics numOvXMPages; numVMemBufs; sysZone ] //--------------------------------------------------------------------------- let CopyFile(dDisk, dFP, sDisk, sFP, LeaderProc, maxPage, dontCopyZero; numargs na) be //--------------------------------------------------------------------------- // Copies a file pointed to by sFP on sDisk to one pointed to by dFP // on dDisk. The file is exactly copied, including the leader page, // except the last page hint in the destination file is set appropriately. // If maxPage is supplied, it specifies the maximum page to be copied. // If LeaderProc is supplied, LeaderProc(buffer) is called with the leader // page in the buffer as it goes by. // If dontCopyZero is true, the leader page is not copied (however, the // fixing of the last page hint and the call to LeaderProc still occur). // This procedure once worked on any kind of disk but now works only on // Tridents due to its use of the special TFS DCreadnD action. [ DefaultArgs(lv na, -4, Noop, #177777, false) // Begin by allocating only one buffer, into which we read the leader page. // Then, using the last page hint, allocate more buffers (up to a total of // maxNBuffers) for doing the remainder of the file. Thus we maximize // the transfer rate for large files, but avoid doing the relatively // expensive Allocates (which snarf pages from VMem) for small files. let maxNBuffers = numOvXMPages ne 0? numVMemBufs/3, 5 let nBuffers = 1 let CAs = SysAllocate(3*maxNBuffers+7) let sDAs = CAs+maxNBuffers+2 let dDAs = sDAs+maxNBuffers+2 let buf1 = SysAllocate(1 lshift sDisk>>DSK.lnPageSize) CAs!1 = buf1 sDAs!1 = sFP>>FP.leaderVirtualDa dDAs!0 = fillInDA dDAs!1 = dFP>>FP.leaderVirtualDa dDAs!2 = fillInDA // Following triples of variables must be declared in the order of their // appearance in an FA structure. let hintLastDA, hintLastPage, hintLastNumChars = nil, 0, nil let lastDA, lastPage, numChars = nil, nil, nil let page = 0 // CopyFile (cont'd) // loop to copy groups of pages [ // Read pages from source file. // On first iteration, read only the leader page. SetBlock(sDAs+2, fillInDA, nBuffers) lastPage = Umin(page+nBuffers-1, maxPage) lastPage = ActOnDiskPages(sDisk, CAs-page+1, sDAs-page+1, sFP, page, lastPage, DCreadD, lv numChars, 0, 0, 0, 0, 0, hintLastPage) sDAs!1 = sDAs!(lastPage-page+2) //DA of lastPage+1 if lastPage eq maxPage then sDAs!1 = eofDA //simulate EOF if reach maxPage if page eq 0 then [ // Have leader page in buf1 now. MoveBlock(lv hintLastDA, lv buf1>>LD.hintLastPageFa, lFA) nBuffers = Umin((hintLastPage eq 0? 5, hintLastPage), maxNBuffers) for i = 2 to nBuffers do [ // Allocate more buffers, but stop if we exhaust storage. CAs!i = Allocate(sysZone, 1 lshift sDisk>>DSK.lnPageSize, true) if CAs!i eq 0 then [ nBuffers = i-1; break ] ] CAs!(nBuffers+1) = buf1 //for creating extra page // Guess what the last DA will be, and put that in the leader page. hintLastDA = dDAs!1 + hintLastPage buf1>>LD.hintLastPageFa.da = hintLastDA LeaderProc(buf1) ] // The last page must be treated specially -- do not write it yet. if sDAs!1 eq eofDA then [ lastPage = lastPage-1; if lastPage uls page break ] // Write pages onto destination file. In case writing a new file, // we must create one extra page to ensure that we will start // with an existing page on the next iteration. The lastAction for // this page is DCreadnD to prevent its label from being rewritten // in the case of overwriting an existing file. If the label were to be // rewritten, WriteDiskPages would set the next DA to eofDA and the // remainder of the file would be lost! (In the case of a new file, // data from CAs!(lastPage+1) is initially written into the extra page. // There is an extra entry in CAs for this purpose). // Additional hairiness: if this is the first iteration and we are not // to copy page zero, then we read it instead of writing. SetBlock(dDAs+3, fillInDA, nBuffers) (page eq 0 & dontCopyZero? ActOnDiskPages, WriteDiskPages)(dDisk, CAs-page+1, dDAs-page+1, dFP, page, lastPage+1, DCreadnD, 0, 0) // lastPage is the last page copied // lastPage+1 is the "extra" page // lastPage+2 is eofDA (unless overwriting an existing file) MoveBlock(dDAs, dDAs+lastPage-page+1, 3) //DAs for lastPage to lastPage+2 if sDAs!1 eq eofDA break page = lastPage+1 ] repeat // CopyFile (cont'd) lastPage = lastPage+1 // lastPage-1 is the last page copied -- the next to last page of the file. // lastPage is the last page of the file -- it has been read from the // source file, assigned in the destination file, but not yet written out. // page is the page number corresponding to CAs!1. // dDAs!1 is the DA for lastPage, and dDAs!2 is the DA for lastPage+1 if it // exists or eofDA otherwise. let leftOverDA = dDAs!2 //save DA of lastPage+1 for possible truncation dDAs!2 = eofDA // A full last page is anomalous and would cause WriteDiskPages to // do the wrong thing. Force it to be less than full. numChars = Umin(numChars, (2 lshift sDisk>>DSK.lnPageSize)-1) WriteDiskPages(dDisk, CAs-page+1, dDAs-lastPage+1, dFP, lastPage, lastPage, 0, 0, numChars) // truncate any remaining pages if leftOverDA ne eofDA then DeleteDiskPages(dDisk, buf1, leftOverDA, dFP, lastPage+1) // see whether our hint last FA guess was correct, and fix it if wrong. lastDA = dDAs!1 unless MultEq(lv hintLastDA, lv lastDA, lFA) do [ dDAs!0 = fillInDA dDAs!1 = dFP>>FP.leaderVirtualDa dDAs!2 = fillInDA ActOnDiskPages(dDisk, CAs+1, dDAs+1, dFP, 0, 0, DCreadD) MoveBlock(lv buf1>>LD.hintLastPageFa, lv lastDA, lFA) ActOnDiskPages(dDisk, CAs+1, dDAs+1, dFP, 0, 0, DCwriteD) ] for i = 1 to nBuffers do SysFree(CAs!i) SysFree(CAs) ]