From 7591e85f7b44ba01a5d9f5d2067c15e314496414 Mon Sep 17 00:00:00 2001 From: ncpfs archive import Date: Tue, 28 Apr 2026 20:39:57 +0200 Subject: [PATCH] Import ncpfs 0.1 --- .downloads/ncpfs-0.1.tgz | Bin 0 -> 32256 bytes COPYING | 339 +++++++++++++++ LICENSE | 117 ----- Makefile | 78 ++++ README | 90 ++++ README.md | 3 - dir.c | 917 +++++++++++++++++++++++++++++++++++++++ file.c | 176 ++++++++ inode.c | 375 ++++++++++++++++ ioctl.c | 88 ++++ ipxutil.h | 53 +++ linux | 1 + ncp.h | 101 +++++ ncp_fs.h | 145 +++++++ ncp_fs_i.h | 59 +++ ncp_fs_sb.h | 61 +++ ncp_mount.h | 49 +++ ncpfs-0.1.lsm | 14 + ncplib.c | 598 +++++++++++++++++++++++++ ncplib.h | 102 +++++ ncpmount.c | 332 ++++++++++++++ nwcrypt.c | 141 ++++++ nwcrypt.h | 5 + sock.c | 533 +++++++++++++++++++++++ startnet | 3 + 25 files changed, 4260 insertions(+), 120 deletions(-) create mode 100644 .downloads/ncpfs-0.1.tgz create mode 100644 COPYING delete mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README delete mode 100644 README.md create mode 100644 dir.c create mode 100644 file.c create mode 100644 inode.c create mode 100644 ioctl.c create mode 100644 ipxutil.h create mode 120000 linux create mode 100644 ncp.h create mode 100644 ncp_fs.h create mode 100644 ncp_fs_i.h create mode 100644 ncp_fs_sb.h create mode 100644 ncp_mount.h create mode 100644 ncpfs-0.1.lsm create mode 100644 ncplib.c create mode 100644 ncplib.h create mode 100644 ncpmount.c create mode 100644 nwcrypt.c create mode 100644 nwcrypt.h create mode 100644 sock.c create mode 100755 startnet diff --git a/.downloads/ncpfs-0.1.tgz b/.downloads/ncpfs-0.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..bd9c5299a458d7261a2429d737fdcf377c679343 GIT binary patch literal 32256 zcmV(%K;pk2iwFR$7=|zc1MEC&QyWLN`6~U2#wmjYLY4&B8)QfkBN8?kT!53!CaI}G zYN08l8QpmpaF+Yq_dI%Lx@USMkxKS%sxG@RsGp}#pZ7T}e>fjUU;Md0olfV?-kyNp z-Q71k_#6Iu!@t#!=)CUicJY5ZuSKW3(|rT|dtdwqev&A5LjYwe{gE8r$?9=3j20gf z&+$d?&F=1Q51{{R{P=kPui#&279@V$p8PrY|9W?~^8S0>z1MXAoj0#{dV4*%|GnMa zF7*FJ?|)}!_w`@p{$Fj@#ikG%0l;T!dKb(eL+^GHi^jXA=zjIpp164wKLpb|8HzK7 zEgt-;UjNb?L(5n=XQ$^M|KXg!`{2C4_;`Npd{_Sx+C5*|x?q|=oF*eF-cCI~d3Y5+ z&Slh|9F{t~`Gc();I3$P#*yP;U$%d~@ehN`tJ8~f(KS0iUJWkKkKPZQv%$G&V9PhB z=f{J~pPaO{X^j7HbanOP#pN-L?{(_+SDWGocbdpZjDt{&r|xZp$SDJGb#ZbnIuFPP zmfq9z>r3wK)$36lCc`+tAZO&puK2B9^ZZzR0-E#!zhAEsjz8+x0G{iK z6j1;-6^l3!F?@nfF%QBRAv34lkAmBD-j8lPoWN%>cX4tghQUmVi40|V-b_Z3dn<9O zfT`y}EPz`^;!(yeF>@bpB;i91Ba{gBlXxQs7QlnakprLl+>Q`T0-P)o>TNUucn=Wk zO=a{L#d7vF(Opg92hZq^Mxg_QB7jvtIrvFO%y8m{)FVU3cW3hF-LHP_*NX#?&8fFY z5L?|}ORLS@D7p{A(a%L{puq(!#okOhK@t;szYFfgH1KalGzpUFNSum$*NcTaj%8S{ z!4Kdd0v_{Vh>N5lLis;HSn=0o^oKGIADtm$iSU2qkHiGl4`4Ar-++c$rW2V0wC0uI zRb)-K)O4#i&4Q7HHoOi8|1R~6yij#K!Qx-yS;qA*{{QE{QvbtcwTFME{kK5>@9ph& zQ~M8WfL<5$|8Dp7o4>67@9plq?)+8szfcAMQNZt2{igCA2L9N)Wwks;o8bM$@yD}4 zX&gw0WGZdyTd6hbV!=@w5)CJEWSc9)&<|`KaL=)=9nR#-sjX+`PN%`p+B2)RI?Nt7 zn!SqT?M(XdYS6xbRi@red-6gvPEZG3oSa+@t{WrS6paQ5Zu|uwwhu?7&cwE8kW$)& z4rp)Yw#p?~701rU4~++$1OIOAG{w63-<>9!7jEngNfFc$eiYlGbVm)PS3MtOxY(p$ zEuk7w>WfYIciv(}xOtO|TeJwNp=Q6lFfIy1hmogn@-aXS+Gv_`5eX_FlNHxdWly12C*RYpYrC2aGTdw3UgZ>hmN; zU_QOPJ#C9yIk<>|0~Q={>!Rz|@ljYjrK4#O+$Hm6$aILw4J@XZEno@Jm`n>iSO>2` z0?9_b#P=Q=nE?paVaTRwtI&oMa1GE88p?PbT%!kzp%@bQ0iBZ43Y)a7D7LDFQFCPQ zO;JqI`fwH}na^d|$cd;~2Y2Uwg2JJ}4e0;Wys`Qy4iChJyCK%sdAxWFpFeLj(U9hb z?E}%k5!+(pXahE{)v$SM3;uelKUD%U4ep;0$WZ~1f9im2Spg9xNHl@y1b_`_1Y$@j zT5mc45K@Xb|9E!Rg1x{Am_}0G!8V`*T%ZOr`*r!=qEYx447!1Taey}eKJ;R#b!j3z zp9&v}^CCHw0C;(0FBryCo=SffhR_nC`~$;hGk0Fx#t%d^mqX8;3c!UAtTK`d#)JTs z8%c8*j~Yjh{t(-q`kAxM%x=MZsROZJTQ6Tr3ztsMhcajw2jL?txL_U?K^+1%W0~QK zMFOFNFOxnOCjqVAnYg3o#5X(Cy*$p!<9WjD&bY_CBb-?-cyfW(+-B_+0uEigw7}DAS-!m^lTQTbqb|Q+@s(8zPlF@_00i5h4^u_kz^y>KZ zvX2}Mo1glE{H&I^mtx|6k|;F6o#%}r;SR$ff?v~V8Vb?k958DfgvjIAuV%y(Hx_P) zD~Oq#0d2L#HMDI+;(qcdJp4oYL2^66un(vz`GCP6TqUPQGC9z)$FLBe*-vISGHeS? zI%o2m;uWm9kr9CQJG$XjQtxEUEEyrC@5o@hc+}L5Vuvv+QRl*8`W6+tLV`#*(hG`7)@vR%PgAu%{C%O$;3Q?t^nRxcp&o*`Pz9)~+2N zf4Dq7zy7}QLOb$4!a3hQ96Mls!pxVWU;Gy>LEePcgL;i=#M+fZY|_YvW5B4%N_8FrRCl)Rg%=TaL=b|E2R6t_8ullp znz7+GN5?0mT6yn`G#I~4QLm8#2k4Pl0~k`ek|ub>h~mq^(J=t)ygPdL-N3mz{pSF8 z%89LExy&Ygx?@MO;kCrKCm)>e2bbrAGlJ6!6GCJ^KA`#i;QhtrPZUoj_DxTmANl+O z#$eME{DtU>ysa&@u>$93@7FdOtm2?9mi%OHr+!w7W*_|nJy(p5Z;5Ve z=|a{Y17lT#G(6^j=lEQ=^bOD9>C2`u6*pZbiUH$X#obe&JchcM1T4qV!6u&@o$ z)gUrG)avwq#JGa-Ewd&qo>^A8EZ|W*-JE!6=jpP6h59MmY!UJq03>sb7*r23GW6u& z)KVT)IO*4)%XggxGCd!HTP>F>HO*zWiD2f%qKqtYkHvWa#+hIyj+mz<l~5(zMN; zxp9o%MdPO+0j&Ap!Q6ruUkf%+H>^p5hjyJc^`cN+=ZsmYZPAD&eb#O0&4A257TyCHgC@IGBgQZRpM- z0g`NnM1F%&9Dhs&223)p_$Y4gzD?An{jNXF)Fr?+eLFsngN;hKrQhV#dUAu<;oBIP?pA~hYEY1fm^mW$B+ z^k0$LE-*HQzoi=vC(LT&h?%W8hI>AfQeFLU|KTU*UKH* zPK&&zlhgB)3siAUbxllgVx8eLcse87JdQMu1g=_$*`>p&_WUF_pVWRW zIz>yo*i1>kw$6#}EZoEHrbRl8RhXs3VfpRnQUQT6#R6^9CLmXl3S-hTmGWQ-)9WBH z%dZGft<3lbgfk1@AMiS6qMTPv=G030Y$J?ebgRU6Npe^h?zGfoO8t3(Z<+3{Fx?#v zWfY~8Es_WI&+L+B#m`E9XwxqpS3u|IKikdtuLD3E~5K)ZEn(GxKrDl4JtjOQ8AgK`> zEyqN%SkJ=1jS)Z4fx(FKg2Sb@jy1|{&LzuVhAf!U@pVgdJ#%U*f*sm+&rCho;?fi| zccNlAr^N+v;ErQnp=k}TSf<-O5YGENC+*X$CY2R&{gCPa02uSb#dyFzdZ_YYE{AbM zu0mR$r5s=v++s;d31ItCokJWLpaOcqpoAH$AW!xGqK->vG&Ke>OLYRAwmdz*_@LA& zOAcJcOqUA5v+!J1vnc9=)uy6r5N$qlEsN9SFjzgeH1C!nX;kkqE<_36cCYhYKdb`uha;-?`E?WcWuUT_ZU!bp zsaSz@DqJtvcVR;ZcH@^{HmZUw7`jj?GrJU~Fy6XO`D6%M7?2B)J!5NoG z?AzSkio7td_*0x?*a zrD{36l8yZhy>Uys(G+-U&No#>6^cE!4EF2gVneAXEW*(7qYDaU!Y^m+9PX?sCs1R7 z0z6&o;f)dSpNZRAL2&Q@9IDJ#O}emZvdHIskws`*tUZ^>WjwrABW;A-GWz~cmWLPh zJLdz+sS-n-^B^;WlXmpY)Ep*EY}pcopFl=vRi)K>zOM4Z7pNoJ%3?IqrrBv#_9Crl z$v4>Q8ihQ~MUj4sJ#hg_1dIkQ+f(shWz3-shlAQLEOtRvWr%hF<&tR^mgSyH()Pf5 z6}C7oIULqfd31GjOPlxXH1op8NQRi9IUoF3%xnN&0xf+Q!wQI&APKeNM8Dyis@GPJWGIn-6%>z$>oq#^vLBp7%iZ(#!$9Fxl!>XjGU>Fbh}<8G83BYjwCYb z9{=dK2U>(nFFCZ<_~{8CU*2n{1vm-=->&?-{YVF~!DKlL`3- z`y!jPNU++CnRZGk#G{;O{r zh#eV)cqyqZ3?X{DL}&?5O;k%5Z*Ff}&I@NG zl<7g>j1A04H?mH3&Q2%51=a%z2&3rJ@L-J1_SH9Pq?#$6Hfw9&w4vjNnY%2;YkEg9 zMXUD}^Q(HFk-*hEhE*MU5*N%f?a=H3P`DhX~)xQ3%V7 z2tSwPbyn7py2glHNoeP&+~-@q5@&;x>$SBtQ+$`}LjQmbvBBe<)JWRZg-w0sJ6k63 zW40l_rA!M?UEjca+gcb$U)PXKWy3f$N3To4Iva_yU3KqGr^?vTT}SlkZX_n)BL0>m za1S`9kyo?c02S4>5=ql^f;l0-mYZa+@_0qWpjxxeRa43}MQ0TD!MZNa%8b1SIkLHs zf8_h|6-0PiWxAN6jW;rjU5Tt0*!VZAro*_yY_38irlw`LkxYqI>q*Pq>O|@K1%3aN zF%1t~E1liYK>E?1%8x;x^&a1v7PG&{NB=($x8-ks$^nCMxz@#`i~edfpiE|}hmm0z zgl5EzDk)$LrzO?6yu6yjn2dNu2U$?Tc)h2r?uxg2jLzjx_u8p1@6D4~d5uQcd~kkk z`u2uJl5V5y5GOM{YPNTh$|?PQ$tPaBkbbIZ*dlAKkV~Sy&Qbj-DVDm3H;D(zGMS?s z#qaq-0Yf3c43GxhogN6K2&hO&kTQ$w{CBa0o4=pHf>A{eIzBa`DHm=RkjGK;l9tp~ zNGqYN@5HJaggH7`kQB z)j}Xl-!3}x_0qG$46{m(x%4)TV#Z#aM@cX?+`{sn^1+K@tj(u6sl&sJIca;%GWNnK zrm|-16L{Mc&qf*IfR`#yv1;^8z*;4296qvD2{3@X9kD3Lit&~{qil)t>#RlZdX;Ta zssU0WzVcz~!biU8Va(@{Yj!KSTshAo@;MJrHOEn@QzFMSWdW=ctt~3+uOzeX%Fj_( ze@UzpPQ#<33Gp)fLWk#(3qK-A$polG1=?03mer^wv;RDPn*o&GtjzBf5twJh$v8th zpY=QEy8BwLJnJ)bFKJa{+OOHG#WKGydQ+=>9GA+M=}TY8E)ZM=PEs}S!Ii-gi%R}eWSvvS&X z2BEojkk&2NN-CR*`*mZ+Vj!k9@kS&`ul6@WMOpd25hp6Ny4F?|h{_^Sb+dxmTBzf2%Hv~*)J*!a?O^5hesETOcRuNtCoQx5^PqS>gVD%as z8U+blE{jC~`~_0<^~jk2ckjSKV_y;kB)!d^ML;OqWBRTwoOQoZYw+H|2bz9xP zbz8k|uhrY#Y4zUhw01hXt(~3SR)_y5p8E7HlK|A;UH?Shym!N+c_`ikXn#+9_-`tX zl3Q^l=i&ln$a(Nd983C=vU*)U#1inK#0xUUXMA25Jw{Ia+4#Obto2ReVGl#6(?uL<+!`RlY~4;Ej#?AF3%@m}63U&|Hmum0WM zySBBFB#FZBSL!)GBHJ@9+*i$d`C;SHR_$Slzvv#en&n64OC2gTw`#RVPq#K|wTJ8jC__>g z>-9&Bd^DdAk4iZ3Z?Ljj!wB3COvg8EISm>90k zzcEm))~ioyYBHS1>S+yRR5}ZIbWmiF{Y{VnSDJXU>kSnJYqE8;lewE*)!O>nZxL)# z?<&ln_jEk$;_n0yj?*r^!geJm#(YHxJ><7ID)|Z$@m8(;xbdh|Nu#sQ0gb5xzX5&cCPM zxAA}JwD9v=-Cb?R;AXuczdw3Z)?$*4@32B1tZahpz(gL(-f_{o)nL8;;ZgmOjKn*_ zbA;c&Bb5DL9+^JL|8fy`V|7EMu;P~(ON0aiuG&k>b{+G?WnEyo(;1}k9p}R|` z+j)`XeMvs$hD?{dGtY~Y!RSEpU2(PRcHP7+2d$TT=Z9zA7w0eWffh;ePUaL*2L97? zj4D<>l@Vty0m?8WYqJ&&sfwa181~~r=jK2eBLKN^CZ3~e2oxxZF*1Yeby9U^jE=*b zs*|;Z!%WH@aYC6vUgD=oMExVj!~jE*z$M#~Gqw~p#Zs&7ngp+e#n5xw8ut~PewnGz5J`5NuQNYJ(m$_nC6RB&W0c{eF|Fz}7)7(d`f97>?Ve<3 zbh{2nAF!K}s)jETi6+@?I3ANj`aGtVFJLVjs0;vp`W!@eYI7MO2nNyh7*#KN`I5?o z{g1CPj?MT66*4?ID+Fq9esK{@S?xylqu?@{h2#eTMRdJQH<<>L;Z4$_gLUv#piiOG zpe)mZ6a0v7FO}ftAs2GW8nKN`pc}k%C96m_7VcjGUZZfNkTd-_V}W4xk+CkqemrSOg4nquE{i-Mg3^VH!h9qm>Q=GgUu1+;rAW4Q?c<&JTM9_Je5_+WJ#;Q_Oztg zl@Ii8-n*EO8Eu?G4mDvw=`At1vS$rz5ECyP@&}^jq7RXdJ}gcT@HHW4OhW$%NyOp* z;cvqJm+}8*y$aQ#QPueHXP0Js5J>~4`0 z4#a?VUP3tfUX#vt#Xe9f-mz;oiPaWK1%rXMAWMb+ci&}pBG zf2JNC^FQ`bq$7;h!Wjpf7YifDC_y^U??JoWggvmzmlK1%Y#+9|ZORLVDa4=}$hcy@ z#84F2`<~WHLd#${h)D;mK4CIBfOUv!e8XkY1VCX9)l=0AfdG?#Ac`TFH$hjUl0L&n zq0$JpVxYA4GuAUUQ!y-qqzL@WwQo}TvYDPXUADDR^$h-k8T%BZZL9-|Ykl@OnvVP- z^fxH7Z;jmUKsq=-+vm4F^GjGPTjMFp`;`xC9FvN!pfgf3Ct2ixqx7q+I>Y4}fq_-V z414;O09MstG4;^ZNW!p)i1>M?ea6VNRbh4m!*uPG2yR#iilMQyCcMdx6-pDc_{KxS zGxoKk8k;*sZ!(T%#LX&-LM%P_+k-S;qbG+XE%>8=wbxY>`91(K$YNgXK9S%)yHWhK zci28aQRcgf?e>R`4R&-$#5;QzQa%_J%j|A6v%TN~1s!s@&opJvkBaM(fli)E$3F1I z0Ur&1{VTEH=T4)2M5~}xGp#^DngZ}(dm<4f6Fi>|RXI%>EC!!|C>MhCzpi%`dF}3G zJWO!^wa*hsNZYHtG7`}h#JFhy@*s-CaT567UVE|Y{R+>_KF{zWs^fEC=$mHc^!qcl z-~SZ-&u=6BEtUVC)M^_n|J9pY%_p0f|L@6NmjCKcwwiZR{`(b^|AIjGxj>OEr9;U%2$JaK&Vylr-*voYLK0GHe!>8@)pb{J=5;#$$s~&{ibz% zex^!jxPi%SDQ#sG!9v<&|3zBwfQ@PHF%_Sg|r3{K+}ywOut1RnOA^-40#PC@oM`}rczKI;9wVkVoOUSM1S0-g;|KZls!&Kt3vj4D^X8B`<7HVKrT+^@iNV&jUCHg_6D zaAA1vdaO27t95R;JL^!@f?t)+e3>2ThJ&nG<_}QRf&5mMf6}|IrXo}E~qKy zFrcKy8VuuT7GB(RLGBt~1`G4Zh2(Dekb({8Z(m}fT8ef=snP5d#$9T^utg3O(ZuI& zs0gsjBC=~~HZ>8L_ezrgjk+KN`(63-A$;)5%WX?k;NHcs^iM6w|91Tk3k|>37>o5k^;)f2 zWBt!YtKUdu3NPk70?7rM#4IX*Z)Y-O)+5rzrK z)6s|th>DEtt*iL)=y4oej)JuEhYI+;exAyR;u7SI28$X8!@QDUIvq!O1sDDCEVl(p zih18hqhei@Q-bdF!?0Jqy0_@Mn|{bnnXOmLavRvmi`g?OoWMD=MQ0_A@nU{`(#rg`Pt0;=%ZOwx_SbuH;*hr#3_j~$v zbSYJAUXR|cS>3` z^)LMG?P>e0WuA%HnI&{_k{yHt{c6kD`iq|+lCF;F>uxeF1!6;U3)%nPnXX}>ijPAp*j-SdeuJMSf*altM{DvAGDvVXM+zGWP>NQCOhg? z)QppuRCIV!`U@~Dy13}hFkYW)KPEO)m{CB_Q4%-kuor2yY$hL}oq{_jRjk#o4y>0G zX4BAClY*9Fft8JuLCH6@wC+sc&mfqJ__c-BF5W=4RVPaLNO?gr&)z zn&Tx;DZSz+cVj08AxCk?sY{*|9VA84-EjJwWWKC>U0c(6iJStA|DE(8d3tC%b|40C zO}l>PWPV?NZo7;oAh1zRKTalBpDDS8`t$_e2aYoDU*S(3J(AoN0v|rgpiXcnd3Eh{ z{!GxHpEQ$%fO;8vv`U^i`dKuA=v&>d^M+MU3QSZlbzP;ZrwM$fiOHXql%T!A973Hl z#-4oHN#;f*AOqpNGp0F!r@0m-F*HLFVYu&tAuszIAn!5x7*SlQkOKn*pZ)#}=nB2) zuF8{bTA1YROM`a}@guJwjnmGy<61dVW!*|tc9fVb2wW{DbuXNrPg24vAwAKJgvI*Z5Ov0 z>1c%?+5y%E9|8B^8sy`%T-;MZsv&Bol5hEWBgh?cNgZ^{^w~vg&0I@(T>4e}rrp|3!En8w1MvzyXO#64r?y(9 zmPwD!Ga%)+%bBkj&NuaKKF<3Z-;DA;k3MSuKzsW}IUh%G-Ur!@rWhJI+n+ZWKauX{ z74_`Bb=yb#-SbYXyMKIsbmo*Cw49QBEum2c7HSVy*AnLF>Xzyc^R<4Q z!0k5L!$LhI+Na`MDJT;qVxG#;c9-c7qd4eZ7?YK`22aX6PQDOg+Zap432`b{X{ej7 zN@C`Ub+OxGL$5Uvr#K~2#L+|~2zU63E4$Fuh$iB4P&NT8HUZ~LX-PW=7A*R}pE(t1 zrbz|*QxRGts=K_bkeWz6V2fb$Ok^2%77CRI>`rJ zSBwX-gz*b!A-(&Etx&zxF@@Yi2Yiafg*`~77S*1qK@`ME<_i=^&|}_YJH)q=6vE1h z_+fmW3ekwAd^c+K@q9`zNo`-q&$DST3Shl)h|e#SP9eiUkeGl@q6P`m#cRBXM*P?i zqgVH$!42P0OFHgOSvKD7!_9)jFJE-5NqUG(4;|PG2s;nsbVNcspM6#RfT?kGwg^g) z6Tsdv+|)JdzORGf5Q80hP9DbqgKA?)I@u5V{ZEnGjrl!u8{Ab(9TI82=K&GUY4{ZtNy;NxE2c111fQshNp!Y97g~6u2;(09{V2L@%7J7`N~; z0-U&vqQTe10u)`#vN3GSjXl+St7?kxF{r-DfNHfhrW0T+Utso&gC+JZx+zUe*g>nl zj@9mFgh`^MVwyz^Iy%L?FLKf)GP0yKLei%bS6%r|oIfE`Iz;L9_uqeStK*75Z}m?} z$PsB=U?#aK5IeTDS+R7Kae zk&{@9B*?Goi}VWInYa8nrn~%>Bc7$T)m~-_tU^$3G*44H2eC()q)M4NLUB%`%f*c@ za~mZg4GVV14FGD|^@blsBh8@@GZm(T;FC({*(NP;rjKuuq%+R6IHzCnHj)?w5?$fR zFQt|3^|E@LUBe@L4;EvN9@4QeCA%$(%K}i2T&=8W)?eJLU(n27+{_o9t_}MFn@#b0 zqX}}~1%AU0uh2tai1@D7;MY;S0&bIzI1=|azmSRd? z7z>>|9{F08wvLO%;X)@VW5cuQZec=S)%V8_P@Farv+B(y5{(~@tBZmS+nmGz%g#zi z^UY_-cKpdq{Ode_?i9wBbFefAg>shf&}D1dC~+r`h`t>TbllpM5So`9CF_=5(CAAh z)3CSJ9|pc4vB8orUakYPx0(m@BKKq~zho-75j`$TiQ{&0S?LaQ1?Y6{iGtuog`tVO zwU0plyRZqAg$Um@#oYluGqy8R=;RVGi)<2!1TxzgFD9m_IyiTKdO1h6jkZvm&ULWf zY$7)fDL3=ipX;Ck-|%sMgIc359K25Z_$V_6b?r~bFz&X*gwl<_#oLlUR;=(5)H12P zoN=Dz4fB1`DZCdA2f_570@-;ST*Gf=UI4Ek>76!Upq|4bPP@k^t)nu>93(Fay*nt# z@*5wfBQg9Kytg~}kfgCq+LK+sbG-kf=?Lw2siU}fyW|8}ajb5+SIJ5lrHYnn5N^?0 zrHdttHC=u6YORP`t1s83-<02$HFK0%A-WzjJy>UYz&Bd!?Z3`*lKY%+ao$t$S+A_@ z&a+YmR6V2B{QPV%rt_aBbG&6fSsQKH9lDM@O?K*-4lMVTlw55x z3u2a7=2dG+wn1}IBmB?#eBVo43Q?vwef#tIzxd}X`FRXy`(Fxve+g2t)4RS>Dz|s@ z`Bt4%9QvepEBu=5u=VbGCtXj2%|6blHkH(gTuY9k>BTyd4lTQgvuBT+Xewrhm!~Y` zYis&NOmbq@)=1Tai5`furA0vNrJZ>!lMuh;bU##YP=kr;Q7e+4B!%IoR^`XC*1RV~ zJdEXZQ$%_86B(C+(&;X;>(`!UE3BXCkn?UxuiJF!ys0v!IWLY(S$14rzY~x?hwl_E z{Uzn)upnJgJ@|yORsv2)@)uxl6jG$b z;ZgkT^Cq`rt$Y|=hT|?;?9F-;s*Rsb>Jgi|XTl-qSy@LfFa5HNno(kB@;W+N zpGBAJ0>zJe`_)1iPb^njEZ!=6nYZ0B)Fx=tdrFcsgD%{iYLU!`Yet%ux^2DaE-5?&uSAfp*Nf+v%P^3;-C+Nk*(&nFrV z%fR4PcOa2Dm${Rjk5`!PGMKuL?ih00n&sLbt%2<}X{bAG(}iWTA78vbLkRxi{+}G` z;}7xwtTi__8;SqtlN$MdHtz2G@A_8#$({56WCZvs|IfSS{+ahkeA&b9_;$XZ49q-N z<h3!k6IWj;fzx|B~oCt3=+>PZo6^_O-%zhw^iAj&dlx4nYhp!Ekh$>f z{nY52n67N)^s}gw*6ADlrdYooGa2n*D2&B};ZRCHwNBeF-^qL7l7vNsm6_a$B2&ur z%jLXhSZ4M7%!=Rtc2OX^h)egSX~p$zbAXc!nMBsM2Q^QtP+KTpt2)AtdXC`~%VI-9 zvxKfeSK#N^Z8|vPdXB6tt-4nryP?dSd1|J$S6{qUKq_y*HB9xK$Ox%{o0`@tz|G6e zt}Uf&#TO}$d|Y3)e}2>2=3LWoh4ZI6mv5pSd=SgIn8Uz%`YUv_qAb;c^6|YR{EFeE zEeNt+(WqflStGPoKo{G&DAX=5+}gfJ%Xexgi*x3R#aU75UiE%yM<2=-4mumA>^U(w z1==PYlxR)T*vXY0MH=Bgs(B_iAnyU2m<1*=tWkuSOd9s`W)UpTomlEFspp^Dk4(An zzHF2!nZ}qHjUR_?=RX}@^Dj~-KXLZ&{}J+Evr&IiH}c;`ljOgx&CR>~{-=It zG13A^f+G)0c-C^JWSkXJ z^2xd+^7Pg+Qs}N7z}yjG*#4Z?pH-85v)r1JNlaFg2%7|<#HdbgX%{-2j(brs^1yu~ zODAT|w1D{~<@1oRe|-AEqvs3G$@UWPO~>#Z7Hsmxb^GKO{v{{0wU-y#n%3<<>sjGE z(1(Eto%tfcu620dUqF&_3CivY1O-iXD-aWZFz6x^SajI1Tzy{UbU!RnUBKP@DeQfp zhiV_nh}54gvfv=L`1t5&4F$8u)3RvxbedM2y*_!r((D_{XFpgr{lRVKKlqgwu(5mr za0^&8{eCnCiplrQIGRuUfg8Sj*_uq;u9en1?nW1zG7dK%kWaR}K-3y4J zNHk~DAx7N8i7Z(so^fo%dV|8VP1AwvZA9vxZ5;-MMp)xPp*`Pz+~Y0Ng*w_qX?9QB z)=lXGsIbq^G6qd>D8Bb+rO1Mt_bT^nmNAPusAYYx5@tr$R`Nj;QoUF|uml|4c$vgv z^=KgE0@Se~C)7>)sFim(8Coz5fj9Zn8o`4>{->7~lW&XlS2G~7C3aofL8Xo>dFCm+ znoV1#Oc7FZUdg-7m^ZmYqp>VGm`OJn)0H0u+>gP{;=MEPW%1CMvqK5A$12Z@N+M??HdoU6gJ>Ze~PExHf8mbYx8VWI5eg9l4_vqR}EO+H4k*06tRI zoXU1pBc*AQmQ3_c;^pSXYKXbH@!Mf0@89}!gEj204*M`HWw=s8+jcT7#T6-!C@Rx( zqio6gsSXI=ijKw>9F2{yI~(j>Ds(a!EGLl*u$_PEim8llKz|EL$WxVEiq@D-&qfnU z*@`T0DEMyeK{=Dz9jO2NdgJMbOo3U~dn$BEd3*&-V;GdQNOYgZttg&PMc-RwdKCJS zKzyyiRl+rKL`*YKc^Q+~D7tu3-Zz z-1Y624w5vU5|#jyfV*kwz7YuoMTv})@3JTq^2*t*pHyN+qkQLT-jFQ(bbN?OPbu<$ zkrs|eQ53}0QpGU}iVAHd{G(9O&P||bsRiOs6R!rAO7GT}13k)8l3}YknMB&R{9D}? zHy1JN+i>XLn?jgdei1^|ZnY0)BiM^S@TdJNg4qhUbp$q^!7G0}pxg!4_E7o_T``rC z#Ei0l#;>HIan_y8WYN~@$lNPz?baVf{GIc#WTSN(6O@E*N%>cugrJsPk_ZbK_hG<9mO&A!Vt1Fn8KyVA`T>RlSH z;)O!t<5db1lP)JKjg36IB5jwq-f~*cCzK6!<729eYmf~iTTl)i;+|ys3$u$~Z#pK9J`1MR~;}?(~l`Qte~VY(5Wr>!P`{D6TctWz94^M;{Qrahiq)Yc;fLa$TL^(g)u^(B&X`zK*W( z=yQ%KE10Vo%rFiEM?#!O}atK(>dQuKiNTtOS@8 zT+;D#FQXai)+jHWOb*tm1M;bRR;=KAZiMi)R8ngfbuwWwaUjzzE_K^t8AQpCWpN3zOQJ#W zzV@M_G9T+73TijbAnU1+geH866%*g|#_aIEUd6o1 z8`BLlar_C{$sZf&INbfwgDyqp{+tBY(O-zJXVYZak-`8%AMLr8S7?w~U^xq0 zcm1p~Nr`s11-p)eF^I;&!Xyz3(Ijto-04~rE47TgRpbW=&EyKw1UWZ?@1Iopx24KC zKwprr^4=|p_J0dXoV&zZk>fv>#^%m{F`->y!uDG#mmYofoQlz=c%ZKacn&T7=BsGw zZI`0Q#6ZP8p92-?C-LUvS1-$DY6V;W-;S|mxIwo5j&}Rc({kB^<;0q zMHz-xRH7i08AM&u=}-E!PfOzt=wKV^bnij(X2(vRxRa}Nv7i1VdJp${D{(Mp{&bcT zGzcy+LwdGFT0_h-#J^hOSrzDk!HuH-m2pZBxk5P|J~b6Mm6ly`dZUc;)Cn1)(EJ|F zq0Y$vtpAX5_b=GMsXx{W9r=Hcrn)}Bnv_GbXo<4rV}?$uJeHq$DP%R08>}@N4!R?s znDLrx)nok%Do7Zz7}o?=7)=9LYofzfJx;~HvO{F(a8(q?@?ohsmPSs6YbQv9Tehtk^Gn{lstNP&YoD4v69<~W@!!8EopHEWR zJPhf2v3Ky2pI6x!VJ%}+#cy541q)9k&?!37oSy7=&)RQV$LD7cU;oErO7S)c@M-~-;V`yn1A%i;A{n=jUN2Fb?)6?^lv-XR_)=pv$`G$uo zNfdTuN)^><9YTYOx}OZBO(FO1FK$9N4*>-Zq3OkQTi%dzGOYa2`r*anA37(h@%YK( z?=}Sd57imwnS}qJ$HB#Xh_V5&3%oK=bZklx0>!2}P{vD)5XN?tjb%Vte1c5O1xViuLMsNhG@r=Uxa1*D$` zCEB?veL&noA7|N4`pC`lclGrA=%{`4YDYucW+G#M2;A$x(p*tspErNPE$i5=w@zD~ zv%S-^&btm588l@WkVKO1NDn~C%-~>%R8VFEl=d*+F?AMk+|KdKGu_{sy`>`Te;bnq z0A@UifQgSUh1emz!|4n#^PiP+&DvmRo%9;jT_CS;ZJfZRfHU$!Y(a{6W3s?+vQm#; zlZ&TI7X??>SvZ8lFuj4#1?%E@=So^KDQ6F;x&-ko#tN)OhCN)6LzBv1kJL+XTJ8^$ zNtLXrui#RK6hK6A$}@$3kAu%MZLXYCer;b(g5V=vAU+1cM1?aXVGr@-^Zla#Nzq&} zuk8gCBel7nMhI{C;~D|o15y)>0+#r>`E`IQ+6Qngh^}E&jNK7)l01Jpz=lMDZei=b zPo3L_Ym;yRzL&}F_NMcyd(vwCXxBnYTVZ3aetMnDkgSipi0gZ=_S#1p0o|OEhF$gi zf!ra<&+^6?W;rFG;ZQCqut(WHJ~}#Wy*%%<4wj51SU(y1h}0my+-|gQTsox(%V)#1 z=`I^)h_2hr_)D52r+o*=dwtj4CyPPymnsJUlAO55o%UJ#r&f1w|3}RpA0v}t(ggf4)NlCfJ*}86tcWZfACn?gBDh{&cY!2nb$dULa3+2-S z@l=CQ3#a&Zo@i>mCxR(8O~g|C(-BJZca;7;Uu|183gF(JwoVS;+4bzaD7v=PrHp0W z&f)P{SAs$l{X_!*x3cF-tuj)z2_kJ~q*w3M*B{H2dM^NJ33dxrXm{8a~X`FV7XBWgD4UNPCjNbnjCOeu`kBXf+ukW$XG!N|Hd%K6Tn5iAoI ze7hV7IiM~8m3k1R6Ea_d2A{Xx<F)9y_#fZ5zd`5av1p>BPSb zw&g~)o4KEfQ<6x-Cy)%NDe?_3+E~TPu`kfYkk$7iRXuFoK*7d|mIqfH*I^PB)7Fk)0 z#AjBzL{~XNAJX-H!;Z75L8SYof#EQz?QhTxIFB^aJ4QgQ{wC2P54OZu`8FS`S*>tuq|V3QpXKiJ)ygeSKmYY3e81d9v;UiUYmiACJHn+0x zAq|(bas?cH|noGR4Cnm~^9Y z>ArDE6P;RaR;#Z`XO-t;q;q=Jm-*v3<7}&IKh8>93%nPT6j=ri{>Pv@pJdjXxFY`f z9`k?m`%m~^%Dna4ya8SO{Xf}2J$IXo`eE+%G*m`o8-+$h@{ND)zd{qkY+h=5C z1DpG8a-DjHc{xqJIK(WFc~30y`^2C6qs&Xg5MLXnUrgfj$6ij)nU|RO74>cI*ZDYv zRp!(Ljvi+}o3xd1$8}4P3PS-vkW9Ik!8n-u7(vGeiai~pI-e301B8Rmvj8|Iu-5L3 z9N^%$68$A%6zB(Ovg{FZ9;)WeKId_2PRl+nFq40il7^<&%*fkSGJ4W^@DKzV~q|6f!$2`bg?f>cWKGlksHLJNy# z=-+ce@)M0-!XQ--*{?#+ksc4AD1RYgH>mVpk&hMGP)g1_;IicD50S!crxCmdWLm?iM#K)hBH&xm4x#x*?s$CGB;U z(`$^nRJ+@pXVniC`fjm$GP_J}1qJA?VL7|)q-V5E+Q~GQWSUFb`6X=$8tJyQPIsBs zNEswdPX(wbx)jR{a(a;!r*<<3Cc38}e>ybz%>JcUC!G{7inng%{nFK`Q>tLXjbSY$ zTo>VZps!WwP~jHU;t$#b+s&@xJjZ`1wue*{08Kap&d`{H1IPp*ZP|ZGRor&q@IVPjA^&vD8=|W&O zyDamR_Fi^j^D4=^pUMe+(Z(98ySrY`>uJqfgVpTfAHYRUfZ&ER)tg5CppSZW0O(}S zrt=V8_GZ`@G+?Vz=lHOTqqffA*Y1nchC z=v@xqlqo~>+J*kQoxPLp$=SP;mSl`8VKxwkLK@{CmU+6)RC54;JuF!I!%~VLJ}guI z%k*ExT#S*s*}QS9PTojO6+Qj9Jee1`v- zeWGWP`q(Fnz*Y+ufnCGw+8uX(+)I*+7VXOpgppQCq&0pPV+Tb^OQ8?xhd7OppL~d^ zW_MHwV8^#D@XMoJwY8!B9H^D`iZIh*nK1rQST^h;g;0(8@^(A3{I^e>wq${e|GODk=)M+Q!bl&_4{yEQ=NXIt<;j~?l& zw2lx1h>YO}H!vM+87XMw>vZwc%f3#U_z?d1l(s(b9hA5Oryowg)1INJd$yA=_24gB zenF)*ooRY1YKc7LM2zx@z6QhWaVh}3)briTU>3~7K`9ArnAT})Q5s}FAWFDDu^OQl zM=yZggvkzv$jYD-HH{*6G}goByd4cbhqF@M)qNZ0#e>LUqrhot74&zCGNoIP|4Q{v5=$g$P~_KI>P zS<}d3Yzx+g(bI9OWq@t#Z9;a|>8&v*;5HMq8m&GCR5agw>Y6wDQ5ZO3PGsl4jzE<*gyDt4ioYZJ$kEg}`z;?c<{zsYjaA zffIyX_yHAjw2px;w)q8>6O9N4-8m;Q0gv z%%D-*!7FS7>h`1YAf`(zRkR+_5AMkQq9NeYty^}EDw ztC-V3{_b?!r*D5g{}=y!B|nc-OH1-X5@m0GD5olD_8}EEH~<+x| z!t;QK%iGBL1o~BdKMvBafLe{g!lA}3tioRmI@#Vk9Q5E%|LVRWhpPOly3pI-7mjk-??Z(o9|J6@>g{!;)Q`Qr)BV?c#Q{+E_nR_h)0*JoH5E8L*l2k93J>ByzeT3@e>g@6#-iy8cAJ0#yfX1lR z>GSL3H?7^rSJ5bVJc4tL;hpA_ZRM?%c$L;ncT|6%s?{CLa;5s8F4Vd*L#ThL%YTFu zkP6@!TW`jo298jE3~m1Ln0tTx*HSVWRI`=(gS>h>Y6w(hsGRCMYSfP=H||H}Zv&DK z!gz)+J!Ct$U!1Qsqx`A=&AIRLLcXKPTZ^|UjXpU$Xu5()Al2252)^R&4#xc(jzR>2 zUNjEyHXYAms2g4c|EWrX6_vl1uEXJg9%0ER=uvm3v2f)a3(WtCZvSRH2KeHW&CNpo zPkdAO|3(c21&se+19_oQy@UK8|9A7wuz%Tq;|uf0$bU7;|ImE$q*iOz$^Wn3*u2Z~ z-`1T$0DN>tB&&V$vx?y!ht>sPu~UC~!vz^fz>NyK1FAjXMe1OB<9}4of^~dkx2^W7 z^x@y)t7`w#99Zr@o^v-8j|3H@AlzZ3fQxB>!2vF2*Zwrvp?hZEM|%MhXmIgp0F%mXOs^~ZrtB9kTygE8pugHgPs7~g+Vc1uPVLuqhKxAQT{DP(m z)#Hw^$xEC?M`od30{b0McDNlCgitA>IXYY3C87-bW6^=ksg05jfT{cZm{#p2O;Adi0uaaV(Rlk=hu|mqjcezxD}e% zH_F+<8;Po$i3c1{$>srNVo92}Ejq7{msI4^Rt+R0SOdQfHRWSyfj zRhD@^?HE_mZZ{mmHlyRB@t(46vU<9MAf8q9LKWPhNlhnN;kXb!vPxQmp!_T5(K7!3 z7x8+nTCWb{(eKUwYfm<|68+Bx@&Aol8PHTP3A zr1#lC{fU8&qfc;MEr^&eJpT|WXVv>LxIqPUtY>DasXhkNaWEufpmex52i7=E7N>Xm z-{4X_hYRlI_23fjO{$c7+)(!3_#r(IhvoKewr|t&WEze@f?AJ5h7wWyq{a4QkVYQI zqaFY`$L#uF&DuwmoJqWa=haemyB3^W4`nsrHtUlJm?zBm)yW}j*uYzM{`Zeh-nEZj z{UQ0^;I8)s&j04-MgzZZ)%pH+m-WA!wLA0vhuxh>@@gHmPWKK$=zDS4-dFHn>!{O8 z!f5=YCE<$tVGd#e9QHbhcj@cF)9>hdP%jj%S@sR|%-wgW*S57zS|aNalo=LlIpEQGbpZ*D54r_jNo(4r_+STz@!>u7g3< z(>`J+(*Wi+3{b{BBdt006pIv$F(mZreG-X9wm|{a{}^!CDx`cK-h~DcMHq^&sQ!4s zQ@}eAh~F=6C|1SPM|mJ$RQ9wCj$t41sm*|gx}5tc{|5mR*+oNPnWyKxz7ADJI8O}2 z!O=+ca`g2L7mE7Ta2B)ew93~Uujm#w!8=KegA;s2?srA?1XCWw76iw%8oXRZbTQL6 zsq3orL(v`Fpeo-6g999`AS@L@j-8^qS=eEn9X>Ld`N7qfac6o-sNm6gLUxTofkNWy za9xY9$`vz|zWPBY^JyQu2KE_E31AppE1Y?{p^tulGpi}q5lnTk5t;+?DVSrQXNjGR zLB_x4`D(cDFcRtRF!Gz}cR-iM7<&dd^S$*f!lpBf$wd1{R4%4n;Q&1X%W1%7Ex;lP zHUq%ufy@ucisT7_8I0+u%OKnnP8?A>{>ViU?#eVUx(%*X_0G7BGYUooMI{4~yI_h! ziUhoj!(KQHXQ9v`?3p3z%18J9H!i^h782BxmdU|W{3yqGw$u)K=B z!;ap}8INYYH;e&7xEC1DSIFmsST6;8#lr#%VuYvDs1aTe&Le;anhTSF4#E_AfOgUnt~lurqYs{8VU zA3eZ8H1ip87)F`^QHI?X&jr5o=TBUR6U_niGuA(6_mzX2PhPa(K2z5&p?d9s$VPg5> zMU}yR?}X=f52WXS;)s&WVx~`Q!CC{(p6DLneTGbM9Xv$V#t)G6EsEm|?gZ-2ew9;hnU^1EbO(h9J^P@z~Ql zqwd+G-$Ti>g``|g29Y?49F4~$R`-B++=GL%2ju+|3kZ?G6$PuJbKxwKgh>=U6A78I zL(pM|Ni0ca^I06ykp~V6y=zqQky>7O)A=|9`p^XKCP@cFL$kyd=;w(hT(vauEMYww zqqu!R1LEEi4b7owwdT&|4Q3lDT z^FR#xG3EUgzGD_ShyvyW^(qT7{>>^ERxPVZHxFW4j=}A^%==<}SZxBXiB$Y6U#sFIaEUXca zx1gNI&!%9?bf)zkli@wk>4H+z|R5N;v>Ot%)i06228Vir@ zVZwAM9EzuMDV{H{{pdsS-KT^y?GSwjL;^GTA*X=nVH%VZg$2J%z!UE{NDu`QXw!CS z#|G!X2pbKCL(RYF=^9;jBlRf^u2VFTdPyX_QtNX+5Q9$WQWapf!SF)s9Q7W++^7rW zb;QyQCNS_>OE7joUST5UP$1)`k&zJoa~@7v1;ah2I;)mJa@Tqjsykw(0_o(0n;3kh zF_Ys#mr;-2#zHxz0E`1sybyr!I!R4f7%DuP72->%d+6aj{5TpzZ=}ja(P&B%Vj_1? z9Q6RhSj||RjYZxY0gON4J#2PG&K=1a~7=fo{o9fHZG@bbyang*BtbO6kN_8%Hy z4ghjb0vdP*4J($JfP8OA`ZNSn;6%&_1tUxUM}lfR^K8`-RO8#-6fGKK#gk=40My%2 zuuE5TaY%-u>x~zD4yJtl)rvFL4WSBSm&Q#XT0|h};{r*fjDP!LJVtErDZeFLtc0c~>toqBp+dc> zrhnG#m~0YG<1HnZ%QY8^>ggq$(a2DH&>#YXu2NL=bK&B9My&HICTnjV{t;&?CV} zEL{4AfZQ*bYFS$VXX9(d3T{t5q#^D&%ekB5>;nmY%z#(hgJSseO1@$b`bF3kju8Y* zDXcpy$*01YX@i*er|eJ#OGX)mv}gVhqa-pt6EQ~2wyYkwAobxGB~hf5aBa(|JG2@B znI4CGkonoN*7Czk>oo$ZZ?F!po&rjvZ0E(3fOb64!fYk89{aeEi4VDke$56;A>*J?^%$YnZdSr;JF5$&XMj#iSfN-+jj zA-RR-Y=-hgZ1?4alA_s#9x(ZfjI?0&jv|(I0?=9`dNM+En4br|)6n9OLDI1=rf9+j zQd5gmt6M1)*MT(SEK?n1uuqHIB#d&}9D4hW0;!iLRytHtJf)-g<&{O-L$R2%l5;c( zC`3ul6jJIU3m9lMP--KQ84!zD5ya{mxc`w7UHg$2$Pyl7Dq?T&d4h^ux+vKg&*E{>Ri6}XlrS4#Hh)PLqoF>!}cMBr6X89--6N;k^^AfN4thRMc zxuVho5?-(|s1KUOY1;?;-r$Yb*SE4M8R@RVUN~b_{m{QQ)_b`qX4k|00CS1ZHr=~n z!x-r+905C}yf2A@u~5lgX5|hV9Qy`qczj=!y3QWYNPtB%HEJBSo6&6ydpu(^eJ=&X z)D=fu9k!}$N5P;IBA_f1^_Q&JacW2%AVGe-BJ0zrNt&k($x_0eB7BHlU7}^+*YkqY zO|)o+=Io%r7^+<;ggk}78ANk=F`tsX%5gYJl75O({ed!wyzWUDkXWUH#yg%tX>RL!{0~h0SezmXTec@)O53tg*N)yIhF>6E?SX z`jJTEF?#J>U|O}&dmcea|-ASl)WUbjIp;tC_I=%qF7ftr4@} z?1|Yw>zU;1-8J4bCK?a>h^lFH1J{I`b#f`N4!0#I=uv?9vDh1t8%|^lY+_;=gqX2! zc*AOY^A}trNveQ#@Dhpg(8WSrJ{89Z(%|g@kmRwOO9OfGJtT%`QJ*5yGU_1Os2sBNt!Ri=t(!`Lm7~i$E=sSz{A*|5AkJmIPK3zF;Q)%a6NyRknq6n z-tt8CSShLPDY}wnmPmQ7iUZ}CQOL80MGI=%QMpa#Q=(uwIycyvx$tNDi%-4feG(^b z6{zS3RJ{@H8L8{Eqn_yMSaA^2i#qCrwAoy9#~u92mm3YP&YGdN@`)!6F8XpRJ!&`1 z`7vI^hOkV9*tSy9TVXszm~prXY)Z=qDDKSM4eBJxtE-% z9uJ~1Z@>V!&wxBj$<<25SA;Vt3=(^Hlm%w4dVUEpl1Z}riSd^ZGANvo3Gr1Fl1zA( zIyE+$l1m&;6h}erJ~=pD%O$Y~U#2tBGHoDDdI|k{k@ySvSr`XnMNn#NXC~4sUlsyVrL%NYXDJne=uOZ z10n%z_ho?9Cs$-aca~^5y#QZhTb##)*es>Ou9^PKZR$J+V0F%zgn$uT1-t|hOgiSV z41if9ReQ|FDGX&*acMCK`4OC8O3>kPlpHffIOl+sTEQsh$6hqJ$yFzRS0zuFLRWNz zl=i-u2A@K*-tv~CQ?ooPjy?Tsw%{!;$p9$D;&H+xCQ5a1ZFV1Ycn~px!-q)RVeauH zoRZtRRx`zTaHR!5kH8s%+=;FW(10KN5{YMrBO1bZP_mf=5RqIUNx0UpssS*lUqyW* zZXxXP92SjqQP&vHN4;R0_=oDdF==xy=(e7!o4H6Zsk9vGgwx(b;_bZDzE??Zam2E< zk4B7<;5m=9WNy(Tj2?RscTXh(-Y(ve+YjV3drp@pzVJ8py;5?)rT{ zp!^`23%ZP7M*fgaH=VudCyjP27I2C;{k`e&OT1o8g|KQDpAy`0|H+8wNmN zgpHUcoQ4Q)sL4~Oqhk|lfcC#$RWDlmd*_{&I(ywxC#T04BUQCK+AH=zy*zET)bUHT z|9bEARjY#aPFq;R?hAcntq!2pG5y{8`K)zxrcPR?Z`x;P(A$f5YVYI(I^TP7*iwgk zZvmREpZ8lQXX@?i){%FN-M?+ad^%@)*r0u+-k!G4+DEUbLv+MFZNGYbrd}T(9<)x$ zTlX=HP7OKcL964zJbr2)xQn{C*MVu@GjSL6a(LqJE+SF0&=abV`2iEUF z-|aUr;}#ThtSE8>egQok9iIW5U}Z4Rvtt5{uB&^8$w2>JI#>gG4zCccfj|hI0KiY% zogdX+#}mN*`}v;f2%rmnyxBY2r;SQ&BraFIJ3dFQ0qZ_IzzUwOfPiYLgVxK|{#pAc z*j}g!!*tHyv;^LrGXl`zp*m{q!+iHn-zkhs(B4Nlc&DwCy*7X!eWXuMv6tf`rY(&s zZXRGs>nFs4^P@vtEoQ}p_2ggz_ObU0unmD}?YsB34TIwjr8mDqO`ssz{C9vx$Lh`A zJ9f-|C+GpwG@jcoRsd2Y*tz%O7-8@N=F_HW!9)-sxJ3thZ}wibIu*|#0SztAQ zwZ!F^9C@%S>cb8}qiy~q$=@?V!99GbFUl*WBT%A44*B!ZLg#FHpf`@l-2q7A3i%A- zN$s`YfCYdDF(nO>u6u9;xxfStKD!ig$RF|zf1Vq4>E;Idrw)+wli>4bV5L6;E8X?L zO85?uv;wwU2x`ul+>$o|zpO+7nKIf;qcO~%p8#UiX^cC@s!qp4!|_P1Xj-TrlK2vV zNDlHa{K%BkBZtRu#*`@LR}_wKE5`p852{yV5UM}1G_TQl3zn91cHNVkY2A-75DM+U z-iyxhAxImC?`%nVhcG}kUr`c(|3|M?uOC#CLy$iG37;Xt9}ICEglmczP!IBU%IJ&q z6>P^IuK&QENR?exu5KoHu_L=y;^nR9M{_W(1zYt?Tjw>2b8jqsZ+3h^rWCP$B;%2( zI7Y4J4JtJHl3WDuWu#HC9y{euP2MiCW}Z%aFYrbYbh+M#8GIx~#wZxi0bs!>USCJ5 zO_%3*9#>3~wR3!lV5#0osGe)k^#{F1YkAi7g@!RdG7vq(E$hM6! zd$6N%86VCi%DCjo&AsGhik3CP3vY~1<70lI`C2^Fee|1{41q?#VoW_`+@Wx6!Q_%4xE*m8p z4E6)gM!m+YpHm`uoHclckG%m)&TinOMdRlcRR_6j8V)J?2nr}%P{9Ztaj4&P{R9{! zn%n{+YxF8&DN1x2h(GrJk~YJ$UcQ;2>kF-@^+6nS>}bN1~!- z0R64FLoJkKUGu^`8~7@im1{>O@de+kQA`9;(eJER>>OZy)$$*bn0oso=*E zS(yl6>~`Dc`^{xUj-jwA7=0A@UZnkH_1^v+{q#w+dw`I5P*D${28g#hx;%?V-UAb% zKAQ>6&4Koys!s4yP0l_^c%dlL!?fgsDlUc+z`&JD~X12;srB^;$SpiuT{5d)u!ti%3fxypFc*Mtbd8(Z!to_ z9f6Vw0DBxHkq+4`)+47m)qXItBGTYr+6kRUgTL53(@q>22mFQrb_$LPA~-ibq7a7K z(Sgv0Gl|JHg)a^WoS=U-W-}_w=Gr<4XO~XbKF;g}6L0jRp#+;4k?aOg4#YVcomcPy zyU37UFy^(Q)&0a{ZOvf-C!e~-rhHW>W93X6Nrhs(=9Ts9f$^S#VlUtjkQC^qHU z)f_~_21cjkfYtTp_Qu9`{i%9XtJiAYOPKJsN`-Dz&om==4rgXtS#$9^>{z#*nF(WH z;#~Ya%;PoeTl5X?W}nsTmrSI1jb64Z{$J2Nwo@7(?_U&8GAo+!$DMdj4~D%Wx!g43Y*CMrJOz69%(bL`PGdbkhL zY?#gPVUjNR!X6nR)a`BwBybMVAA5 zyQ*Vng!FV%yR(hLF&VqzG)Vo#fnV4V9CQqNz}zqtTQLD77Kmu-PkTpw@y#57izF(r zAGvszgP3XPlfje;Su7NI$kufxQ}TipTcVL3-=uo-ZbOZ5c^aj4Y>8{w8U55W@kt|U zgv@#jmt`#&&`I@?JznxNW8(dI1Gk>T6Ky5q_E@zd0-~3vO0_sT&=u*7u60JTLekN^~x{!A12Yux`y+YOZJx6A*t zDE@oB!SUZ~jVJZZO^W|s+o<2+`0tI{-O2y=&EsA;ejHy>hJxgMCfa1yCn}g-)l>^b zoi@noy*?*0;42wcsW+M%^j*2D~{oh8TUT6JZqgii0 zq5NMB%>Q*~WdU{4;F#3rX(_i<`&@rgcI#0#oNT??#%8_jc1bxuyPd=1vo89Q zwK`|gCH!+cO^V%48}QGqdC4t6zcQwRp*%4NTYN(r#Ix>I;G=BxPj9W;o!7U} zSpopCLw`QyY#01C!1@FitZ^5_aO=~|k3XhsT)}o(-}E)u`h=;V-`74+_Ur=*Q8=GO zV6>3Dr}eIyroo1OF!cOYc;tjh=$tz5O5Un%}{{ zG^i%So8J*oqHG3Gby2QP)#f)JUA%V$zd9%#2)$^duid-uc}J&is-+N9*ErgHLwSHy zY2#_x23-%;5!0KlJVs~WH5)Kr{?ZKxI|)GLTMDl76)nN<^9KE}<9$g_{iox@WKQ)h zch*PeZ(g)cyT>oF+WDJSN0qiVoN03`pDwz9*mIsmGk@6a4I{W8?IfR3>y-UEnNKh2 z&04l-9L>5n!K}NW3J-Kt#3s8^h z0!a%Hq%ecqWdF(r##6f|^6$4ckslpKu<6d;>eJC3N7htX+1BNkx;(nQQ*ZWuru<{%^A3;%Wd-~ez% z7g>1jciwf*T5n8|MREPs&#is*!<8b7!uSX6Q%zH_$V!~%OYUY}v}y5$z0)pQJ`=WK zO$5r&k+@~b8ctd3=l#R;4iGfr47C)qp)73e13C~}=^f7OEq@%mPqhU6(1cRs6V)`> zWbYTDliQ=&sW@FSGIWkl6n+Ew=W=#sp->;aP?7cx4C$lqf<&pW^9g<^_=*oB1l=fU znF7SJ#R5c|KSl9BQvTPvn?J<-9~^|9Y40{|#L2U6KF)`tWQ2d4I+KXHz1n zB++b3F|#qh(O5LBiOq#00F4zk(yCG58z=sCFjSp?^#2h|F;bGz^otpkJ;1RMyQ#1< z?2RHpiyw&<8Q`&zv7=OFz9(X?wv9&On-!bD|4?rq4I5L5`>@Zme?h^7kI<~}AjY2h z{t)Y;ua|OcO*or3$ja6FcTJ8LfokCwz%=zl_6GiBX4JBPU^;x5SpESvSgp`PnbMa( z$6>L7osO@nc(AYj_ZY>1>_2q?5->@*v0B(VKecCXe$i~aZ#F*c6j!Kky{~UAs_^vv z(?!J_?>7pI=TDPVoxm6c6DYLR`^M(h2es?{v-bH(rS|zL{8y8oTkzio{MVA7oABR0 z{8z7V-52oROZab(>U)a+dMdT>zwe}F-RcEc)m%3K=KvMD`3k)QkWQ?GK5ded`gS~EM4p7b}pe%(V|%DY(UhJxEVyW>>UnHRkDCM&>|x*9gU!^> zhWz|aM%cGj{DOMM9yYC6={0J6Y)S2wg)6$}hQWu8N`l#&(#w{0MqXIB^0Ja(V8fh? zeG4b|EI2fzM~#TQ(O|CK*W zG5&eKS^uEc?OlO-i)g;&I6%W;dYukguS2`|6gm3pq=nya&c-oH=Z-%Hs# zEI{o7sLBi-`Mj%=@*NAR$fin^PV4>P z1MNPJFu)y+4}Xyj#2vUB zhyMtK=3e;#wt3GoF5*{u5zxIL3k3e1nDL}3lX0?q&7=-nN~8!IkV%b9g?sxyw$8ep z_J6cKI9lUH_$SN62Lc}UHV!VM8NMp*0-48DgfNj(cBK#bCyPv2r$WLS{`JuCG2JKp z)lGCpU0}oudQ1|hJ93xXn`^M^hJ(c&u2HHZ+i+Kr-Yu)0R4g_NW~YJ=9L9QS!C?8S z7Ie-(b;T4T2apw%&>NCLW==?T7fwfDoazmm2`us}=qD?w{je;NU35DXd)qR^857Lc z3QU>_yz|CdJ_5`6Ja4_hyeJnIE$z=EVaojukayS zsAH;7c5RPZwE5HU=-U6T{s{TM1_Cqt|2}Efn)MB`|24Pnu>ar2lP7mY{{L;YOgq2c f{eS0A4}m}b{rT_De}De_HUItpoDFt#00;sAvob)Y literal 0 HcmV?d00001 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 17cb286..0000000 --- a/LICENSE +++ /dev/null @@ -1,117 +0,0 @@ -GNU GENERAL PUBLIC LICENSE -Version 2, June 1991 - -Copyright (C) 1989, 1991 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - -Preamble - -The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. - -To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. - -For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. - -We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. - -Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. - -Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. - -The precise terms and conditions for copying, distribution and modification follow. - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. - -1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. - -You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. - - c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - -3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. - -If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - -4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. - -5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. - -6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. - -7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. - -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - -8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. - -9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. - -10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - -NO WARRANTY - -11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -END OF TERMS AND CONDITIONS - -How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. - -To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author - - This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. - -signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a37b865 --- /dev/null +++ b/Makefile @@ -0,0 +1,78 @@ +# +# Makefile for the linux ncp-filesystem routines. +# + +INCLUDES = -I/usr/src/linux/include + +CFLAGS = -Wall -Wstrict-prototypes -O2 -DMODULE -fomit-frame-pointer \ + $(INCLUDES) \ +# -DDEBUG_NCP=1 -DDEBUG_NCP_MALLOC +# -DDEBUG_NCP_MALLOC + +CC = gcc -D__KERNEL__ -I. +AS = as +ARCH = i386 + +.c.s: + $(CC) $(CFLAGS) -S $< +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< + +OBJS= inode.o dir.o file.o sock.o ioctl.o ncplib.o nwcrypt.o + +all: ncpfs.o ncpmount + +ncpfs.o: $(OBJS) + $(LD) -r -o ncpfs.o $(OBJS) + +ncpmount: ncpmount.o + gcc -o ncpmount ncpmount.o + +ncpmount.o: ncpmount.c + gcc -c ncpmount.c -Wall -I. -g + +ncplib_user.o: ncplib.o + gcc -c ncplib.c -Wall -I. -g -o ncplib_user.o + +nwcrypt.o: nwcrypt.c + gcc -c -O2 -Wall nwcrypt.c + +ncpumount: ncpumount.o + gcc -o ncpumount ncpumount.o + +dep: + $(CPP) -M $(INCLUDES) *.c > .depend + +clean: + rm -f *.o *~ + +realclean: clean + rm -fr ncpmount ncpumount .depend $(DISTFILE) mnt + +modules: ncpfs.o + +SRCPATH=$(shell pwd) +SRCDIR=$(shell basename $(SRCPATH)) +DISTFILE=$(SRCDIR).tgz +BACKUPFILE=ncpfs01.tgz +HOME=/home/me + +backup: + (rm -f $(DISTFILE); cd ..; tar cvf - $(SRCDIR) | gzip -1 \ + > $(HOME)/tarz/backup/$(BACKUPFILE)) + (cd $(HOME)/tarz/backup; ls -l $(BACKUPFILE); mcopy $(BACKUPFILE) a:) + +dist: realclean + (cd ..; \ + tar cvf - $(SRCDIR) | \ + gzip -9 > $(DISTFILE); \ + mv $(DISTFILE) $(SRCDIR)) + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/README b/README new file mode 100644 index 0000000..e584f28 --- /dev/null +++ b/README @@ -0,0 +1,90 @@ +This is version 0.1 of ncpfs, a free NetWare client for Linux. For me +it works with 1.3.32 and 1.3.35. + +I know that this piece of software is VERY incomplete, as it only +gives you read-only access. I have to apologize for that. But I +thought I should make it publically available, because I have tried to +ask several people about the legal status of the code. I did not get +very satisfying answers. So I publish ncpfs to open it for +criticism. If nobody complains, I will go on working. + +To install ncpfs, just type 'make', 'insmod ncpfs.o' and +'ncpmount server mount-point user password'. Please note that your IPX +system has to be configured correctly. There has to be a route to the +internal network of your server. I use tools from the dosemu-0.60.3 +distribution to do this. Once again I have to apologize for this +cryptic description, but before I'm sure that I'm allowed to do all +this I will not do more. + + +I read that Novell is not very open when it comes to technical details +of the Netware Core Protocol. This might be especially true for the +encryption stuff. I took the necessary code from Dr. Dobb's Journal +11/93, Undocumented Corner. I asked Jon Erickson about +the legal status of this piece of code: + + +--- +Date: Thu, 12 Oct 1995 13:44:18 +0100 +From: Volker Lendecke +To: jon@ddj.com +Subject: legal status of your source code? + + +Hello! + +I hope that you're the right one to write to, you are the first on your WWW +server. If you are not, could you please forward this message to the right +person? Thanks. + +I'm currently exploring the possibility to write a free (in the GNU GPL +sense) NCP filesystem, which would allow me to access a novell server +transparently. For that I would like to use the encryption functions you +published in DDJ 11/93, Undocumented Corner. I would make some cosmetic +changes, such as other indentations, minor code changes and so on. But I do +not know if that allows me to publish this code under GPL. One alternative +would be to publish a diff against your listing, but that would probably +contain much of your code as well, and it would be very inconvenient for +the average user. + +I think that you have some kind of standard procedure for such a +case. Please tell me what I should do. + +Many thanks in advance, + + Volker + + +=================================================================+ + ! Volker Lendecke Internet: lendecke@namu01.gwdg.de ! + ! D-37081 Goettingen, Germany ! + +=================================================================+ + +-- + + +I got the following answer: + +--- +From: Jon Erickson +X-Mailer: SCO System V Mail (version 3.2) +To: lendecke@namu01.gwdg.de +Subject: Re: legal status of your source code? +Date: Thu, 12 Oct 95 5:42:56 PDT + +Volker, +Code from Dr. Dobb's Journal related articles is provided for +anyone to use. Clearly, the author of the article should be +given credit. +Jon Erickson + +--- + +With this answer in mind, I took the code and made it a bit more +C-like. The original seemed to be translated by a mechanical pascal->c +translator. Jon's answer encouraged me to publish nwcrypt.c under the +GPL. If anybody who knows more about copyright and sees any problems +with this, please tell me. + + + + diff --git a/README.md b/README.md deleted file mode 100644 index d8b54c3..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ncpfs - -Historic import of ncpfs releases: Linux utilities for accessing Novell NetWare/NCP resources, imported from the old ftp.gwdg.de mirror with version tags. \ No newline at end of file diff --git a/dir.c b/dir.c new file mode 100644 index 0000000..2a1c611 --- /dev/null +++ b/dir.c @@ -0,0 +1,917 @@ + /* + * dir.c + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#include +#ifdef MODULE +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ncplib.h" + +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+3) & ~3) + +static int +ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count); + +static int +ncp_readdir(struct inode *inode, struct file *filp, + void *dirent, filldir_t filldir); +static int +ncp_read_volume_list(struct ncp_server *server, int start_with, + int cache_size); + +static int +ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos, + int cache_size, struct ncp_dirent *entry); + +static int +get_pname(struct inode *dir, const char *name, int len, + char **res_path, int *res_len); + +static int +get_pname_static(struct inode *dir, const char *name, int len, + char *path, int *res_len); + +static struct inode * +ncp_iget(struct inode *dir, char *path, struct ncp_dirent *finfo); + +static void +put_pname(char *path); + +static struct ncp_inode_info * +ncp_find_inode(struct ncp_server *server, const char *path); + +static int +ncp_lookup(struct inode *dir, const char *__name, + int len, struct inode **result); + +static int +date_dos2unix(unsigned short time,unsigned short date); + +/* +static void +date_unix2dos(int unix_date,unsigned short *time, unsigned short *date); +*/ + +static inline void +str_upper(char *name) +{ + while (*name) { + if (*name >= 'a' && *name <= 'z') + *name -= ('a' - 'A'); + name++; + } +} + +static inline void +str_lower(char *name) +{ + while (*name) { + if (*name >= 'A' && *name <= 'Z') + *name += ('a' - 'A'); + name ++; + } +} + +static struct file_operations ncp_dir_operations = { + NULL, /* lseek - default */ + ncp_dir_read, /* read - bad */ + NULL, /* write - bad */ + ncp_readdir, /* readdir */ + NULL, /* select - default */ + ncp_ioctl, /* ioctl - default */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* fsync */ +}; + +struct inode_operations ncp_dir_inode_operations = { + &ncp_dir_operations, /* default directory file ops */ + NULL, /* create */ + ncp_lookup, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL /* smap */ +}; + + +static int +ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count) +{ + return -EISDIR; +} + +/* In ncpfs, we have unique inodes across all mounted filesystems, for + all inodes that are in memory. That's why it's enough to index the + directory cache by the inode number. */ + +static unsigned long c_ino = 0; +static int c_size; +static int c_seen_eof; +static int c_last_returned_index; +static struct ncp_dirent* c_entry = NULL; + +static int +ncp_readdir(struct inode *inode, struct file *filp, + void *dirent, filldir_t filldir) +{ + int result, i = 0; + int index = 0; + struct ncp_dirent *entry = NULL; + struct ncp_server *server = NCP_SERVER(inode); + + DDPRINTK("ncp_readdir: filp->f_pos = %d\n", (int)filp->f_pos); + DDPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n", + inode->i_ino, c_ino); + + if (!inode || !S_ISDIR(inode->i_mode)) { + printk("ncp_readdir: inode is NULL or not a directory\n"); + return -EBADF; + } + + if (c_entry == NULL) + { + i = sizeof (struct ncp_dirent) * NCP_READDIR_CACHE_SIZE; + c_entry = (struct ncp_dirent *) ncp_kmalloc(i, GFP_KERNEL); + if (c_entry == NULL) { + printk("ncp_readdir: no MEMORY for cache\n"); + return -ENOMEM; + } + for (i = 0; i < NCP_READDIR_CACHE_SIZE; i++) { + c_entry[i].path = + (char *) ncp_kmalloc(NCP_MAXNAMELEN + 1, + GFP_KERNEL); + if (c_entry[i].path == NULL) { + DPRINTK("ncp_readdir: could not alloc path\n"); + while (--i>=0) + kfree(c_entry[i].path); + kfree(c_entry); + c_entry = NULL; + return -ENOMEM; + } + } + } + + if (filp->f_pos == 0) { + ncp_invalid_dir_cache(inode->i_ino); + } + + if (inode->i_ino == c_ino) { + for (i = 0; i < c_size; i++) { + if (filp->f_pos == c_entry[i].f_pos) { + entry = &c_entry[i]; + c_last_returned_index = i; + index = i; + break; + } + } + if ((entry == NULL) && c_seen_eof) + return 0; + } + + if (entry == NULL) { + DPRINTK("ncp_readdir: Not found in cache.\n"); + + if (inode->i_ino == (int)&(server->root)) { + + result = ncp_read_volume_list(server, filp->f_pos, + NCP_READDIR_CACHE_SIZE); + DPRINTK("ncp_read_volume_list returned %d\n", result); + + } else { + + result = ncp_do_readdir(server, inode, filp->f_pos, + NCP_READDIR_CACHE_SIZE, + c_entry); + DPRINTK("ncp_readdir returned %d\n", result); + } + + + if (result < 0) { + c_ino = 0; + return result; + } + + if (result > 0) { + c_seen_eof = (result < NCP_READDIR_CACHE_SIZE); + c_ino = inode->i_ino; + c_size = result; + entry = c_entry; + c_last_returned_index = 0; + index = 0; + + for (i = 0; i < c_size; i++) { + str_lower(c_entry[i].path); + } + } + } + + if (entry == NULL) { + /* Nothing found, even from a ncp call */ + return 0; + } + + while (index < c_size) { + + /* We found it. For getwd(), we have to return the + correct inode in d_ino if the inode is currently in + use. Otherwise the inode number does not + matter. (You can argue a lot about this..) */ + + int path_len; + int len; + struct ncp_inode_info *ino_info; + char complete_path[NCP_MAXPATHLEN]; + + len = strlen(entry->path); + if ((result = get_pname_static(inode, entry->path, len, + complete_path, + &path_len)) < 0) + return result; + + ino_info = ncp_find_inode(server, complete_path); + + /* Some programs seem to be confused about a zero + inode number, so we set it to one. Thanks to + Gordon Chaffee for this one. */ + if (ino_info == NULL) { + ino_info = (struct ncp_inode_info *) 1; + } + + DDPRINTK("ncp_readdir: entry->path = %s\n", entry->path); + DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos); + + if (filldir(dirent, entry->path, len, + entry->f_pos, (ino_t)ino_info) < 0) { + break; + } + + if ( (inode->i_ino != c_ino) + || (entry->f_pos != filp->f_pos)) { + /* Someone has destroyed the cache while we slept + in filldir */ + break; + } + filp->f_pos += 1; + index += 1; + entry += 1; + } + return 0; +} + +static int +ncp_read_volume_list(struct ncp_server *server, int start_with, int cache_size) +{ + struct ncp_dirent *entry = c_entry; + + int total_count = 0; + int i; + + for (i=0; i 0) { + + if (total_count < start_with) { + DPRINTK("ncp_read_volumes: skipped vol: %s\n", + info.volume_name); + } else if (total_count >= start_with + cache_size) { + return (total_count - start_with); + } else { + DPRINTK("ncp_read_volumes: found vol %s\n", + info.volume_name); + entry->attr = aDIR; + entry->mtime = 0; + entry->ctime = 0; + entry->atime = 0; + entry->size = 1024; + entry->f_pos = total_count; + strcpy(entry->path, info.volume_name); + entry += 1; + } + + total_count += 1; + } + } + return (total_count - start_with); +} + +static int +ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos, + int cache_size, struct ncp_dirent *entry) +{ + struct ncp_filesearch_info fsinfo; + struct ncp_file_info finfo; + + int total_count = 0; + + void doit(int attr) { + + if (ncp_file_search_init(server, 0, NCP_FINFO(dir)->path, + &fsinfo) != 0) { + DPRINTK("could not fs init\n"); + return; + } + + while (ncp_file_search_continue(server, &fsinfo, attr, "*", + &finfo) == 0) { + + if (total_count < fpos) { + DPRINTK("ncp_do_readdir: skipped file: %s\n", + finfo.file_name); + } else if (total_count >= fpos + cache_size) { + return; + } else { + DPRINTK("ncp_do_readdir: found file: %s\n", + finfo.file_name); + + entry->attr = attr; + entry->mtime = + date_dos2unix(finfo.update_time, + finfo.update_date); + entry->ctime = + date_dos2unix(0, finfo.creation_date); + entry->atime = + date_dos2unix(0, finfo.access_date); +; + entry->size = (attr & aDIR) != 0 ? + 1024 : finfo.file_length; + entry->f_pos = total_count; + strcpy(entry->path, finfo.file_name); + entry += 1; + } + total_count += 1; + } + return; + } + + doit(0); + doit(aDIR); + + return (total_count - fpos); +} + +void +ncp_init_dir_cache(void) +{ + c_ino = 0; + c_entry = NULL; +} + +void +ncp_invalid_dir_cache(unsigned long ino) +{ + if (ino == c_ino) { + c_ino = 0; + c_seen_eof = 0; + } +} + +void +ncp_free_dir_cache(void) +{ + int i; + + DPRINTK("ncp_free_dir_cache: enter\n"); + + if (c_entry == NULL) + return; + + for (i = 0; i < NCP_READDIR_CACHE_SIZE; i++) { + ncp_kfree_s(c_entry[i].path, NAME_MAX + 1); + } + + ncp_kfree_s(c_entry, + sizeof(struct ncp_dirent) * NCP_READDIR_CACHE_SIZE); + c_entry = NULL; + + DPRINTK("ncp_free_dir_cache: exit\n"); +} + + +/* get_pname_static: it expects the res_path to be a preallocated + string of len NCP_MAXPATHLEN. */ +static int +get_pname_static(struct inode *dir, const char *name, int len, + char *path, int *res_len) +{ + char *parentname = NCP_INOP(dir)->finfo.path; + int parentlen = NCP_INOP(dir)->finfo.len; + +#if 1 + if (parentlen != strlen(parentname)) { + printk("get_pname: parent->finfo.len = %d instead of %d\n", + parentlen, strlen(parentname)); + parentlen = strlen(parentname); + } + +#endif + DPRINTK("get_pname_static: parentname = %s, len = %d\n", + parentname, parentlen); + + if (len > NCP_MAXNAMELEN) { + return -ENAMETOOLONG; + } + + /* Fast cheat for . */ + if (len == 0 || (len == 1 && name[0] == '.')) { + + memcpy(path, parentname, parentlen + 1); + *res_len = parentlen; + return 0; + } + + /* Hmm, what about .. ? */ + if (len == 2 && name[0] == '.' && name[1] == '.') { + + char *pos = strrchr(parentname, '\\'); + + if ( (pos == NULL) + && (parentlen == 0)) { + + /* We're at the top */ + + path[0] = '\\'; + path[1] = '\0'; + *res_len = 2; + return 0; + } + + + if (pos == NULL) { + printk("ncp_make_name: Bad parent NCP-name: %s", + parentname); + return -ENODATA; + } + + len = pos - parentname; + + memcpy(path, parentname, len); + path[len] = '\0'; + } + else if (parentlen == 0) { + + memcpy(path, name, len); + path[len] = ':'; + path[len+1] = '\0'; + len = len+1; + } + else + { + if (len + parentlen + 2 > NCP_MAXPATHLEN) + return -ENAMETOOLONG; + + memcpy(path, parentname, parentlen); + path[parentlen] = '\\'; + memcpy(path + parentlen + 1, name, len); + path[parentlen + 1 + len] = '\0'; + len = parentlen + len + 1; + } + + *res_len = len; + + DPRINTK("get_pname: path = %s, *pathlen = %d\n", + path, *res_len); + return 0; +} + +static int +get_pname(struct inode *dir, const char *name, int len, + char **res_path, int *res_len) +{ + char result[NCP_MAXPATHLEN]; + int result_len; + int res; + + if ((res = get_pname_static(dir,name,len,result,&result_len) != 0)) { + return res; + } + + if ((*res_path = ncp_kmalloc(result_len+1, GFP_KERNEL)) == NULL) { + printk("get_pname: Out of memory while allocating name."); + return -ENOMEM; + } + + strcpy(*res_path, result); + *res_len = result_len; + return 0; +} + +static void +put_pname(char *path) +{ + ncp_kfree_s(path, 0); +} + +/* Insert a NEW ncp_inode_info into the inode tree of our filesystem, + under dir. The caller must assure that it's not already there. We + assume that path is allocated for us. */ + +static struct inode * +ncp_iget(struct inode *dir, char *path, struct ncp_dirent *finfo) +{ + struct inode *inode; + struct ncp_inode_info *new_inode_info; + struct ncp_inode_info *root; + + if (!dir) { + printk("ncp_iget: dir is NULL\n"); + return NULL; + } + + if (!path) { + printk("ncp_iget: path is NULL\n"); + return NULL; + } + + if (!finfo) { + printk("ncp_iget: finfo is NULL\n"); + return NULL; + } + + new_inode_info = ncp_kmalloc(sizeof(struct ncp_inode_info), + GFP_KERNEL); + + if (new_inode_info == NULL) { + printk("ncp_iget: could not alloc mem for %s\n", path); + return NULL; + } + + new_inode_info->state = INODE_LOOKED_UP; + new_inode_info->nused = 0; + new_inode_info->dir = NCP_INOP(dir); + + new_inode_info->finfo = *finfo; + new_inode_info->finfo.opened = 0; + new_inode_info->finfo.path = path; + new_inode_info->finfo.len = strlen(path); + + NCP_INOP(dir)->nused += 1; + + /* We have to link the new inode_info into the doubly linked + list of inode_infos to make a complete linear search + possible. */ + + root = &(NCP_SERVER(dir)->root); + + new_inode_info->prev = root; + new_inode_info->next = root->next; + root->next->prev = new_inode_info; + root->next = new_inode_info; + + if (!(inode = iget(dir->i_sb, (int)new_inode_info))) { + printk("ncp_iget: iget failed!"); + return NULL; + } + + return inode; +} + +void +ncp_free_inode_info(struct ncp_inode_info *i) +{ + if (i == NULL) { + printk("ncp_free_inode: i == NULL\n"); + return; + } + + i->state = INODE_CACHED; + while ((i->nused == 0) && (i->state == INODE_CACHED)) { + struct ncp_inode_info *dir = i->dir; + + i->next->prev = i->prev; + i->prev->next = i->next; + + ncp_kfree_s(i->finfo.path, i->finfo.len+1); + ncp_kfree_s(i, sizeof(struct ncp_inode_info)); + + if (dir == NULL) return; + + (dir->nused)--; + i = dir; + } +} + +void +ncp_init_root(struct ncp_server *server) +{ + struct ncp_inode_info *root = &(server->root); + + DPRINTK("ncp_init_root: server %s\n", server->m.server_name); + + root->finfo.opened = 0; + root->finfo.attr = aDIR; + root->finfo.size = 1024; + root->finfo.mtime = 0; + root->finfo.ctime = 0; + root->finfo.atime = 0; + + server->root_path = '\0'; + root->finfo.path = &(server->root_path); + root->finfo.len = 0; + + root->state = INODE_LOOKED_UP; + root->nused = 1; + root->dir = NULL; + root->next = root->prev = root; + return; +} + +void +ncp_free_all_inodes(struct ncp_server *server) +{ + /* Here nothing should be to do. I do not know whether it's + better to leave some memory allocated or be stuck in an + endless loop */ +#if 1 + struct ncp_inode_info *root = &(server->root); + + if (root->next != root) { + printk("ncp_free_all_inodes: INODES LEFT!!!\n"); + } + + while (root->next != root) { + printk("ncp_free_all_inodes: freeing inode\n"); + ncp_free_inode_info(root->next); + /* In case we have an endless loop.. */ + schedule(); + } +#endif + + return; +} + +/* We will search the inode that belongs to this name, currently by a + complete linear search through the inodes belonging to this + filesystem. This has to be fixed. */ + +static struct ncp_inode_info * +ncp_find_inode(struct ncp_server *server, const char *path) +{ + struct ncp_inode_info *result = &(server->root); + + if (path == NULL) + return NULL; + + do { + if (strcmp(result->finfo.path, path) == 0) + return result; + result = result->next; + + } while (result != &(server->root)); + + return NULL; +} + +static int +ncp_lookup(struct inode *dir, const char *__name, int len, + struct inode **result) +{ + char *name = NULL; + struct ncp_dirent finfo; + struct ncp_server *server; + struct ncp_inode_info *result_info; + int error; + int found_in_cache; + int path_len; + + *result = NULL; + + if (!dir || !S_ISDIR(dir->i_mode)) { + printk("ncp_lookup: inode is NULL or not a directory.\n"); + iput(dir); + return -ENOENT; + } + + DPRINTK("ncp_lookup: %s\n", __name); + + server = NCP_SERVER(dir); + + /* Fast cheat for . */ + if (len == 0 || (len == 1 && __name[0] == '.')) { + *result = dir; + return 0; + } + + /* Now we will have to build up an NCP filename. */ + if ((error = get_pname(dir, __name, len, &name, &path_len)) < 0) { + iput(dir); + return error; + } + + result_info = ncp_find_inode(NCP_SERVER(dir), name); + + if (result_info != 0) { + + if (result_info->state == INODE_CACHED) + result_info->state = INODE_LOOKED_UP; + + put_pname(name); + + /* Here we convert the inode_info address into an + inode number */ + + *result = iget(dir->i_sb, (int)result_info); + iput(dir); + + if (*result == NULL) { + return -EACCES; + } + + return 0; + } + + /* Ok, now we have made our name. We have to build a new + ncp_inode_info struct and insert it into the tree, if it is + a name that exists on the server */ + + /* If the file is in the dir cache, we do not have to ask the + server. */ + + found_in_cache = 0; + + if (dir->i_ino == c_ino) { + int first = c_last_returned_index; + int i; + + i = first; + do { + DDPRINTK("ncp_lookup: trying index: %d, name: %s\n", + i, c_entry[i].path); + if (strcmp(c_entry[i].path, __name) == 0) { + DPRINTK("ncp_lookup: found in cache!\n"); + finfo = c_entry[i]; + finfo.path = NULL; /* It's not ours! */ + found_in_cache = 1; + break; + } + i = (i + 1) % c_size; + DDPRINTK("ncp_lookup: index %d, name %s failed\n", + i, c_entry[i].path); + } while (i != first); + } + + if (found_in_cache == 0) { + + char this_name[len+1]; + + memcpy(this_name, __name, len); + this_name[len] = 0; + + if (dir->i_ino == (int)&(server->root)) { + + /* We want to look up a volume. We only want + to know whether it exists, nothing more. */ + int vol_no; + + DPRINTK("ncp_lookup: looking up volume %s\n", + this_name); + + if (ncp_get_volume_number(server,this_name, + &vol_no)!=0) { + put_pname(name); + iput(dir); + return -ENOENT; + } + + finfo.attr = aDIR; + finfo.mtime = 0; + finfo.ctime = 0; + finfo.atime = 0; + finfo.size = 1024; + + } else { + struct ncp_file_info ninfo; + if (ncp_get_finfo(server, 0, NCP_FINFO(dir)->path, + this_name, &ninfo) != 0) { + error = -ENOENT; + } + + finfo.attr = ninfo.file_attributes; + finfo.mtime = date_dos2unix(ninfo.update_time, + ninfo.update_date); + finfo.ctime = date_dos2unix(0, ninfo.creation_date); + finfo.atime = date_dos2unix(0, ninfo.access_date); + finfo.size = (ninfo.file_attributes & aDIR) != 0 + ? 1024 : ninfo.file_length; + } + + if (error < 0) { + put_pname(name); + iput(dir); + return error; + } + } + + if (!(*result = ncp_iget(dir, name, &finfo))) { + put_pname(name); + iput(dir); + return -EACCES; + } + + DDPRINTK("ncp_lookup: %s => %lu\n", name, (unsigned long)result_info); + iput(dir); + return 0; +} + + + + +/* The following are taken directly from msdos-fs */ + +/* Linear day numbers of the respective 1sts in non-leap years. */ + +static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 }; + /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ + + +extern struct timezone sys_tz; + +/* +static int +utc2local(int time) +{ + return time - sys_tz.tz_minuteswest*60; +} +*/ + +static int +local2utc(int time) +{ + return time + sys_tz.tz_minuteswest*60; +} + +/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ + +static int +date_dos2unix(unsigned short time,unsigned short date) +{ + int month,year,secs; + + month = ((date >> 5) & 15)-1; + year = date >> 9; + secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400* + ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && + month < 2 ? 1 : 0)+3653); + /* days since 1.1.70 plus 80's leap day */ + return local2utc(secs); +} + + +/* Convert linear UNIX date to a MS-DOS time/date pair. */ +#if 0 +static void +date_unix2dos(int unix_date,unsigned short *time, unsigned short *date) +{ + int day,year,nl_day,month; + + unix_date = utc2local(unix_date); + *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ + (((unix_date/3600) % 24) << 11); + day = unix_date/86400-3652; + year = day/365; + if ((year+3)/4+365*year > day) year--; + day -= (year+3)/4+365*year; + if (day == 59 && !(year & 3)) { + nl_day = day; + month = 2; + } + else { + nl_day = (year & 3) || day <= 59 ? day : day-1; + for (month = 0; month < 12; month++) + if (day_n[month] > nl_day) break; + } + *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9); +} +#endif diff --git a/file.c b/file.c new file mode 100644 index 0000000..581be2b --- /dev/null +++ b/file.c @@ -0,0 +1,176 @@ +/* + * file.c + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * + */ + +#include +#ifdef MODULE +#include +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "ncplib.h" +#include + +static int +ncp_fsync(struct inode *inode, struct file *file) +{ + return 0; +} + +int +ncp_make_open(struct inode *i, int right) +{ + struct ncp_dirent *dirent; + + if (i == NULL) { + printk("ncp_make_open: got NULL inode\n"); + return -EINVAL; + } + + dirent = &(NCP_INOP(i)->finfo); + + DPRINTK("ncp_make_open: dirent->opened = %d\n", dirent->opened); + + if ((dirent->opened) == 0) { + struct ncp_file_info finfo; + + /* tries max. rights */ + if (ncp_open_file(NCP_SERVER(i), 0, dirent->path, 0, + AR_READ | AR_WRITE, &finfo) == 0) { + dirent->access = O_RDWR; + } + else if (ncp_open_file(NCP_SERVER(i), 0, dirent->path, 0, + AR_READ, &finfo) == 0) { + dirent->access = O_RDONLY; + } else { + return -EACCES; + } + + dirent->opened = 1; + memcpy(&(dirent->file_id), finfo.file_id, NCP_FILE_ID_LEN); + } + + if ( ((right == O_RDONLY) && ( (dirent->access == O_RDONLY) + || (dirent->access == O_RDWR))) + || ((right == O_WRONLY) && ( (dirent->access == O_WRONLY) + || (dirent->access == O_RDWR))) + || ((right == O_RDWR) && (dirent->access == O_RDWR))) + return 0; + + return -EACCES; +} + +static int +ncp_file_read(struct inode *inode, struct file *file, char *buf, int count) +{ + int bufsize, to_read, already_read; + off_t pos; + int errno; + + DPRINTK("ncp_file_read: enter %s\n", NCP_FINFO(inode)->path); + + if (!inode) { + DPRINTK("ncp_file_read: inode = NULL\n"); + return -EINVAL; + } + + if (!S_ISREG(inode->i_mode)) { + DPRINTK("ncp_file_read: read from non-file, mode %07o\n", + inode->i_mode); + return -EINVAL; + } + + if ((errno = ncp_make_open(inode, O_RDONLY)) != 0) + return errno; + + pos = file->f_pos; + + if (pos + count > inode->i_size) + count = inode->i_size - pos; + + if (count <= 0) + return 0; + + bufsize = NCP_SERVER(inode)->buffer_size; + + already_read = 0; + + /* First read in as much as possible for each bufsize. */ + while (already_read < count) { + + int read_this_time; + + if ((pos % bufsize) != 0) { + to_read = bufsize - (pos % bufsize); + } else { + to_read = bufsize; + } + + to_read = min(to_read, count - already_read); + + if (ncp_read(NCP_SERVER(inode), NCP_FINFO(inode)->file_id, + pos, to_read, buf, &read_this_time) != 0) { + return -EIO; /* This is not exact, i know.. */ + } + + pos += read_this_time; + buf += read_this_time; + already_read += read_this_time; + + if (read_this_time < to_read) { + break; + } + } + + file->f_pos = pos; + + if (!IS_RDONLY(inode)) inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + + DPRINTK("ncp_file_read: exit %s\n", NCP_FINFO(inode)->path); + + return already_read; +} + +static struct file_operations ncp_file_operations = { + NULL, /* lseek - default */ + ncp_file_read, /* read */ + NULL, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + ncp_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* release */ + ncp_fsync, /* fsync */ +}; + +struct inode_operations ncp_file_inode_operations = { + &ncp_file_operations, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL /* truncate */ +}; diff --git a/inode.c b/inode.c new file mode 100644 index 0000000..0eeccf7 --- /dev/null +++ b/inode.c @@ -0,0 +1,375 @@ +/* + * inode.c + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#include +#ifdef MODULE +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ncplib.h" + +extern int close_fp(struct file *filp); + +static void ncp_put_inode(struct inode *); +static void ncp_read_inode(struct inode *); +static void ncp_put_super(struct super_block *); + +static struct super_operations ncp_sops = { + ncp_read_inode, /* read inode */ + NULL, /* notify change */ + NULL, /* write inode */ + ncp_put_inode, /* put inode */ + ncp_put_super, /* put superblock */ + NULL, /* write superblock */ + NULL, /* stat filesystem */ + NULL + }; + +/* ncp_read_inode: Called from iget, it only traverses the allocated + ncp_inode_info's and initializes the inode from the data found + there. It does not allocate or deallocate anything. */ + +static void +ncp_read_inode(struct inode *inode) +{ + /* Our task should be extremely simple here. We only have to + look up the infomation somebody else (ncp_iget) put into + the inode tree. The address of this information is the + inode->i_ino. Just to make sure everything went well, we + check it's there. */ + + struct ncp_inode_info *inode_info + = (struct ncp_inode_info *)(inode->i_ino); + +#if 1 + struct ncp_inode_info *root = &(NCP_SERVER(inode)->root); + struct ncp_inode_info *check_info = root; + + do { + if (inode_info == check_info) { + if (check_info->state == INODE_LOOKED_UP) { + DDPRINTK("ncp_read_inode: found it!\n"); + goto good; + } + else { + printk("ncp_read_inode: " + "state != INODE_LOOKED_UP\n"); + return; + } + } + check_info = check_info->next; + } while (check_info != root); + + /* Ok, now we're in trouble. The inode info is not there. What + should we do now??? */ + printk("ncp_read_inode: inode info not found\n"); + return; + + good: +#endif + inode_info->state = INODE_VALID; + + NCP_INOP(inode) = inode_info; + + if (NCP_INOP(inode)->finfo.attr & aDIR) + inode->i_mode = NCP_SERVER(inode)->m.dir_mode; + else + inode->i_mode = NCP_SERVER(inode)->m.file_mode; + + DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode); + + inode->i_nlink = 1; + inode->i_uid = NCP_SERVER(inode)->m.uid; + inode->i_gid = NCP_SERVER(inode)->m.gid; + inode->i_size = NCP_INOP(inode)->finfo.size; + inode->i_blksize = 1024; + inode->i_rdev = 0; + if ((inode->i_blksize != 0) && (inode->i_size != 0)) + inode->i_blocks = + (inode->i_size - 1) / inode->i_blksize + 1; + else + inode->i_blocks = 0; + + inode->i_mtime = NCP_INOP(inode)->finfo.mtime; + inode->i_ctime = NCP_INOP(inode)->finfo.ctime; + inode->i_atime = NCP_INOP(inode)->finfo.atime; + + if (S_ISREG(inode->i_mode)) + inode->i_op = &ncp_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &ncp_dir_inode_operations; + else + inode->i_op = NULL; + +} + +static void +ncp_put_inode(struct inode *inode) +{ + struct ncp_dirent *finfo = NCP_FINFO(inode); + + if (finfo->opened != 0) { + + if (ncp_close_file(NCP_SERVER(inode), finfo->file_id) != 0) { + /* We can't do anything but complain. */ + printk("ncp_put_inode: could not close\n"); + } + } + + ncp_free_inode_info(NCP_INOP(inode)); + + if (S_ISDIR(inode->i_mode)) { + DPRINTK("ncp_put_inode: put directory %ld\n", + inode->i_ino); + ncp_invalid_dir_cache(inode->i_ino); + } + + clear_inode(inode); +} + +struct super_block * +ncp_read_super(struct super_block *sb, void *raw_data, int silent) +{ + struct ncp_mount_data *data = (struct ncp_mount_data *) raw_data; + struct ncp_server *server; + struct file *ncp_filp; + struct file *wdog_filp; + kdev_t dev = sb->s_dev; + int error; + + if (!data) { + printk("ncp_read_super: missing data argument\n"); + sb->s_dev = 0; + return NULL; + } + + if (data->version != NCP_MOUNT_VERSION) { + printk("ncp warning: mount version %s than kernel\n", + (data->version < NCP_MOUNT_VERSION) ? + "older" : "newer"); + } + + + if ( (data->ncp_fd >= NR_OPEN) + || ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL) + || (!S_ISSOCK(ncp_filp->f_inode->i_mode))) { + printk("ncp_read_super: invalid ncp socket\n"); + sb->s_dev = 0; + return NULL; + } + + if ( (data->wdog_fd >= NR_OPEN) + || ((wdog_filp = current->files->fd[data->wdog_fd]) == NULL) + || (!S_ISSOCK(wdog_filp->f_inode->i_mode))) { + printk("ncp_read_super: invalid wdog socket\n"); + sb->s_dev = 0; + return NULL; + } + + /* We must malloc our own super-block info */ + server = (struct ncp_server *)ncp_kmalloc(sizeof(struct ncp_server), + GFP_KERNEL); + + if (server == NULL) { + printk("ncp_read_super: could not alloc ncp_server\n"); + return NULL; + } + + ncp_filp->f_count += 1; + wdog_filp->f_count += 1; + + lock_super(sb); + + NCP_SBP(sb) = server; + + sb->s_blocksize = 1024; /* Eh... Is this correct? */ + sb->s_blocksize_bits = 10; + sb->s_magic = NCP_SUPER_MAGIC; + sb->s_dev = dev; + sb->s_op = &ncp_sops; + + server->ncp_filp = ncp_filp; + server->wdog_filp = wdog_filp; + server->lock = 0; + server->wait = NULL; + server->packet = NULL; + server->buffer_size = 0; + + server->m = *data; + server->m.file_mode = (server->m.file_mode & + (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; + server->m.dir_mode = (server->m.dir_mode & + (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; + + server->packet_size = NCP_PACKET_SIZE; + server->packet = ncp_kmalloc(NCP_PACKET_SIZE, GFP_KERNEL); + + if (server->packet == NULL) { + printk("ncpfs: could not alloc packet\n"); + error = -ENOMEM; + unlock_super(sb); + goto fail; + } + + ncp_init_root(server); + + /* + * Make the connection to the server + */ + + if (ncp_catch_watchdog(server) != 0) { + printk("ncp_read_super: Could not catch watchdog\n"); + error = -EINVAL; + unlock_super(sb); + goto fail; + } + + ncp_lock_server(server); + error = ncp_connect(server); + ncp_unlock_server(server); + unlock_super(sb); + + if (error < 0) { + sb->s_dev = 0; + printk("ncp_read_super: Failed connection, bailing out " + "(error = %d).\n", -error); + ncp_kfree_s(server->packet, server->packet_size); + ncp_dont_catch_watchdog(server); + goto fail; + } + + DPRINTK("ncp_read_super: NCP_SBP(sb) = %x\n", (int)NCP_SBP(sb)); + + if (!(sb->s_mounted = iget(sb, (int)&(server->root)))) { + sb->s_dev = 0; + printk("ncp_read_super: get root inode failed\n"); + goto disconnect; + } + + if (ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE, + &(server->buffer_size)) != 0) { + sb->s_dev = 0; + printk("ncp_read_super: could not get bufsize\n"); + goto disconnect; + } + + DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size); + + if (ncp_login_user(server, server->m.username, + server->m.password) != 0) { + sb->s_dev = 0; + printk("ncp_read_super: login failed\n"); + goto disconnect; + } + + MOD_INC_USE_COUNT; + return sb; + + disconnect: + ncp_lock_server(server); + ncp_disconnect(server); + ncp_unlock_server(server); + ncp_kfree_s(server->packet, server->packet_size); + ncp_dont_catch_watchdog(server); + fail: + ncp_filp->f_count -= 1; + wdog_filp->f_count -= 1; + ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server)); + return NULL; +} + +static void +ncp_put_super(struct super_block *sb) +{ + struct ncp_server *server = NCP_SBP(sb); + + lock_super(sb); + + ncp_lock_server(server); + ncp_disconnect(server); + ncp_unlock_server(server); + + close_fp(server->ncp_filp); + + ncp_dont_catch_watchdog(server); + close_fp(server->wdog_filp); + + ncp_free_all_inodes(server); + + ncp_kfree_s(server->packet, server->packet_size); + + sb->s_dev = 0; + ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server)); + NCP_SBP(sb) = NULL; + + unlock_super(sb); + + MOD_DEC_USE_COUNT; +} + + +#ifdef DEBUG_NCP_MALLOC +int ncp_malloced; +int ncp_current_malloced; +#endif + +#ifdef MODULE + +char kernel_version[] = UTS_RELEASE; + +/* looks ugly, taken from gcc-info */ +/* static void *shut_up_gcc = (&shut_up_gcc, kernel_version);*/ + +static struct file_system_type ncp_fs_type = { + ncp_read_super, "ncpfs", 0, NULL + }; + +int +init_module( void) +{ + DPRINTK("ncpfs: init_module called\n"); + +#ifdef DEBUG_NCP_MALLOC + ncp_malloced = 0; + ncp_current_malloced = 0; +#endif + + ncp_init_dir_cache(); + register_filesystem(&ncp_fs_type); + return 0; +} + +void +cleanup_module(void) +{ + DPRINTK("ncpfs: cleanup_module called\n"); + ncp_free_dir_cache(); + unregister_filesystem(&ncp_fs_type); +#ifdef DEBUG_NCP_MALLOC + printk("ncp_malloced: %d\n", ncp_malloced); + printk("ncp_current_malloced: %d\n", ncp_current_malloced); +#endif +} + +#endif diff --git a/ioctl.c b/ioctl.c new file mode 100644 index 0000000..0bf7a2e --- /dev/null +++ b/ioctl.c @@ -0,0 +1,88 @@ +/* + * ioctl.c + * + * Copyright (C) 1995 by Volker Lendecke + * + */ +#include +#ifdef MODULE +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +int +ncp_ioctl (struct inode * inode, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + int result; + struct ncp_ioctl_request request; + struct ncp_server *server; + + switch(cmd) { + case NCP_IOC_NCPREQUEST: + + if (!suser()) { + return -EPERM; + } + + if ((result = verify_area(VERIFY_READ, (char *)arg, + sizeof(request))) != 0) { + return result; + } + + memcpy_fromfs(&request, (struct ncp_ioctl_request *)arg, + sizeof(request)); + + if ( (request.function > 255) + || (request.size > + NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) { + return -EINVAL; + } + + if ((result = verify_area(VERIFY_WRITE, (char *)request.data, + NCP_PACKET_SIZE)) != 0) { + return result; + } + + server = NCP_SERVER(inode); + ncp_lock_server(server); + + /* FIXME: We hack around in the server's structures + here to be able to use ncp_request */ + + server->has_subfunction = 0; + server->current_size = + request.size + sizeof(struct ncp_request_header); + memcpy_fromfs(&(server-> + packet[sizeof(struct ncp_request_header)]), + request.data, request.size); + + + ncp_request(server, request.function); + + DPRINTK("ncp_ioctl: copy %d bytes\n", + server->reply_size); + memcpy_tofs(request.data, server->packet, + server->reply_size); + + ncp_unlock_server(server); + + return server->reply_size; + default: + return -EINVAL; + } + + return -EINVAL; +} diff --git a/ipxutil.h b/ipxutil.h new file mode 100644 index 0000000..6459bbf --- /dev/null +++ b/ipxutil.h @@ -0,0 +1,53 @@ + +/* + + IPX support library + + Copyright (C) 1994, 1995 Ales Dryak + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __IPXUTIL_H__ + +#define __IPXUTIL_H__ + +#include +#include + +#define IPX_SAP_PTYPE (0x04) +#define IPX_SAP_PORT (0x0452) + +#define IPX_SAP_FILE_SERVER (0x004) + +#define IPX_BROADCAST_NODE "\xff\xff\xff\xff\xff\xff" +#define IPX_THIS_NODE "\0\0\0\0\0\0" + + +typedef unsigned long IPXNet; +typedef unsigned short IPXPort; +typedef unsigned char IPXNode[IPX_NODE_LEN]; + +void ipx_print_node(IPXNode node); +void ipx_print_network(IPXNet net); +void ipx_print_port(IPXPort port); +void ipx_print_saddr(struct sockaddr_ipx* sipx); + +static __inline__ void +ipx_assign_node(IPXNode dest, IPXNode src) { + memcpy(dest, src, IPX_NODE_LEN); +} + +#endif diff --git a/linux b/linux new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/linux @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/ncp.h b/ncp.h new file mode 100644 index 0000000..e07afbe --- /dev/null +++ b/ncp.h @@ -0,0 +1,101 @@ +/* + * ncp_fs.h + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#ifndef _LINUX_NCP_H +#define _LINUX_NCP_H + +#include + +#define NCP_PTYPE (0x17) +#define NCP_PORT (0x0451) + +#define NCP_ALLOC_SLOT_REQUEST (0x1111) +#define NCP_REQUEST (0x2222) +#define NCP_DEALLOC_SLOT_REQUEST (0x5555) + +struct ncp_request_header { + __u16 type; + __u8 sequence; + __u8 conn_low; + __u8 task; + __u8 conn_high; + __u8 function; + __u8 data[0]; /* Depends upon request type */ +} __attribute__ ((packed)); + +#define NCP_REPLY (0x3333) +#define NCP_POSITIVE_ACK (0x9999) + +struct ncp_reply_header { + __u16 type; + __u8 sequence; + __u8 conn_low; + __u8 task; + __u8 conn_high; + __u8 completion_code; + __u8 connection_state; + __u8 data[0]; /* Depends upon request type */ +} __attribute__ ((packed)); + + +#define NCP_BINDERY_USER (0x0001) +#define NCP_BINDERY_NAME_LEN (48) +struct ncp_bindery_object { + __u32 object_id; + __u16 object_type; + __u8 object_name[NCP_BINDERY_NAME_LEN]; +}; + + +#define NCP_VOLNAME_LEN (16) +#define NCP_NUMBER_OF_VOLUMES (64) +struct ncp_volume_info { + __u32 total_blocks; + __u32 free_blocks; + __u32 purgeable_blocks; + __u32 not_yet_purgeable_blocks; + __u32 total_dir_entries; + __u32 available_dir_entries; + __u8 sectors_per_block; + char volume_name[NCP_VOLNAME_LEN+1]; +}; + +struct ncp_filesearch_info { + __u8 volume_number; + __u16 directory_id; + __u16 sequence_no; + __u8 access_rights; +}; + +#define NCP_MAX_FILENAME 14 + +/* these define the attribute byte as seen by NCP */ +#define aRONLY (1L<<0) +#define aHIDDEN (1L<<1) +#define aSYSTEM (1L<<2) +#define aEXECUTE (1L<<3) +#define aDIR (1L<<4) +#define aARCH (1L<<5) + +#define AR_READ (0x01) +#define AR_WRITE (0x02) +#define AR_EXCLUSIVE (0x20) + +#define NCP_FILE_ID_LEN 6 +struct ncp_file_info { + __u8 file_id[NCP_FILE_ID_LEN]; + char file_name[NCP_MAX_FILENAME+1]; + __u8 file_attributes; + __u8 file_mode; + __u32 file_length; + __u16 creation_date; + __u16 access_date; + __u16 update_date; + __u16 update_time; +}; + +#endif /* _LINUX_NCP_H */ diff --git a/ncp_fs.h b/ncp_fs.h new file mode 100644 index 0000000..ae1fdef --- /dev/null +++ b/ncp_fs.h @@ -0,0 +1,145 @@ +/* + * ncp_fs.h + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#ifndef _LINUX_NCP_FS_H +#define _LINUX_NCP_FS_H + +#include +#include +#include + +#include +#include +#include + +/* + * ioctl commands + */ + +struct ncp_ioctl_request { + unsigned int function; + unsigned int size; + char *data; +}; + +#define NCP_IOC_NCPREQUEST _IOR('n', 1, unsigned char *) + +/* + * The packet size to allocate. One page should be enough. + */ +#define NCP_PACKET_SIZE 4070 + +#define NCP_MAXPATHLEN 255 +#define NCP_MAXNAMELEN 14 + +#ifdef __KERNEL__ + +/* The readdir cache size controls how many directory entries are + * cached. + */ +#define NCP_READDIR_CACHE_SIZE 64 + + +#define NCP_MAX_RPC_TIMEOUT (60) /* 6 seconds */ + +/* Guess, what 0x564c is :-) */ +#define NCP_SUPER_MAGIC 0x564c + + +#define NCP_SBP(sb) ((struct ncp_server *)(sb->u.generic_sbp)) +#define NCP_INOP(inode) ((struct ncp_inode_info *)(inode->u.generic_ip)) + +#define NCP_SERVER(inode) NCP_SBP(inode->i_sb) +#define NCP_FINFO(inode) (&(NCP_INOP(inode)->finfo)) + +static inline int min(int a, int b) { + return a + +extern int ncp_malloced; +extern int ncp_current_malloced; + +static inline void * +ncp_kmalloc(unsigned int size, int priority) +{ + ncp_malloced += 1; + ncp_current_malloced += 1; + return kmalloc(size, priority); +} + +static inline void +ncp_kfree_s(void *obj, int size) +{ + ncp_current_malloced -= 1; + kfree_s(obj, size); +} + +#else /* DEBUG_NCP_MALLOC */ + +#define ncp_kmalloc(s,p) kmalloc(s,p) +#define ncp_kfree_s(o,s) kfree_s(o,s) + +#endif /* DEBUG_NCP_MALLOC */ + +#if DEBUG_NCP > 0 +#define DPRINTK(format, args...) printk(format , ## args) +#else +#define DPRINTK(format, args...) +#endif + +#if DEBUG_NCP > 1 +#define DDPRINTK(format, args...) printk(format , ## args) +#else +#define DDPRINTK(format, args...) +#endif + + +/* linux/fs/ncpfs/file.c */ +extern struct inode_operations ncp_file_inode_operations; +int ncp_make_open(struct inode *i, int right); + +/* linux/fs/ncpfs/dir.c */ +extern struct inode_operations ncp_dir_inode_operations; +void ncp_free_inode_info(struct ncp_inode_info *i); +void ncp_free_all_inodes(struct ncp_server *server); +void ncp_init_root(struct ncp_server *server); +int ncp_stat_root(struct ncp_server *server); +void ncp_init_dir_cache(void); +void ncp_invalid_dir_cache(unsigned long ino); +void ncp_invalidate_all_inodes(struct ncp_server *server); +void ncp_free_dir_cache(void); + +/* linux/fs/ncpfs/ioctl.c */ +int ncp_ioctl (struct inode * inode, struct file * filp, + unsigned int cmd, unsigned long arg); + +/* linux/fs/ncpfs/inode.c */ +struct super_block *ncp_read_super(struct super_block *sb, + void *raw_data, int silent); +int ncp_notify_change(struct inode *inode, struct iattr *attr); +void ncp_invalidate_connection(struct ncp_server *server); +int ncp_conn_is_valid(struct ncp_server *server); + +/* linux/fs/ncpfs/sock.c */ +int ncp_request(struct ncp_server *server, int function); +int ncp_connect(struct ncp_server *server); +int ncp_disconnect(struct ncp_server *server); +int ncp_catch_watchdog(struct ncp_server *server); +int ncp_dont_catch_watchdog(struct ncp_server *server); +void ncp_lock_server(struct ncp_server *server); +void ncp_unlock_server(struct ncp_server *server); + +/* linux/fs/ncpfs/mmap.c */ +int ncp_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_NCP_FS_H */ diff --git a/ncp_fs_i.h b/ncp_fs_i.h new file mode 100644 index 0000000..3da4c81 --- /dev/null +++ b/ncp_fs_i.h @@ -0,0 +1,59 @@ +/* + * ncp_fs_i.h + * + * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke + * + */ + +#ifndef _LINUX_NCP_FS_I +#define _LINUX_NCP_FS_I + +#include + +#ifdef __KERNEL__ + +/* + * Contains all relevant data on a NCP networked file. + */ +struct ncp_dirent { + int opened; /* is it open on the fileserver? */ + __u8 file_id[NCP_FILE_ID_LEN]; + __u32 attr; /* Attribute fields, NCP value. Either + only lower 8 bits used or upper + bits contain extended attributs */ + __u32 size; /* File size. */ + + time_t atime, /* Times, as seen by the server, normalized */ + mtime, /* to UTC. The ugly conversion happens in */ + ctime; /* proc.c */ + + __u16 access; /* Access bits. */ + __u16 next_search; /* Next search index, for proc_readdir */ + unsigned long f_pos; /* for ncp_readdir */ + + char *path; /* Complete path, MS-DOS notation, with '\' */ + int len; /* Namelength. */ +}; + + +enum ncp_inode_state { + INODE_VALID = 19, /* Inode currently in use */ + INODE_LOOKED_UP, /* directly before iget */ + INODE_CACHED, /* in a path to an inode which is in use */ + INODE_INVALID +}; + +/* + * ncp fs inode data (in memory only) + */ +struct ncp_inode_info { + enum ncp_inode_state state; + int nused; /* for directories: + number of references in memory */ + struct ncp_inode_info *dir; + struct ncp_inode_info *next, *prev; + struct ncp_dirent finfo; +}; + +#endif +#endif diff --git a/ncp_fs_sb.h b/ncp_fs_sb.h new file mode 100644 index 0000000..fc5e955 --- /dev/null +++ b/ncp_fs_sb.h @@ -0,0 +1,61 @@ +/* + * ncp_fs_sb.h + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#ifndef _NCP_FS_SB +#define _NCP_FS_SB + +#include +#include + +#ifdef __KERNEL__ + +#define NCP_DEFAULT_BUFSIZE 1024 + +struct ncp_server { + + struct ncp_mount_data m; /* Nearly all of the mount data is of + interest for us later, so we store + it completely. */ + + struct file *ncp_filp; /* File pointer to ncp socket */ + + struct file *wdog_filp; /* File pointer to wdog socket */ + void *data_ready; /* The wdog socket gets a new + data_ready callback. We store the + old one for checking purposes and + to reset it on unmounting. */ + + u8 sequence; + u8 task; + u16 connection; /* Remote connection number */ + + u8 completion; /* Status message from server */ + u8 conn_status; /* Bit 4 = 1 ==> Server going down, no + requests allowed anymore */ + + int buffer_size; /* Negotiated bufsize */ + + int reply_size; /* Size of last reply */ + + int packet_size; + unsigned char *packet; /* Here we prepare requests and + receive replies */ + + int lock; /* To prevent mismatch in protocols. */ + struct wait_queue *wait; + + int current_size; /* for packet preparation */ + int has_subfunction; + int ncp_reply_size; + + struct ncp_inode_info root; + char root_path; /* '\0' */ +}; + +#endif /* __KERNEL__ */ + +#endif diff --git a/ncp_mount.h b/ncp_mount.h new file mode 100644 index 0000000..83007be --- /dev/null +++ b/ncp_mount.h @@ -0,0 +1,49 @@ +/* + * ncp_mount.h + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#ifndef _LINUX_NCP_MOUNT_H +#define _LINUX_NCP_MOUNT_H + +#include +#include +#include +#include + +#define NCP_MOUNT_VERSION 1 + +#define NCP_USERNAME_LEN (NCP_BINDERY_NAME_LEN) +#define NCP_PASSWORD_LEN 20 + +/* Values for flags */ +#define NCP_MOUNT_SOFT 0x0001 +#define NCP_MOUNT_INTR 0x0002 + +struct ncp_mount_data { + int version; + unsigned int ncp_fd; /* The socket to the ncp port */ + unsigned int wdog_fd; /* Watchdog packets come here */ + unsigned int message_fd; /* Not used yet, maybe for messages */ + uid_t mounted_uid; /* Who may umount() this filesystem? */ + + struct sockaddr_ipx serv_addr; + unsigned char server_name[49]; + + unsigned char username[NCP_USERNAME_LEN+1]; + unsigned char password[NCP_PASSWORD_LEN+1]; + + unsigned int time_out; /* How long should I wait after + sending a NCP request? */ + unsigned int retry_count; /* And how often should I retry? */ + unsigned int flags; + + uid_t uid; + gid_t gid; + mode_t file_mode; + mode_t dir_mode; +}; + +#endif diff --git a/ncpfs-0.1.lsm b/ncpfs-0.1.lsm new file mode 100644 index 0000000..d5b2072 --- /dev/null +++ b/ncpfs-0.1.lsm @@ -0,0 +1,14 @@ +Begin3 +Title: ncpfs +Version: 0.1 +Entered-date: 19. October 1995 +Description: With ncpfs you can mount volumes of your novell + server under Linux. +Keywords: filesystem kernel ncp novell +Author: lendecke@namu01.gwdg.de (Volker Lendecke) +Maintained-by: lendecke@namu01.gwdg.de (Volker Lendecke) +Primary-site: linux01.gwdg.de:/pub/smbfs + ~30k ncpfs-0.1.tgz + ~ 1k ncpfs-0.1.lsm +Copying-policy: GPL +End diff --git a/ncplib.c b/ncplib.c new file mode 100644 index 0000000..a500fb7 --- /dev/null +++ b/ncplib.c @@ -0,0 +1,598 @@ +#include "ncplib.h" +#include "nwcrypt.h" + +typedef __u8 byte; +typedef __u16 word; +typedef __u32 dword; + +#ifdef __KERNEL__ + +#define ncp_printf DPRINTK + +#else + +#include +#include +#include +#include + +#define ncp_printf printf + +static void +assert_server_locked(struct ncp_server *server); + +static void +assert_server_not_locked(struct ncp_server *server) +{ + if (server->lock != 0) { + ncp_printf("ncpfs: server already locked!\n"); + } +} + +static void +ncp_lock_server(struct ncp_server *server) +{ + assert_server_not_locked(server); + server->lock = 1; +} + +static void +ncp_unlock_server(struct ncp_server *server) +{ + assert_server_locked(server); + server->lock = 0; +} + +static int +ncp_request(struct ncp_server *server, int function) { + + struct ncp_reply_header *reply + = (struct ncp_reply_header *)(server->packet); + struct ncp_ioctl_request request; + int result; + + assert_server_locked(server); + + if (server->has_subfunction != 0) { + *(word *)(server->packet) = server->current_size - 2; + } + + request.function = function; + request.size = server->current_size; + request.data = server->packet; + + if ((result = ioctl(server->mount_fid, NCP_IOC_NCPREQUEST, + &request)) < 0) { + return result; + } + + server->ncp_reply_size = result - sizeof(struct ncp_reply_header); + + return reply->completion_code; +} + +static inline int +min(int a, int b) { + if (alock == 0) { + ncp_printf("ncpfs: server not locked!\n"); + } +} + +static void +ncp_add_byte(struct ncp_server *server, byte x) +{ + assert_server_locked(server); + *(byte *)(&(server->packet[server->current_size])) = x; + server->current_size += 1; + return; +} + +static void +ncp_add_word(struct ncp_server *server, word x) +{ + assert_server_locked(server); + *(word *)(&(server->packet[server->current_size])) = x; + server->current_size += 2; + return; +} + +static void +ncp_add_dword(struct ncp_server *server, dword x) +{ + assert_server_locked(server); + *(dword *)(&(server->packet[server->current_size])) = x; + server->current_size += 4; + return; +} + +static void +ncp_add_mem(struct ncp_server *server, const char *source, int size) +{ + assert_server_locked(server); + memcpy(&(server->packet[server->current_size]), source, size); + server->current_size += size; + return; +} + +static void +ncp_add_pstring(struct ncp_server *server, const char *s) +{ + int len = strlen(s); + assert_server_locked(server); + if (len > 255) { + ncp_printf("ncpfs: string too long: %s\n", s); + len = 255; + } + ncp_add_byte(server, len); + ncp_add_mem(server, s, len); + return; +} + +static void +ncp_init_request(struct ncp_server *server) +{ + ncp_lock_server(server); + +#ifdef __KERNEL__ + server->current_size = sizeof(struct ncp_request_header); +#else + server->current_size = 0; + server->packet = server->ncp_data; +#endif + server->has_subfunction = 0; +} + +static void +ncp_init_request_s(struct ncp_server *server, int subfunction) +{ + ncp_init_request(server); + ncp_add_word(server, 0); /* preliminary size */ + + ncp_add_byte(server, subfunction); + + server->has_subfunction = 1; +} + +static char * +ncp_reply_data(struct ncp_server *server, int offset) +{ + return &(server->packet[sizeof(struct ncp_reply_header) + offset]); +} + +static byte +ncp_reply_byte(struct ncp_server *server, int offset) +{ + return *(byte *)(ncp_reply_data(server, offset)); +} + +static word +ncp_reply_word(struct ncp_server *server, int offset) +{ + return *(word *)(ncp_reply_data(server, offset)); +} + +static dword +ncp_reply_dword(struct ncp_server *server, int offset) +{ + return *(dword *)(ncp_reply_data(server, offset)); +} + +int +ncp_negotiate_buffersize(struct ncp_server *server, + int size, int *target) { + + int result; + + ncp_init_request(server); + ncp_add_word(server, htons(size)); + + if ((result = ncp_request(server, 33)) < 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + *target =min(ntohs(ncp_reply_word(server, 0)), size); + + ncp_unlock_server(server); + return 0; +} + + +/* + * result is a 8-byte buffer + */ +int +ncp_get_encryption_key(struct ncp_server *server, + char *target) +{ + int result; + + ncp_init_request_s(server, 23); + + if ((result = ncp_request(server, 23)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + if (server->ncp_reply_size < 8) { + ncp_printf("ncp_reply_size %d < 8\n", + server->ncp_reply_size); + ncp_unlock_server(server); + return result; + } + + memcpy(target, ncp_reply_data(server, 0), 8); + ncp_unlock_server(server); + return 0; +} + +int +ncp_get_bindery_object_id(struct ncp_server *server, + int object_type, char *object_name, + struct ncp_bindery_object *target) +{ + int result; + ncp_init_request_s(server, 53); + ncp_add_word(server, ntohs(object_type)); + ncp_add_pstring(server, object_name); + + if ((result = ncp_request(server, 23)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + if (server->ncp_reply_size < 54) { + ncp_printf("ncp_reply_size %d < 54\n", + server->ncp_reply_size); + ncp_unlock_server(server); + return result; + } + + target->object_id = ntohl(ncp_reply_dword(server, 0)); + target->object_type = ntohs(ncp_reply_word (server, 4)); + memcpy(target->object_name, ncp_reply_data(server, 6), 48); + ncp_unlock_server(server); + return 0; +} + +int +ncp_login_encrypted(struct ncp_server *server, + struct ncp_bindery_object *object, + unsigned char *key, + unsigned char *passwd) +{ + dword tmpID = htonl(object->object_id); + unsigned char buf[128]; + unsigned char encrypted[8]; + int result; + + shuffle((byte *)&tmpID, passwd, strlen(passwd), buf); + nw_encrypt(key, buf, encrypted); + + ncp_init_request_s(server, 24); + ncp_add_mem(server, encrypted, 8); + ncp_add_word(server, htons(object->object_type)); + ncp_add_pstring(server, object->object_name); + + if ((result = ncp_request(server, 23)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + ncp_unlock_server(server); + return 0; +} + +int +ncp_login_user(struct ncp_server *server, + unsigned char *username, + unsigned char *password) +{ + int result; + unsigned char ncp_key[8]; + struct ncp_bindery_object user; + + if ((result = ncp_get_encryption_key(server, ncp_key)) != 0) { + return result; + } + + if ((result = ncp_get_bindery_object_id(server, NCP_BINDERY_USER, + username, &user)) != 0) { + return result; + } + + if ((result = ncp_login_encrypted(server, &user, + ncp_key, password)) != 0) { + return result; + } + return 0; +} + + + +int +ncp_get_volume_info_with_number(struct ncp_server *server, int n, + struct ncp_volume_info *target) +{ + int result; + int len; + + ncp_init_request_s(server, 44); + ncp_add_byte(server, n); + + if ((result = ncp_request(server, 22)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + target->total_blocks = ncp_reply_dword(server, 0); + target->free_blocks = ncp_reply_dword(server, 4); + target->purgeable_blocks = ncp_reply_dword(server, 8); + target->not_yet_purgeable_blocks = ncp_reply_dword(server, 12); + target->total_dir_entries = ncp_reply_dword(server, 16); + target->available_dir_entries = ncp_reply_dword(server, 20); + target->sectors_per_block = ncp_reply_byte(server, 28); + + memset(&(target->volume_name), 0, sizeof(target->volume_name)); + + len = ncp_reply_byte(server, 29); + if (len > NCP_VOLNAME_LEN) { + ncp_printf("ncpfs: volume name too long: %d\n", len); + ncp_unlock_server(server); + return -EIO; + } + + memcpy(&(target->volume_name), ncp_reply_data(server, 30), len); + ncp_unlock_server(server); + return 0; +} + +int +ncp_get_volume_number(struct ncp_server *server, const char *name, int *target) +{ + int result; + + ncp_init_request_s(server, 5); + ncp_add_pstring(server, name); + + if ((result = ncp_request(server, 22)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + *target = ncp_reply_byte(server, 0); + ncp_unlock_server(server); + return 0; +} + + +int +ncp_file_search_init(struct ncp_server *server, + int dir_handle, const char *path, + struct ncp_filesearch_info *target) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, dir_handle); + ncp_add_pstring(server, path); + + if ((result = ncp_request(server, 62)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + target->volume_number = ncp_reply_byte(server, 0); + target->directory_id = ntohs(ncp_reply_word(server, 1)); + target->sequence_no = ntohs(ncp_reply_word(server, 3)); + target->access_rights = ncp_reply_byte(server, 5); + ncp_unlock_server(server); + return 0; +} + + +int +ncp_file_search_continue(struct ncp_server *server, + struct ncp_filesearch_info *fsinfo, + int attributes, const char *name, + struct ncp_file_info *target) +{ + int result; + + ncp_init_request(server); + + ncp_add_byte(server, fsinfo->volume_number); + ncp_add_word(server, htons(fsinfo->directory_id)); + ncp_add_word(server, htons(fsinfo->sequence_no)); + + ncp_add_byte(server, attributes); + ncp_add_pstring(server, name); + + if ((result = ncp_request(server, 63)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + fsinfo->sequence_no = ntohs(ncp_reply_word(server, 0)); + + memset(&(target->file_name), 0, sizeof(target->file_name)); + memcpy(&(target->file_name), ncp_reply_data(server, 4), + NCP_MAX_FILENAME); + + target->file_attributes = ncp_reply_byte(server, 18); + target->file_mode = ncp_reply_byte(server, 19); + target->file_length = ntohl(ncp_reply_dword(server, 20)); + target->creation_date = ntohs(ncp_reply_word(server, 24)); + target->access_date = ntohs(ncp_reply_word(server, 26)); + target->update_date = ntohs(ncp_reply_word(server, 28)); + target->update_time = ntohs(ncp_reply_word(server, 30)); + + ncp_unlock_server(server); + return 0; +} + +int +ncp_get_finfo(struct ncp_server *server, + int dir_handle, const char *path, const char *name, + struct ncp_file_info *target) +{ + int result; + + struct ncp_filesearch_info fsinfo; + + if ((result = ncp_file_search_init(server, dir_handle, path, + &fsinfo)) != 0) { + return result; + } + + if ((result = ncp_file_search_continue(server, &fsinfo, 0, name, + target)) == 0) { + return result; + } + + if ((result = ncp_file_search_init(server, dir_handle, path, + &fsinfo)) != 0) { + return result; + } + + return ncp_file_search_continue(server, &fsinfo, aDIR, name, target); +} + +int +ncp_open_file(struct ncp_server *server, + int dir_handle, const char *path, + int attr, int access, + struct ncp_file_info *target) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, dir_handle); + ncp_add_byte(server, attr); + ncp_add_byte(server, access); + ncp_add_pstring(server, path); + + if ((result = ncp_request(server, 76)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + memcpy(&(target->file_id), ncp_reply_data(server, 0), + NCP_FILE_ID_LEN); + + memset(&(target->file_name), 0, sizeof(target->file_name)); + memcpy(&(target->file_name), ncp_reply_data(server, 8), + NCP_MAX_FILENAME); + + target->file_attributes = ncp_reply_byte(server, 22); + target->file_mode = ncp_reply_byte(server, 23); + target->file_length = ntohl(ncp_reply_dword(server, 24)); + target->creation_date = ntohs(ncp_reply_word(server, 28)); + target->access_date = ntohs(ncp_reply_word(server, 30)); + target->update_date = ntohs(ncp_reply_word(server, 32)); + target->update_time = ntohs(ncp_reply_word(server, 34)); + + ncp_unlock_server(server); + return 0; +} + +int +ncp_close_file(struct ncp_server *server, const char *file_id) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, 0); + ncp_add_mem(server, file_id, 6); + + if ((result = ncp_request(server, 66)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + ncp_unlock_server(server); + return 0; +} + +#ifndef __KERNEL__ + +int +ncp_read(struct ncp_server *server, const char *file_id, + __u32 offset, __u16 to_read, + char *target, int *bytes_read) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, 0); + ncp_add_mem(server, file_id, 6); + ncp_add_dword(server, htonl(offset)); + ncp_add_word(server, htons(to_read)); + + if ((result = ncp_request(server, 72)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + *bytes_read = ntohs(ncp_reply_word(server, 0)); + + memcpy(target, ncp_reply_data(server, 2), *bytes_read); + + ncp_unlock_server(server); + return 0; +} + +#else + +/* We have to read into user space */ +int +ncp_read(struct ncp_server *server, const char *file_id, + __u32 offset, __u16 to_read, + char *target, int *bytes_read) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, 0); + ncp_add_mem(server, file_id, 6); + ncp_add_dword(server, htonl(offset)); + ncp_add_word(server, htons(to_read)); + + if ((result = ncp_request(server, 72)) != 0) { + ncp_printf("ncp_request_error: %d\n", result); + ncp_unlock_server(server); + return result; + } + + *bytes_read = ntohs(ncp_reply_word(server, 0)); + + memcpy_tofs(target, ncp_reply_data(server, 2), *bytes_read); + + ncp_unlock_server(server); + return 0; +} + +#endif diff --git a/ncplib.h b/ncplib.h new file mode 100644 index 0000000..49797ba --- /dev/null +++ b/ncplib.h @@ -0,0 +1,102 @@ +#ifndef _NCPLIB_H +#define _NCPLIB_H + +#ifdef __KERNEL__ + +#include +#ifdef MODULE +#include +#include +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include + +struct ncp_server { + int current_size; + int has_subfunction; + int mount_fid; + int ncp_reply_size; + char *packet; + int lock; + + char ncp_data[NCP_PACKET_SIZE]; +}; + +#endif __KERNEL__ + +#include + +int +ncp_negotiate_buffersize(struct ncp_server *server, int size, + int *target); +int +ncp_get_encryption_key(struct ncp_server *server, + char *target); +int +ncp_get_bindery_object_id(struct ncp_server *server, + int object_type, char *object_name, + struct ncp_bindery_object *target); +int +ncp_login_encrypted(struct ncp_server *server, + struct ncp_bindery_object *object, + unsigned char *key, + unsigned char *passwd); +int +ncp_login_user(struct ncp_server *server, + unsigned char *username, + unsigned char *password); +int +ncp_get_volume_info_with_number(struct ncp_server *server, int n, + struct ncp_volume_info *target); + +int +ncp_get_volume_number(struct ncp_server *server, const char *name, + int *target); + +int +ncp_file_search_init(struct ncp_server *server, + int dir_handle, const char *path, + struct ncp_filesearch_info *target); + +int +ncp_file_search_continue(struct ncp_server *server, + struct ncp_filesearch_info *fsinfo, + int attributes, const char *path, + struct ncp_file_info *target); + +int +ncp_get_finfo(struct ncp_server *server, + int dir_handle, const char *path, const char *name, + struct ncp_file_info *target); + +int +ncp_open_file(struct ncp_server *server, + int dir_handle, const char *path, + int attr, int access, + struct ncp_file_info *target); +int +ncp_close_file(struct ncp_server *server, const char *file_id); + +int +ncp_read(struct ncp_server *server, const char *file_id, + __u32 start, __u16 to_read, + char *target, int *bytes_read); + +#endif /* _NCPLIB_H */ diff --git a/ncpmount.c b/ncpmount.c new file mode 100644 index 0000000..2bb6be9 --- /dev/null +++ b/ncpmount.c @@ -0,0 +1,332 @@ +/* + * ncpmount.c + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ /* generates a warning here */ +extern pid_t waitpid(pid_t, int *, int); +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "ipxutil.h" + +static char *progname; +static char *mount_point; +static char *server_name; + +struct sap_query { + unsigned short query_type; /* net order */ + unsigned short server_type; /* net order */ +}; + +struct sap_server_ident { + unsigned short server_type; + char server_name[48]; + IPXNet server_network; + IPXNode server_node; + IPXPort server_port; + unsigned short intermediate_network; +} __attribute__ ((packed)); + + +void +ipx_fprint_node(FILE* file,IPXNode node) +{ + fprintf(file,"%02X%02X%02X%02X%02X%02X", + (unsigned char)node[0], + (unsigned char)node[1], + (unsigned char)node[2], + (unsigned char)node[3], + (unsigned char)node[4], + (unsigned char)node[5] + ); +} + +void +ipx_fprint_network(FILE* file,IPXNet net) +{ + fprintf(file,"%08lX",ntohl(net)); +} + +void +ipx_fprint_port(FILE* file,IPXPort port) +{ + fprintf(file,"%04X",ntohs(port)); +} + +void +ipx_fprint_saddr(FILE* file,struct sockaddr_ipx* sipx) +{ + ipx_fprint_network(file,sipx->sipx_network); + fprintf(file,":"); + ipx_fprint_node(file,sipx->sipx_node); + fprintf(file,":"); + ipx_fprint_port(file,sipx->sipx_port); +} + +void +ipx_print_node(IPXNode node) +{ + ipx_fprint_node(stdout,node); +} + +void +ipx_print_network(IPXNet net) +{ + ipx_fprint_network(stdout,net); +} + +void +ipx_print_port(IPXPort port) +{ + ipx_fprint_port(stdout,port); +} + +void +ipx_print_saddr(struct sockaddr_ipx* sipx) +{ + ipx_fprint_saddr(stdout,sipx); +} + +int +ipx_sap_find_server(char *name, int server_type, int timeout, + struct sockaddr_ipx *result) +{ + struct sockaddr_ipx ipxs; + char data[1024]; + int sock; + int opt; + int res = -1; + int name_len = strlen(name); + fd_set rd, wr, ex; + struct timeval tv; + + if (name_len > 48) { + return -1; + } + + sock=socket(AF_IPX,SOCK_DGRAM,PF_IPX); + if (sock==-1) { + return -1; + } + + opt=1; + /* Permit broadcast output */ + if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST, &opt,sizeof(opt))==-1) + { + perror("setsockopt"); + goto finished; + } + + memset(&ipxs, 0, sizeof(ipxs)); + ipxs.sipx_family=AF_IPX; + ipxs.sipx_network=htonl(0x0); + ipxs.sipx_port=htons(0x0); + ipxs.sipx_type=IPX_SAP_PTYPE; + + if(bind(sock,(struct sockaddr*)&ipxs,sizeof(ipxs))==-1) + { + perror("bind"); + goto finished; + } + + *(unsigned short *)data = htons(0x0001); + *(unsigned short *)&(data[2]) = htons(server_type); + + memset(&ipxs, 0, sizeof(ipxs)); + ipxs.sipx_family=AF_IPX; + ipxs.sipx_network=htonl(0x0); + ipx_assign_node(ipxs.sipx_node, IPX_BROADCAST_NODE); + ipxs.sipx_port=htons(IPX_SAP_PORT); + ipxs.sipx_type=IPX_SAP_PTYPE; + + if (sendto(sock, data, 4, 0, + (struct sockaddr *)&ipxs, sizeof(ipxs)) < 0) { + perror("sendto"); + goto finished; + } + + FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); + FD_SET(sock, &rd); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + if (select(sock+1, &rd, &wr, &ex, &tv) == -1) { + perror("select"); + goto finished; + } + + if (FD_ISSET(sock, &rd)) { + int len = recv(sock, data, 1024, 0); + int i; + struct sap_server_ident *ident; + + for (i = 2; i < len; i += 64) { + ident = (struct sap_server_ident *)(data+i); + if ( (strncmp(name,ident->server_name,name_len)==0) + && (name_len < 48) + && (ident->server_name[name_len] == '\0')) + { + result->sipx_family = AF_IPX; + result->sipx_network = + ident->server_network; + result->sipx_port = ident->server_port; + ipx_assign_node(result->sipx_node, + ident->server_node); + res = 0; + goto finished; + } + } + } + + finished: + close(sock); + return res; +} + +int ipx_sscanf_node(char *buf, unsigned char node[6]) +{ + int i; + int n[6]; + + if ((i = sscanf(buf, "%2x%2x%2x%2x%2x%2x", + &(n[0]), &(n[1]), &(n[2]), + &(n[3]), &(n[4]), &(n[5]))) != 6) { + return i; + } + + for (i=0; i<6; i++) { + node[i] = n[i]; + } + return 6; +} + +int +main(int argc, char **argv) +{ + struct ncp_mount_data data; + struct stat st; + struct sockaddr_ipx addr; + int ncp_sock, wdog_sock; + int flags; + + progname = argv[0]; + + if (geteuid() != 0) { + fprintf(stderr, "%s must be installed suid root\n", progname); + exit(1); + } + + memset(&data, 0, sizeof(struct ncp_mount_data)); + + if (argc != 5) { + fprintf(stderr, "usage: %s server mount-point" + " user password\n", progname); + exit(1); + } + + server_name = argv[1]; + mount_point = argv[2]; + + + if (stat(mount_point, &st) == -1) { + fprintf(stderr, "could not find mount point %s: %s\n", + mount_point, strerror(errno)); + exit(1); + } + + ncp_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + if (ncp_sock == -1) { + fprintf(stderr, "could not open ncp socket: %s\n", + strerror(errno)); + exit(1); + } + + wdog_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + if (wdog_sock == -1) { + fprintf(stderr, "could not open wdog socket: %s\n", + strerror(errno)); + exit(1); + } + + addr.sipx_type = NCP_PTYPE; + + if (bind(ncp_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "bind(ncp_sock, ): %s\n", + strerror(errno)); + exit(1); + } + + if (bind(wdog_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + fprintf(stderr, "bind(wdog_sock, ): %s\n", + strerror(errno)); + exit(1); + } + + if (ipx_sap_find_server(server_name, IPX_SAP_FILE_SERVER, + 3, &addr) != 0) { + fprintf(stderr, "could not find server %s\n", server_name); + exit(1); + } + + printf("server: "); + ipx_fprint_saddr(stdout, &addr); + printf("\n"); + + data.version = NCP_MOUNT_VERSION; + data.ncp_fd = ncp_sock; + data.wdog_fd = wdog_sock; + data.mounted_uid = getuid(); + data.serv_addr = addr; + strcpy(data.server_name, server_name); + data.time_out = 20; /* 2 seconds */ + data.retry_count = 2; + data.uid = 501; /* me */ + data.gid = 100; /* users */ + data.file_mode = data.dir_mode = S_IRWXU|S_IRWXG|S_IRWXO; + strcpy(data.username, argv[3]); + strcpy(data.password, argv[4]); + + flags = MS_MGC_VAL; + + if (mount(NULL, mount_point, "ncpfs", flags, (char *)&data) < 0) { + perror("mount error"); + fprintf(stderr, "Maybe you should try your password in " + "upper case\n"); + return -1; + } + + close(ncp_sock); + close(wdog_sock); + + return 0; +} + diff --git a/nwcrypt.c b/nwcrypt.c new file mode 100644 index 0000000..eceafe8 --- /dev/null +++ b/nwcrypt.c @@ -0,0 +1,141 @@ +/*$********************************************************* +$* +$* This code has been taken from DDJ 11/93, from an +$* article by Pawel Szczerbina. Please read the file +$* README for my questions about the legal status of +$* this code. +$* +$* Password encryption routines follow. +$* Converted to C from Barry Nance's Pascal +$* prog published in the March -93 issue of Byte. +$* +$* Adapted to be useable for ncpfs by +$* Volker Lendecke in +$* October 1995. +$* +$**********************************************************/ + +/******************* Data types ***************************/ +typedef unsigned char buf32[32]; +typedef unsigned char buf16[16]; +typedef unsigned char buf8[8]; +typedef unsigned char buf4[4]; +typedef unsigned char u8; + +static u8 encrypttable[256] = +{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8, + 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9, + 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6, + 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0, + 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD, + 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE, + 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7, + 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1, + 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4, + 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2, + 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3, + 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0, + 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8, + 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3, + 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0, + 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD}; + +static buf32 encryptkeys = +{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D, + 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35, + 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11, + 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0}; + + +static void +shuffle1(buf32 temp, unsigned char *target) +{ + short b4; + unsigned char b3; + int s, b2, i; + + b4 = 0; + + for (b2 = 0; b2 <= 1; ++b2) + { + for (s = 0; s <= 31; ++s) + { + b3 = (temp[s]+b4) ^ (temp[(s+b4)&31] - encryptkeys[s]); + b4 = b4 + b3; + temp[s] = b3; + } + } + + for (i = 0; i <= 15; ++i) { + target[i] = encrypttable[temp[ 2*i ]] + | (encrypttable[temp[ 2*i + 1]] << 4); + } +} + + +void +shuffle(unsigned char *lon, const unsigned char *buf, int buflen, + unsigned char *target) +{ + int b2, d, s; + buf32 temp; + + while ( (buflen > 0) + && (buf[buflen - 1] == 0)) { + buflen = buflen - 1; + } + + for (s = 0; s < 32; s++) { + temp[s] = 0; + } + + d = 0; + while (buflen >= 32) + { + for (s = 0; s <= 31; ++s) + { + temp[s] = temp[s] ^ buf[d]; + d = d + 1; + } + buflen = buflen - 32; + } + b2 = d; + if (buflen > 0) + { + for (s = 0; s <= 31; ++s) + { + if (d + buflen == b2) + { + b2 = d; + temp[s] = temp[s] ^ encryptkeys[s]; + } else { + temp[s] = temp[s] ^ buf[b2]; + b2 = b2 + 1; + } + } + } + + for (s = 0; s <= 31; ++s) + temp[s] = temp[s] ^ lon[s & 3]; + + shuffle1(temp,target); +} + + +void +nw_encrypt(unsigned char *fra,unsigned char *buf,unsigned char *til) +{ + buf32 k; + int s; + + shuffle(&(fra[0]), buf, 16, &(k[ 0])); + shuffle(&(fra[4]), buf, 16, &(k[16])); + + for (s = 0; s <= 15; ++s) + k[s] = k[s] ^ k[31 - s]; + + for (s = 0; s <= 7; ++s) + til[s] = k[s] ^ k[15 - s]; +} + + diff --git a/nwcrypt.h b/nwcrypt.h new file mode 100644 index 0000000..a2f4b64 --- /dev/null +++ b/nwcrypt.h @@ -0,0 +1,5 @@ +void +shuffle(unsigned char *lon, const unsigned char *buf, int buflen, + unsigned char *target); +void +nw_encrypt(unsigned char *fra,unsigned char *buf,unsigned char *til); diff --git a/sock.c b/sock.c new file mode 100644 index 0000000..ff79887 --- /dev/null +++ b/sock.c @@ -0,0 +1,533 @@ +/* + * linux/fs/ncp/sock.c + * + * Copyright (C) 1992, 1993 Rick Sladkey + * + * Modified 1995 by Volker Lendecke to be usable for ncp + * + */ + +#include +#ifdef MODULE +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define _S(nr) (1<<((nr)-1)) + +static void +ncp_wdog_data_ready(struct sock *sk, int len) +{ + struct socket *sock = sk->socket; + + if (!sk->dead) + { + unsigned char packet_buf[2]; + struct sockaddr_ipx sender; + int addr_len; + int result; + unsigned short fs; + + fs = get_fs(); + set_fs(get_ds()); + + result = sock->ops->recvfrom(sock, (void *)packet_buf, 2, 1, 0, + (struct sockaddr *)&sender, + &addr_len); + + if ( (result != 2) + || (packet_buf[1] != '?') + /* How to check connection number here? */ + ) + { + /* Error, throw away the complete packet */ + sock->ops->recvfrom(sock, (void *)packet_buf, 2, 1, 0, + (struct sockaddr *)&sender, + &addr_len); + + printk("ncpfs: got strange packet on watchdog " + "socket\n"); + + } else { + int result; + DPRINTK("ncpfs: got watchdog from:\n"); + DPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X," + " conn:%02X,type:%c\n", + htonl(sender.sipx_network), + sender.sipx_node[0], sender.sipx_node[1], + sender.sipx_node[2], sender.sipx_node[3], + sender.sipx_node[4], sender.sipx_node[5], + ntohs(sender.sipx_port), + packet_buf[0], packet_buf[1]); + + packet_buf[1] = 'Y'; + result = sock->ops-> + sendto(sock, (void *)packet_buf, 2, 1, 0, + (struct sockaddr *)&sender, + sizeof(sender)); + printk("send result: %d\n", result); + } + set_fs(fs); + } +} + + +int +ncp_catch_watchdog(struct ncp_server *server) +{ + struct file *file; + struct inode *inode; + struct socket *sock; + struct sock *sk; + + if ( (server == NULL) + || ((file = server->wdog_filp) == NULL) + || ((inode = file->f_inode) == NULL) + || (!S_ISSOCK(inode->i_mode))) { + + printk("ncp_catch_watchdog: did not get valid server!\n"); + server->data_ready = NULL; + return -EINVAL; + } + + sock = &(inode->u.socket_i); + + if (sock->type != SOCK_DGRAM) { + printk("ncp_catch_watchdog: did not get SOCK_STREAM\n"); + server->data_ready = NULL; + return -EINVAL; + } + + sk = (struct sock *)(sock->data); + + if (sk == NULL) { + printk("ncp_catch_watchdog: sk == NULL"); + server->data_ready = NULL; + return -EINVAL; + } + + DDPRINTK("ncp_catch_watchdog.: sk->d_r = %x, server->d_r = %x\n", + (unsigned int)(sk->data_ready), + (unsigned int)(server->data_ready)); + + if (sk->data_ready == ncp_wdog_data_ready) { + printk("ncp_catch_watchdog: already done\n"); + return -EINVAL; + } + + server->data_ready = sk->data_ready; + sk->data_ready = ncp_wdog_data_ready; + return 0; +} + +int +ncp_dont_catch_watchdog(struct ncp_server *server) +{ + struct file *file; + struct inode *inode; + struct socket *sock; + struct sock *sk; + + if ( (server == NULL) + || ((file = server->wdog_filp) == NULL) + || ((inode = file->f_inode) == NULL) + || (!S_ISSOCK(inode->i_mode))) { + + printk("ncp_dont_catch_watchdog: " + "did not get valid server!\n"); + return -EINVAL; + } + + sock = &(inode->u.socket_i); + + if (sock->type != SOCK_DGRAM) { + printk("ncp_dont_catch_watchdog: did not get SOCK_STREAM\n"); + return -EINVAL; + } + + sk = (struct sock *)(sock->data); + + if (sk == NULL) { + printk("ncp_dont_catch_watchdog: sk == NULL"); + return -EINVAL; + } + + if (server->data_ready == NULL) { + printk("ncp_dont_catch_watchdog: " + "server->data_ready == NULL\n"); + return -EINVAL; + } + + if (sk->data_ready != ncp_wdog_data_ready) { + printk("ncp_dont_catch_watchdog: " + "sk->data_callback != ncp_data_callback\n"); + return -EINVAL; + } + + DDPRINTK("ncp_dont_catch_watchdog: sk->d_r = %x, server->d_r = %x\n", + (unsigned int)(sk->data_ready), + (unsigned int)(server->data_ready)); + + sk->data_ready = server->data_ready; + server->data_ready = NULL; + return 0; +} + + + +#define NCP_SLACK_SPACE 1024 + +#define _S(nr) (1<<((nr)-1)) + +static int +do_ncp_rpc_call(struct ncp_server *server, int size) +{ + struct file *file; + struct inode *inode; + struct socket *sock; + unsigned short fs; + int result; + char *start = server->packet; + select_table wait_table; + struct select_table_entry entry; + int (*select) (struct inode *, struct file *, int, select_table *); + int init_timeout, max_timeout; + int timeout; + int retrans; + int major_timeout_seen; + char *server_name; + int n; + int addrlen; + unsigned long old_mask; + + /* We have to check the result, so store the complete header */ + struct ncp_request_header request = + *((struct ncp_request_header *)(server->packet)); + + struct ncp_reply_header reply; + + + file = server->ncp_filp; + inode = file->f_inode; + select = file->f_op->select; + sock = &inode->u.socket_i; + if (!sock) { + printk("ncp_rpc_call: socki_lookup failed\n"); + return -EBADF; + } + init_timeout = server->m.time_out; + max_timeout = NCP_MAX_RPC_TIMEOUT*HZ/10; + retrans = server->m.retry_count; + major_timeout_seen = 0; + server_name = server->m.server_name; + old_mask = current->blocked; + current->blocked |= ~(_S(SIGKILL) +#if 0 + | _S(SIGSTOP) +#endif + | ((server->m.flags & NCP_MOUNT_INTR) + ? ((current->sig->action[SIGINT - 1].sa_handler == SIG_DFL + ? _S(SIGINT) : 0) + | (current->sig->action[SIGQUIT - 1].sa_handler == SIG_DFL + ? _S(SIGQUIT) : 0)) + : 0)); + fs = get_fs(); + set_fs(get_ds()); + for (n = 0, timeout = init_timeout; ; n++, timeout <<= 1) { + DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n", + htonl(server->m.serv_addr.sipx_network), + server->m.serv_addr.sipx_node[0], + server->m.serv_addr.sipx_node[1], + server->m.serv_addr.sipx_node[2], + server->m.serv_addr.sipx_node[3], + server->m.serv_addr.sipx_node[4], + server->m.serv_addr.sipx_node[5], + ntohs(server->m.serv_addr.sipx_port)); + DDPRINTK("ncpfs: req.typ: %04X, con: %d, tsk: %d, " + "seq: %d\n", + request.type, + (request.conn_high << 8) + request.conn_low, + request.task, + request.sequence); + DDPRINTK("ncpfs: req.func: %d\n", + request.function); + + result = sock->ops-> + sendto(sock, (void *) start, size, 0, 0, + (struct sockaddr *)&(server->m.serv_addr), + sizeof(server->m.serv_addr)); + if (result < 0) { + printk("ncp_rpc_call: send error = %d\n", result); + break; + } + re_select: + wait_table.nr = 0; + wait_table.entry = &entry; + current->state = TASK_INTERRUPTIBLE; + if ( !select(inode, file, SEL_IN, &wait_table) + && !select(inode, file, SEL_IN, NULL)) { + if (timeout > max_timeout) { + /* JEJB/JSP 2/7/94 + * This is useful to see if the system is + * hanging */ + printk("NCP max timeout reached on %s\n", + server_name); + timeout = max_timeout; + } + current->timeout = jiffies + timeout; + schedule(); + remove_wait_queue(entry.wait_address, &entry.wait); + current->state = TASK_RUNNING; + if (current->signal & ~current->blocked) { + current->timeout = 0; + result = -ERESTARTSYS; + break; + } + if (!current->timeout) { + if (n < retrans) + continue; + if (server->m.flags & NCP_MOUNT_SOFT) { + printk("NCP server %s not responding, " + "timed out\n", server_name); + result = -EIO; + break; + } + n = 0; + timeout = init_timeout; + init_timeout <<= 1; + if (!major_timeout_seen) { + printk("NCP server %s not responding, " + "still trying\n", server_name); + } + major_timeout_seen = 1; + continue; + } + else + current->timeout = 0; + } + else if (wait_table.nr) + remove_wait_queue(entry.wait_address, &entry.wait); + current->state = TASK_RUNNING; + addrlen = 0; + + /* Get the header from the next packet using a peek, so keep it + * on the recv queue. If it is wrong, it will be some reply + * we don't now need, so discard it */ + result = sock->ops->recvfrom(sock, (void *)&reply, + sizeof(reply), 1, MSG_PEEK, + NULL, &addrlen); + if (result < 0) { + if (result == -EAGAIN) { + DPRINTK("ncp_rpc_call: bad select ready\n"); + goto re_select; + } + if (result == -ECONNREFUSED) { + DPRINTK("ncp_rpc_call: server playing coy\n"); + goto re_select; + } + if (result != -ERESTARTSYS) { + printk("ncp_rpc_call: recv error = %d\n", + -result); + } + break; + } + if ( (result == sizeof(reply)) + && (reply.type == NCP_POSITIVE_ACK)) { + /* Throw away the packet */ + DPRINTK("ncp_rpc_call: got positive acknowledge\n"); + (void)sock->ops->recvfrom(sock, (void *)&reply, + sizeof(reply), 1, 0, NULL, + &addrlen); + goto re_select; + } + + DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d," + "seq: %d\n", + reply.type, + (reply.conn_high << 8) + reply.conn_low, + reply.task, + reply.sequence); + + if ( (result >= sizeof(reply)) + && (reply.type == NCP_REPLY) + && ( (request.type == NCP_ALLOC_SLOT_REQUEST) + || ( (reply.sequence == request.sequence) + && (reply.conn_low == request.conn_low) +/* seem to get wrong task from NW311 && (reply.task == request.task)*/ + && (reply.conn_high == request.conn_high)))) { + if (major_timeout_seen) + printk("NCP server %s OK\n", server_name); + break; + } + /* JEJB/JSP 2/7/94 + * we have xid mismatch, so discard the packet and start + * again. What a hack! but I can't call recvfrom with + * a null buffer yet. */ + (void)sock->ops->recvfrom(sock, (void *)&reply, + sizeof(reply), 1, 0, NULL, + &addrlen); +#if 1 + printk("ncp_rpc_call: reply mismatch\n"); +#endif + goto re_select; + } + /* + * we have the correct reply, so read into the correct place and + * return it + */ + result=sock->ops->recvfrom(sock, (void *)start, + server->packet_size, 1, 0, NULL, + &addrlen); + if (result < 0) { + printk("NCP: notice message: result=%d\n", result); + } else if (result < sizeof(struct ncp_reply_header)) { + printk("NCP: just caught a too small read memory size..., " + "email to NET channel\n"); + printk("NCP: result=%d,addrlen=%d\n", result, addrlen); + result = -EIO; + } + + current->blocked = old_mask; + set_fs(fs); + return result; +} + + +/* + * We need the server to be locked here, so check! + */ + +static int +ncp_do_request(struct ncp_server *server, int size) +{ + if (server->lock == 0) { + printk("ncpfs: Server not locked!\n"); + return -EIO; + } + + return do_ncp_rpc_call(server, size); +} + +/* ncp_do_request assures that at least a complete reply header is + * received. It assumes that server->current_size contains the ncp + * request size */ +int +ncp_request(struct ncp_server *server, int function) +{ + struct ncp_request_header *h + = (struct ncp_request_header *)(server->packet); + struct ncp_reply_header *reply + = (struct ncp_reply_header *)(server->packet); + + int request_size = server->current_size + - sizeof(struct ncp_request_header); + + int result; + + if (server->has_subfunction != 0) { + *(__u16 *)&(h->data[0]) = request_size - 2; + } + + h->type = NCP_REQUEST; + + server->sequence += 1; + h->sequence = server->sequence; + h->conn_low = (server->connection) & 0xff; + h->conn_high = ((server->connection) & 0xff00) >> 8; + h->task = (current->pid) & 0xff; + h->function = function; + + if ((result = ncp_do_request(server, request_size + sizeof(*h))) < 0) { + return result; + } + + server->completion = reply->completion_code; + server->conn_status = reply->connection_state; + server->reply_size = result; + server->ncp_reply_size = result - sizeof(struct ncp_reply_header); + + return reply->completion_code; + +} + +int +ncp_connect(struct ncp_server *server) +{ + struct ncp_request_header *h + = (struct ncp_request_header *)(server->packet); + int result; + + h->type = NCP_ALLOC_SLOT_REQUEST; + + server->sequence = 0; + h->sequence = server->sequence; + h->conn_low = 0xff; + h->conn_high = 0xff; + h->task = (current->pid) & 0xff; + h->function = 0; + + if ((result = ncp_do_request(server, sizeof(*h))) < 0) { + return result; + } + + server->sequence = 0; + server->connection = h->conn_low + (h->conn_high * 256); + return 0; +} + +int +ncp_disconnect(struct ncp_server *server) +{ + struct ncp_request_header *h + = (struct ncp_request_header *)(server->packet); + + h->type = NCP_DEALLOC_SLOT_REQUEST; + + server->sequence += 1; + h->sequence = server->sequence; + h->conn_low = (server->connection) & 0xff; + h->conn_high = ((server->connection) & 0xff00) >> 8; + h->task = (current->pid) & 0xff; + h->function = 0; + + return ncp_do_request(server, sizeof(*h)); +} + +void +ncp_lock_server(struct ncp_server *server) +{ + while (server->lock) + sleep_on(&server->wait); + server->lock = 1; +} + +void +ncp_unlock_server(struct ncp_server *server) +{ + if (server->lock != 1) { + printk("ncp_unlock_server: was not locked!\n"); + } + + server->lock = 0; + wake_up(&server->wait); +} + diff --git a/startnet b/startnet new file mode 100755 index 0000000..b8baed8 --- /dev/null +++ b/startnet @@ -0,0 +1,3 @@ +#!/bin/sh +ipx_interface add -p eth0 EtherII abcd +ipx_route add 1234 abcd 00001b038b11