From e05e55a64a2986331e02443cb413b63475a9b428 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.6 --- .downloads/ncpfs-0.6.tgz | Bin 0 -> 58493 bytes BUGS | 14 +- Makefile | 71 +--- README | 32 +- ncp.h => kernel-1.3/linux/ncp.h | 34 ++ ncp_fs.h => kernel-1.3/linux/ncp_fs.h | 1 + ncp_fs_i.h => kernel-1.3/linux/ncp_fs_i.h | 0 ncp_fs_sb.h => kernel-1.3/linux/ncp_fs_sb.h | 0 ncp_mount.h => kernel-1.3/linux/ncp_mount.h | 0 kernel-1.3/src/Makefile | 67 ++++ dir.c => kernel-1.3/src/dir.c | 0 file.c => kernel-1.3/src/file.c | 0 inode.c => kernel-1.3/src/inode.c | 35 +- ioctl.c => kernel-1.3/src/ioctl.c | 14 +- ncplib.c => kernel-1.3/src/ncplib.c | 33 +- ncplib.h => kernel-1.3/src/ncplib.h | 6 + sock.c => kernel-1.3/src/sock.c | 0 linux | 1 - man/ipx_configure.8 | 42 +++ man/ncpmount.8 | 190 ++++++++++ man/ncpumount.8 | 28 ++ ncpfs-0.5.lsm => ncpfs-0.6.lsm | 8 +- util/Makefile | 46 +++ ipx.tar => util/ipx.tar | Bin util/ipx_configure.c | 125 +++++++ ipxutil.h => util/ipxutil.h | 21 +- ncplib_user.c => util/ncplib_user.c | 390 +++++++++++++++++++- ncplib_user.h => util/ncplib_user.h | 38 +- ncpmount.c => util/ncpmount.c | 264 ++++++++++--- ncptest.c => util/ncptest.c | 320 +++++++++++++--- util/ncpumount.c | 197 ++++++++++ nwcrypt.c => util/nwcrypt.c | 0 nwcrypt.h => util/nwcrypt.h | 0 start_ipx => util/start_ipx | 0 34 files changed, 1794 insertions(+), 183 deletions(-) create mode 100644 .downloads/ncpfs-0.6.tgz rename ncp.h => kernel-1.3/linux/ncp.h (85%) rename ncp_fs.h => kernel-1.3/linux/ncp_fs.h (98%) rename ncp_fs_i.h => kernel-1.3/linux/ncp_fs_i.h (100%) rename ncp_fs_sb.h => kernel-1.3/linux/ncp_fs_sb.h (100%) rename ncp_mount.h => kernel-1.3/linux/ncp_mount.h (100%) create mode 100644 kernel-1.3/src/Makefile rename dir.c => kernel-1.3/src/dir.c (100%) rename file.c => kernel-1.3/src/file.c (100%) rename inode.c => kernel-1.3/src/inode.c (93%) rename ioctl.c => kernel-1.3/src/ioctl.c (78%) rename ncplib.c => kernel-1.3/src/ncplib.c (94%) rename ncplib.h => kernel-1.3/src/ncplib.h (95%) rename sock.c => kernel-1.3/src/sock.c (100%) delete mode 120000 linux create mode 100644 man/ipx_configure.8 create mode 100644 man/ncpmount.8 create mode 100644 man/ncpumount.8 rename ncpfs-0.5.lsm => ncpfs-0.6.lsm (73%) create mode 100644 util/Makefile rename ipx.tar => util/ipx.tar (100%) create mode 100644 util/ipx_configure.c rename ipxutil.h => util/ipxutil.h (70%) rename ncplib_user.c => util/ncplib_user.c (74%) rename ncplib_user.h => util/ncplib_user.h (87%) rename ncpmount.c => util/ncpmount.c (77%) rename ncptest.c => util/ncptest.c (57%) create mode 100644 util/ncpumount.c rename nwcrypt.c => util/nwcrypt.c (100%) rename nwcrypt.h => util/nwcrypt.h (100%) rename start_ipx => util/start_ipx (100%) diff --git a/.downloads/ncpfs-0.6.tgz b/.downloads/ncpfs-0.6.tgz new file mode 100644 index 0000000000000000000000000000000000000000..dc42af02a2b4d7d2baa2e0850ca404a5f97b85bf GIT binary patch literal 58493 zcmV(!K;^$5iwFRsP`5Aw1MFK{bK6Fi_G`NKM|A1MQWe8Uie)GE6D`p;cO*(nQgQsW z1rkjXF)kc5AesMuzjIDE(BLJRnXSxLZACR1+ahrK^tpcL!hEq=+83+_&etmv*_4?}k^3^3iJ-;}A^+I30_&5C2wu(xvUu5Qwo-?I= z@&E28XZ^o_{5bvd&bT-~f9>>rNdFf?=SFXf zT0cj5rK>`xMe$WfmA)O_jb?+{=zcuaNv`zX*NY!=^{5eud}%+^#=cCMV%A5Ws(}ZwUx>GdSL0Mxmi>RF0AN6f3K6u zX!C7jAb}@vU6lSXefpeKYn?vh?{Rq3#X8DYM)&*uvz{KQ()p&xhkK%Ys&u@ zmv7GhivRzPpYOgS9ef-=>W{B+%aBbV z1dZV4DOrxuSz#+}{q5<$*w|#13x`$o6~BCrwj7+*5~7L=PN8dsjO!I=#wOnX}hdx&l<8#3gJtNgM3&ArfVsxwUn1OkYLWYiSn&3 zD1Q-{Sd~$xpVtLZuB&xXQlida2Y6D|R@4WUP8qYvYsajzbGbzUbAkBgTjhm)o6O6o z-0CAYOLAa^DDIzW{RLQR5#@Z@trlij^p0Z-4HiY8tmgCD(qO52PI~l_ zz^5g$DP0E=a0={^O{z_a^Krv_kUz|s-Lt(=rvN8lO|7FR(QsfLf$xaR?D3~ho$Zt> zHU-801T%O_2%jwlRVH?Q)@zQ!T^5E=j)g=PL^_6MN*374z}G6}UnDr1RKctCjI-2^ zHR5#`p9YT$;gX2tIycY4ub2BfF(OayYhFAzbmM|(S_T_ABUo5+L9bRoY2rE@h?6CmFN@*>J}}=Rf@nSe z#6fblUaW!doX8=owIR1t_*a$ijRn9i4G8f$$iI4(IQxTjBp{VTI22b}DX?70jWpS6 zU%ZUp-7#mpvSkPBHKrDr7d};i<$=xsCeiR=*DwhwX2ckfRVW5ARWTU+Eh&{(4f(J+ zWF?x5fa)1Kt<1*0)u$I{oR=h3cBKxpRHv6`kRSZqnMshDp4SP4N;0ziVQN-zbIy2{ zGqOv0y->rT`2(jRR%3V`b3I5cBt*I}5owIWC*rNU3Q;9z!G*-&eR0Urb_S*<(7eM zF4)@=sk0k0_!DW!07Y;6E@;9g$XuXiHXfFE7R3Z+^dhB#)63aiNZfDUV5nG3)0x&w z2RA6nI!@4e7t}T+UAgZYWWcXp*CF(b)*c*}4*ITL>Ch7KHZt>Oa&U z0%UwT8&2+~dN97x*Z1R_k@QSH+)uQtt9troG@VUG?;q(Se(>)8X7pin&HWW({=Cn~ z=@3+M=E%av4FtzL7v)zM83-9dZB|7j8p^$!G;;PzM$v|yb&*nXvC-B++YAVFZo)>2 zs+;^HGW0n4FudrC*iRmWzZ0Nm1HttaW0r<}P7A>uh8zF9{r64~h|L`DRHB}Ck7UZ4XRyf4z4z&jmL)MV>` zwC>|vdClmP@bnWxo6p0Z5F1lN9F^u$@QMn+ zI=9P`B_Bw-ODhK{^V1HL9-#~m2*{sh(*lJ4e&V!F5L3~$7g-@Spbf1 zjI68)ZWAp_r}(_nTrw_R zLzp)${#7KJO|qzq+NN?kVh+dhh}&=*a&#mZT!JxzV={b7wVvi8kGsSojgkyf0n%It$iA*YP2Mk4ue;RV-RZBTT7i!XV>D)zFd=&1mJBQmB``5V&ftV z-MN8B0sf+-`^{J89yGJ%T(*|u@Euc>Ta||m#z#yS*_1>!JW+FA%!J*ep}6OAsn}@< zG&!_)1ECA>`amj~y31XtXkJizXU{HS8UWmxF=8Zg3u}Tr{#J4GC^~D4fJIbWsURB6 z$Q^u!#*i%w7I!4~b|%L(EX7s!XzuFV|n3dFbAV-Y5DTcEz|7s5X|~3|9v{9f&9mA=vmbP4i~$a@mRIuXdj0esH$LhFWqA|NU!`4s^}M_seWd5L-tbDn|PA_ui?GdE>~*y0X) z*cx~r*%=*R9p1|z<0q=aD)NNT2Su}lc-TdI)W_mpb$ibOsjXq3U1=vEala$7J4aK~ zxvrWQ)Q-{FC<~;5w>k81jVU2UN|VHPl8V|*DjZlt!0D2f(C|f}d&@^5)s=uWfZKM6 z)Oi8|JQzj|@r_#IPW6gZTe@RB$gDYANIL|-;NnxRRhm^)#x#;-K(mHRQ<|3M*bFcB zf-wPc%Ki7M37Ae!oQwz<1^qt;sj;lYbyw0v11Nx*l|}%bd#9nLH5U%;QC!|A z^Uah}{`$t5^oFQ~kscIwP(T$W;9N@G3|~&!gHWK)LuHf}iA-}cp7O6&BqdMQ)jVz8;TNzm5b@^{RI2AQD|QJ_!n>|VUul|s+5CS z?#JJU>bqCi<(^7NNv0nDT%J*mD(18T(DcBey+vJdydgw(ApfeV_qYq@;Zie-ItHQ; z45(%1d`emxkU)!{1`8Uy6I_A)Hrnu`c>JZ5o-!xG44wf5^*tqXVp0H9TZHtS&wH;8 zw=<3-LCH1U>#%~I@)n?0WKo&G{%hYDYMX?VbOepPg9v*KBHO=N)c6xa?w)>%(nQEX zW+|d`#r-$A*VaTO4{F-7f&mf-gl*3Oa)+RD8fQrZTDC0&7`X|TuMjBvnbKozC%DjF zdz^(u_Do?{;z5qyRZd3_4E!Vk|6@8hE`Pbnzo3J~G3!cl5`jdDpk`%!>Jl?6NOJ$q z9O}rG38QfLEZn1UFvg8-r9H6Z_F+y)lp-a{w!Lw08&sqtCcN-iP}Gq%nUfx(VVvvKb}KW;lP97d zxM&EOmX2KG`f|GB)8$A%kJ;l3zsDgp)k;ZQwV<*~Ys-~MWNooXd_xQ_KN@csW3ms+1XwDUgB0Zh8o{&YqF#^b7(Tq+{ z<%YzuEL#aQu@uJ7+zl0SR4GNP?59bSi2`r;{!}SJs&&q|g?Vm{QVzC9&O^D;+?5`Z z{BjR11k?#)1s*`t8rLUdLkE8f)cI()kiqCUa-(Rr25wV3RO{c;1lJLoiOU@Vd)DXS zHiJShBUg4PCDr?I9zYeEW>SL*RL~ zkDpHQljcfdgR(4GQyQpTGNUY#kRm&C&jW0!#t*%K2}1Kws0pspvtnWRF*~c5GK5EH zK~J!mzxmCDq-@u38phalSWQ&kP%QQgpdO?_7yh@bQ;95T5}|lxxOXM0^W|MwZIVfD zs+}6lCe)Pl!)Z1hw{F3~wz)omVm5J_VXmmem$q$plJ2zcYBG!fZy{4@8(!k(Ky>28HV~53KyOdWCqu{ zT-GJet2zrOt_EwH>MwOuY`Wm;0sz}JM6u=(S6_8K$!(|zN-@fiAiI9=zsK|DF;FdV9k>;(KoFzrUFjdF?jaGew;WkdqxzT^3uE z3ES^^r66$b04MlU5c~x87QBX2Gy^s_Vu_I!7PO+lZTse5C?gqF;2v^CjyzmgxRp;b z25{xYJBN^TQn^M{nCJL0+JP@TrZY_7Tm%3MrH zg~!qab<}COZR(OE*nyrKG*i3$%>NRv54KNiyH-Jsewgakb)&uoo&=`#9qZ8)c)nf9Q7hW(fLlw{amO^J_%n_0OP)c}cHbULCRX0sIdr+2gtrl0 zJTG!-cv}vfyw|b_YPJYqiC=o>nyB@r8TaxM)emuzivnU~pO`mGdDTkWHQNk|&x)ZSy8>~;z_`olRa$!_b&~nu&*-C5u^KCoX z3hQ)<(JdU=+a;o?$fcL<@UdS8a72&dSo#iR1W@B(wlRCQnNd>ugAIXQy>`ihup1tFoMw(bBEQ%cNbIJJl*y9TisAisbI(}=`!Ko3}i zpTjDBgVBv2=S93d*iQbr&zmquSJ6pQe*2;{PYF+NMdh^A{3Kt*u*&KF$V$3k079|k zPWThoo6_Av8tgn|n8@%61$XdnH%ZBB>%Pro$#LBq^7RPf5V(_87w|%CU}7nUcv(2& zBbp6L8FRo8c?FWewIfEVqW&uN8%c%Gag9q;T=Y-Kck|l)P+uE!H)qM!`p&a^g@lC4 zV4==s+7k-iw5U~j3Maj`isO{+cQo4OdC)-Z0&2e`<|l-nsiO2-1D&J1g4;DcG6!|Q z9QN+s4m3mv+e0GQ8Iogc#>}0uR4wI_2LRl`u*^F z@Hidn*{7j?nB0Gy4DR%3>bGKV^oPlCsP8}M>raEp$6=42n+)lRkQVL81_bcbJ^y?7 z>ufll>4)LuZZw;L+WRkh@bCcQgZH;XeLMIJX%2tA9zM+U=TF12x+n6VN8o2V8_)}* zvHm<6%|_#oOpq3FC!>#_X8P0p?agq)+qysCbbe7k3?{SDaH_z^FQc38rA`J@uzjLG zk7l3lA7>5obh*L!i~ieaeACmz5fd8z`Y;(zaeoEcqdRas#D}Bt_3h&gF9N>@h4KAN zzFiGIXZI``|E#ADW#;-FZSLRxJr7AE&BG5uXu6Ac8*Dv~q@$ywqw_ea|IZnWCj}F* zjosIPZ8(|6x;yV0&^fM9eDRA^1u~+={}s^aNW9(sl@+spmFNLrYRzpMD*!1X?A(2M zgmdr`;As$800>S9F46w(+uhgoX3^0|KwV2^@}g)S*Y_Iu1@Zy)V66{$R$v$X&l#>F zB*;d@E-WVw4e?Me8SX*EwL^s?(06=a=A*u2_=k9LaMVO(gHBF&1wjmdU)HhoN&OJ! zi?-A5-rm^>Y%MH-H2|9C8Em%3AuWo7>+QTKm$h{`$sr!5rX;yJKOcT$$Ko)Q-TiN7M*Jp`6Vo^>JOu{<9;aJ=?{ zAso??CyKNw4J$XPt0#}l3$g+U#}3>?SXed*^(n*`JbCZNivx-jNHTqi7Y$~8SlgXW zB7f}ICjy^@+H42alPnRZVGydHjFk{k*~KZr?0D?Tho^`^(ki`y`DBV*3|)vqw~IqV zh;{A0Dok2_^wNYm@}&je2erv4+o7=LAg~D^y&L&*1uT3hnYBpem=t-iDQY9UBykF{ zck0cbIQb>b6f6N(Kaj#JcCEk~$=9UM$36%6>A`0o6kTKkq$=cdoKLE+^ahLxcmgaN zzEg42=Wqk*3OM*(Rw;4`3rIKo1+^77tG>B`_Nf6p`HAs)!M)Pm)G#8lR(o4pI9hAW z9B^bH##)r)bd}FEYvV{eJG6L5t+AKQEJel?Y)-B^Ifx#_sFl2}DP2hN(B72^e)3Kp}-DkE^IQ3pH{B6Y}bYby--Tu36`9%&6KwN7BRv5ELu4U;>^ zQQh7D>EAgV+6Bb{S!XuO;N))mcg{9jH8^)6wRIdlXRDWb{UZL{_3@v5v+B-K< zyB(m%8d`zfm(8OC;5H6^H96&N!T`DWVtfPW{J&(adi|svZG!mjkIoq){9X_HVdpm% zF`yQt?Ua@msVmsF*P1-K8WS(~WxraV>La)>Y2(Czw(f0-u zm@uy&mP$yq>GB*-e16H*HvZCTL=Kje@zDI3ig-Xjf*YuoSC;Nt{aDuSd!vG2+wGAP z;+4I}ZyN*FV8_R0G@OfsagoW*Qe>H;cn!bn4A3+_WDCtVQZwB}y@_EDjtJ74qB@AC zY+vzfa1(TH2AbN==#E$)C<1_X2y!Em;L6USq5n1T{0TmYk&Ynj#*j^TLm@R& zQ1K-!XdxPO_BVt^ym8xC-iQd^b5`_%o*NkwU@AzP%k0sRq=#A$#gUCGp%D{>Yo!*d%(y$iyeW&B6z>*J zx_67DF!RBG(!YoQr?;>lkwyM<{Qn`h!2bVgo0}V(C|}yxtUdUDx{uFy&UZqszCJpD z+)O?RK#fx52Tw@y1&983j`R2W$zlCK?1*Z4)sdnNxbN=>QEIGA!qG}NYOhc;D^h$Ju*zu=^V8mfqo^@_k-4S8(?BAo-%*cJLj3OKL?rIb!BY zdjbG5esM1LEC{R1fs<`l8g;ZTAME6v|(v@AEv+g35L}-VmyM zpWiz^E{M`w6I!ucZi^S9%oaV4gAaj&MEm^ff4e@g9%WG?-l{7~#2`LdE(cFUiA>9{Cws@cr*C$?&%+H8R)2WiDPSUCh)zS@DuV6%yewWQI4GBU)i|i{P$CpBAPf$v zv1}QA=JIVAWO-Zs7P%b59pC;4BNn={_`@s!pNoI`Ls9w>7WDhP>Z@S2(*I~?MNJhP z2X~didsiqEul5bgA%K-5S044>i$|OZy=jTXi=0NW3+2ixCbFK zjmN|R(st65bx2cQ+t5fyqi`;iVU4xBA0@IuDIoFLD1>G+H(*^!#R%;*Mz}MI!jsDS zJyrrE?p8rqE!WDQ@a7pP3jhV}?`U~M#Ow(YpeJRq+Z{{a49sm4#UsB1UR&OX6u?ZH zw0aUbS7n!LL677BbXt|+a)Iqb&rrdUWbBEmHV{uBi5;`z%boHlTOv^+0EBOnO2N_) zd7$+J%$}#6@3MWsV1kaG$T2Gc04Xr=V=g)f#*quRk!T$Mq7+*$S$XfA)c?Bv#biVp z)+1I=!>3!vybO$4fdxi&+?!mKU?`>Io_q6zPJH=xj1olYT{eUp%M*Y2sihkir4nkk zwzSsB4lI*QgHxmrvkWOu=*?J7kE1VJgam#c?TyIs1a~N}&eyKb_~xFY5UwivfOk?z zJs#aS?uCns(ireez;($FNM)VoV~AJBu_YmsZllg`lxSoGxG-iIv~h}A!I(m2HDPM; zN+mne5_HGH9+f5KXMJX=lu1ppwCnuFxrBm6V?o(^JX%JVmqJP42vF3LiXrJ~AM-8+Y*6Bkw{SyB8$JLyaF7W}D@y4bkyg zvl7P@1Wq5dOVE3-1k?u>0o^c?FW*;Epdme5Qn+8~-6BkzX{{fUf zY{VrXcK-xIbfttbzBihnx1@pCTPljiyHoUp+H)_&>;CzhqEr3x$8|_KfeLlaN&=4jANIGNZdgYs98bEaf1#b4%3}p~cQ3eLSF!`v z$s*?q=T)kbp#0q$!a1h zCD%i45E`gbpS^a&$TceU_O@BtKWa*Aj^jN#cVRzuSYxm2k3!TekNk1*9QELw!G0!a zE_LB|cy~osJtodWvV$wlHb$&!h6+q1)MW)n-0HX?kwQvCV`Z0_#7t=|>Y>11*q{+V zMC5`!tqZA+OvBhxj%rcqb>x-2Pvj5ZrE9-(gRi?mx;f~EWf5szYpevCfWBJjCH3%C zQ>a;H8&4GQYCAsZlt*ghP6j^gLdJVM0?`cAkv{F%X+|IY(`)xn9q*&^xZ)4c0+6-c zXpzt(KKAbLP6kMEo9pA4=2`ZqYT@G%_RS0jDT*r1KU56ABxyKqH&ewL)`G0EFGL92 zPCDebppnSsu)|K-rwy-mZYba!9mq=|3(i&00m+v;w1y$j7Qd}|?_PkXhN1l{EokLJ zXod|vzK{nGhYpMg0yVuT&4+s?YQDZh?8nt3ApG$}VUeS?@5@qnH^B-xhFqVjIQ zhJtdzC1@p|-CvQ7Cwg@Y1Nrcwxh(S2)V}m2e`8FRgDz6YRv2v_@TC%$1fO)Sq_X{` zw(!6qkwfr-!PVm{7_>5$-AQXcJVPe~W!U4&hznhDvitTwO03QIf0X2WBk_N#9RFo) z^CAAv{jEQ6#^1#H*EZKN77&Vm)*sgYe%D{!%*)U2SN;c!U0vh#U*Fi|{a@Q$U3-xK z-N&bV`UW|rw?}7(r{bBYt*=z8D|n~XlA1_lQ*J`p-M4i|XZ==^v5M4NH#t~Al%m4T zUk{Itn~kPZerdGwGDS4zyvOv?rSn1Xw}De`=pV=MgCsZC!KkBC_w=_A^JSyRwCRjC zW=PJ&$~eOex`<_k^@sX0cN*Q|^vy>c!W9=dG+Uw@Y6Vlb zcSX_!nXQ>=h63+PK6;In!7%_lj&oIXVv=I7Kk2z+X;e+uLsO`^g9{1mNPME)nK(4l z3@W*qku{~ZQ>5>P4_%0{+AS2SJ&m-Z#2^?Kt=&hmf>6a}h@3%^^Cb3^q`|Z>PP#TQ zMoK})d@Wu@BQm$^X~ko!TbRZ%LWuyWyR>c^{kKW`L1QEY7Md5bCYL5U*sW}PBF995 zK~M%_4l9}oV_=g(+6qLg3lvB%Dtz~~7b4|0)X*Kg^m;?MV|^MM)%Rrem*|_k>rQ&( zq9$A{rXca>NO}{P!1Z>7wuUf!U3MWR-DjiConr~7qWc}k7UHd`?=x&-q;6;w01ob3 zYzD@ChEm#0a2hZVj3E!4Xf@d~Ph$gx3koIWk?uqEWl9f{MR+LeGJ3G-H}|HzPpF*{ z=}W7YwBHqJ8l_}(jEHh4kiK=nnzmZa)qR23M0Z?Sj-lcd44Okgj4jp!y1eeU9 zaQMaO7(^2)l$^3eMztx)tdcg8ts2_N%NRtS(WP{I8j2Nf++G>A%YvgMy14PlORDS& z;~g;Spd8qBq(PRlsEnKrb=~$j^7+T_aMAop+ddLN(jy6J5Rz3Y$;_1d0d}U=nXGaR6n>LkiP|7N2-S?g%K47 z7uYO|Y2*;_)qugby<^O^%`g>HxFw$U17DsQ=Fp1LgH14utU*beR#9bg{!fBN6pyw2XgGSww66hv ztj$bbzu8wdDwNm4+wg{)<;f{se(qO=A+iu+*^ z9694kRHlhD^cb|q_!7v%GCo@6l3D8n^l9KkZei4t_1*BQXj-?Zg;JvJSV>$N$=z@< zx6=uwUpU(dZ(5ry62%uIb6(0AaMXDEjCu2=@LX(buHn}~q148g(pDn)`t3^|QZ#F9 z!eF**@pnZIiQo>H!Mw(uq->>)0WgS=8AfE615#1gJE=U6P9R9GPMqDy?|9nHgLbH- z90gfto7y&b&pJA-Hyt?;8i77pB$6u)1+6=f4D6P8!6wRz&%fd1Tryv#UI5AV4c!X_ zax|s`+Hz_>`#oBi@6kM>`r*%wlcU48Xw>=h?g<%65~qIFte=P#ad7mSE|*LW5RX5) zBXYkTauK{qa2e@|m)=(Od1JElu+CvHkTC!w>2z6eOBAPrg9N}eg7C%1Q`hT{(ZiPN z1y+=b8OyTY*nNF?)I4qMH67@z)e^0OoYQ8#E_M%^M|8pA=JU@A#fUeCUtDqzEm=>S ztg&J^MIsELI4Al&N5`Gvt6lu!=Fr7Wnjq#>t1CaQmf7)~bL_(Y6U~3N|LKj;btb;i0Sk3bDj3ovhujhM=#~bz zUfD4ZPWMR;MhTHGQ4vHsXy*q-7|hQ>?+S>@0qW*Kwv+G4Z~qObG^td}7uTJOa>px_ zWu0p>c0Vy369_4>$=>ousmHpyNNySUH1zi%+DPXGs z!%6Uu30$96q9knT+{18sC|ipi2Ucz8{cok;N+?tPz+Yo3#qWc`A3?YK;e#-;m3Fa_ z89zx9{UC^>?>LHXMxg~L3)n_<5h5C+HGZ*}V>+>y!P?N=5*mqf@u#s=9F-I$|@qqHN@reil6l3_d<5Yv1ZFH5AKGa#jyQ2X73!-lI+Po}Jz z&x}}SFomeCufl|rS3{nuG9A<;6dzrXZXq1M$tfRsD37)U3bqn7dbLs!BJE{xbjutC zP2U6tmK#y*RGuktT|VP>V>GRSQlv?7)In|MlH%c%o7j9jt4Jly@x@ilGP70%? zh4Cc2kE86Jbk3_uPWO{I5C;SI*phRs-!n|@M3$dUXz$;YX)~d9Qxhw%zyaT0fjd4O z6}ovU^z!U=lLykB3}{7MY=NGqh&x~rraypna{MkYJ{-*4#qQbZo1+s)>za$_7)_{* zmJk2kmP+64p#S3G82?J=KmA*(lsC$~uz%n7Ut)88Gt&QBMg5=JYHju5{J)RSOH}V( zbxz?>@U|j_EPn6*nI8>xIv|KsCr=NZ62`;CjM{pclnO|%6z`4BzQ+$kRB+z9J_#FP+~+r2i_|=3YFr9^o-VphMm4rb$AM>CBtQSR{^OO1HYXam zrj-&^vlMJ$XOE*Hlu*pn28drDAHd3WoQ(Z1&7G^|)vuujIJf@a`ev=Vy21AUl?VO5 z`=kHH`m@pgpQQh}wz*nG`oCIPtv;;(pSb>zT)w=Y^S`!Q-KZ)1Z+ZU*gxP#J|L^0o zvh0XuI`3QEuzU$W=y%+O*^ldKFD6uY#c{s#yJ(~#S_h58vtL?=d&jLe&UbA7V<$pU z>B-2DN+% z=rK=Wbs?OqW#B5^_mvM*!T7xG#MbNh;MWu+ScOjqJW^Zn2V!)FER;Ske|fwa(iQOKMsq>RAjIX2OOjVXZX!7^V`cZhi7kJ)=yeTudvwJ+j>*vH`c5P z;k#iAWmD#CjDxY;Yn}Ik_EoqYC8O?nGIcl^T~HjOWY!=Uw{E;~Yf2IBs^gDZ9tR9D z^0ZrmWWL<;_)rzLV1~G1$ira4G{ZbHy-dbLJ*~#p2v23a*?R7%eQ8d{GqWYq{W8!) zKa|;6qv%j;5E!7ct!9fgs*=R@?d~rvlmL=KOm)q1R+goXmMlb0Jk_=YCSmMxFnLxO zL5l?75LAoq$Xi!PIjG-e?kG&_d4r4b zrGY{$-xPInIy z#j2H9vHCBk)NiYC`06MsSBsZJJOz9*+ggj4;eZ8Zk!SHDdw|jS8e$ZJJ6WvI(YwRO z;cJ>#nmUQ$iE^;5>U*b0CuWyxiBc4A!JN@&i6R&q!YJ_S)vGwzM{i{`4K+_t4%(p) zltsir1Ns$yR3jD5(8Lp9&0%)xt)t^Qjva@c$U8kbJ46Jb>{=olnM<0C^lX~hI=B5S z0no?`Np;!=X_tasv<{CBX<>22QnDq?37};*cbo#2%-6%h)k(?M&o*vzvWb-8}HSY>qzonmmaMJG&E-p38P z=!8+_VAR+bw^%9;wU7q%+?CUSo&#*E?0czj&+NJ9hT~nL4aweTRz|+n|LCO-dfv*Z zgPy&sxbp4B><=zIG#Kcd5H);j-mGWuDt}ZTj1MRM+ic1GAl&yyx2y~ofUk_edEKt zm679x(^kgqMync8BArb3KSj)3YyYUpEDs+vs7Yn;5c?L>noDDB_a)O;N4~Wlm03$I zbJ}=olzEm?<_K8TeO<)jXZJOmN_~l38Z&DY!p?M|we&((Z_i?7)EpTDv$@%*j5)Ac zYMF-B*IK;H!EO^c^*sz=#vP_Hvrw*a_@<6L#=c~H;U3ofJIj|ViGFu~X}mp?7Zh%= zCK=j@&0zd!b%KyMj)>LxB5vXKhu;?+yLWSP-sbT8qGR_#8E$8Xvx$Uu^7M^y+ljJ zTU}NX&nUMT7PK(&TE7S9B(uGVUUnLTvkbWAfNVJOK8o8`9(bR|x30WLW{pj0<9w^i zr}0;#MJgZBlS7=!%&dIsjxM}$lXl2rl>q~}a{ZInM%rj&_{_?G3(o7f^2w@mfDM&z ziTi+IQ28|ec(_RA;~?}?R(Zb4du+4xDj_VGIbpAWm>gzs$x|w`a2k46_1{-l7oTmc z{3b>_=c~-Pb<2&xm2qPvmwV323vUcPVW&V33=PWn>*@t`W@X-zC+Ik!vkPa(`|5h_ z!y*JyQ%C#sixbV?6>``Ijs14?KYt^!>q!fPw&qZ5Yh~fPtjb!mv+9SZC%=;P6Zv8* z3!hk9C-vs(E~|YYcbzP-dvQZZv)1%Hy)+XgT>%DpeGygMPoS$uBSCIqYcLV zSHY;yL?+Jf%s(F|6l&e;yiadpE;wGggMl|nkh;ajf-;FI4b9!g8&Y0*UtPOh8^`YW z^8K^QhsD>0RRKtFE@1PS`d_%y9&9fD8v3844|pH;ADgwcRi*!3Stb4Nwe|G}`;Wi0 z{^zS^x(XqLHOGLUfuSQ?k7; zQB;dUw@Xbe7ZNpJ*H6iDrN}^RK3i#I!U zZ0|IxDl3`|LWc#Cqm#9^KS1x0wD0=KC-+TssYzFI7F#&qC=E};=x#kw0|}V#Gw5c_#GguSoVqwB(G_(6_@r@o`u995t`*U!J1m#W1-?~Y$#hW^-+f1U1s>JR`dSSo-gTs9v;W0? z&+Ht@nmVr8WO0Q8*|c$1B$g{7Igt`n5RO9iDB-%Dv+YDX=87@~d9x9*>@y}2H&WP6 zg8~Tt+(4kdZW0EfWj0KiDRG~*mbju z+pXhJ>Byr^X=i_Qd%yxjB{YRWJAItFl;ZRlvEVRiZW4g#VX=N_5Z2JMW}XECs2Ez5 z!U4=To1^krwg#1mp5qTnR#leG`pdF`G^`;Gj`2pJAoF}N{Nukxg;nGXavA>&i*aLI zi%z%u9fL=sd4(;Nj=|Ig!y>y0Ct7!0EVD(5+i6j1KCMW2UUbCl50mGSdyRAEDjMAb za-6Z?k>SQnL?zme_9TATqWV*7q|8DHBe54m^4Hl7@dy;N9Rmd5o(pIUgS0?#;EUUr zt!wY7XVY{-49XP$9@Y~ZdW~-E<=Kg zJsLtG&D-;lnkFs6tKx&e=+Bh@;Dz|>%YUkCRgV8$UE5gO+{E}V>y`C~_|Nw*|H1X1 zE&+=%u5cV(IU z)(VO%i1HN`kWYGP4>Q4N9MUMH-XJawdN%2XvOXT;B9HE%y*`RO(351rF&K#$>l#f_ z>I~Aq(Ory=0ra)LZL<+D9DG7kk`~_!x8~tKWc0zH3l~j_DcF{-tQmG=>=}GFbhtgM z3@TI<#WE^oY@5%Rk!}wO7o@O_?>6$!1O1QDf0UMfb^2f1sImWt%35uG6MaEc*K4bl z2l{{i^dHyz3!eZaHE+I*$b6Kvn8@Cg^d}Ag+YiI5-Lr$!*2}Y3B)zRxYHOCXUNXP` zaw3ADI;M+|=x-xUJM`QU(026LD;>$Rqbe%mqoXP(m&@fOW%SUYKheQS$X>lEvgx(Q z0ga-fV;#R2z3k&`SQA)y;x%prM2Rfp@J25LKXQY z_9JHoR1YhyA6q6WYGwB)l?>`{p`si`*@{NWniJbM(QI~ql_uqxU&_T;napw$!1Hs_Wx|2|EraU z^Z&ljf4SawrUF23&*@qKDl7ZQPv`;+KV>}J&wS6f-fyYnpX(>h#?hgu+L@@782Kzo zbD{KWH9rYRt%?R6a>DHb>!uFV5YGdAH6Sl4^0@seIsGGVJi2MMah7QQcLyC( zh6uVC&O&!U#Zsyhni?k@F9|M%PP(9^3o{9QEwsj_>yTIuKKgKmanF8`w|?{Y|8Ug4 zYag)l`~O_8$oSuDYY*|?|Gf2o>t1=dv+u?Kr>Lz}Hl+UN`sU{PD(2Q|5Ay$e`F!Vm zC)9GQ5TE=k7>lb!A^%X{zwCVHIE}--gR}j56E3`>)L5B>BSIXdx5>;{sZlOF&fcqo z-Pc&K^bQ3prFR?!u|#)0;`c=9s3uDLZ;$rR4(g)R4f_7L)E&VoTvF1E-<;g{dDUqF zetxHEM`>Sc#O&0}UrK8$wWwC<+S{Y79dXfaLjmJX2H-opO_}S4j&vHfBmC888;(bCpK2VUJmx5ml1X(b9JVpn%Pnfw$+!* z6yUq8^i@(<=1`8u>3BmP?C~)adn?SPT81`Xh%yub7Qk`ZJHQQ9Wkfgbi7%~N!AhCKjgIu0-!6F^pDGMXoQsByaUeLlQIKk&or zP5~458z(yL+yyoZBS(Ipm&FSON432}iBP;iuGe4g?*0Ajm@=r3N|ln|9KEgYtXu+r zywdj^2jydv;g)c6c^-rT>$YfjM7g{zkmj^Mc120(F2o<=;-CIdl&XN<(b4zfdy`+Nip0G!LWJ89*%MGqFKC#>SzqThzQot-G$Ex?D4AC9+#UJbbnz`75{ z+y}yLK$kv@->kPF9@LLC5Mv^YLj8}{dg`W76%Tf=A}1IJ5*Obav~O7c>32!q=zRjP zLn!Nay?=I2 zrsYI>*@>Kfw!FyBal`&f=v`n?rum>YK>C%e?IB5dL4-t3MmasbBuZ0b&{Hh zJUK-y)k84H>Kk+y^`<*hZh?FLY^6Zl*W(s?RVq6C5XXZ-MbwtmE06cl3F2c)Yuz3$ z6qZ_uumI4Np*j)uRTN2#SSE970}8uc0tod>@+tkLsWmLK(L|!XxPpYk?~Q{Fr=~UE z2Ig)o(Ez!ll&R_k0WcV|Dz`|~X01F+e?}A3G&}pU)suu7~WVbL66kI?UpbxY7KwUII|~o;9VzM!NjN) z{1t~QG9`iLT9Y9vIUC4_&abX7k&fg!0f}i2m_Pn0UhIe`?i2C&F_#z5;rBnE6i|-J z38fv8#}Xy+WcLX=OvqvK)2Hz5bB0X|0)mgT3j?wn1LXgifILkFL?j?FXlI%MgORjj zXmlN(9UK&)FI4OeJr5nr$-s_uUYJKgkxJn=D0B}0VgaK*21E9YEQ8udRnZbJR!>Hv zOm<8cSFXxh_I43!B1K~iJ>~V?VZ56GT!@EW+jo29Dv3kBXO|fPROoleD#oR{;mx3} zo@N&{9HH6IJx(r55t~-yi|i5UEDVOBo)xBSJT1~^u*zckN^`<1O26ZSM0%jBFnNfo z@bAPD-Sn+mMCo|$DW85vi{7_$>36)z{uNA&fhWA%brrr~$Lb&grGeKCP${C7mUW(j z39w>=wwgC19hRc+hoO4+#~hK4U!iQ3+!lNt)(Po`G_5Pu8%?;|l1UpWqq4Z7h?*4? zUv)C@f%W5#LgBVYK}d=sIvThY>KwR1-2Rv^Yf#K{)X&g*l^EmO9X|=h_2rH5@e3R9 za)9!V_lb0P%(wy4z808%lm_ycS%0Q1?VxFvYzCBdF>sH-wy2owTYS>nQmJBU3C|m} zyr4V16sonfcxF9hXJ9bfDclb;T(HHY2b&!tm1!sA*W+g$`DDv12Ve)!lF1eEVo-7} zi(S;PrzoV@d@7cVo9NmN$24o9Lbf?D{X%nIwheP~+Fo$Un7J*oiz>aK%LughL+7`_ zQW5o&#|0w?;mD|O_Ja-dIz^3C@B^DeZr&^=3lLOz7C+*3`{NJssM(_37C}$!86Vry zE;6NiRP`f*5nxP8jH|472J}zIw%*MzclTe>;mv((4C7@g2q#A@0zi*AA9SrL=9MkL zojctO9eav!HlKnM1>f;7o-HAxa-UO!M3N+oO|TS%(j)fEsZ$BrNGlS? zoC%d}@2r+B26r`L>=GaV47D^EutezvLb4brggO!o8~Jx6UQB8YmG06#=%e-kM3dL8I(B*NSsdVWDY6=obVtm2V z0j48e;voePCiaoGh?H++kP+U$3E<79g<3jAvZI?Z%(Nie`xFRGd5IRi&}o?hVmfdt zSE5@6tavX{k z_E1rvRxm4GyQHgzI_IX!SaQo6)Lea)w{#w?8WnA_6ae0NYX6SQDEhG~dpgwv}Aic2Wl)&MYhk4^gO2Wnx8ee-p|gLKI71x*~Z_Gh3n;Gv8 zW}7DYsfhrxb2&Jxe2Kv`o16(LAOG{fNf~NUfc;g^U~&ma0;yT@c>O5nv7#b=!CEcr zUfu=B+N8aWQV1ykhUUbQWAOZ9NmBguSp^O=K-;>B$3H*w5Ml&Bh8~Iqm0Mae*yb#& z9A^2ZFU03>;+@s`*VK1*?;qP4)Z*(<357%Gs>yeI0K0@K!X?A!9m*A8cLS6rx>y)Z zKA*S=(RJY*>BNLp$+eNUUCAsb_jqE@83ajAAP_r#f`-50jM99haW*Yv#VQw;k@flub9!hSQGu(p-TV4*BH8 z)V?#u<>YpIvweq=teg2_ON^DgzUpiF)H}9y$1{ORb1!BL&RR#iEU~=ah_{F;Z(UvE zV$+NZsKFJRZUtrSbVip&!HY zN)bOveB)AlgnbKT^-6#rBY-?#TEO>+!Ov)>%Y4|3hV;@~W04nP(~YLR+)>fOTyEQh zE!Vg3G~RXg6*M`>h`S(2@xhR8yqdP@>j5+!CvX`88T)xEbB=&4lE;~yxVxS0%nIa* z#wG{xBDnZMzI>5GSym;>D&Cc=wJcr2M5+{KebqCQU&odlDe(|JSd)FFcP)(dfGKCY z%T6#k@7+*2uVYRg$zOraMU_xCA9>wqu&qD25$Af7?zgh#49~e1&QWK~`*G zfP6y&3Y8-+4QtajkIAl6A1Ku^?ia;W>YXJNqZt5z3rQ|Pd+{a|rs*$~N-2Yf0bBmA zkoD1f!shR0c!o8wamwLJ)WUj|+RoBhUiPUlZ(lj71=Bn_yhIE0&ZOTbDNORBf>pPa zYZZBcRekr%hM6|$3S?AAD1P`9sTla?a+ttzs6P1s1bAQu(cBR(Ddb>!EFks{c@o-| zs&kg_*$UimaIcs_F$=LY44+8xnX;=#!6^$?%8aTsv9f|4u8}I?K9MoIY$cP@izsMq z^if~BPj0Fm}+>+M8Vod5Ip2)dQm}n}uc$J&tp#JLg(W6K9S(AKGyMqoD zX{O)lXIt7?8}*!0fmP|+sN|`h?A(Dh@8#6j8!|?j^t`+^Xz8LSek)8vQDy?K{a#OU zX2zQxsp6e`cr6au`vzPmST{0y2RsKXaw11mF+LCKj96FiQtJeAi#UU_O@{^eQY#SE z{h%LwV&J#4uK&sFl-VWZUE2l31o6`}(3lg?I#5`>mYDIl+-vsDP^lzL-z%hrI)x9* zyxesHtqz3eHg^{>6H&1u-AH(vzlMVy?-SLp6kZ(9(RQK=4?MB4^~{ z1G2YYhH3}o)tadadx#OQAc z4_gv=xPw{B8J4g@hryb@h6d~@JbntUW_==}0V@y>*LWYEOf7nu;{2Y@Q zNQJMW2P4UVDj^caVvZ>o@n*GUJ2siJd3+~!4NX%gO|A>-sgJ@jy`85j)u&bQ(bnHOJpex1!_=y_!8dzj0W_r+6GdHz8gW+duseLB{6f#prVB+wIS z3`RxBa_k-x8CIs>K<1^Nv>aTg+QXZ?&WoQ}xtvw)gHq*;%msCRAw@c*=3Kwmqu1FV z!WBa}6lXe(n{rXa8qa+Gm!8|~T_@Fj)5Wl|#!t)H)EhP}I6C zNqq-XX$A@$?X}(#i*r)nJ+1$teqyZfs8FRSc2DR9^$+;_?xb;Ak1{2m8Ttn^ac#QU zO0?CPv!1#OOJcQXI(h!olh0eV5mlbe`QM!&KVpM44nP=JYP`zn{B z!1G1s@yZm=8;ycdMvMk&NuITZ?h02I+;lYTyWDg4xL=AS=PruMr!IVqJt3#s+!eGk z=&~)^36Xk?%B*b9Y1?IO63g@|iEa`nQuIa_cc2i*vs6sF1r`t}cZUU*j4I`_$js)W z>|0B;S})I&=J2Igy6KMjrP$o6DX9=U9_U zUfM~+E1EJz{&FUZvk+$9#w*9yLQRI{0XH9T^KN{-+=;6f!}MJRNoc9=-piiYy*B5m zR^H17%gar>KQJ{9s#ZClU8Ht8FM+TwSB#Hk;AoIJ;jm>B?eABBLdm_#!>VqPeQ>YQ$&vK4NhAl}N)dXw{nV`lQ(MzI4{CbN4 z64>VqN*a}uWAdOfcCWla6!MR3Qu|>i2us~ixjsDL*V>MIBjwtm3LOP~pfLc3!>MAJ z9De{icTnni?oix7ozU{7;!~KZo7UhxBFw+w8oOF8uK!s4u~Mwo;J@{1vHGl9tW|5p z+S+Qdwz*ndt*jMSSJ#Ra{-5}~O%II#)Zg5}EARZRJK7zN#9ILEZ<7K1*Au&w3(@q3 z;t1&YVenDxd*u7biAB!FPzC=)JGJm8Y>oeEJ4%_1+cngB=;c*Z15@cjsq_2jc1zYT zkN?@~`vX+Bx%R^G@=)nor2}S6m9Y_`aw^=Fh+GukXD@=7v$%*1k#}%JY6Gt6;{a`S@EaH+E zp)U-zkfS6Nz2*sq7cazmK|B`K^+E|Hx>y!p&1Bw>NIRe=R-j3EELN+9<=WGY%5t7l zR0|ucg{L~D3WZiTDwU_tHr6VYWj2|ST~ZUJ>Qlx!g6G52Ja)XYR(Ohkq3U`mPf1vs znH}-?F*m~_crI$P8#M6_r=IGMiE?AEL2;gLa@@%K2GUQ zG`tmB_7W^h&BUo2sJy<->3Phq%EsQmVhuL#2k-+v@b{de>KOg`t^_vHV% zTH9RTl>VP9t2K-TS_KT-eDME#kN%%|#qTio^ZZ_+GyN=62CKAj4A43KNN2l+&h#+d zZ4bu1MO;tM?MB*g3Z3nwI`M2pUMc5uL+6>^1(^xbLVDm|NwOSoiLd9h!fCqUNgJ=& ztzd3*1`WX$-rM*FJ4Nj?{w9&~!rV2K1Yb1{UmfKwrElxm*XW(?>q;;5o268Zl7>y3 z9KZZnnbAXbifYUlLtu-(`>D*c${s!!l0Dd+UDfKdhvoQLX;z)yu=?O_UM&U za~27!l#W5^MdBqY5=M#J!k$Y)p38tcC!yBA=L7WU@Zi_D#conX>vNWV0vNWnv-s%av z=6AO0O>wr0zhs2mBHR?%<*{&k6vdc+0g43OZfh*a(%jT)qC+CntX9D&?T3}=iYZoM zSDS_cNg-j&#zm4g{6_Q2+AYQWH1=kT4WelAr2ab9(PdT-amR+G!Ml>;O4kDH-Xm&_UAv&e3iiEVWr&h6T9BRqq=se+zA54VK&fCx zhoQtLy{+ZLXBHMqe27+1gg1UZa5BBXYgxk|LN3C&;9IiGP(z zCuzodE+ooMEh7f0lw~#kTaM3*Z<(wOO4y?>2tzwO>Pc-7OJr!wgC;)+v zDB=d$Q269)O?aQ&HhJttJ0Yp3%!8BelHyaGLmWxVh@+UbbSdSvTS7A#)>w{f4mVhF zr+mh4ge5U{)T2gIZU{NAg=qh|QgE?%c5+fbJZ+se-qy)mip&RQU@UCo=uE2l$sf;4 zG~Q-sus59L$0TK<2rok~s47V`zW(7+Bu@f+Q8OBMqy4!C@ki zPM#+jhk`1UJQrln!bbyR%3^0jtd4m&s4RIp$c+_09we~ZMgy0VH~Q^Q2z^AzKKYDD zOiSi5k!}?rkA#%j)u%#gUVOpQ%gNy71= zuVNfss=V#73| zshDduJ&1~tHam8T75^k9D9VgTDb_-NjQ}ah_$aU(^U41?_H6rpZ{K0Wwq2?_^5zGsXY&i}}-HHqML-*-sbpkNm;KBI5tdhlF0x zz6uw5VMqy8X$F#%iWzhpVTFHfX&ptPF@mX47#f_K;bfd@#~%e&)J5rfIcHgHHk_ai zlPpRW^9N)9CiH6Y2?{?`3J}my0s1MY zGOdSLezX|e1b#Q%M(8#oK5Uzw@$9+S5_^EKo)jFSjY<)Hs|SPL4V*RZM<9|EgvyJ2 zqc~dWOW6uP3F+-Ha(x;aUNfj+vXphlE}KbW3+S5#owql}+6lhy0<|JLp8n+yZph<{ zrC_BMLNc+Pc}LrU%+X{d#%PrrIgf!IIr91*j5qYrWrtwE-g!I;(v;V7@CICm0Jek2 z*bVxGbL2?oJm}o;RVlA*ehPB+?KTXrA|uZ$liHfHxF&@&vM}xjBiXng#tn&0FXv_P zH#qmSm$`5<@-Xhgi04gQqubnTuh&BxWwQ;S#pvG#V~EN1FwK&)0|?0PiF?2z5vLY& znv-&ymOepdv_L~tw)@iOXwHs$F{#1fAO$6U!_AtUltArxd75^S)>;@l-uZOjTq=eXDUKQ!HdK7t8i|sO_VT z^2$4s?J)A_swl|Wl=p-~^o>X87E*3#6+DC;k0OPWyX@ViYefT))JQoHxc=#TvS5(!#W<#^@kqSG1A4(1df%m3HQ zj_GX~v6FzEFWbU)CZ}^Jz)A}3)-^FW$WEzrF}>17YNhkuRcJc?OB5J&ypJO8E}LYg zi8dZZE;X$Qf0XXp$*7Na2(gn%xbpFL(P}}gB**a7oJ+!s~vx=4#+JTI0|E7D`Z=Oka9b>U2~)!AxhwqDmDPAw8DSyP=PW!uQBcY>}ury?`? zM>~}nA$;Cpte(LWyoKnC2HaN1?7+|U2jyu2dCZepPnhY4n;=f-)l7}1gtwnGkBPA} zGYwPIgQHG$U}J!d7nLYza1@?37vtS;6#_J3u3{e`QSF}Rj?7qND#}Pw$PnH6vc(oC zh~W6WBljAKfy+XO5HMpfjypM|K+IH%Yy;fbEym0)2-V;m0ed4)2rSu=Bw-j&zwQJV zD&q>yqZTXz`B4qefs1N&(37%hT&xU<0gCj&djf&yj(HWTa^JPw6&il^3%E8jcm zPW7>F=>>`b@D}=rH}u=1v%}LCY`tdV=r93;xOPVa=y!`lTnknC2MT6A5G>BK9n#18 zd7jqIPg%QgDd=^)(UO2G$H2RWZycpXjv#8C7NCQHTnE zTU&Fkm=6k~`LwXjX^cmFh+v$mlAQ?ZkTcQNicVuKv6cg>f_m`bs7 zY%iXUTw!KY4gMV0vwm4FqgXIx8JLX2$ycgawbr>m4yo?8%Imuqz{AKp!r5{Cr1f_9 zbz@KGGg2{0<&=n*LUD2APE+o7+$D5BFsZjJ5bYx@V3KiFg3wRP#wuoA*B?hYu@LaG z%+SSMVVBVj7`h}ejS=m39Rp$TS(S@ivFocN;eSf<<4B=`c#4yEznuNSe_zYLN3gg5 z5b*sKaK={a>NYLx)^+kijguSqsCAwW^Ok$OyZ86{sS2&0K5gc9j@5}pS7#y`YsHEe zof*rG_a0YKR4j7)paZN=2JsyuC0iK4LP-Z=9;$RS046+^_$k`m%i=8xekt-~Fc_en zaxf4wQVCZz3a=QRirVhDec8Ike*uA2H-?T(7UkKCcwDS3R1?YZYquA($Io<#OA#c| zxO1lK4>?F9S9(iHt(ojsh6JY;wHL0$xsx^YR}^o=nCYT8ha}{+Fq&Uha zveMybRLcEp+(&g`!DYbEFZ4hlADWCHd8q^K_@P{~n76Kh2l+nA44A6ogj!+y_Uo^9 z&kjyoFV9{@G0v&sXbc8Hwj$!qJ)DS~;J~D6o!U+5127i~DN>|;DD9!rfTL$XJ@#I3 z;SXA<4WUOI(QQoD4kyf`r95=Q@H!ZEZZ%TsGZxe^ZGh%u)Z3$dxcu(5&YJbs9$pM0 z?+@WQQpKq5)-)=@Qj>5o0&TUeH31FEr=*SuEH+69 z8Be%K+6}~8@oiZkT@-y%s3QFUg6bcWVC;rv(X`&h?cVqe&>bsX1EhK0*c1DCzO+b_zm< zi!%1WT2WELF-S?@4X-RerTm70F9vqFZh8rdeAtsHmLX{cSIX*uI%H?Ak*q?6@{n!h zu~6xS@3vU#@mTDho}RSM8vFF5B&}4UQusvnDEoa3jWiAAby(=Pv$}eHo31Xnt4zD@ zc{)Z*;ez%vV|1)8gxu!;<>h@cy8Q7sydvsvul2X1Le?ax6_%?GC>H|PNs-jiSXog@ z9q)~yPX~SvmFF%0!n*Z8;D)>ZRyt!PCHp_3pZoAn#FASsYjj8SLKgY|rq}gk_%Sn8 z2LwYuS<}FNI}>bJmBy=IHQ4g!JYe@zz>bGKi_+!>c{dJnt2L=^sE~6hBRJS?p0>#P zxOtoDriLSEQ=+b^8$#5xF`p)3P-1L;Eg(u_Th?pBlJaV$&Fm8|lR8TSpi>@82hx@$ zi=cN&tkspFu1kt|_)`*#fVsm$Xh-Hs7nh`&tgXgp5* zI7D|8sQK6Nx?;cn^6Yhs9(r~U4vzL5r4!83I`jsqQY7DH`hf?5=e=1a8P2_0JGOh_)rdhvNNElfFX7Naomv^{<4&724mEH&ih zH6T7ITtZDuLW&rzo^=bn0R9)a_DFF!|JazNGlc$5kAeu9*Z#PD znTPI(>!J9lboL(YA+0;9|Ib;ydAg;zs7E2mbqjG8iLXlR94f(ZL08DZx^N!mf!)As z0XcO-ZD{nWP=JwXg*thclHeB#X~xxh^vsb-5dfNnbi@2(S+!_e4p@^Dbt%P-u}(GD z$k9A0wcMQy_z~=dsI9N7n4PG-ph^(S`aRsqF;L z#YM1*$yW?d)X~$~2tl(bCt2p{7nL|t77~{#MtOV$8r}d$0pxk?>1by9namsZZldQt zQC;^S97RcIzf(I{B6hmoz0Lw7J~3BQ%@b0y~_QqvsO zrt*K%{<0L+FA4gqs53%hT4&JQc2x8Bd~lL^h86ZK#%$J zLQiGy4*J?`=sUB(?<@j;=Z*)kHhTbYeoh0QU5T+Cc^FJaZBJ>D|I?diOfm@Soa2E* zTlS&O_pFggk4v)$r}>>(V`L`u_G8q{{GT(_Av=7Sdz|`S#t)NFt%p995s(?fRng4( zPLWQ-2o~Ub1G)#Q*v5jI&vB1Xna^~aj7eB!^) zpNrj|%G%EWxHT1B2{oe@^%(3STM8l(ot14=3Z!Q|9Cx%pT3D zNsg~-j*qcT+N&VXjJ)}*(FNUZ=#5Pu;K@yuvIN+?TmZNeaU&nB_jDYQ0n4m|GaZ)b zkc^K?R+Ck%o^ke#o@bpJSv{-sVS%1Iv93F}?Ypx`=UQ_iRjV+wbGoD{+RLCU11n1& zB>v^tf~3W<{&EZyFwe?~+xjWidS#1WT31&siNiGgxP&wUg%J{Zv4gGv2jk!}%*U}n zZ^?o_3#KdLrTWE(PFk$0LyxSjYkz#%;^g998Vq|X$zrMyozFN z&0G$Pqznp4^^&wTsMWq~4R`>h7vo^;_99iE#FmI?V^oJ8Vq}#mb=J%}!wLLF!O!fz zo|*N4ZRS~DBq0pha}J}&#-)S4S+P10OD5CiJ=-_boZM}6+KTKy@xC@HYX>u<7 zu%+U5&VzjHNRv=cv%l%%%%G&Jm4d|q-Ifr{-u~&qT(h?P)Q!Gw?sLtJs;zE1x&6Ak zT~pjFoN!8>!zV#>Ld4so0nmR%IZ#+;M8vGHY-JNLAuT~SySqcwEL zm(y7QibjiK8o*;jVj~SjGC-lu-B98xs-CuVk}SvzfE{V>I5DZRZF6dE8mAgnq{Qke zHi?PWtIS~Q;;7DI_zzBB=1<)&R~rO^CyVRJ00S@_Gb-`)o~vZL9}G}aSL@QGZB3e` zC~g_Yq|8RcR4Ej?4|Ch&$oD~BN(|I9NzrhSKgInY6+xSIM}8 zSPHDBdO^g6y0x#9R`t67izunGH1#sDcT(3aqM18MBmuDrRN5z8@Xu@~5>`zr3K!deK_)>1{H?L&_L`tA+uZk?k$I4_1+ zQ(KKMuiAAS9o*Q=+cD;nL4eY997pQY}0-So_dgFJQHi$JcF&zNt_i4mO|Kk z{B$THT*iTtWtdA*9<@ckL4Q@3JA&c|!_PzF<+g3ODRVyv_x;f!ZK}_=;4U`OQEx%a zTcEfbijI5(&yY^(gAn(*+v{0H?Ik*Cylw3s9GIg^!#>4H7~DfuKM8BhTNv?f#Xu!b z%Zz&fUIQcrRnRFcviN}HVI;-5vNSuC7>_cJB%{>l+lU@!sC!SB_T;h;4~p2K%J6X! z(*n2Z#_d;@Nk9C{x$n)dzCSD?nz19ERVr$udhQ4#Dv|d;xVOsvk+nhelx1Ba*d|2s zQ_IRCWa@6>hzXi1>5atcnjqmGV>G?L&9xBbcD!CZ{@=`t$#fRVxJ%yw>OK2wpx#tm zu@5kfD)!Mun4*Z#H-#$N5)XrQ^NmM_?>-F<-{G6z{=1`-eUX%?eb5f*u0#W%vo&>XWNG-sGz`9RKFs6^xs(Rd1jb!v3Q zc`!yp(Gi(#5<;LED964SUwQ*lX=6s3{IR0@Y7QkLTu1tM_BthCh1ne{8Ejs=gRye^ z$+i<@c&WD2l(3pJME;BHE3*c@RT#aZIh~o$Ya%lvf2r{%U^;<;@3h=@yT!3i)4?)) z`^_yPo3+QQUvH1cXD0hOQ10;sm@K4C|_SA5%qCkt!-+6eHsZyRq8EzN;02&RDqTCsEtNl3~ z%p6-gZA+_db{~5zCYhXa1yroLT$Baz2&SmXY#YU0jRGPV2e({*C~%u|dkP-Gx731J zLc%XYw$-mM$v`inA^8v&`JaCJsW4rLp~M(Xi*KGsn!&-cXdQ{9k?FNr&(es3Sr$Le z+JiM#1cg08(RZ?ks--U+l?!pjM*@58_d0EN)XA>;WckTGZyy@^9D4`K>09mGr$IF) zfLZu{IfC;pK(BuvzieMw+a>6c6|!Q9@n?)J&Zr0nq*)PvX53Si9g*xRlAojLkm@kS zC#vkhF0cC0sf9Fo{o=cQ?d$FKsybSzFh)MT#r>E9Gmu~gb4b}!ykUOn7$v{iY&*}o zaad(D-ny(l$fjL1oEcAs5=!fV$Ke2laA}gY0k6y63GfJ`Kj26~$q~q*k<4AH zr5h=m7Dw{FFU^Mx^g?yNUOrMwoTe10 zf{)KBsoeZhC6#EFu!k9Xfzd-Y+quo)8EbHAnagmiqsxjoz5v2xVGIX`#R6IC_7Y^} zROGLPJRGGs)}d`{8EO3M@^d2F>YX546k1#LzYt>lh3vml?f=ld`rWyIm}UQmMt@}g zS6km)-PpkRudAEY)d%~(d-VUH{4Aj9r(FI_Ul6q-{<|u~iQm2w&7Rx2@@`b=xA?N? zdz~z&5mJ;n3CY?Uui4O}jwCzHl|*W&*;_=1&Ga(CK?N2H-kOXLJHy3=KS%{Oz1s|@ z6yCVwee~O2YWu@asZn&LCyV)KNjk|1&+Sa;oY4b2alY$#U3}_kHS>c}LFB8?pXc#g zsR|btBXBE_>Gnq?@Y?eyO*Nir?JL6(1|M`2bo8hsZ?=KMARwJ-qY7R)LS5Qu9)n}H zU^<`^8O>#xl6rhz#$3_*!AGx+|0d$J^uvqG&ItdQF&Ki6W%y6&mLSC2iA+?E6Urt{ z=R!ke_>Zr7DhMX402V?cOpIBMT1v(oi^dZ-6OUKki3XqS!ELwYTS7drfGPo_(V+(( zTa#pzU?5ol9(MwT3xYE35ZwW^KSGT>Fa2ZqT9gZ{7nj#m=;6iOdjO+3XKNS@MtO)MU#HlSfsE3q0Yo6wj#L;WcpSR0v9um5VTU!D$(LF8YJ{- z$;~qI^u)m5m`DMOc2!g>Z+QKxkvK^V2N2JiN9k~2a*c56F!5$(u64-h#-Zl z4vFn6`mO9SLkLrDY&6R;4mA$Ubj|dJV+_9f0dt@H^hD*u@%<*aW-pHzDA))s!S)s8 z?nQ>X#0prEloBzJ3kEj82lo*);aDV*C*SS&r^MZ%!fU>2z!(+J^d`i zmJ3tml51-y{#j7&v*bSRhT1?NGMkCHmY-&c9Xnpg>6l>QLgMUUKV{PUbt2cKg{hg? zF1o+ASPvu+u^?0y6GCyr7^)qS^oT#D5<|Nq_2XoauqhRy*kUQ+#HNPDvgdnCbo?lq z279|@z%xQ;t5HQ~PvW>t_`jACd?SvQZjFzk$7&RlGA~TaPsIMBqmx{c0`sGNqzV2y$trsiv7-KbwQ4d;uOZmd^K;Zv-l{ZnqPldwy}f_NqLLd zj82Q9`Fx68ZmAR^)5%!BrrSW6p(_*C!Wn;KYd zDw?FNjt#Csv0}qW8%4Tv+C+TO$W$mL;Ku`1{<>5-Wzx5%M95l@XnzxwICY2%k>fv? z#-_qQozTt!u<7^9rboAiDI0CFJG%9Rr_jIW%^hrs(;!)X+gX4Mu%34!!6*51JlMtEdc3<=r>LYe#G+=rnoiK7N33C+)@i0E zBjsevay87SOD-kS&33B5gW?+|WDaR2rT8*3Hs+4+N93@n2iN-6m}K}EnUud8{TT&X ztQ1fG%6?GC2n!VAzLaBLlxf&St2LluIx-LT>-W820%iK{C-p<-+rMA|@$p~&>bt)O zBUK*enr*0g(5#_~k}_F%ApfG=fJHdA=VsJ@;0Qq~5S7w5U?~WSF?eS2SrbMWj67Sm z!Je2nWNntCEaelHoZ2quvps4NmA6)mr9~P3p9KBb&SCg#n)PVCjS#k}=fB1TBO@51 zM6bsY`P5OGI7zpam<@iXL=BbuVT>NrK#1@CNf}XP) z{GYSNye(o;ZW7zvV&#s6U^FQg&EcbBo|d90qNO&GV_R&C!PBQv_VedEqKb&PvK+H{ zpHuv%gltO2X0AA@IHU>OvP((f+w4Nsc?#9$DYQCIp|yDmNg|0ICwd$hH5VQ4<-tgKTQ?H5kMJ!{f)(tFYRvl)$4047tT$ zm@IbGwLkhqrwl`VS7#(M*}a2Fld*}+^`@yn&zn7?|!$7re9Bm zS(!lp&1Bd7Yh=;)d!+A*}g>(wR>0p3wBeJrwz}7g)3z|JZ z9${_}%}d5%#se{VA3DyW0X|AHDr!tNp3WQ;+&0H(Eym&8GRDPlr!q$pC~FObOj zdi6Tg0|q=sdlO$ik(+6-{&!u34Wb{*ImteNjKm!Z zFZg=5ai|c^4i&GDr*pR>uX%zl;QA?)<9zEA$=>>AFMCIahbQ${XU+QljJ_n64|^`f zBy0zZ_4df7ORBTUlX5+N+ zbG@~@_jh$1l993Xx^6wHXG|BqQxAjC$LPTV$^+WLHS9u1n$OL^uoTqNkMb!fm9hk% zopdH~n93xM!z=n?F^g*Aqlr??BK}Te6?J;VC{k7BP=$Y5Tx0sW(dWf{i>DSESMa2M zeDJGT&Wu*1O3ShovaZ!UI67^~m`+3$Q4-I!OjxN_g6&m$4G~0Y^j3M5Sa8UOhi%hQ zW=(`k;Pfy}JbbrWt(xO=Ng^$*)-bW4IHpA3v?B4oF|klEIlPq9&B;ElkN%#1Y+2hc zl`Ul2OR}9$fZ%;U?4tbDC!u1D0>EEGq2$v!zJrd_|z%?*#IgoSQ zZ9;7&fj)_4MKPG*NkBfK#f>*E^M1X}w&M-Q({1cJlq6K0Y#IhF=;89V*HVVWZv9L` zFvpxNmUWFrOx+o{fapBR_W~;)s4}{}8g!@yW!5>yUn|b+Q%zm7EcurV#i|KU>RFa{ zlo=Sbg)ET|p!I>1a4)+;Ch=oMf+t~u^WyK7~n>5R$ zG^ic|q&H^i8#QIBpy|>)fh*EXfwN*gL^esw+MUtk{3Sm+y{~-0@H7@^B?_s7Xmu&y zm=#+<=R27JtIqVvE&#VcNWa4fT^dQo7*!gVFpl>K>_}s?0QX=tZ#N3_d2EQ4lcg&# z$QLifGp=Qv`G#m<=y&YK^(5gtg8ZIt?qzOuJ5*lF4)3>&JVHdsZPr%SAhtYBPcO|d zEE+OxNC_?BoSE2at9OxThFVCkCZSneO&evI<)dJE+`NhdYP`r8MO2B50F=fFW0}`x zLG8q@Icbv@tCTW*pX1~{XZZ%_vCoJzbLX_g-l+DDN;1#sn~Nn83-7J;O%$cxVv|(n z-W}=NVH$$RhMU~#kYSGSsmNPHTNbtTjihT8eOLpd{HLHmeAV~s^RD|}pQu*0Bkp82 zqC^4P6XBkwt(d$-U|iS$xH$4Ka8YD$*i|tc1>_AHx%dS4^=MXvKgoJMisJjp3y8X3 zUti+Wwbd$K0>hr?4O_t=|5$6gN!g#x{mDQ~deAW|-+6TY(gszXrRrn#vn8(GFv)37 zQH+$p8)jkG?v>Y?3=`vxv>cuJ<9}Y>PyF}I&Gk(c|8sq9ZG+=~Rv+wt?$1KZmM^NQ?T zby%-u?M=xHR|7}Q=w?X#*OhlOmkVCg@kp$-b05uEZd$?l@2FwrcV;?UA}^v`nxv@8 zvl1s6&XtUY>|Rnp=>-@5Kn>07%pTR0#`&)*7&9$~Da*(i!Ug)elYj|#{TzWJ5+=-$ zFXtk?0Cg9PI{)W79ZU1$GO>O^0B9m<2R~(A?5%{0wp?~|E z@T6@(@l7~e-u|mOsoh2kC6bS!eyhI6BUQ-o|27%>y_K&fV-RbV&Gq$6`44<5QvS1E zsa4VcTeY&j_FrQCLH=`3{{LwG{oyD4duL+5dF6kro0~Pc{Fz)ai$#;9}&W-n_tQ;UWyW<7;>1ZHt>=f_hK_4lX~GCmSlW0z$c-1&PtT&tD)zOLA z700_Lr;WX{gWVHxe0Fkt)U206#y65)KM}Fr5zP{ZRa1yQlSQKZ?T^IzMoLW%cf}L`9+Bj&vX|)Vn-&nF14@WZN zOka(%322~o(l~A%pZ$O6%VIzCB;3TTQt{>J< zb`M(rb5=k3mC9ApKv2a_>doV$!)9HUtR+hx)^|}++G=n$yI8EjwGuX56Lm-}DA#r$7n9nVre9W50bB&MNy^j3^3P{F7bf^R_*)FHeqk z_xE;Toeq!o>tgA*Pu*_Xe@j-a(>IMKS77zT-zw4nmVi&iM+BTE+oT$+9-^svBAYGN zA^RBqrsNYVOx4jA5h1|NTL<;S58M2}ON&E~uw<{8mu1C(qkhGU@S^1zAY8*_89X!d z5+p%}jfo;5DpTfD`~)rwG>1f%U1~M9S{y$QR)e0Su?-+mFghDtbhBSn0~O(@O%LcY z)CXrmB9+inz)$<~={&*z@vARAdEBA%=UMz8WUj6&{%>`&vQ|a@Z)3Ce!2jLP`>#Bd zmdxe9Tyl(zYkHT%M7`fQLOII4w#_u4M);~_rD5c9PBc%&n{dSs+VR&{Oh%1`Sn9ar z2l1rjshaXIjP0ReKuT)zc^Q+_d_YeIz^TQYOtX2TotAt}UeZ^prHGcIh=7PfmsLT! zwog;o>)vQAg$OOy1ev0NvOTRfa7D9QNY#h7UFb8MaitvPZ?b=rbVY0D-wBBxKm z{Ho2k(Nom#G8C;1CFi(Ry`>5q8R?T^tPmOP86_$Dm|ap$P*Qo4<|wiQM)w}AE(X0o zMZw0=9*WQ;PpPGHk7ZBu>Q&x{>aL}``#5qf5c)lsqMe;eA$3pB)QC=3=}kH^n#YNe zsjodl(Q^@xH;egX{IkE9k6i*#KRN&}t0*4m&=BFL`Cr1mER170JT%>C4U@=jMRe z{~`IzuhiB)*v=!9VJ(!xa?mBVWG6QDW+e^o)mR3@k?blDs*Ccq6gFBS7njAN(R!>b zi({IXqIy@j2+*&Ji|(2xt_){x%?9_yGiFd+>Oit~RlbnjiuEzr9fpshM~iB$0#gQv z-3ieg9+iRcxdPvdcwyOv6ut4|dym^gFB=5C$uL1Pb8$>_a!IEDozIF#MBF0e!g-Fc zF7h2>01ebvbbRT01}^CoBpcX-P9P6+cW^`RK#C*jWE{P_R;WCW; zDKLXXGx%OG;xmX;Mp*`J&}AM+2wTuDt5kOpbF8^Y)xYu%lk{7pm>|`Nik70orPy&& zwC}4#Ly1@w(JWXF75}uiTqFvpFAEM0y~PXhw6K$^?eiFsYkI$uSTVeZn1>bFX>-va8p5voIG~jzTZRV zfDFpEnWtXEc4YX_$|oWa@#hZPxn$9PQz}?3lIp^aj58{mi*gOVj^0q!S&#N3`3M`* zwiEuP;d1T$A@09f|M( z9)&>1&H8BSZY|gYF?zj=iO#95uHy>F{FBmtP8;%E>vH#F&R9cZjPs0IleL+{j!H_G z^7#F!#ICMnh{G`uY#6Q%#Xx3wqDJb{UVB1*!?0qgJg3|W^Wt(BY4a`#cY^VSY>Nq= zZSp^t#s6I!{8y(K_orUcQg36#i@P7aPK(~apb`2z_gt%5MjNS{-@3U(_wu`T9gun0 z;z8TO z>*eE@zash=Kdqm<#S-|&oF`Ag2}c!+d#Z?58vSF@8$fvNkK32gDwudogKcBTPeNz% zGh5%^G9M1?vFZ0H8n&*;`@pJ~iun2+v*UGLj7-PA@*^?F3%vq!^0=Q<9eTws2Q317 zUOOBf<1oP39<(9jV@Ky=z>wyRf<L(>xjl z%XunmJzKJj`hA)d_5-r@;*)u#S~6Ou#XObUS8Do-qe{+rp#dh3>^huaGGOblHb#Oyg$C{Ve zhx*u*;+iq~naq=?$Qk3rJ&u)L0IRs3y|)P*@|ub2q+8O{zMvj*+$q$LE`ibZmf%Xi zm)7bj?nUQLnH6%!KJ#Rtc}VC-B^hgLRcz*3YAd5SEh#a3Khuff2Xpb`Z)!1Jr_{l< z!kJ!8q89ad?dj30Ytt&(1|02Z#x@*zJs%DjS2{i=PoU|GXZC1LV{&|!?ieGd6FnN? z6wVqQn+wyxiHz;=FV4TMu38tW z6g^1YW$JB9LN9iZkz>(N91HZOEa=0-nc9&ZIgh>m%0c*{aK*C{At+C{V>+R|3wKBM z{VZ)6TA?0PZMBf@STR1&wJ)4!lT(eoY?J@_bR9ii=7$azlP>L3G>@X91i`}KNoGR% zTFBTz6~KUJ3VY{T{cpzeR2(a&A;tRYj2&vgR4|Kbrbtno2+(f<&DPiE!?X3ZuZL*7 z9Hkc;2}rPngr}EJoB$n^r8*f z_@?#I?alN*H7ALI3_M1iE?rF>15+A?1tHlJ_vkPLMu(*sh*%*)grO#s; zzbdCxwmtK?BfZfSUP-o#k4Y~Zhx_%DUt4F*`iWHe$>~WEkMZj*`c2RX*(0^5HLB#e z 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 literal 0 HcmV?d00001 diff --git a/BUGS b/BUGS index cea62ba..ec20fff 100644 --- a/BUGS +++ b/BUGS @@ -3,15 +3,17 @@ them to be bugs. But there are really problems that might be fixed in the future. -Invalid directory timestamps: -I did not yet find out how to get valid timestamps for directories -from a NetWare server. So I simply return 0, which means 01.01.70. If -anybody knows how to get these values, please mail -lendecke@namu01.gwdg.de. - 'df' returns 0: Free disk space is distributed among the volumes in NetWare. df is only able to report one number per mounted filesystem. As connections are quite expensive for NetWare (with lwared that might change ...), I rejected the alternative to mount only a single volume for a unix mount point. So I simply return 0. + + +In your kernel log, there will appear messages like + +Nov 25 16:09:08 lx01 kernel: alloc_skb called nonatomically from interrupt 0000002e + +These are a bit annoying, but completely harmless. Maybe this will be +fixed in the future. diff --git a/Makefile b/Makefile index 6f31368..1e34194 100644 --- a/Makefile +++ b/Makefile @@ -2,76 +2,45 @@ # Makefile for the linux ncp-filesystem routines. # -INCLUDES = -I/usr/src/linux/include +KERNEL = 1.3 -CFLAGS = -Wall -Wstrict-prototypes -O2 -DMODULE -fomit-frame-pointer \ - $(INCLUDES) \ -# -DDEBUG_NCP=2 -DDEBUG_NCP_MALLOC -# -DDEBUG_NCP_MALLOC +INCLUDES = -I/usr/src/linux/include -Ikernel +BINDIR = ./bin -CC = gcc -D__KERNEL__ -I. -AS = as -ARCH = i386 +CFLAGS = -Wall $(INCLUDES) +CC = gcc -.c.s: - $(CC) $(CFLAGS) -S $< -.c.o: - $(CC) $(CFLAGS) -c $< -.s.o: - $(AS) -o $*.o $< +all: kernel + make -C util + make -C kernel/src ncpfs.o + cp kernel/src/ncpfs.o bin -OBJS= dir.o inode.o file.o sock.o ioctl.o ncplib.o - -all: ncpfs.o ncpmount ncptest - -ncpfs.o: $(OBJS) - $(LD) -r -o ncpfs.o $(OBJS) - -ncplib.o: ncplib.c ncplib.h - $(CC) $(CFLAGS) -finline-functions -c $< - -ncpmount: ncpmount.o ncplib_user.o nwcrypt.o - gcc -o ncpmount ncpmount.o ncplib_user.o nwcrypt.o - -ncpmount.o: ncpmount.c - gcc -c ncpmount.c -Wall -I. -g - -ncptest: ncptest.o ncplib_user.o nwcrypt.o - gcc -o ncptest ncptest.o ncplib_user.o nwcrypt.o - -ncptest.o: ncptest.c - gcc -c ncptest.c -Wall -I. -g - -ncplib_user.o: ncplib_user.c ncplib_user.h - gcc -c ncplib_user.c -Wall -I. -g - -nwcrypt.o: nwcrypt.c - gcc -c -O2 -Wall nwcrypt.c +kernel: + rm -f kernel + ln -s kernel-$(KERNEL) kernel dep: $(CPP) -M $(INCLUDES) *.c > .depend clean: - rm -f *.o *~ + rm -f kernel + rm -f `find . -type f -name '*.o' -print` + rm -f `find . -type f -name '*~' -print` + rm -f `find . -type f -name '.depend' -print` + rm -f `find . -type f -name '*.out' -print` + realclean: clean - rm -f ncpmount ncptest .depend $(DISTFILE) *.out + rm -fr bin/* ncpfs.tgz util/mnt + make -C util realclean 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 - rm -fr mnt (cd ..; \ tar cvf - $(SRCDIR) | \ gzip -9 > $(DISTFILE); \ diff --git a/README b/README index 9704108..4b218d3 100644 --- a/README +++ b/README @@ -1,22 +1,22 @@ -This is version 0.5 of ncpfs, a free NetWare client for Linux. For me -it works with 1.3.39, although this version has severe problems with -the socket layer. Your connection will block after you have sent 64k -of requests to the server. Alan Cox told me he would like to have that -fixed in 1.3.42 or so. +This is version 0.6 of ncpfs, a free NetWare client for Linux. You +need at least kernel 1.3.44 for this version. It does NOT work with +any lower one, especially not with version 1.2.x. -I know that this piece of software is VERY incomplete, 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'. After that, you find the +neccessary kernel module and the mounting tools in ./bin. Type 'insmod +ncpfs.o' and then 'ncpmount server mount-point'. For further +information, please look at the manual pages in ./man. -To install ncpfs, just type 'make', 'insmod ncpfs.o' and then -'ncpmount server mount-point'. +Please note that your IPX system has to be configured correctly. If +you want to take the 'Plug-and-Play' route, you can simply say +'ipx_configure --auto_interface=on --auto_primary=on'. If ncpmount +does not work immediately, you should wait for about 1 minute and try +again. In that period, an IPX packet should have passed by and your +network interface should have configured itself automatically. -Please note that your IPX system has to be configured correctly. There -has to be a route to the internal network of your server. Please see -the file start_ipx for an example. +If all that does not work and you want to do the configuration by +hand, note that there has to be a route to the internal network of +your server. Please see the file util/start_ipx for an example. I use tools written by Greg Page, Caldera. I hope I did not do too much harm to their business. For your convenience I included the file diff --git a/ncp.h b/kernel-1.3/linux/ncp.h similarity index 85% rename from ncp.h rename to kernel-1.3/linux/ncp.h index 36eb85b..83ffefd 100644 --- a/ncp.h +++ b/kernel-1.3/linux/ncp.h @@ -45,6 +45,7 @@ struct ncp_reply_header { #define NCP_BINDERY_USER (0x0001) #define NCP_BINDERY_UGROUP (0x0002) #define NCP_BINDERY_PQUEUE (0x0003) +#define NCP_BINDERY_FSERVER (0x0004) #define NCP_BINDERY_NAME_LEN (48) struct ncp_bindery_object { __u32 object_id; @@ -177,6 +178,39 @@ struct nw_info_struct __u8 entryName[256] __attribute__ ((packed)); }; +/* modify mask - use with MODIFY_DOS_INFO structure */ +#define DM_ATTRIBUTES (0x0002L) +#define DM_CREATE_DATE (0x0004L) +#define DM_CREATE_TIME (0x0008L) +#define DM_CREATOR_ID (0x0010L) +#define DM_ARCHIVE_DATE (0x0020L) +#define DM_ARCHIVE_TIME (0x0040L) +#define DM_ARCHIVER_ID (0x0080L) +#define DM_MODIFY_DATE (0x0100L) +#define DM_MODIFY_TIME (0x0200L) +#define DM_MODIFIER_ID (0x0400L) +#define DM_LAST_ACCESS_DATE (0x0800L) +#define DM_INHERITED_RIGHTS_MASK (0x1000L) +#define DM_MAXIMUM_SPACE (0x2000L) + +struct nw_modify_dos_info +{ + __u32 attributes __attribute__ ((packed)); + __u16 creationDate __attribute__ ((packed)); + __u16 creationTime __attribute__ ((packed)); + __u32 creatorID __attribute__ ((packed)); + __u16 modifyDate __attribute__ ((packed)); + __u16 modifyTime __attribute__ ((packed)); + __u32 modifierID __attribute__ ((packed)); + __u16 archiveDate __attribute__ ((packed)); + __u16 archiveTime __attribute__ ((packed)); + __u32 archiverID __attribute__ ((packed)); + __u16 lastAccessDate __attribute__ ((packed)); + __u16 inheritanceGrantMask __attribute__ ((packed)); + __u16 inheritanceRevokeMask __attribute__ ((packed)); + __u32 maximumSpace __attribute__ ((packed)); +}; + struct nw_file_info { struct nw_info_struct i; int opened; diff --git a/ncp_fs.h b/kernel-1.3/linux/ncp_fs.h similarity index 98% rename from ncp_fs.h rename to kernel-1.3/linux/ncp_fs.h index 6b903d1..8373185 100644 --- a/ncp_fs.h +++ b/kernel-1.3/linux/ncp_fs.h @@ -27,6 +27,7 @@ struct ncp_ioctl_request { }; #define NCP_IOC_NCPREQUEST _IOR('n', 1, unsigned char *) +#define NCP_IOC_GETMOUNTUID _IOR('u', 1, uid_t) /* * The packet size to allocate. One page should be enough. diff --git a/ncp_fs_i.h b/kernel-1.3/linux/ncp_fs_i.h similarity index 100% rename from ncp_fs_i.h rename to kernel-1.3/linux/ncp_fs_i.h diff --git a/ncp_fs_sb.h b/kernel-1.3/linux/ncp_fs_sb.h similarity index 100% rename from ncp_fs_sb.h rename to kernel-1.3/linux/ncp_fs_sb.h diff --git a/ncp_mount.h b/kernel-1.3/linux/ncp_mount.h similarity index 100% rename from ncp_mount.h rename to kernel-1.3/linux/ncp_mount.h diff --git a/kernel-1.3/src/Makefile b/kernel-1.3/src/Makefile new file mode 100644 index 0000000..ee74518 --- /dev/null +++ b/kernel-1.3/src/Makefile @@ -0,0 +1,67 @@ +# +# Makefile for the linux ncp-filesystem routines. +# + +INCLUDES = -I/usr/src/linux/include -I.. + +CFLAGS = -Wall -Wstrict-prototypes -O2 -DMODULE -fomit-frame-pointer \ + $(INCLUDES) \ +# -DDEBUG_NCP=2 -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= dir.o inode.o file.o sock.o ioctl.o ncplib.o + +all: ncpfs.o + +ncpfs.o: $(OBJS) + $(LD) -r -o ncpfs.o $(OBJS) + +ncplib.o: ncplib.c ncplib.h + $(CC) $(CFLAGS) -finline-functions -c $< + +dep: + $(CPP) -M $(INCLUDES) *.c > .depend + +clean: + rm -f *.o *~ + +realclean: clean + rm -f ncpmount ncptest .depend $(DISTFILE) *.out + +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 + rm -fr mnt + (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/dir.c b/kernel-1.3/src/dir.c similarity index 100% rename from dir.c rename to kernel-1.3/src/dir.c diff --git a/file.c b/kernel-1.3/src/file.c similarity index 100% rename from file.c rename to kernel-1.3/src/file.c diff --git a/inode.c b/kernel-1.3/src/inode.c similarity index 93% rename from inode.c rename to kernel-1.3/src/inode.c index b34f9f5..5d1aa7c 100644 --- a/inode.c +++ b/kernel-1.3/src/inode.c @@ -357,11 +357,12 @@ ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) memcpy_tofs(buf, &tmp, bufsiz); } -/* DO MORE */ static int ncp_notify_change(struct inode *inode, struct iattr *attr) { int result = 0; + int info_mask; + struct nw_modify_dos_info info; if ((result = inode_change_ok(inode, attr)) < 0) return result; @@ -379,6 +380,38 @@ ncp_notify_change(struct inode *inode, struct iattr *attr) ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) return -EPERM; + info_mask = 0; + memset(&info, 0, sizeof(info)); + + if ((attr->ia_valid & ATTR_CTIME) != 0) { + info_mask |= (DM_CREATE_TIME|DM_CREATE_DATE); + ncp_date_unix2dos(attr->ia_ctime, + &(info.creationTime), &(info.creationDate)); + } + + if ((attr->ia_valid & ATTR_MTIME) != 0) { + info_mask |= (DM_MODIFY_TIME|DM_MODIFY_DATE); + ncp_date_unix2dos(attr->ia_mtime, + &(info.modifyTime), &(info.modifyDate)); + } + + if ((attr->ia_valid & ATTR_ATIME) != 0) { + __u16 dummy; + info_mask |= (DM_LAST_ACCESS_DATE); + ncp_date_unix2dos(attr->ia_ctime, + &(dummy), &(info.lastAccessDate)); + } + + if (info_mask != 0) { + if ((result = + ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), + NCP_ISTRUCT(inode), + info_mask, + &info)) != 0) { + result = -EACCES; + } + } + if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; diff --git a/ioctl.c b/kernel-1.3/src/ioctl.c similarity index 78% rename from ioctl.c rename to kernel-1.3/src/ioctl.c index a1c71cb..36d8f36 100644 --- a/ioctl.c +++ b/kernel-1.3/src/ioctl.c @@ -58,9 +58,8 @@ ncp_ioctl (struct inode * inode, struct file * filp, 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); + memcpy_fromfs(server->packet, request.data, + request.size+sizeof(struct ncp_request_header)); ncp_request(server, request.function); @@ -73,6 +72,15 @@ ncp_ioctl (struct inode * inode, struct file * filp, ncp_unlock_server(server); return server->reply_size; + + case NCP_IOC_GETMOUNTUID: + if ((result = verify_area(VERIFY_WRITE, (uid_t*) arg, + sizeof(uid_t))) != 0) { + return result; + } + put_fs_word(NCP_SERVER(inode)->m.mounted_uid, (uid_t*) arg); + return 0; + default: return -EINVAL; } diff --git a/ncplib.c b/kernel-1.3/src/ncplib.c similarity index 94% rename from ncplib.c rename to kernel-1.3/src/ncplib.c index 2192dca..7a14317 100644 --- a/ncplib.c +++ b/kernel-1.3/src/ncplib.c @@ -1,5 +1,4 @@ #include "ncplib.h" -#include "nwcrypt.h" typedef __u8 byte; typedef __u16 word; @@ -41,7 +40,7 @@ ncp_add_dword(struct ncp_server *server, dword x) } static void -ncp_add_mem(struct ncp_server *server, const char *source, int size) +ncp_add_mem(struct ncp_server *server, const void *source, int size) { assert_server_locked(server); memcpy(&(server->packet[server->current_size]), source, size); @@ -318,6 +317,34 @@ ncp_do_lookup(struct ncp_server *server, return 0; } +int +ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, + struct nw_info_struct *file, + __u32 info_mask, + struct nw_modify_dos_info *info) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, 7); /* subfunction */ + ncp_add_byte(server, 0); /* dos name space */ + ncp_add_byte(server, 0); /* reserved */ + ncp_add_word(server, 0x8006); /* search attribs: all */ + + ncp_add_dword(server, info_mask); + ncp_add_mem(server, info, sizeof(*info)); + ncp_add_handle_path(server, file->volNumber, + file->DosDirNum, 1, NULL); + + if ((result = ncp_request(server, 87)) != 0) { + ncp_unlock_server(server); + return result; + } + + ncp_unlock_server(server); + return 0; +} + int ncp_del_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name) @@ -441,7 +468,7 @@ ncp_search_for_file_or_subdir(struct ncp_server *server, ncp_add_byte(server, 0); /* data stream (???) */ ncp_add_word(server, 0xffff); /* Search attribs */ ncp_add_dword(server, RIM_ALL); /* return info mask */ - ncp_add_mem(server, (unsigned char *)seq, 9); + ncp_add_mem(server, seq, 9); ncp_add_byte(server, 2); /* 2 byte pattern */ ncp_add_byte(server, 0xff); /* following is a wildcard */ ncp_add_byte(server, '*'); diff --git a/ncplib.h b/kernel-1.3/src/ncplib.h similarity index 95% rename from ncplib.h rename to kernel-1.3/src/ncplib.h index 86e52c8..3278cdf 100644 --- a/ncplib.h +++ b/kernel-1.3/src/ncplib.h @@ -119,6 +119,12 @@ ncp_do_lookup(struct ncp_server *server, char *path, /* may only be one component */ struct nw_info_struct *target); +int +ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, + struct nw_info_struct *file, + __u32 info_mask, + struct nw_modify_dos_info *info); + int ncp_del_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name); diff --git a/sock.c b/kernel-1.3/src/sock.c similarity index 100% rename from sock.c rename to kernel-1.3/src/sock.c diff --git a/linux b/linux deleted file mode 120000 index 945c9b4..0000000 --- a/linux +++ /dev/null @@ -1 +0,0 @@ -. \ No newline at end of file diff --git a/man/ipx_configure.8 b/man/ipx_configure.8 new file mode 100644 index 0000000..8bb1c96 --- /dev/null +++ b/man/ipx_configure.8 @@ -0,0 +1,42 @@ +.TH IPX_CONFIGURE 8 "IPX Utilities" "Caldera, Inc." +.SH NAME +ipx_configure \- query/configure IPX behavior +.SH SYNOPSIS +.B ipx_configure +[\-\-help] +[\-\-auto_interface=[on|off]] +[\-\-auto_primary=[on|off]] +.SH DESCRIPTION +.B ipx_configure +queries or configures IPX behavior with respect to automatic IPX +interface detection. IPX can be configured to automatically create +interfaces as they are detected. It can also be configured to +automatically select a primary interface when none is explicitly +selected. By default, it is configured to +.B NOT +have this behavior. +Without arguments, +.B ipx_configure +returns the current configuration state. The behavior with +arguments is described in the section +.B OPTIONS. +.SS OPTIONS +.TP +.I "\-\-auto_interface=[on|off]" +This argument either turns on or off the behavior of automatically creating +interfaces. +.TP +.I "\-\-auto_primary=[on|off]" +This argument either turns on or off the behavior of automatically selecting +a primary interface. +.TP +.I "\-\-help" +Print out information about utility. +.SH FILES +.I /proc/net/ipx_interface +.SH BUGS +This functionality really belongs in +.B +ifconfig(8). +.SH AUTHOR +Greg Page diff --git a/man/ncpmount.8 b/man/ncpmount.8 new file mode 100644 index 0000000..a168c3c --- /dev/null +++ b/man/ncpmount.8 @@ -0,0 +1,190 @@ +.TH NCPMOUNT 8 25/11/1995 ncpmount ncpmount +.SH NAME +ncpmount \- mount program for ncpfs +.SH SYNOPSIS +.B ncpmount +.B server mount-point +[ +.B -h +] [ +.B -n +.I +] [ +.B -P +.I password +] [ +.B -C +] [ +.B -s +.I server name +] [ +.B -c +.I client name +] [ +.B -U +.I user name +] [ +.B -u +.I uid +] [ +.B -g +.I gid +] [ +.B -f +.I file mode +] [ +.B -d +.I dir mode +] + +.SH DESCRIPTION +This program is an interface to the NCP filesystem. + +.B ncpfs +is a filesystem which understands the NCP protocol. This is the +protocol Novell NetWare clients use to talk to NetWare servers. ncpfs +was inspired by +.B lwared, +a free NetWare emulator for Linux written by Ales Dryak. See +ftp://klokan.sh.cvut.cz/pub/linux for this very intersting program. + +.SH OPTIONS +.B server +.RS 3 +.B server +is the name of the server you want to use on the server. +.RE + +.B mount-point +.RS 3 +.B mount-point +is the directory you want to mount the filesystem over. It's the same +as in the normal mount command. + +If the real uid of the caller is not root, +.B ncpmount +checks whether the user is allowed to mount a filesystem on the +mount-point. So it should be safe to make +.B ncpmount + setuid root. The filesystem stores the uid of the user who called +ncpmount. So +.B ncpumount +can check whether the caller is allowed to unmount the filesystem. +.RE + +.B -h +.RS 3 +.B -h +is used to print out a short help text. +.RE + +.B -C +.RS 3 +By default, passwords are converted to uppercase before they are sent +to the server, because most servers require this. You can turn off +this conversion by +.B -C. +.RE + +.B -n +.RS 3 +.B -n +should be given to mount shares which do not require a password to log in. +.RE + +.B -P +.I password +.RS 3 +You may want to give the password required by the server on the +command line. You should be careful to use passwords in scripts. + +If neither +.B -n +nor +.B -P +are given, ncpmount prompts for a password. This makes it difficult to +use in scripts such as /etc/rc. But that's not ncpmount's fault, but a +general problem with the fact that you need a password on every +login. If anybody has a satisfying solution to this problem, please +tell me. +.RE + +.B -U +.I user name +.RS 3 +If the user name your NetWare administrator gave to you differs +from your unix user-id, you should use +.B -U +to tell the server about you NetWare user name. +.RE + +.B -u +.I uid, +.B -g +.I gid +.RS 3 +Currently I did not implement a mapping from NetWare users/groups to +unix users/groups. Unix requires that each file has an owner +and a group it belongs to. With +.B -u +and +.B -g +you can tell ncpmount which id's it should assign to the files in the +mounted direcory. + +The defaults for these values are the current uid and gid. +.RE + +.B -f +.I file mode, +.B -d +.I dir mode +.RS 3 +Like +.B -u +and +.B -g, +these options are also used to cover deficiencies in the +implementation of ncpfs. I did not implement a scheme to map NetWare +permissions to unix permissions. So ncpmount has to be told which +permissions it should assign to the mounted files and direcories. The +values have to be given as octal numbers. The default values are taken +from the current umask, where the file mode is the current umask, and +the dir mode adds execute permissions where the file mode gives read +permissions. + +Note that these permissions can differ from the rights the server +gives to us. If you do not have write permissions on the server, you +can very well choose a file mode that tells that you have. This +certainly cannot override the restrictions imposed by the server. +.RE + +.SH NOTES +If you have difficulties in mounting, please make sure that you have configured your ipx subsystem correctly. It is especially important that there is a route to the internal network of your server. + +.SH ENVIRONMENT VARIABLES +.B USER / LOGNAME +.RS 3 +The variables USER or LOGNAME may contain the username of the person +using the client. USER is tried first. If it's emtpy, LOGNAME is +tried. +.RE + +.SH DIAGNOSTICS + +Most diagnostics issued by ncpfs are logged by syslogd. Normally +nothing is printed, only error situations are logged there. + +.SH SEE ALSO +.B syslogd(8), ncpumount(8) + +.SH CREDITS +ncpfs would not have been possible without lwared, written by Ales +Dryak (A.Dryak@sh.cvut.cz). + +The encryption code was taken from Dr. Dobbs's Journal 11/93. There +Pawel Szczerbina described it in an article on NCP. + +The ncpfs code was initially hacked from smbfs by Volker Lendecke +(lendecke@namu01.gwdg.de). smbfs was put together by Paal-Kr. Engstad +(pke@engstad.ingok.hitos.no) and later polished by Volker. diff --git a/man/ncpumount.8 b/man/ncpumount.8 new file mode 100644 index 0000000..6724407 --- /dev/null +++ b/man/ncpumount.8 @@ -0,0 +1,28 @@ +.TH NCPUMOUNT 8 25/11/1995 ncpumount ncpumount +.SH NAME +ncpumount \- umount for normal users +.SH SYNOPSIS +.B ncpumount +.B mount-point + +.SH DESCRIPTION +With this program, normal users can unmount ncp-filesystems, provided +that it is suid root. + +.B ncpumount +has been written to give normal linux-users more control over their +resources. It is safe to install this program suid root, because only +the user who has mounted a filesystem is allowed to unmount it again. + +For root it is not necessary to use ncpumount. The normal umount +program works perfectly well, but it would certainly be problematic to +make umount setuid root. + +.SH OPTIONS +.B mount-point +.RS 3 +.B mount-point +is the directory you want to unmount. + +.SH SEE ALSO +.B ncpmount(8) diff --git a/ncpfs-0.5.lsm b/ncpfs-0.6.lsm similarity index 73% rename from ncpfs-0.5.lsm rename to ncpfs-0.6.lsm index b30c36f..71cadf8 100644 --- a/ncpfs-0.5.lsm +++ b/ncpfs-0.6.lsm @@ -1,14 +1,14 @@ Begin3 Title: ncpfs -Version: 0.5 -Entered-date: 17. November 1995 +Version: 0.6 +Entered-date: 25. November 1995 Description: With ncpfs you can mount volumes of your novell server under Linux. Keywords: filesystem kernel ncp novell netware Author: lendecke@namu01.gwdg.de (Volker Lendecke) Maintained-by: lendecke@namu01.gwdg.de (Volker Lendecke) Primary-site: linux01.gwdg.de:/pub/ncpfs - ~50k ncpfs-0.5.tgz - ~ 1k ncpfs-0.5.lsm + ~59k ncpfs-0.6.tgz + ~ 1k ncpfs-0.6.lsm Copying-policy: GPL End diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 0000000..9e4c9de --- /dev/null +++ b/util/Makefile @@ -0,0 +1,46 @@ +# +# Makefile for the linux ncp-filesystem routines. +# + +INCLUDES = -I/usr/src/linux/include -I../kernel +BINDIR = ../bin +UTILS = ncpmount ncpumount ncptest ipx_configure + +CFLAGS = -Wall $(INCLUDES) -g +CC = gcc + +all: $(UTILS) + +ncpmount: ncpmount.o ncplib_user.o nwcrypt.o + $(CC) -o ncpmount ncpmount.o ncplib_user.o nwcrypt.o + cp ncpmount $(BINDIR) + +ncpumount: ncpumount.o ncplib_user.o nwcrypt.o + $(CC) -o ncpumount ncpumount.o ncplib_user.o nwcrypt.o + cp ncpumount $(BINDIR) + +ipx_configure: ipx_configure.c + $(CC) $(CFLAGS) ipx_configure.c -o ipx_configure + cp ipx_configure $(BINDIR) + +ncptest: ncptest.o ncplib_user.o nwcrypt.o + $(CC) -o ncptest ncptest.o ncplib_user.o nwcrypt.o + +nwcrypt.o: nwcrypt.c + $(CC) -c -O3 -Wall nwcrypt.c + +dep: + $(CPP) -M $(INCLUDES) *.c > .depend + +clean: + rm -f *.o *~ + +realclean: clean + rm -f $(UTILS) .depend $(DISTFILE) + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/ipx.tar b/util/ipx.tar similarity index 100% rename from ipx.tar rename to util/ipx.tar diff --git a/util/ipx_configure.c b/util/ipx_configure.c new file mode 100644 index 0000000..c2b00ca --- /dev/null +++ b/util/ipx_configure.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct option options[] = { + { "auto_primary", required_argument, NULL, 1 }, + { "auto_interface", required_argument, NULL, 2 }, + { "help", no_argument, NULL, 3}, + { NULL, 0, NULL, 0 } +}; + +char *progname; + +void +usage(void) +{ + fprintf(stderr, + "Usage: %s --auto_primary=[on|off]\n\ +Usage: %s --auto_interface=[on|off]\n\ +Usage: %s --help\n\ +Usage: %s\n", progname, progname, progname, progname); +} + +int +map_string_to_bool(char *optarg) +{ + if ((strcasecmp(optarg, "ON") == 0) || + (strcasecmp(optarg, "TRUE") == 0) || + (strcasecmp(optarg, "SET") == 0) || + (strcasecmp(optarg, "YES") == 0)) { + return 1; + } else if ((strcasecmp(optarg, "OFF") == 0) || + (strcasecmp(optarg, "FALSE") == 0) || + (strcasecmp(optarg, "CLEAR") == 0) || + (strcasecmp(optarg, "NO") == 0)) { + return 0; + } + + return -1; +} + +int +main(int argc, char **argv) +{ + int s; + int result; + char errmsg[80]; + char val; + int option_index = 0; + int got_auto_pri = 0; + int got_auto_itf = 0; + ipx_config_data data; + + progname = argv[0]; + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + + sprintf(errmsg, "%s: ioctl", progname); + while ((result = getopt_long(argc, argv, "", options, + &option_index)) != -1) { + switch (result) { + case 1: + if (got_auto_pri) + break; + got_auto_pri++; + + val = map_string_to_bool(optarg); + if (val < 0) { + usage(); + exit(-1); + } + + result = ioctl(s, SIOCAIPXPRISLT, &val); + if (result < 0) { + perror(errmsg); + exit(-1); + } + break; + case 2: + if (got_auto_itf) + break; + got_auto_itf++; + + val = map_string_to_bool(optarg); + if (val < 0) { + usage(); + exit(-1); + } + + result = ioctl(s, SIOCAIPXITFCRT, &val); + if (result < 0) { + perror(errmsg); + exit(-1); + } + break; + case 3: + usage(); + break; + } + } + result = ioctl(s, SIOCIPXCFGDATA, &data); + if (result < 0) { + perror(errmsg); + exit(-1); + } + if (argc == 1) { + fprintf(stdout, "Auto Primary Select is %s\n\ +Auto Interface Create is %s\n", + (data.ipxcfg_auto_select_primary) ? "ON" : "OFF", + (data.ipxcfg_auto_create_interfaces) ? "ON" : "OFF"); + } + exit(0); +} + diff --git a/ipxutil.h b/util/ipxutil.h similarity index 70% rename from ipxutil.h rename to util/ipxutil.h index 6459bbf..c9c56ec 100644 --- a/ipxutil.h +++ b/util/ipxutil.h @@ -27,10 +27,29 @@ #include #include +#define IPX_RIP_PTYPE (0x01) #define IPX_SAP_PTYPE (0x04) #define IPX_SAP_PORT (0x0452) +#define IPX_RIP_PORT (0x0453) -#define IPX_SAP_FILE_SERVER (0x004) +#define IPX_SAP_GENERAL_QUERY (0x0001) +#define IPX_SAP_GENERAL_RESPONSE (0x0002) +#define IPX_SAP_NEAREST_QUERY (0x0003) +#define IPX_SAP_NEAREST_RESPONSE (0x0004) + +#define IPX_SAP_FILE_SERVER (0x0004) + +#define IPX_RIP_REQUEST (0x1) +#define IPX_RIP_RESPONSE (0x2) + +struct ipx_rip_packet { + __u16 operation __attribute__ ((packed)); + struct ipx_rt_def { + __u32 network __attribute__ ((packed)); + __u16 hops __attribute__ ((packed)); + __u16 ticks __attribute__ ((packed)); + } rt[1] __attribute__ ((packed)); +}; #define IPX_BROADCAST_NODE "\xff\xff\xff\xff\xff\xff" #define IPX_THIS_NODE "\0\0\0\0\0\0" diff --git a/ncplib_user.c b/util/ncplib_user.c similarity index 74% rename from ncplib_user.c rename to util/ncplib_user.c index 55f9f2c..234d970 100644 --- a/ncplib_user.c +++ b/util/ncplib_user.c @@ -6,9 +6,17 @@ typedef __u16 word; typedef __u32 dword; #include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include "ipxutil.h" #define ncp_printf printf @@ -38,10 +46,10 @@ ncp_unlock_server(struct ncp_server *server) } static int -ncp_request(struct ncp_server *server, int function) { +ncp_ioctl_request(struct ncp_server *server, int function) { struct ncp_reply_header *reply - = (struct ncp_reply_header *)(server->packet); + = (struct ncp_reply_header *)(server->ncp_data); struct ncp_ioctl_request request; int result; @@ -53,7 +61,7 @@ ncp_request(struct ncp_server *server, int function) { request.function = function; request.size = server->current_size; - request.data = server->packet; + request.data = server->ncp_data; if ((result = ioctl(server->mount_fid, NCP_IOC_NCPREQUEST, &request)) < 0) { @@ -70,6 +78,347 @@ ncp_request(struct ncp_server *server, int function) { return result; } +static int +do_ncp_call(struct ncp_server *server, int request_size) +{ + struct ncp_request_header request = + *((struct ncp_request_header *)(&(server->ncp_data))); + + fd_set rd, wr, ex; + struct timeval tv; + + int result; + int retries = 3; + + while (retries > 0) { + retries -= 1; + + result = sendto(server->ncp_sock, server->ncp_data, + request_size, + 0, (struct sockaddr *)&(server->addr), + sizeof(server->addr)); + + if (result < 0) { + return result; + } + + re_select: + FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); + FD_SET(server->ncp_sock, &rd); + + tv.tv_sec = 3; + tv.tv_usec = 0; + + if (select(server->ncp_sock+1, &rd, &wr, &ex, &tv) == -1) { + perror("select"); + return -1; + } + + if (FD_ISSET(server->ncp_sock, &rd)) { + int len = recv(server->ncp_sock, + server->ncp_data, NCP_PACKET_SIZE, + 0); + struct ncp_reply_header *r = + (struct ncp_reply_header *)&(server->ncp_data); + + if ( (len == sizeof(*r)) + && (r->type == NCP_POSITIVE_ACK)) { + goto re_select; + } + if ( (len >= sizeof(*r)) + && (r->type == NCP_REPLY) + && ( (request.type == NCP_ALLOC_SLOT_REQUEST) + || ( (r->sequence == request.sequence) + && (r->conn_low == request.conn_low) + && (r->conn_high == request.conn_high)))) { + server->reply_size = len; + break; + } + } + } + return 0; +} + +static int +ncp_user_request(struct ncp_server *server, int function) +{ + struct ncp_request_header *h = + (struct ncp_request_header *)&(server->ncp_data); + struct ncp_reply_header *r = + (struct ncp_reply_header *)&(server->ncp_data); + + int result; + + assert_server_locked(server); + + if (server->has_subfunction != 0) { + *(__u16 *)(server->packet) = server->current_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 = 1; + h->function = function; + + if (do_ncp_call(server, server->current_size + sizeof(*h)) != 0) { + return -1; + } + + server->completion = r->completion_code; + server->conn_status = r->connection_state; + server->ncp_reply_size = + server->reply_size - sizeof(struct ncp_reply_header); + + result = r->completion_code; + + if (result != 0) { + ncp_printf("ncp_completion_code: %d\n", result); + } + return result; +} + +int +install_wdog(struct ncp_server *server) +{ + int parent_pid = getpid(); + int pid; + int sock = server->wdog_sock; + + fd_set rd, wr, ex; + struct timeval tv; + char buf[1024]; + struct sockaddr_ipx sender; + int sizeofaddr = sizeof(struct sockaddr_ipx); + int pktsize; + + + if ((pid = fork()) < 0) { + return -1; + } + + if (pid != 0) { + /* Parent, should go on as usual */ + server->wdog_pid = pid; + return 0; + } + + while (1) { + + FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); + FD_SET(sock, &rd); + + /* every 120 seconds we look if our parent is + still alive */ + tv.tv_sec = 120; + tv.tv_usec = 0; + + if (select(sock+1, &rd, &wr, &ex, &tv) == -1) { + continue; + } + + if (getppid() != parent_pid) { + /* our parent has died, so nothing to do + anymore */ + exit(0); + } + + if (FD_ISSET(sock, &rd)) { + pktsize = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *)&sender, + &sizeofaddr); + if (pktsize < 0) { + perror("recvfrom"); + continue; + } + if ( (pktsize != 2) + || (buf[1] != '?')) { + continue; + } + buf[1] = 'Y'; + pktsize = sendto(sock, buf, 2, 0, + (struct sockaddr *)&sender, + sizeof(sender)); + if (pktsize < 0) { + perror("send"); + } + } + } +} + +int +ncp_connect(struct ncp_server *server) +{ + struct ncp_request_header *h = + (struct ncp_request_header *)&(server->ncp_data); + + struct sockaddr_ipx addr; + int len = sizeof(struct sockaddr_ipx); + + int ncp_sock, wdog_sock; + int ncp_port, wdog_port; + + server->is_connected = NOT_CONNECTED; + + ncp_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + if (ncp_sock == -1) { + perror("open ncp socket"); + return -1; + } + + wdog_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + if (wdog_sock == -1) { + perror("open wdog socket"); + close(ncp_sock); + return -1; + } + + addr.sipx_family = AF_IPX; + addr.sipx_network = htonl(0x0); + ipx_assign_node(addr.sipx_node, IPX_THIS_NODE); + addr.sipx_port = htons(0x0); + addr.sipx_type = NCP_PTYPE; + + if (bind(ncp_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("bind ncp socket"); + close(ncp_sock); close(wdog_sock); + return -1; + } + + if (bind(wdog_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("bind wdog socket"); + close(ncp_sock); close(wdog_sock); + return -1; + } + + if ( (getsockname(ncp_sock, (struct sockaddr *)&addr, &len) != 0) + || (len != sizeof(struct sockaddr_ipx))) { + perror("getsockname ncp socket"); + close(ncp_sock); close(wdog_sock); + return -1; + } + ncp_port = ntohs(addr.sipx_port); + + if ( (getsockname(wdog_sock, (struct sockaddr *)&addr, &len) != 0) + || (len != sizeof(struct sockaddr_ipx))) { + perror("getsockname wdog socket"); + close(wdog_sock); close(wdog_sock); + return -1; + } + wdog_port = ntohs(addr.sipx_port); + + if (wdog_port != ncp_port+1) { + fprintf(stderr, "did not alloc 2 consecutive ports\n"); + close(ncp_sock); close(wdog_sock); + return -1; + } + + server->ncp_sock = ncp_sock; + server->wdog_sock = wdog_sock; + + h->type = NCP_ALLOC_SLOT_REQUEST; + + server->sequence = 0; + h->sequence = server->sequence; + h->conn_low = 0xff; + h->conn_high = 0xff; + h->task = 1; + h->function = 0; + + if (do_ncp_call(server, sizeof(*h)) != 0) { + int saved_errno = errno; + close(ncp_sock); close(wdog_sock); + errno = saved_errno; + return -1; + } + + install_wdog(server); + + server->sequence = 0; + server->connection = h->conn_low + (h->conn_high * 256); + + server->is_connected = CONN_SOCKET; + + return 0; +} + +static int +ncp_user_disconnect(struct ncp_server *server) +{ + struct ncp_request_header *h + = (struct ncp_request_header *)(server->ncp_data); + int result; + + 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 = 1; + h->function = 0; + + if ((result = do_ncp_call(server, sizeof(*h))) != 0) { + return result; + } + + close(server->ncp_sock); + close(server->wdog_sock); + kill(server->wdog_pid, SIGTERM); + wait(NULL); + return 0; +} + + +int +ncp_disconnect(struct ncp_server *server) +{ + int result = -1; + + switch (server->is_connected) { + case CONN_MOUNTED: + result = close(server->mount_fid); + case CONN_SOCKET: + result = ncp_user_disconnect(server); + default: + } + if (result >= 0) { + server->is_connected = NOT_CONNECTED; + } + return result; +} + + +int +ncp_connect_mount(struct ncp_server *server, const char *mount_point) +{ + server->mount_fid = open(mount_point, O_RDONLY, 0); + + if (server->mount_fid == -1) { + return -1; + + } + + server->is_connected = CONN_MOUNTED; + return 0; +} + +static int +ncp_request(struct ncp_server *server, int function) +{ + switch (server->is_connected) { + case CONN_MOUNTED: + return ncp_ioctl_request(server, function); + case CONN_SOCKET: + return ncp_user_request(server, function); + default: + } + return -ENOTCONN; +} + static inline int min(int a, int b) { if (acurrent_size = 0; - server->packet = server->ncp_data; + server->packet = + &(server->ncp_data[sizeof(struct ncp_request_header)]); server->has_subfunction = 0; } @@ -160,7 +510,7 @@ ncp_init_request_s(struct ncp_server *server, int subfunction) static char * ncp_reply_data(struct ncp_server *server, int offset) { - return &(server->packet[sizeof(struct ncp_reply_header) + offset]); + return &(server->ncp_data[sizeof(struct ncp_reply_header) + offset]); } static byte @@ -813,6 +1163,36 @@ ncp_do_lookup(struct ncp_server *server, return 0; } +int +ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, + struct nw_info_struct *file, + __u32 info_mask, + struct nw_modify_dos_info *info) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, 7); /* subfunction */ + ncp_add_byte(server, 0); /* dos name space */ + ncp_add_byte(server, 0); /* reserved */ + ncp_add_word(server, 0x8006); /* search attribs: all */ + + ncp_add_dword(server, info_mask); + ncp_add_mem(server, info, sizeof(*info)); + ncp_add_handle_path(server, file->volNumber, + file->DosDirNum, 1, NULL); + + if ((result = ncp_request(server, 87)) != 0) { + ncp_unlock_server(server); + return result; + } + + ncp_unlock_server(server); + return 0; +} + + + int ncp_del_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name) diff --git a/ncplib_user.h b/util/ncplib_user.h similarity index 87% rename from ncplib_user.h rename to util/ncplib_user.h index a26441c..6541cd2 100644 --- a/ncplib_user.h +++ b/util/ncplib_user.h @@ -4,21 +4,50 @@ #include #include #include +#include + +enum connect_state { + NOT_CONNECTED = 0, + CONN_MOUNTED, + CONN_SOCKET +}; struct ncp_server { int current_size; int has_subfunction; - int mount_fid; int silent; int ncp_reply_size; char *packet; int lock; + enum connect_state is_connected; + + int mount_fid; + + struct sockaddr_ipx addr; + int ncp_sock; + int wdog_sock; + int wdog_pid; + __u8 sequence; + __u16 connection; + int completion; + int conn_status; + int reply_size; + char ncp_data[NCP_PACKET_SIZE]; }; #include +int +ncp_connect_mount(struct ncp_server *server, const char *mount_point); + +int +ncp_connect(struct ncp_server *server); + +int +ncp_disconnect(struct ncp_server *server); + int ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target); @@ -128,10 +157,17 @@ ncp_do_lookup(struct ncp_server *server, char *path, /* may only be one component */ struct nw_info_struct *target); +int +ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, + struct nw_info_struct *file, + __u32 info_mask, + struct nw_modify_dos_info *info); + int ncp_del_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name); + int ncp_open_create_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name, diff --git a/ncpmount.c b/util/ncpmount.c similarity index 77% rename from ncpmount.c rename to util/ncpmount.c index d40ba91..bf7f173 100644 --- a/ncpmount.c +++ b/util/ncpmount.c @@ -28,6 +28,8 @@ extern pid_t waitpid(pid_t, int *, int); #include #include #include +#include +#include #include #include @@ -148,6 +150,134 @@ ipx_print_saddr(struct sockaddr_ipx* sipx) ipx_fprint_saddr(stdout,sipx); } +static int +ipx_make_reachable(__u32 network) +{ + struct rtentry rt_def; + /* Router */ + struct sockaddr_ipx *sr = (struct sockaddr_ipx *)&rt_def.rt_gateway; + /* Target */ + struct sockaddr_ipx *st = (struct sockaddr_ipx *)&rt_def.rt_dst; + + fd_set rd, wr, ex; + struct timeval tv; + + struct ipx_rip_packet rip; + struct sockaddr_ipx addr; + int addrlen; + int sock; + int opt; + int res=-1; + int i; + + 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(&addr, 0, sizeof(addr)); + addr.sipx_family=AF_IPX; + addr.sipx_network=htonl(0x0); + addr.sipx_port=htons(0x0); + addr.sipx_type=IPX_RIP_PTYPE; + + if(bind(sock,(struct sockaddr*)&addr,sizeof(addr))==-1) + { + perror("bind"); + goto finished; + } + + addr.sipx_family = AF_IPX; + addr.sipx_network = htonl(0x0); + addr.sipx_port = htons(IPX_RIP_PORT); + addr.sipx_type = IPX_RIP_PTYPE; + ipx_assign_node(addr.sipx_node, IPX_BROADCAST_NODE); + + rip.operation = htons(IPX_RIP_REQUEST); + rip.rt[0].network = htonl(network); + + if (sendto(sock, &rip, sizeof(rip), 0, + (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("sendto"); + goto finished; + } + + do + { + + FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); + FD_SET(sock, &rd); + + tv.tv_sec = 3; + tv.tv_usec = 0; + + if (select(sock+1, &rd, &wr, &ex, &tv) == -1) + { + goto finished; + } + + if (FD_ISSET(sock, &rd)) + { + int len; + + addrlen = sizeof(struct sockaddr_ipx); + + len = recvfrom(sock, &rip, sizeof(rip), 0, + (struct sockaddr *)sr, &addrlen); + + if (len < sizeof(rip)) + { + continue; + } + } + else + { + goto finished; + } + } while (ntohs(rip.operation) != IPX_RIP_RESPONSE); + + if (rip.rt[0].network != htonl(network)) { + goto finished; + } + + rt_def.rt_flags = RTF_GATEWAY; + st->sipx_network = htonl(network); + sr->sipx_family = st->sipx_family = AF_IPX; + i = 0; + do { + res = ioctl(sock, SIOCADDRT, &rt_def); + i++; + } while ((i < 5) && (res < 0) && (errno == EAGAIN)); + + if (res != 0) { + + switch (errno) { + case ENETUNREACH: + fprintf(stderr, + "%s: Router network (%08lX) not reachable.\n", + progname, htonl(sr->sipx_network)); + break; + default: + perror("ioctl"); + break; + } + goto finished; + } + + finished: + close(sock); + return res; +} + int ipx_sap_find_server(char *name, int server_type, int timeout, struct sockaddr_ipx *result) @@ -160,16 +290,26 @@ ipx_sap_find_server(char *name, int server_type, int timeout, int name_len = strlen(name); fd_set rd, wr, ex; struct timeval tv; - int packets; + struct sap_server_ident *ident; + + struct ncp_server server; + struct nw_property prop; + struct net_address + { + __u32 network __attribute__ ((packed)); + __u8 node[6] __attribute__ ((packed)); + __u16 port __attribute__ ((packed)); + } *n_addr = (struct net_address *)∝ + if (name_len > 48) { - return -1; - } + return -1; + } sock=socket(AF_IPX,SOCK_DGRAM,PF_IPX); if (sock==-1) { - return -1; - } + return -1; + } opt=1; /* Permit broadcast output */ @@ -191,7 +331,7 @@ ipx_sap_find_server(char *name, int server_type, int timeout, goto finished; } - *(unsigned short *)data = htons(0x0001); + *(unsigned short *)data = htons(IPX_SAP_NEAREST_QUERY); *(unsigned short *)&(data[2]) = htons(server_type); memset(&ipxs, 0, sizeof(ipxs)); @@ -202,58 +342,83 @@ ipx_sap_find_server(char *name, int server_type, int timeout, ipxs.sipx_type=IPX_SAP_PTYPE; if (sendto(sock, data, 4, 0, - (struct sockaddr *)&ipxs, sizeof(ipxs)) < 0) { + (struct sockaddr *)&ipxs, sizeof(ipxs)) < 0) + { perror("sendto"); goto finished; } - packets = 10; - while (packets > 0) { + do + { - packets -= 1; - 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"); + + if (select(sock+1, &rd, &wr, &ex, &tv) == -1) + { goto finished; } - - if (FD_ISSET(sock, &rd)) { + + 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; - } + + if (len < 96) + { + continue; } } else { - printf("nobody answered, server %s not found\n", - name); - exit(1); + goto finished; + } + } while (ntohs(*((__u16 *)data)) != IPX_SAP_NEAREST_RESPONSE); + + ident = (struct sap_server_ident *)(data+2); + + /* If the server we got back is the correct one, we normally + would not need to ask for the NET_ADDRESS property. But we + try to connect anyway to check whether there's a valid + route to the server's internal network. Because this one + request is not very expensive, we always do it. */ + + server.addr.sipx_family = AF_IPX; + server.addr.sipx_network = ident->server_network; + server.addr.sipx_port = ident->server_port; + ipx_assign_node(server.addr.sipx_node, ident->server_node); + + if (ncp_connect(&server) != 0) + { + if ( (errno != ENETUNREACH) + || (ipx_make_reachable(ntohl(server.addr.sipx_network))!=0) + || (ncp_connect(&server) != 0)) { + goto finished; } } + if (ncp_read_property_value(&server, NCP_BINDERY_FSERVER, + name, 1, "NET_ADDRESS", + &prop) != 0) + { + ncp_disconnect(&server); + goto finished; + } + + if (ncp_disconnect(&server) != 0) + { + goto finished; + } + + result->sipx_family = AF_IPX; + result->sipx_network = n_addr->network; + result->sipx_port = n_addr->port; + ipx_assign_node(result->sipx_node, n_addr->node); + + res = 0; + finished: close(sock); return res; @@ -455,6 +620,7 @@ main(int argc, char *argv[]) struct stat st; struct ncp_server serv; struct ncp_server *server = &serv; + char mount_name[256]; int fd; int Got_Password; @@ -536,8 +702,12 @@ main(int argc, char *argv[]) addr.sipx_type = NCP_PTYPE; if (bind(ncp_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - fprintf(stderr, "bind(ncp_sock, ): %s\n", + fprintf(stderr, "\nbind: %s\n", strerror(errno)); + fprintf(stderr, + "\nMaybe you want to use \n" + "ipx_configure --auto_interface=on --auto_primary=on\n" + "and try again after waiting a minute.\n\n"); exit(1); } @@ -626,23 +796,19 @@ main(int argc, char *argv[]) printf("mount failed\n"); close(wdog_sock); close(ncp_sock); - printf("Maybe you have no route to the internal net " - "of your server.\n"); return -1; } close(ncp_sock); close(wdog_sock); - server->mount_fid = open(mount_point, O_RDONLY, 0); - server->silent = 1; - - if (server->mount_fid == -1) { + if (ncp_connect_mount(server, mount_point) != 0) { fprintf(stderr, "Could not open %s: %s\n", mount_point, strerror(errno)); umount(mount_point); return -1; } + server->silent = 1; if (ncp_login_user(server, data.username, data.password) != 0) { fprintf(stderr, "login failed\n"); @@ -654,7 +820,11 @@ main(int argc, char *argv[]) return -1; } - ment.mnt_fsname = server_name; + strcpy(mount_name, server_name); + strcat(mount_name, "/"); + strcat(mount_name, data.username); + + ment.mnt_fsname = mount_name; ment.mnt_dir = mount_point; ment.mnt_type = "ncpfs"; ment.mnt_opts = "rw"; diff --git a/ncptest.c b/util/ncptest.c similarity index 57% rename from ncptest.c rename to util/ncptest.c index eb26532..c818658 100644 --- a/ncptest.c +++ b/util/ncptest.c @@ -39,7 +39,222 @@ extern pid_t waitpid(pid_t, int *, int); #include "ipxutil.h" static char *progname; -static char *mount_point; + +static void +str_upper(char *name) +{ + while (*name) { + *name = toupper(*name); + name = name + 1; + } +} + +struct sap_query { + unsigned short query_type; /* net order */ + unsigned short server_type; /* net order */ +}; + +struct sap_server_ident { + unsigned short server_type __attribute__ ((packed)); + char server_name[48] __attribute__ ((packed)); + IPXNet server_network __attribute__ ((packed)); + IPXNode server_node __attribute__ ((packed)); + IPXPort server_port __attribute__ ((packed)); + 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; + char name[strlen(_name)+1]; + int name_len = strlen(_name); + fd_set rd, wr, ex; + struct timeval tv; + int packets; + + if (name_len > 48) { + return -1; + } + + strcpy(name, _name); + str_upper(name); + + 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; + } + + packets = 10; + while (packets > 0) { + + packets -= 1; + + 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; + } + } + } + else + { + printf("nobody answered, server %s not found\n", + name); + exit(1); + } + } + + 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; +} + void test_filesearch(struct ncp_server *server) @@ -178,6 +393,43 @@ test_trunc(struct ncp_server *server) return; } +void +test_touch(struct ncp_server *server) +{ + struct nw_info_struct sys; + struct nw_info_struct me; + struct nw_info_struct blub; + int info_mask; + struct nw_modify_dos_info info; + + if (ncp_do_lookup(server, NULL, "SYS", &sys) != 0) { + printf("lookup error\n"); + return; + } + if (ncp_do_lookup(server, &sys, "ME", &me) != 0) { + printf("lookup error\n"); + return; + } + if (ncp_do_lookup(server, &me, "BLUB.TXT", &blub) != 0) { + printf("lookup error\n"); + return; + } + + info_mask = 0; + memset(&info, 0, sizeof(info)); + + info_mask |= DM_MODIFY_DATE; + info_mask |= DM_MODIFY_TIME; + + if (ncp_modify_file_or_subdir_dos_info(server, &blub, info_mask, + &info) != 0) { + printf("modify error\n"); + return; + } + + return; +} + void test_ls(struct ncp_server *server) { @@ -351,57 +603,37 @@ test_print(struct ncp_server *server) int main(int argc, char **argv) { - struct stat st; struct ncp_server serv; struct ncp_server *server = &serv; - progname = argv[0]; - if (geteuid() != 0) { - fprintf(stderr, "%s must be installed suid root\n", progname); - exit(1); - } - - if (argc == 2) { - mount_point = argv[1]; - } else { - fprintf(stderr, "usage: %s mount-point\n", progname); - printf("defaulting to %s mnt\n", progname); - mount_point = "mnt"; + if (argc != 2) { + printf("usage: %s server\n", argv[0]); + exit(1); } + + if (ipx_sap_find_server(argv[1], IPX_SAP_FILE_SERVER, + 3, &(serv.addr)) != 0) { + printf("could not find server %s\n", argv[1]); + exit(1); + } + + if (ncp_connect(server) != 0) { + printf("could not connect\n"); + exit(1); + } + + if (ncp_login_user(server, "me", "ME") != 0) { + printf("login error\n"); + exit(1); + } + + test_touch(server); + + ncp_disconnect(server); - if (stat(mount_point, &st) == -1) { - fprintf(stderr, "could not find mount point %s: %s\n", - mount_point, strerror(errno)); - exit(1); - } - - server->mount_fid = open(mount_point, O_RDONLY, 0); - server->silent = 0; - - if (server->mount_fid == -1) { - fprintf(stderr, "Could not open %s: %s\n", - mount_point, strerror(errno)); - return -1; - } - -#if 0 - for (i=0; i<5; i++) { - struct ncp_volume_info info; - ncp_get_volume_info_with_number(server, i, &info); - printf("vol %d: %s\n", i, info.volume_name); - } - - test_filesearch(server); - test_getfinfo(server); - test_mkdir(server); - test_ls(server); - -#endif - test_trunc(server); - return 0; } diff --git a/util/ncpumount.c b/util/ncpumount.c new file mode 100644 index 0000000..f70040c --- /dev/null +++ b/util/ncpumount.c @@ -0,0 +1,197 @@ +/* + * ncpumount.c + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#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 + +static char *progname; + +static void +usage(void) +{ + printf("usage: %s mount-point\n", progname); +} + +static int +umount_ok(const char *mount_point) +{ + int fid = open(mount_point, O_RDONLY, 0); + uid_t mount_uid; + + if (fid == -1) { + fprintf(stderr, "Could not open %s: %s\n", + mount_point, strerror(errno)); + return -1; + } + + if (ioctl(fid, NCP_IOC_GETMOUNTUID, &mount_uid) != 0) { + fprintf(stderr, "%s probably not ncp-filesystem\n", + mount_point); + return -1; + } + + if ( (getuid() != 0) + && (mount_uid != getuid())) { + fprintf(stderr, "You are not allowed to umount %s\n", + mount_point); + return -1; + } + + close(fid); + return 0; +} + +/* Make a canonical pathname from PATH. Returns a freshly malloced string. + It is up the *caller* to ensure that the PATH is sensible. i.e. + canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.'' + is not a legal pathname for ``/dev/fd0.'' Anything we cannot parse + we return unmodified. */ +char * +canonicalize (const char *path) +{ + char *canonical = malloc (PATH_MAX + 1); + + if (path == NULL) + return NULL; + + if (realpath (path, canonical)) + return canonical; + + strcpy (canonical, path); + return canonical; +} + + +int +main(int argc, char *argv[]) +{ + int fd; + + char* mount_point; + + struct mntent *mnt; + FILE* mtab; + FILE* new_mtab; + + progname = argv[0]; + + if (geteuid() != 0) { + fprintf(stderr, "%s must be installed suid root\n", progname); + exit(1); + } + + if (argc != 2) { + usage(); + exit(1); + } + + mount_point = canonicalize(argv[1]); + + if (umount_ok(mount_point) != 0) { + exit(1); + } + + if (umount(mount_point) != 0) { + fprintf(stderr, "Could not umount %s: %s\n", + mount_point, strerror(errno)); + exit(1); + } + + if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) + { + fprintf(stderr, "Can't get "MOUNTED"~ lock file"); + return 1; + } + close(fd); + + if ((mtab = setmntent(MOUNTED, "r")) == NULL) { + fprintf(stderr, "Can't open " MOUNTED ": %s\n", + strerror(errno)); + return 1; + } + +#define MOUNTED_TMP MOUNTED".tmp" + + if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) { + fprintf(stderr, "Can't open " MOUNTED_TMP ": %s\n", + strerror(errno)); + endmntent(mtab); + return 1; + } + + while ((mnt = getmntent(mtab)) != NULL) { + if (strcmp(mnt->mnt_dir, mount_point) != 0) { + addmntent(new_mtab, mnt); + } + } + + endmntent(mtab); + + if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { + fprintf(stderr, "Error changing mode of %s: %s\n", + MOUNTED_TMP, strerror(errno)); + exit(1); + } + + endmntent(new_mtab); + + if (rename(MOUNTED_TMP, MOUNTED) < 0) { + fprintf(stderr, "Cannot rename %s to %s: %s\n", + MOUNTED, MOUNTED_TMP, strerror(errno)); + exit(1); + } + + if (unlink(MOUNTED"~") == -1) + { + fprintf(stderr, "Can't remove "MOUNTED"~"); + return 1; + } + + return 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 8 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -8 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * End: + */ diff --git a/nwcrypt.c b/util/nwcrypt.c similarity index 100% rename from nwcrypt.c rename to util/nwcrypt.c diff --git a/nwcrypt.h b/util/nwcrypt.h similarity index 100% rename from nwcrypt.h rename to util/nwcrypt.h diff --git a/start_ipx b/util/start_ipx similarity index 100% rename from start_ipx rename to util/start_ipx