;to do: ; redo packet count stuff ; redo long file name gtjfn stuff ; timeout and closf open files (and readdir jfn cache) ; create makes multiple versions? ; re-read nfs-access file sometimes ; write $DUSW kludge Title NFS -- Network File System server ;Copyright (C) 1987 by Mark K. Lottor. All Rights Reserved. .DECSAV $PRSW==-1 ;pre-read switch (win) $GWSW==-1 ;fast getwd kludge $DUSW==0 ;directory update tad kludge (N.Y.I.) .INSRT MID:MACROS .INSRT MID:SYMBOLS .INSRT MID:SOUT PPKT==0 ;packet printer .INSRT DSK:UDP.MID .INSRT DSK:XDR.MID RTXSW==-1 ;rpc retransmit cache .INSRT DSK:RPC.MID ;preserved acs Q1==5 Q2==6 Q3==7 P1==10 P2==11 P3==12 P4==13 ;acs 14 (RP) and 15 (SP) are defined in RPC Define RetSkp Jrst Popj1 Termin PDLen==40 PDL: Block PDLen ;debugging stuff Debug: 0 ;non-zero for debugging messages $DBTIM==0 ;if debugging, print time data too ;do crlf Define DEBUGX Skipe Debug Call OUT.X Termin ;debugs (start of a debug line) Define DEBUGS Skipe Debug Call OUT.S Termin ;debug "foo" Define DEBUGM *string Skipe Debug Call [Push P,1 Move 1,[440700,,[asciz \string\]] PSOUT% Pop P,1 Ret] Termin ;debug foo Define DEBUGA addr Skipe Debug Call [Push P,1 Hrroi 1,addr PSOUT% Pop P,1 Ret] Termin ;debug foo (8-bit foo) Define DEBUG8 addr Skipe Debug Call [Push P,1 Move 1,[441000,,addr] PSOUT% Pop P,1 Ret] Termin ;debugo octal Define DEBUGO num Skipe Debug Call [Push P,2 Move 2,num Call OUT.O Pop P,2 Ret] Termin ;debugd decimal Define DEBUGD num Skipe Debug Call [Push P,2 Move 2,num Call OUT.D Pop P,2 Ret] Termin ;debugf -- display file handle in FH.* Define DEBUGF Skipe Debug Call OUT.F Termin Prog==100003. ;NFS Vers==2. ;version 2 OurPrt==2049. ;on this port RegArg: 4,,RegDat RegDat: Block 4 ;Dispatch table, nx4 array, [program ? version ? procedure ? address] DspTab: Prog ? Vers ? 00. ? D.NULL ;null procedure Prog ? Vers ? 01. ? D.GFA ;get file attributes Prog ? Vers ? 02. ? D.SFA ;set file attributes Prog ? Vers ? 04. ? D.LFN ;lookup file name Prog ? Vers ? 06. ? D.RF ;read from file Prog ? Vers ? 08. ? D.WF ;write to file Prog ? Vers ? 09. ? D.CRE ;create file Prog ? Vers ? 10. ? D.DEL ;delete file Prog ? Vers ? 11. ? D.REN ;rename file Prog ? Vers ? 14. ? D.CDIR ;create directory Prog ? Vers ? 15. ? D.DDIR ;delete directory Prog ? Vers ? 16. ? D.RD ;read from directory Prog ? Vers ? 17. ? D.GSA ;get system attributes Prog ? Vers ? 99. ? D.STAT ;get server statistics 0 ;end of table PHigh==20. ;highest procedure to do stats on ;Statistics area St.Cnt: Block PHigh ;count of calls St.CPU: Block PHigh ;cpu time St.Tim: Block PHigh ;elapsed time S.CPU: 0 S.Tim: 0 S.LTim: 0 ;the filehandle format (32 bytes) ;words 0-1: handle type (4 bits) ; 0 == reserved ; 1 == directory ; 2 == file ; 3 == file with long name ; handle id (24 bits) (index block address for files) ; (zero if directory) ; structure code (18 bits) ; directory number (18 bits) ;words 2-7: file name string (24 bytes) (type 2 only) FH%TYP==740000,,000000 FH.TRS==0 FH.TDI==1 FH.TFL==2 FH.TLN==3 FH%HND==037777,,777400 FH%ST1==000000,,000360 FH%ST2==777760,,000000 FH%DIR==000017,,777760 FH.TYP: 0 FH.HND: 0 FH.STR: 0 FH.DIR: 0 FH.NAM: Block 100 FH: Block 100 ;a file handle FN: Block 100 ;a file name FN.BYT: 0 ;fn byte size if any ;open file cache tables, all parallel OFN==10. ;size of cache OF.HND: Block OFN ;open file handle (zero==slot available) OF.STR: Block OFN ;structure code OF.TIM: Block OFN ;TAD of last use OF.JFN: Block OFN ;JFN on file OF.ACC: Block OFN ;access to file (R,W,E, etc.) OF.HST: Block OFN ;host number of file opener ;pre-read stuff OF.PRD: Block OFN ;status of pre-read buffer,,next buffer OF.MIS: Block OFN ;miss count OF%PON==Bit(0) ;pre-reading is in use OF%EOF==Bit(1) ;end count is EOF IFN $PRSW,[ OF.BSZ==5. ;buffer size in pages (per file) OF.STA: Block OFN*OF.BSZ ;buffer status (0 = ok) OF.BEG: Block OFN*OF.BSZ ;beginning offset OF.CNT: Block OFN*OF.BSZ ;byte count PR.BEG: 0 PR.CNT: 0 PR.OFN: 0 PR.FLAG:0 OF.BUF: Block OF.BSZ*1000*OFN ;pre-read buffers ] WF.OFF: 0 ;write file offset WF.CNT: 0 ;write file count ;IP address to directory mappings ADNam: Asciz "SYSTEM:NFS-ACCESS.CMD" ADJfn: 0 AD.Siz==200. AD.Len: 0 AD.Adr: Block AD.Siz AD.Dir: Block AD.Siz AD.Acc: Block AD.Siz AD%RD==Bit(0) AD%WR==Bit(1) ;attributes AT.MOD: 0 AT.SIZ: 0 AT.ATM: 0 AT.MTM: 0 ;directory cookie jfn caching stuff DC.Siz==10. DC.JFN: Block DC.Siz DC.DIR: Block DC.Siz DC.KKY: Block DC.Siz DC.TAD: Block DC.Siz IFN $GWSW,[ ;getwd kludge cache GW.Siz==10. GW.HST: Block GW.Siz GW.DIR: Block GW.Siz GW.PTR: 0 ];IFN $GWSW InBuf: Block 1000 ;file input buffer FooBuf: Block 100 ;foo goes here TmpJfn: 0 ;temp jfn holder LstDir: 0 ;last dir we gtfdb'd... LstDPT: 0 ; and last prot of it FdbBlk: Block .FBSS2+1 DIRBLK: Block 20 TSize: 0 ;max data transfer size ;sending: ip/udp/rpc take up 13 words (52 bytes) ;receiving: ?.. Jimmy==4 ;the Jimmy factor Page==512. ;words per page Psize==Jimmy*Page ;page size (whatever that means) ;statistics counters ST.OCR: 0 ;OFN cache filled ST.LNF: 0 ;Long file name cache filled (nyi) APRID: 0 ;processor serial number Start: RESET% Move P,[PDL(-PDLen)] ;init stack Movei 1,2000. DISMS% ;give portmapper and mountserver a chance Movei 1,.FHSLF RPCAP% Txne 2,SC%WHL Txnn 2,SC%NWZ Jrst [TypeCR "[NFS] Need wheel and net-wizard privs." Jrst Die] Seto 3, EPCAP% Move 1,[SIXBIT /APRID/] SYSGT% Movem 1,APRID Movei 1,OurPrt Call UDP"GetIQ ;get a udp queue Jrst [TypeCR "[NFS] Can't get a UDP queue." Jrst Die] Imuli 2,4 ;max pkt size to bytes Subi 2,160. ;minus ip/udp/rpc stuff Movem 2,TSize ;save max transfer size DEBUGM "Max data transfer size = " DEBUGD 2 DEBUGM " bytes." DEBUGX Movei 1,OurPrt Call RPC"Init ;init rpc Jrst [TypeCR "[NFS] Can't init RPC library." Jrst Done] Movei 2,RegDat Movei 1,Prog PutUns 1,2 Movei 1,Vers PutUns 1,2 Movei 1,17. ;UDP PutUns 1,2 Movei 1,OurPrt PutUns 1,2 Move 1,RegArg Call RPC"Reg ;register with portmapper Jrst [TypeCR "[NFS] Can't register with portmapper." Jrst Done] Call GetACF ;get access control info TypeCR "[NFS] initialized." Move 1,[Call D.IRD] Movem 1,RPC"R.IDsp ;setup intermediate dispatch routine Movei 1,Dsp Movei 2,DspTab Call RPC"DoRPC ;handle RPC requests forever TypeCR "[NFS] RPC death." Jrst Done ;dispatcher Dsp: Move 4,RPC"R.Proc Cail 4,PHigh Jrst Dsp1 Push P,1 Movei 1,.HPELP HPTIM% jfcl Movem 1,S.TIM Sub 1,S.LTIM ;time between requests IFN $DBTIM,[ DEBUGM "Idle " DEBUGD 1 DEBUGX ];IFN $DBTIM Movei 1,.HPRNT HPTIM% jfcl Movem 1,S.CPU Pop P,1 Dsp1: Call (1) ;DO IT NOW! Move 4,RPC"R.Proc Cail 4,PHigh Ret Aos St.Cnt(4) ;count calls Movei 1,.HPRNT HPTIM% jfcl Sub 1,S.CPU Addm 1,St.CPU(4) ;add up runtime IFN $DBTIM,[ DEBUGM " CPU/Elapsed " DEBUGD 1 ];IFN $DBTIM Movei 1,.HPELP HPTIM% jfcl Movem 1,S.LTIM Sub 1,S.TIM Addm 1,St.TIM(4) ;add up time IFN $DBTIM,[ DEBUGM " " DEBUGD 1 DEBUGX ];IFN $DBTIM Ret ;return statistics ; format: ok (zero) ; [proc#, calls, cpu, time] until (proc# = 0) D.STAT: Setz 1, PutUns 1,SP ;ok Movei 4,DspTab D.STAL: Skipn (4) ;eot? Jrst D.STAX ;yep, end Move 3,2(4) ;get proc num Jumpe 3,D.STA1 ;skip null routine PutUns 3,SP ;proc# Move 2,St.Cnt(3) PutUns 2,SP ;count of calls Move 2,St.CPU(3) PutUns 2,SP ;cpu Move 2,St.Tim(3) PutUns 2,SP ;time D.STA1: Addi 4,4 Jrst D.STAL D.STAX: Setz 1, PutUns 1,SP Ret ;the null procedure D.NULL: DEBUGS DEBUGM "Null" DEBUGX Ret ;get file attributes D.GFA: DEBUGS DEBUGM "Get file attributes of " Call GetHandle ;read file handle DEBUGF DEBUGX Move 1,FH.TYP Cain 1,FH.TDI ;is it a dir? Jrst D.GFAD ;yes, do other stuff Call Fh2JFN ;convert it to a JFN Jrst ER.NSF Movem 1,TmpJFN Call ChkLst ;check access to file Jrst [Call ER.ACC Jrst RelRet] SATOK: Setz 1, ;status OK PutUns 1,SP Move 1,TmpJFN Call PutAtr ;send attributes RelRet: Move 1,TmpJFN RLJFN% Erjmp .+1 Ret ;get file attributes of a directory D.GFAD: Setz 3, ;status OK PutUns 3,SP Hrlz 1,FH.STR Hrr 1,FH.DIR Call PutDat ;send directory attributes Ret ;set file attributes D.SFA: DEBUGS DEBUGM "Set file attributes of " Call GetHandle DEBUGF DEBUGX Move 1,FH.TYP Cain 1,FH.TDI ;directory? Jrst ER.DIR ;yep. loser. Call Fh2Jfn ;get jfn on file Jrst ER.NSF Movem 1,TmpJFN Call ChkWr ;check write access Jrst RelRet Move 1,TmpJFN Call GetAtr ;get new attributes Move 1,TmpJFN Call SetAtr ;set them Jrst SATOK ;create file D.CRE: DEBUGS DEBUGM |Create file "| Call GetDA Ret DEBUGA FN DEBUGM ";b" DEBUGD FN.BYT DEBUGM |" in dir | DEBUGF DEBUGX Hrlz 1,FH.STR Hrr 1,FH.DIR Call ChkWDr ;write access to dir? Ret GTDAL% Caml 2,3 ;over quota? Jrst ER.OVQ Call Da2Str Movx 1,GJ%SHT\GJ%NEW\GJ%FOU Hrroi 2,FooBuf GTJFN% ;get jfn on new file Erjmp ER.NSF Movem 1,TmpJfn Movx 2,OF%WR OPENF% ;open for write Erjmp [Call ER.NSF Jrst RelRet] Txo 1,CO%NRJ CLOSF% Erjmp .+1 Move 1,TmpJfn Hrli 1,.FBBYV Movx 2,FB%BSZ ;mask Move 3,FN.BYT Dpb 3,[.BP FB%BSZ,3] CHFDB% ;set file byte size Erjmp .+1 Call GetAtr Move 1,TmpJfn Call SetAtr ;set new attributes Move 1,TmpJfn Hrli 1,.SFAUT Hrroi 2,[Asciz /NFS-SERVER/] SFUST% ;that's us Erjmp .+1 Hrli 1,.SFLWR Hrroi 2,[Asciz /NFS-SERVER/] SFUST% Erjmp .+1 Move 1,TmpJfn Call Jfn2Fh Setz 1, PutUns 1,SP ;status OK Call PutFH ;send file handle Move 1,TmpJFN Call PutAtr ;send attributes Move 1,TmpJFN RLJFN% Erjmp .+1 Ret ;delete file D.DEL: DEBUGS DEBUGM |Delete file "| Call GetDA Ret DEBUGA FN DEBUGM |" in dir | DEBUGF DEBUGX Hrlz 1,FH.STR Hrr 1,FH.DIR Call ChkWDr ;write to dir allowed? Ret Call Da2Str Movx 1,GJ%SHT\GJ%OLD Hrroi 2,FooBuf GTJFN% ;get jfn on file to delete Erjmp ER.NSF Movem 1,TmpJFN Call O.Cls ;close file if in cache Move 1,TmpJFN Txo 1,DF%EXP ;die unix luser! DELF% Erjmp .+1 Setz 1, PutUns 1,SP ;OK Ret ;rename file D.REN: DEBUGS DEBUGM |Rename "| Call GetDA Ret DEBUGA FN DEBUGM |" in dir | DEBUGF DEBUGX Hrlz 1,FH.STR Hrr 1,FH.DIR Call ChkWDr ;can we write to the 'from' directory? Ret Call Da2Str Movx 1,GJ%SHT\GJ%OLD Hrroi 2,FooBuf GTJFN% Erjmp ER.NSF Movem 1,TmpJfn Call ChkRd ;and can we read the 'from' file? Jrst RelRet ;this line intentionally left blank DEBUGM | to "| Call GetDA Jrst RelRet DEBUGA FN DEBUGM |" in dir | DEBUGF DEBUGX Hrlz 1,FH.STR Hrr 1,FH.DIR Call ChkWDr ;can we write to 'to' dir? Jrst RelRet Move 1,TmpJfn Call O.Cls ;close file if in cache Call Da2Str Movx 1,GJ%SHT\GJ%NEW\GJ%FOU Hrroi 2,FooBuf GTJFN% Erjmp [Call ER.NSF Jrst RelRet] Move 2,1 Move 1,TmpJfn RNAMF% ;rename it! Erjmp ER.IO Move 1,2 RLJFN% Erjmp .+1 Setz 1, PutUns 1,SP ;OK Ret ;create directory D.CDIR: DEBUGS DEBUGM |Create directory "| Call GetDA Ret DEBUGM |" in dir | DEBUGF DEBUGM " -- not implemented." DEBUGX Jrst ER.ROS ;delete directory D.DDIR: DEBUGS DEBUGM |Delete directory "| Call GetDA Ret DEBUGM |" in dir | DEBUGF DEBUGM " -- not implemented." DEBUGX Jrst ER.ROS ;read from directory ;Watch out! This is gross. D.RD: DEBUGS DEBUGM "Read from directory " Call GetHandle ;get directory to read from DEBUGF DEBUGX Move 1,FH.TYP Caie 1,FH.TDI ;is it a dir? Jrst ER.NOD ;no Hrlz 1,FH.STR Hrr 1,FH.DIR Call ChkDir ;access to dir ok? Jrst D.RDX ;no, so pretend dir is empty GetUns P1,RP ;read cookie (4 bytes) GetUns P2,RP ;get max byte count to return Subi P2,120. ;minus safety margin (1 entry max size) Call DC.FND ;is jfn in cache? Jrst D.RD1 ;yeah, go continue it Hrroi 1,FooBuf Hrlz 2,FH.STR Hrr 2,FH.DIR DIRST% Erjmp [TypeCR "[NFS] DIRST% failure #232" Jrst ER.IO] Hrroi 2,[Asciz /*.*.*/] Push P,1 Call NRootP Hrroi 2,[Asciz /*.DIRECTORY/] ;if root only list dirs Pop P,1 Setz 3, $SOUT Setz Q1, Movx 1,GJ%SHT\GJ%OLD\GJ%IFG Hrroi 2,FooBuf GTJFN% Erjmp D.RDX Movem 1,DC.JFN(P4) ;save jfn here IFN $GWSW,[ Call NRootP Jrst [Skipn P1 Call GW.Snd Jrst D.RD3] Cain P1,1 ;oreo? Setz P1, ;yeah, make it generic ];IFN $GWSW Jumpe P1,D.RD1 ;no cookie, cold start D.RDL1: Hrrz 1,DC.JFN(P4) Move 2,[1,,.FBADR] Movei 3,Q1 GTFDB% Erjmp [TypeCR "[NFS] GTFDB% failure #72" Jrst ER.IO] Camn Q1,P1 ;found cookie? Jrst D.RDL2 ;yep Move 1,DC.JFN(P4) GNJFN% Erjmp D.RDX Jrst D.RDL1 D.RDL2: Move 1,DC.JFN(P4) GNJFN% Erjmp D.RDX D.RD1: Call NRootP Jrst [Caile P2,666. Movei P2,666. ;if root dir use smaller packets Jrst .+1] Setz 1, PutUns 1,SP ;status OK Subi P2,4 ;send an entry D.RD3: Hrrz 1,DC.JFN(P4) Move 2,[8.,,.FBHDR] Movei 3,FDBBlk GTFDB% Erjmp [TypeCR "[NFS] fatal GTFDB error #2]" Jrst ER.IO] Move Q1,FDBBlk+.FBCTL Txnn Q1,FB%DIR ;is it a dir? Jrst D.RD35 ;no Hrrz Q2,FDBBlk+.FBGEN ;yes, get dir number Hrl Q2,FH.STR ;add in structure code Move 1,Q2 Call ChkDir Jrst D.RDGN ;dir not listable Jrst D.RD03 D.RD35: Hrlz 1,FH.STR Hrr 1,FH.DIR Move 2,FDBBlk+.FBPRT Call ChkLsC ;listable Jrst D.RDGN ;no, get next file D.RD03: Seto 1, PutInt 1,SP ;valid entry Subi P2,4 Move 1,[441000,,FooBuf] Hrrz 2,DC.JFN(P4) Move 3,[002000,,000000] ;dir Txnn Q1,FB%DIR ;is it a dir? Move 3,[002220,,000001];no, do file JFNS% Erjmp [TypeCR "[NFS] JFNS% death #461" Jrst ER.IO] Move 1,[441000,,FooBuf] Call Lower ;lowercase it Txnn Q1,FB%DIR ;was it a dir? Jrst D.RD55 ;no Hlrz 1,Q2 ;get structure code Hrrz 2,Q2 ;get dir number Add 1,2 ;form fileid (str+dir) SkipA D.RD55: Move 1,FDBBlk+.FBADR PutUns 1,SP ;fileid Subi P2,4 ;now find length of string and send it D.RD4: Movni 2,1 Move 1,[441000,,FooBuf] D.RD6: Ildb 3,1 Addi 2,1 Jumpn 3,D.RD6 Movei 3,FooBuf Sub P2,2 PutStr 2,SP Subi P2,4 ;plus string length word ;cookie for next entry Move 1,FDBBlk+.FBADR PutUns 1,SP ;send cookie Subi P2,4 Movem 1,DC.KKY(P4) Call DC.Tim D.RDGN: Move 1,DC.JFN(P4) GNJFN% Erjmp D.RD2 Skiple P2 ;did we send enough? Jrst D.RD3 ;no, keep going Setz 1, PutUns 1,SP ;say last entry Setz 1, PutUns 1,SP ;send not end of dir Ret D.RD2: Setz 1, PutUns 1,SP ;say last entry Seto 1, PutUns 1,SP ;say it is EOF D.RDCJ: Hrrz 1,DC.JFN(P4) RLJFN% Erjmp .+1 Setzm DC.JFN(P4) Setzm DC.DIR(P4) Setzm DC.KKY(P4) Setzm DC.TAD(P4) Ret ;end of dir entries D.RDX: Setz 1, PutUns 1,SP ;status OK PutUns 1,SP ;end of entries Seto 1, PutUns 1,SP ;no entries in dir Jrst D.RDCJ ;finds cache entry for a directory cookie/jfn ;takes cookie in P1, rets index to cache in P4 ;retskps if fresh cookie DC.FND: Hrlz 2,FH.STR Hrr 2,FH.DIR Move 3,[.INFIN] Hrlzi 4,-DC.Siz DC.FNX: Caml 3,DC.TAD(4) Jrst [Move 3,DC.TAD(4) Hrrz P4,4 Jrst .+1] Came P1,DC.KKY(4) Jrst DC.FNC Came 2,DC.DIR(4) Jrst DC.FNC Hrrz P4,4 Ret ;found old cookie DC.FNC: Aobjn 4,DC.FNX Hrrz 1,DC.JFN(P4) Skipe 1 RLJFN% Erjmp .+1 Setzm DC.JFN(P4) Setzm DC.KKY(P4) Movem 2,DC.DIR(P4) RetSkp DC.Tim: GTAD% Movem 1,DC.TAD(P4) Ret IFN $GWSW,[ GW.Snd: Setz 1, PutUns 1,SP ;status ok Subi P2,4 ;find dir entry, ret if none Call GetHst Hrlzi 4,-GW.Siz GW.Snx: Camn 1,GW.HST(4) Jrst GW.FnE Aobjn 4,GW.Snx Ret ;didn't find an entry GW.FnE: Seto 1, PutInt 1,SP ;valid entry Subi P2,4 Hlrz 1,GW.DIR(4) Hrrz 2,GW.DIR(4) Add 1,2 PutUns 1,SP ;fileid Subi P2,4 Move 1,[440700,,InBuf] Move 2,GW.DIR(4) DIRST% Erjmp [TypeCR "[NFS] DIRST% death #287" Jrst ER.IO] ; Setzm GW.HST(4) ;don't want these anymore ; Setzm GW.DIR(4) Move 1,[441000,,FooBuf] Move 2,[440700,,InBuf] Ildb 3,2 Caie 3,"< Jrst .-2 Ildb 3,2 Cain 3,"> Setz 3, Idpb 3,1 Jumpn 3,.-4 Move 1,[441000,,FooBuf] Call Lower ;lowercase it ;now find length of string and send it Movni 2,1 Move 1,[441000,,FooBuf] GW.SnL: Ildb 3,1 Addi 2,1 Jumpn 3,GW.SnL Movei 3,FooBuf Sub P2,2 PutStr 2,SP Subi P2,4 ;plus string length word Movei 1,1 PutUns 1,SP ;oreo cookie Subi P2,4 Call DC.Tim Ret ];IFN $GWSW ;check read access to jfn in ac1 ;retskp if ok, ret if not ChkRd: Push P,Q1 Move Q1,1 ;save jfn here Movx 1,RC%EMO Move 2,Q1 RCDIR% ;get dir jfn is in Erjmp .+1 Move 1,3 Call ChkDir ;check dir access Jrst [Pop P,Q1 Jrst ER.ACC] Push P,1 ;save dir num Move 1,Q1 Move 2,[1,,.FBPRT] Movei 3,4. GTFDB% Erjmp [Move 4,[500000,,770000] Jrst .+1] Pop P,1 ;this dir Pop P,Q1 ;restore jfn Trne 4,40 ;world read access? RetSkp ;yep, ok Movx 2,AD%RD Call ChkAcc ;check for dir read access Jrst ER.ACC RetSkp ;allowed ;see if a host has certain access to a directory ;uses current host ;takes ac1/ dir# ; ac2/ access bits ;retskps if true, kills ac3-4 ChkAcc: Push P,1 Call GetHst Move 3,1 Pop P,1 Movn 4,AD.Len Hrlz 4,4 ChkAc1: Came 3,AD.Adr(4) ;found host? Jrst ChkAc2 ;no Came 1,AD.Dir(4) ;same dir? Jrst ChkAc2 ;no Move 3,AD.Acc(4) ;get access bits Txne 3,AD%RD ;read access? RetSkp ;yes, win Jrst ChkAcF ;no, quit ChkAc2: Aobjn 4,ChkAc1 ChkAcF: Ret ;lose ;check write access to jfn in ac1 ;retskp if ok, ret if not ChkWr: Push P,1 ;save jfn Move 2,1 Movx 1,RC%EMO RCDIR% ;get dir jfn is in Erjmp [Pop P,1 Jrst ER.IO] Move 1,3 ;this dir SkipA ChkWDr: Push P,1 ;check a dir instead of a jfn Movx 2,AD%WR Call ChkAcc Jrst [Pop P,1 Jrst ER.ACC] Pop P,1 RetSkp ;retskp if dir access ok ;ac1/ dir num ChkDir: Hrrz 2,1 Cain 2,1 RetSkp ;ok if root Camn 1,LstDir ;same as last time? Jrst [Move 2,LstDPT ;yep, skip jsys Jrst ChkDi1] Movei 2,DirBlk Setzm DirBlk Setz 3, GTDIR% Hrrz 2,DirBlk+.CDDPT ;get dir protection Movem 1,LstDir Movem 2,LstDPT ChkDi1: Trne 2,40 ;world access? RetSkp ;yep Movx 2,AD%RD Call ChkAcc Ret RetSkp ;check if handle is of a root-directory. skip if not. NRootP: Movei 1,1 ;root's are always 1 Camn 1,FH.DIR Ret RetSkp ;check list access of jfn in ac1 ;retskp if ok, ret if not ChkLst: Push P,Q1 ;save jfn Push P,Q2 ;for dir num Move Q1,1 Movx 1,RC%EMO Move 2,Q1 RCDIR% ;get dir jfn is in Erjmp [Pop P,Q2 Pop P,Q1 Jrst ER.IO] Move Q2,3 ;save dir num Move 1,Q2 Call ChkDir Jrst [Pop P,Q2 Jrst ChkLsF] ;no access to dir Move 1,Q1 Move 2,[1,,.FBPRT] Movei 3,4. GTFDB% Erjmp [TypeCR "[NFS] GTFDB error #1" Move 4,[500000,,770000] Jrst .+1] Move 1,Q2 ;dir Pop P,Q2 Trne 4,02 ;dir listing ok? Jrst ChkLsP ;yep Movx 2,AD%RD Call ChkAcc Jrst ChkLsF Jrst ChkLsP ;yes ChkLsF: Move 1,Q1 Pop P,Q1 Ret ChkLsP: Move 1,Q1 Pop P,Q1 RetSkp ;same as ChkLst but doesn't use jsys calls ;takes ac1/ dir# file is in, ac2/ file protection ChkLsC: Push P,Q1 Push P,Q2 Move Q1,1 Move Q2,2 Call ChkDir Jrst ChkLcF Trne Q2,02 Jrst ChkLcP Move 1,Q1 Movx 2,AD%RD Call ChkAcc Jrst ChkLcF ChkLcP: Pop P,Q2 Pop P,Q1 RetSkp ChkLcF: Pop P,Q2 Pop P,Q1 Ret ;Return file system attributes D.GSA: DEBUGS DEBUGM "Read file system attributes of " Call GetHandle DEBUGF DEBUGX Move 1,FH.STR Hrli 1,600000 GDSKC% ;Get DiSK Crap Erjmp ER.NSF Setz 3, ;status OK PutUns 3,SP Move 3,TSize ;max transfer size PutUns 3,SP Movei 3,PSize ;page size in bytes PutUns 3,SP Move 3,1 Add 3,2 PutUns 3,SP ;total page count PutUns 2,SP ;free pages PutUns 2,SP ;pages available to user Ret ;lookup file name D.LFN: DEBUGS DEBUGM |Lookup file name "| Call GetDA ;get directory args Ret DEBUGA FN DEBUGM |" in dir | DEBUGF DEBUGX Hrroi 1,FooBuf Hrlz 2,FH.STR Hrr 2,FH.DIR DIRST% Erjmp ER.NOD ;oops Setz 4, ;no dot seen yet Move 2,[440700,,FN] D.LFN1: Ildb 3,2 Cain 3,". Seto 4, ;saw a dot Idpb 3,1 Jumpn 3,D.LFN1 Jumpe 4,D.LFN2 ;it's a dir name (or maybe a file) Move 1,FN Camn 1,[Asciz /./] ;dumb unix machine looking for "."? Jrst D.LFDT ;yep Camn 1,[Asciz /../] ;dumb unix machine looking for ".."? Jrst D.LFD2 ;yep ;else its a file D.LFNF: Movx 1,GJ%SHT\GJ%OLD Hrroi 2,FooBuf GTJFN% Erjmp ER.NSF Movem 1,TmpJFN Call ChkLst Jrst [Call ER.ACC Jrst RelRet] Call Jfn2Fh ;send reply Setz 1, PutUns 1,SP ;status OK Call PutFH ;send file handle Move 1,TmpJFN Call PutAtr ;send attributes Move 1,TmpJFN RLJFN% Erjmp .+1 Ret ;send dir handle D.LFN2: Movei 3,"> Dpb 3,1 Setz 3, Idpb 3,1 Move 1,[440700,,FooBuf] D.LFN3: Ildb 2,1 Caie 2,"> Jrst D.LFN3 Movei 2,". Dpb 2,1 D.LFN4: Move 1,[440700,,FooBuf] Call Dir2FH Jrst D.LFN6 ;no such dir Push P,1 ;save dir num Call ChkDir ;ok? Jrst [Call ER.ACC Pop P,1 Ret] Setz 1, PutUns 1,SP ;status OK Call PutFH ;send file handle Pop P,1 ;get dir num Call PutDat ;send attributes Ret ;lookup file STR: