// IFSDirOpen.bcpl -- Open and close IFS files // Copyright Xerox Corporation 1979, 1981, 1982 // Last modified July 22, 1982 3:39 PM by Taft get "Ifs.decl" get "IfsFiles.decl" get "Streams.d" get "IfsDirs.decl" get "IfsRs.decl" external [ // Outgoing procedures IFSOpenFile; OpenIFSStream; OpenIFSFile; CreateIFSStream CloseIFSStream; CloseIFSFile CreateIFSFile; StreamsFD; CreateDirectoryEntry IFSCleanup; IFSErrors; UpdatePageUsage // Incoming procedures -- other parts of IFSDirs LookupIFSFile; DestroyFD; LockDirFD; UnlockDirFD; ModifyDirFD LookupFD; GetDIFRec; DIFRecFromDR; UpdateDIFRec; GetBufferForFD UserOwns; CheckAccess; CheckAllocation TransferLeaderPage; EmptiestUnit; GetDiskFromFD; LockFile; UnlockFile // Incoming procedures -- B-Tree UpdateRecord // Incoming procedures -- rest of IFS IFSError; Min; DoubleIncrement; FreePointer CopyString; ExtractSubstring; ByteBlt // Incoming procedures -- operating system DefaultArgs; SysAllocate; SysFree; Zero; MoveBlock; Noop; SysErr CreateDiskStream; CreateDiskFile; FileLength; Closes; KsHintLastPageFa ReturnFrom; CallersFrame // Incoming statics CtxRunning ] manifest [ // Private "open modes" used only during Close modeDontUnlock = 376B // don't unlock (or destroy) fd modeDontDestroy = 377B // don't destroy fd ] //---------------------------------------------------------------------------- let IFSOpenFile(name, lvErrorCode, mode, itemSize, lc, fs, dirName; numargs na) = valof //---------------------------------------------------------------------------- // Opens an IFS file, going directly from a name to a stream // by application of the LookupIFSFile and OpenIFSStream procedures. // This is the simplest case. // Note: caller should not lock the directory, and the directory // is unlocked when we return. [ DefaultArgs(lv na, -1, lv na, modeRead, charItem, 0, 0, 0) if lc eq 0 then lc = (mode eq modeRead? lcVHighest, mode eq modeWrite? lcCreate+lcVNext, lcCreate+lcVHighest) let fd = LookupIFSFile(name, lc, lvErrorCode, fs, dirName) let str = 0 if fd ne 0 then [ str = OpenIFSStream(fd, lvErrorCode, mode, itemSize) if str eq 0 then DestroyFD(fd) ] resultis str ] //---------------------------------------------------------------------------- and OpenIFSStream(fd, lvErrorCode, mode, itemSize; numargs na) = valof //---------------------------------------------------------------------------- // Opens a file and returns a stream given an fd. // Combines the actions of OpenIFSFile and CreateIFSStream. // Returns zero if unsuccessful. // Note: caller should not lock the directory, and the directory // is unlocked when we return. [ DefaultArgs(lv na, -1, lv na, modeRead, charItem) let str = 0 @lvErrorCode = OpenIFSFile(fd, mode) if @lvErrorCode eq 0 then [ str = CreateIFSStream(fd, itemSize) if str eq 0 then [ CloseIFSFile(fd); @lvErrorCode = ecCreateStreamFailed ] ] resultis str ] //---------------------------------------------------------------------------- and OpenIFSFile(fd, mode, buffer; numargs na) = valof //---------------------------------------------------------------------------- // Attempts to open the file designated by fd. // Mode is one of modeRead, modeWrite, modeReadWrite, or modeAppend. // Returns zero if successful and an error code if unsuccessful. // buffer is optional: if supplied, a copy of the file's leader page // is returned in it. // Note: caller should not lock the directory, and the directory // is unlocked upon return. [ let callerSuppliedBuffer = na ge 3 & buffer ne 0 unless callerSuppliedBuffer do buffer = GetBufferForFD(fd) fd>>FD.mode = mode let fs = fd>>FD.fs fd>>FD.oldPageNumber = 0 let result = valof [ let ec = LookupFD(fd, lockRead) let owner = UserOwns(fd) // If file exists, read its leader page for access check if fd>>FD.lookupStatus eq lsExists then TransferLeaderPage(fd, buffer) // Must unlock directory while doing access checks (below), because // they are potentially very time-consuming (Grapevine and all that). UnlockDirFD(fd) if ec ne 0 resultis ec // Handle cases depending on mode switchon mode into [ case modeRead: case modeReadWrite: case modeReadWriteShared: // Check read protection if fd>>FD.lookupStatus eq lsExists then unless CheckAccess(lv buffer>>ILD.readProt, owner) do resultis ecAccessDenied if mode eq modeRead then endcase // Fall into write cases for modeReadWrite* // OpenIFSFile (cont'd) case modeWrite: case modeAppend: test fd>>FD.lookupStatus eq lsExists ifso // File already exists, check write/append protection unless CheckAccess((mode eq modeAppend? lv buffer>>ILD.appendProt, lv buffer>>ILD.writeProt), owner) resultis ecAccessDenied ifnot [ // File does not exist, must create it. // Make sure not trying to create new version of DIF if fd>>FD.dr>>DR.type eq drTypeDIF resultis ecIllegalDIFAccess // Check DIF for create permission and // disk utilization within allocation. let dfd = GetDIFRec(fd) // Get DIF dir entry if dfd eq 0 resultis ecDirNotFound let difRec = DIFRecFromDR(dfd>>FD.dr) unless CheckAccess(lv difRec>>DIFRec.createProt, owner) do [ DestroyFD(dfd); resultis ecAccessDenied ] unless CheckAllocation(difRec) do [ DestroyFD(dfd); resultis ecAllocExceeded ] // Now get default properties from the previous // version if any, or from the DIF otherwise. ec = LookupFD(fd, lockWrite) if ec eq 0 then [ test fd>>FD.lookupStatus eq lsOtherVersion ifso [ // Previous version exists, get its leader page TransferLeaderPage(fd, buffer) manifest oNIP = offset ILD.inhProps/16+lenInhProps Zero(buffer+oNIP, fs>>IFS.pageLength-oNIP) ] ifnot [ // No previous version, use DIF properties Zero(buffer, fs>>IFS.pageLength) MoveBlock(lv buffer>>ILD.inhProps, lv difRec>>DIFRec.inhProps, lenInhProps) ] // Ensure uniform capitalization of directory name ByteBlt(lv fd>>FD.dr>>DR.pathName, 2, lv dfd>>FD.dr>>DR.pathName, 2, fd>>FD.lenDirString-2) // Now time to actually create the file if fd>>FD.lookupStatus ne lsExists then ec = CreateIFSFile(fd, buffer) ] UnlockDirFD(fd) DestroyFD(dfd) if ec ne 0 resultis ec ] endcase default: IFSError(ecUndefinedOpenMode) ] // Lock the file in the Open File Table ec = LookupFD(fd, lockRead) if ec eq 0 & fd>>FD.lookupStatus ne lsExists then ec = ecFileNotFound if ec eq 0 then unless LockFile(fd, mode) do ec = ecFileBusy UnlockDirFD(fd) resultis ec ] unless callerSuppliedBuffer do SysFree(buffer) resultis result ] //---------------------------------------------------------------------------- and CreateIFSFile(fd, buffer) = valof //---------------------------------------------------------------------------- // Creates a new IFS file whose name is described by the fd. // The buffer is used to write the leader page. it must // already contain all properties besides those // that can be derived from the fd and userInfo. // Returns zero if successful and an error code if unsuccessful. // Directory must be write-locked by caller and remains so upon return. [ let fs = fd>>FD.fs let dr = fd>>FD.dr CopyString(lv buffer>>ILD.pathName, lv dr>>DR.pathName) CopyString(lv buffer>>ILD.author, CtxRunning>>RSCtx.userInfo>>UserInfo.userName) // Decide which unit to create the file on let unit = EmptiestUnit(fs) if unit ls 0 then resultis ecFileSystemFull // Make a name to hand to TFS. // It is the first 39 characters of the IFS filename let tfsName = ExtractSubstring(lv dr>>DR.pathName, 1, Min(dr>>DR.pathName.length, maxLengthFn)) // Create file CreateDiskFile(fs>>IFS.lpdt^unit, tfsName, lv dr>>DR.fp, 0, 0, 0, buffer) dr>>DR.fp.unit = unit SysFree(tfsName) // Create directory entry CreateDirectoryEntry(fd) fd>>FD.lookupStatus = lsExists fd>>FD.oldPageNumber = -1 // so leader page is counted in disk allocation resultis 0 ] //---------------------------------------------------------------------------- and CreateIFSStream(fd, itemSize) = valof //---------------------------------------------------------------------------- // Creates and returns a stream for an open (locked) file described by fd. // Returns zero if unsuccessful. [ let ksType = selecton fd>>FD.mode into [ // Select correct mode for streams package case modeRead: ksTypeReadOnly case modeWrite: case modeAppend: ksTypeWriteOnly case modeReadWrite: ksTypeReadWrite // This catches modeReadWriteShared, which is illegal for stream access default: IFSError(ecUndefinedOpenMode) ] let str = CreateDiskStream(lv fd>>FD.dr>>DR.fp, ksType, itemSize, IFSCleanup, IFSErrors, 0, nil, GetDiskFromFD(fd)) if str ne 0 then [ str>>ST.par1 = fd //remember fd for this stream // If writing, remember the page number of the last page // so we can update the page utilization when the file is closed. // If we just created the file, CreateIFSFile has already set it to -1. if fd>>FD.oldPageNumber eq 0 then fd>>FD.oldPageNumber = KsHintLastPageFa(str)>>FA.pageNumber // If appending, position the stream to end-of-file if fd>>FD.mode eq modeAppend then FileLength(str) ] resultis str ] //---------------------------------------------------------------------------- and StreamsFD(str) = str>>ST.par1 //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- and CreateDirectoryEntry(fd) be //---------------------------------------------------------------------------- // Directory must be write-locked by caller. [ ModifyDirFD(fd) // Mark directory as having been modified UpdateRecord(fd>>FD.fs>>IFS.dirBTree, fd, DirEntryGenerator, fd, 0, fd>>FD.pathStk, true) ] //---------------------------------------------------------------------------- and DirEntryGenerator(record, fd) = valof //---------------------------------------------------------------------------- // Procedure called from UpdateRecord to supply the directory // entry being inserted. The entry should not already exist // except in the case of a DIF being updated. [ let dr = fd>>FD.dr test record eq 0 ifso record = SysAllocate(dr>>DR.length) ifnot unless dr>>DR.type eq drTypeDIF do IFSError(ecDirEntryAlreadyExists) MoveBlock(record, dr, dr>>DR.length) resultis record ] //---------------------------------------------------------------------------- and IFSCleanup(str, buffer) be //---------------------------------------------------------------------------- // Cleanup procedure called from the streams package when a stream is closed. // fd>>FD.mode may be set by the caller to one of the private "open modes" // to indicate special handling: // modeDontUnlock: leave the file locked and don't destroy the FD // modeDontDestroy: don't destroy the FD // Ordinarily the file is unlocked and the FD destroyed. [ let fd = str>>ST.par1 if fd eq 0 return // Stream never really got created // Compute change in length of file. The end hint is up-to-date now since // TruncateDiskStream has been done by this point if it is to be done at all. CloseIFSFile(fd, KsHintLastPageFa(str)>>FA.pageNumber - fd>>FD.oldPageNumber) unless fd>>FD.mode ge modeDontUnlock do DestroyFD(fd) ] //---------------------------------------------------------------------------- and CloseIFSStream(str, dontUnlock; numargs na) = valof //---------------------------------------------------------------------------- // Closes an IFS file given a stream handle, and destroys the // stream, but does not destroy the fd. If dontUnlock is supplied and true, // does not unlock the file. Returns the fd. [ let fd = str>>ST.par1 fd>>FD.mode = na gr 1 & dontUnlock? modeDontUnlock, modeDontDestroy Closes(str) // Destroy stream and close file resultis fd ] //---------------------------------------------------------------------------- and CloseIFSFile(fd, dPages; numargs na) be //---------------------------------------------------------------------------- // Closes an IFS file given its fd, but does not destroy the fd. // If dPages is supplied and nonzero, the page utilization for // the directory is updated in the DIF. [ if na ge 2 & dPages ne 0 then UpdateDIFRec(fd, UpdatePageUsage, dPages) // Now unlock the file unless we were told not to by CloseIFSStream. unless fd>>FD.mode eq modeDontUnlock do UnlockFile(fd) ] //---------------------------------------------------------------------------- and UpdatePageUsage(record, dPages) = valof //---------------------------------------------------------------------------- [ if record eq 0 then IFSError(ecCantFindDIF) let difRec = DIFRecFromDR(record) DoubleIncrement(lv difRec>>DIFRec.diskPageUsage, dPages) if difRec>>DIFRec.diskPageUsage^0 ls 0 then Zero(lv difRec>>DIFRec.diskPageUsage, 2) // don't let it go negative resultis record ] //---------------------------------------------------------------------------- and IFSErrors(str, ec, par) be //---------------------------------------------------------------------------- [ SysErr(str, ec, par) // ***just punt for now ]