From 64f8406b07f5713c01d6c82dfe6cd18c68794d30 Mon Sep 17 00:00:00 2001 From: dimok321 <15055714+dimok789@users.noreply.github.com> Date: Thu, 16 Sep 2010 19:59:41 +0000 Subject: [PATCH] *Mem2 fix *updated libntfs (write fix) *updated libfat *lots of changes in the startup code, removed almost everything. This might cause problems for some drives at loading the gamelist and needs to be adjusted later but better this time. more cleanup is needed in main.cpp and will come. *using libogc sd/usb for config loading and reload to cIOS afterwards *added missing boothomebrew stuff pune forgot NOTE: From now on we will be doing a lot of revs which we won't be compiling and releasing. This revs are officially not available for public so don't making issues regarding those revs. Those will be closed right away. We need first to cleanup a lot of crap and update loader to new standards before releasing stuff again. --- HBC/META.XML | 4 +- data/app_booter.dol | Bin 0 -> 100768 bytes gui.pnproj | 2 +- source/homebrewboot/BootHomebrew.c | 151 --- source/homebrewboot/BootHomebrew.cpp | 131 +++ source/homebrewboot/BootHomebrew.h | 12 +- source/homebrewboot/HomebrewBrowse.cpp | 211 ++--- source/homebrewboot/dolloader.c | 10 +- source/homebrewboot/dolloader.h | 6 +- source/homebrewboot/elf_abi.h | 590 ------------ source/homebrewboot/elfloader.c | 101 -- source/homebrewboot/elfloader.h | 17 - source/libfat/bit_ops.h | 6 +- source/libfat/cache.h | 130 +++ source/libfat/common.h | 53 +- source/libfat/directory.c | 46 +- source/libfat/directory.h | 19 +- source/libfat/disc.h | 110 +++ source/libfat/fat.h | 32 +- source/libfat/fat_cache.c | 22 +- source/libfat/{disc_fat.c => fat_disc.c} | 50 +- source/libfat/fatdir.c | 2 +- source/libfat/fatdir.h | 10 +- source/libfat/fatfile.c | 2 +- source/libfat/file_allocation_table.h | 12 +- source/libfat/filetime.h | 6 +- source/libfat/libfat.c | 62 +- source/libfat/libfatversion.h | 10 + source/libfat/lock.h | 4 +- source/libfat/mem_allocate.h | 7 +- source/libfat/partition.c | 10 +- source/libfat/partition.h | 11 +- source/libntfs/acls.c | 19 +- source/libntfs/acls.h | 2 - source/libntfs/attrib.c | 859 +++++++++++++---- source/libntfs/attrib.h | 17 + source/libntfs/attrib_frag.c | 2 +- source/libntfs/cache.c | 933 ++++++++++++------- source/libntfs/cache.h | 223 ++--- source/libntfs/cache2.c | 374 ++++++++ source/libntfs/cache2.h | 135 +++ source/libntfs/collate.c | 143 +-- source/libntfs/collate.h | 5 +- source/libntfs/compat.c | 4 +- source/libntfs/compress.c | 599 ++++++++++-- source/libntfs/compress.h | 6 +- source/libntfs/config.h | 175 ++-- source/libntfs/device_io.h | 18 +- source/libntfs/dir.c | 809 +++++++++++----- source/libntfs/dir.h | 25 +- source/libntfs/efs.c | 248 +++-- source/libntfs/efs.h | 9 +- source/libntfs/gekko_io.c | 90 +- source/libntfs/gekko_io.h | 6 +- source/libntfs/index.c | 19 +- source/libntfs/index.h | 5 +- source/libntfs/inode.c | 386 ++++++-- source/libntfs/inode.h | 42 +- source/libntfs/lcnalloc.c | 38 +- source/libntfs/lcnalloc.h | 1 + source/libntfs/logfile.c | 8 +- source/libntfs/mft.c | 10 +- source/libntfs/misc.c | 303 ------ source/libntfs/misc.h | 44 - source/libntfs/ntfs.c | 178 ++-- source/libntfs/ntfs.h | 22 +- source/libntfs/ntfsdir.c | 14 +- source/libntfs/ntfsdir.h | 1 + source/libntfs/ntfsfile.c | 2 +- source/libntfs/ntfsfile.h | 4 +- source/libntfs/ntfsfile_frag.c | 97 +- source/libntfs/ntfsinternal.c | 12 +- source/libntfs/ntfstime.h | 74 +- source/libntfs/object_id.c | 637 +++++++++++++ source/libntfs/object_id.h | 35 + source/libntfs/param.h | 82 ++ source/libntfs/reparse.c | 514 +++++----- source/libntfs/reparse.h | 14 +- source/libntfs/runlist.c | 37 +- source/libntfs/runlist.h | 3 +- source/libntfs/security.c | 485 +++++++--- source/libntfs/security.h | 67 +- source/libntfs/types.h | 6 +- source/libntfs/unistr.c | 291 ++++-- source/libntfs/unistr.h | 16 +- source/libntfs/volume.c | 135 ++- source/libntfs/volume.h | 41 +- source/main.cpp | 392 +------- source/memory/mem2.cpp | 45 +- source/memory/mem2.h | 50 +- source/memory/mem2alloc.cpp | 439 +++++---- source/memory/{mem2alloc.h => mem2alloc.hpp} | 83 +- source/menu.cpp | 12 +- source/menu/menu_disclist.cpp | 16 +- source/sys.cpp | 84 +- source/sys.h | 3 +- source/video.cpp | 6 +- 97 files changed, 6994 insertions(+), 4299 deletions(-) create mode 100644 data/app_booter.dol delete mode 100644 source/homebrewboot/BootHomebrew.c create mode 100644 source/homebrewboot/BootHomebrew.cpp delete mode 100644 source/homebrewboot/elf_abi.h delete mode 100644 source/homebrewboot/elfloader.c delete mode 100644 source/homebrewboot/elfloader.h create mode 100644 source/libfat/cache.h create mode 100644 source/libfat/disc.h rename source/libfat/{disc_fat.c => fat_disc.c} (73%) create mode 100644 source/libfat/libfatversion.h create mode 100644 source/libntfs/cache2.c create mode 100644 source/libntfs/cache2.h create mode 100644 source/libntfs/object_id.c create mode 100644 source/libntfs/object_id.h create mode 100644 source/libntfs/param.h rename source/memory/{mem2alloc.h => mem2alloc.hpp} (95%) diff --git a/HBC/META.XML b/HBC/META.XML index 3a148aba..5b8486e5 100644 --- a/HBC/META.XML +++ b/HBC/META.XML @@ -2,8 +2,8 @@ USB Loader GX USB Loader GX Team - 1.0 r943 - 201009161155 + 1.0 r944 + 201009161846 Loads games from USB-devices USB Loader GX is a libwiigui based USB iso loader with a wii-like GUI. You can install games to your HDDs and boot them with shorter loading times. The interactive GUI is completely controllable with WiiMote, Classic Controller or GC Controller. diff --git a/data/app_booter.dol b/data/app_booter.dol new file mode 100644 index 0000000000000000000000000000000000000000..2f1df769b1e92bf560d22f9fb149c6a12530f331 GIT binary patch literal 100768 zcmdqK4|tT-wdlR)4`f1Oo%YW0j2dAA(IyhZSZVJJ6OxHUI}q$i>*Rx4TPG)>f4p3+3iQZr;KRo&Y&F|qDTQRAWV#+Y(UkEw4g4bgOn zI+7piEP4NVHqC5#6lc$$wkSD8DMDYZ8qC5#6lc$$w zkS7Zq0bnZK9u1Y|j&w2A_mt+gR!2#@eBs9Fy*;JPKUo_1lBohlLiHtD0?J4@%48f{ zT??~E0;=mslqbPs^7Qfy@?=%lQNBB zQU0iRNb2?MSH9Y8&qV5%ES+#ypr=E5rkG0gtWm{1o0T`ZS{b!w&%|2hJ+kV~Y-H1& z2h$r5HP#OHOiXV&r@f=*uCmCcTrR!rP&B$qRYvyRRTi#Lj;4TjT6c|7Mv1C23SE6) zGTo&ed`r2yRk`eRpb8Z#C4JScR$k_2VyNZLMC_%bGeb%_Rssv-D9Heij3om+8Q{qP zFXQsdxCDO2F%0a*lr1J*mmQfW{Yd_ZzWmZxwQ2Pi>b$Ew_R^8xN}B0fszNPG^Dvd1 z0%LK09H;ARG&q#gU6P-p%O6x@z^Ny_)tWDJd~PT`_?E9BcMLvi`c_AcsX8i@GM6c@ zxj+@qe)`fSF*W)2nB%ff-&1_>BHtW&Ah&+iUpr?^@$;^zXyQ zIr?{l@wfW7ziYFq?Aot_-3~RiyHtg`>r{1jvx;_isQT_TD%QPO&F8V$`e$|*cE zTRAGjo0XcL?U`t_U*1<~$pnva>E(T)7UeMiZRy=c`;5NO)5_7sd*~+Ri8;p2GrBJC zGyezQow?l9mR!#0n$g!K-&IgwZn zqp?i?4jLW$_a39`fTXW9y7liWV}<_hHG1^#TH_x5`>^qS{ky@qSO4}K|8$_QgRylm z#tz2X!I(Q3dj~k^02dwL!~{1cI5O9$a>G=8@lN;4@y>$x4b#E{^X@b2R0;hT8$WOf z99}77+^^ru%w`n`l{$CNR;tx#e;>KyRz~{=&_1-P+#9U4=!eqcE$-eAm-C(|M_cih zF=Z#poL6bJIpGcK*T2yI-C+HmuYb$kioqgl=8erEQ#_$g;OkTq;+=aI3tkO%{s!hS zfZWkC<}a7`A1`08lvDcp2z3_b`}i1jj`#C%`}k<(C%ln|>7$iDukz!SpKu(0nRnqK z>BpLX&+&PcbSusLP#QGxjr@F{D91XMrzcGl&|dyqP0(|Gyf14_aEt6g#up1u8|m|t z0#7KRg2LPK&iem((F4*4K1fQM(0E45BXc#(x4z%knlM$|<@^uh))BN!db~v~mNaQc zmp#$va6>Nlq9JvmFI`5;Ckl?0E8+j#JCYY~vD(z{Bl3XZWLe(J8omyG=#@3xM9+Y) zH2t1eX(<0fp$XqIXr%z^LR>l z#xJ+#Ntwz}j3KcB~cH;0tH9X{to$ zgq(3ReRuKP%Cpj@hZe7@?t~6+rtdDETX|MmFi`j6YID%4dmD9ra7vxqDEkAeti8Sl z1$V}LXP@9xa2U#>6PdsuG~VLK8AG(w&C_nRWeoFvA5YS5JJCTom8?GTtZcS3KYO%eXwbkF!5sA1~0yFVEbEt#^xz9X684A51(Wd7!T$_x`!I zoQ3YuKTefrC*-6hn?;r<+yP5ADo;c1y~DCDkoNZ{(*C^Jl9SkF@C|lZG5uV?(@gtX zW{bXmTyCG}vC@z>--^Op1;xPJkMvS@`ZvM zLrvKbT&Fx!J9EQPGxtV*-9ml^Qr`Fbnmx*s^4hY5@_;EeEqWsB$%e~+Z#={EO0coK z;L~MwX6~Jl`P0=is@W41UUGFURb^fCRoRIAVtHqMI>NJ3=Fq4pN`AjQH=J_JNOlz} zw{b|z%u@6~Wsc#Swa{2IIVo~DeaH!3dIE`-aqZy#-LzE}JM8~M`nhVp{p0KBFaO;9 z|B-&)n^8|CI+dfJ-`y^?b@wE-bN39jdv}Y}F^uCaBJp95#j{X<6I39oDfMf88a{uFQmt*Tz;IMRo z38Jre4!ImVhbB3856y67hguwmhBA(!p@$s9LtC6`=z!DnrpxJlbCT2l<_u@x%@$|* zn;B>D&4-+!H@7&WZys>Q-f}q`-F+;;k0v{I@dB_O~8#Cg0lPG~YVlT>7@l z+4c4$=gPNdID6l2aX$QZ#@YY&L(a$F-r^j5`+#$+(7W);Xpi6z+Xy%v{;j|+evOf^ zXMsHn>{(#X0(%zNv%sDO_AIdP2loBIz8~241N(kp-w*8jfqg%)zYOdz1N+Os{xYz? z4D2rh`^&)oGO!;2_5;9v0N4)z`vG7-0PF{V{Q$5FF1JonJGahIySFl@TQlm=)`!&4 z)-7syD|5PyIo-ybZevcjwKxLXnA2^{={Dwc8*{pyIo-~jZf8!nGpE~`)9uXZcII?D zbGm~$-NBshU`}^1r#qO_9n9$t=5z;hdM4ZnK1BwK%y@wjAymAz z*yLNVPoKgrKY*=zP{mfb)a)g0HG9P*)wrZgHLjSU;!EmPd_{|zyQEdkU6E1OF6mU) zu6Rf#maJ8Y6B{Qum z*^^O5rc)U`4=FRVR+&9pRA=TX)!B1EEzKNMOO1Yf2oI^d&9&-oqWulzZ!oqx`;7nfW`O?t$?qqxPxM^!HVo(ef_0@^zIde^1rkK zmdpG2E$do~&m+OOTY+W1Q_>zc7Uk2mEI}Rk)oiiwQi{(cS`Qz)%r2-|H<`q zz@g%yX3;IqyX60-HSU+dmG?s(_d$vO34ISZkNcoT^FRHc#9vHhEBt1Z= zUB*G$N!abQeJz>3bFOYD)i%o~zTNb)xw;+W)wy2d)i4j^yfe_)G|LC&Hj3aQeY^DU@EGeEANv5&A2|GVt5k9Nj)1f`1fCpHf3&(&?x6f?RUD2g zN9y9s?oE}`#!^Xh>@*et|HT$QLcQd>uD-Xa6`Xj!yvx{Xk0k+4)TCtk4kcqLF%&rQ z$XJy04Gi+@tT)Cg3m4|q_onKpFSr<|8skdp3odkh{AhVx==wfza564Dlz(J0v@l~- zTnJ5^ZmfaB%=x-l(AZHOG!|C}Q}bx2B^HeTxU(j{fq7be^}R+B>22pHo33Y^Psf5O z_m_924>s>gd4?LB=sV?!B-0N@t$FiYY|Y#4Q5mbxPA`_ZOp%wG5Vgj9h%tZh%ezu{ z(#|;2q@5zhe0P5SY3EgNcDptIc6xED%}P%#oZA?`i*dik{ErKslU^Q;8rv`3#T*BC zSE>4|lQQP$>Rhp*V!^Z-j7B%+ROqf;PSV*Ec_&p74VKPl{ON0hGN;CaGFIN9b)zL3 zOtqY!j0?T51R=OUqMez6)jCm^;M(o*v&JBzU{w|Tph`U$)K4Ob`AosEJse`=Q0Z?t%Uw7LgefmD5CW2(M7 zDC_yCyu2y*#tB|ldo5nJ_$hUz@}}l})hoO#^L}dm(YaVypomv2V zQ&my8&@*G}_DTI!0%M*Ib;bY`9(k-;r-iyz(bVEjj&H7O5td?0eKv2;c< z{)oG#X*_Vkk3!$ZgW+AN2fw<@=tPzZt#rN(F9j?b?Y!6*$q6q7EE=`aizUq<&$v4P z{{ed^ZNEV~3uy-#tJ|S3;VtIPC;S_}TiTIt$J|s=v~kX>(8isSWa@f&5c)7)6&`d{ z^3J}iW3F*0vZ}4lYuwNnG&*N9CLUz=sdJR72nI{9#5cp3Wj>4y^T3=K2>F>8TT5kR{hir(pX2N?R4iT zv~8tn+BU`k1H1sQWsL$?GN70zS%36L8rPeSJuG+@e3Q0H=!3FZo7Y#S%}}&%V{U7zXol43olacBboQ4!E?#&j z+;->V6|8r&?^1y-zRfn}F_)vuWdf@14&~{1M7c@xk>=NFedISNZ$mCC-^5)68fSM3 z4W84Uj~(dTqTH2Z^`1eo@$}#OusIZa0oK038R|$`^Y`fIls8_b9`DpUr1ce#wYSbb zZ7DqO40X1dtf!xnJ?@y}^AC{bh$)vNso>8@)H^NRc_5jJ9IDD}BaUFp+sT;Yk_)F$N8XX0B{4_U=Z$YeYncP&RaK<_$uF@5 z3CT~_|Aw^6cc)BHXG)Y!|Pw;eLjAj8br-BC8e-&B=PFrK(lclx2EM@yr77(ei@erXs!e^`x;FeDIzA zwZ_+@(gwJyOids9X)5)Wk9yNWg}I%Rl`0gR)ZM4NON*2PopT!Bp(zilvo)Sp=T+*U$|;1vcYa+s;uD>Q$y^EBfT8|MB&^ULy}4I}!VonUqBS*6F?CALe#K z^9oo>!xamM%#3o!m3Nx7ld4cvW}zy!Ve_RQLMAZpdOk%qz^AnH1a#0f0=}%s9EleI zt}^(ebxJ3Dz~-c6H~_yqppvX-rP4>ZZQ)R=jXsK$-|izMZKlc^j`zb@$_#W}V*&Jo zd=wopHK`$as?3nK!%<)+FJEqC8G|u*tx=w?&8nC^gQ@A}q`bd!{NDfi@vDzCe)hGm z6&ci!`{0#R$7|!r+B>yq)YmydY1tbcP+plU)>8*{E99Nu{j~9+JyOwK#GaXKx|OZJ zOw=@8vAxw;psGgbL5GCT!=ww}3a^HgTM6%`rZw z!l5d`A@cc+nB&vkvi=&WH>*Nvn0K{BGpFP6WxzoUYS$r)D;xiVmyr;*vEJl4_DdxgE!{zz9_QY82?_C#tU_H`_#+(XGY@Kjt|8~ zxpTKUA$Meosn^UB?2{vTnj~aGr!&r>(QuuoYnfB>Pohy^&q}_n|J>TsOnySIL1o7M z*vK6x^dNl4Z=|1mJsH7olOA{-*#++Rp?j^)icS|bS1aTgdci>M4Rf)Ip(o^`59G29 zx#zDUPf40NfG_=li9V?pbDWowy1>0PGN|{MeN$Geam=Bcd60tqS{D77Da+C2;SbunJJM^#ZD)?)`#Ji)FMZ^FpE*H! z+6(DxiJIF{%lZU7l$}kwuc<-}%&Rc-dQhk%jXpQVrPqvZDZ5;GD4PJ^USpNu|8Jk* z-NzgtFMhXfjiG`vcTEX*P1+n%kxi-`ov0IhmY4*L+4%ItThxH?UTcdfK8=%ki}?bN z_kn|lq0h%PeQrhvW6gwyXEW|lq(j>-)sZ#O=YCZ$>p1pHB{ohF8lKvad#eySU|qhh zS#aH4@ekK`m$H62vup3C<&$`J`Gx5yEh zE5^>4OJ_!IL*KjYJRf~|(?1xJxayrc4SiqIVve&)wY*{7?P^9QtaeP5wxtjF#?w&> zFHYc_c8mr8q1QUYt=q@m2;2-lb>Fvoaam5KNJ-jr?02LC%U22Cu3V#n9GeE zPjzAi>+eyKcfvy(v%nKFD5)-!+oK^DEh-G@P#|T1Gd9PoBnsEOmz`+UnzP;H{)Y& zr>@u!U4R?0WyRiQE=ri^0Q0Qv+?cAqy*jbEJ*J8t4XZISb{}zuW+>~dk#xpREdRO= z`gZV+oxOIm%#-MM#GY7sSH<-u#xhkEYH79O#P2u^4-_M-h+D*_dWmtjv|eoH)Iuwc z{EltHFXRX9{6_NUlHYFUuh%^Eq?&5y$H~8g{6%*DGV=Y@583&1$$yOeq@BNj{1Wo3 z?fh%Wf0=y4&Ywwsfc&VPp9r{c_(2lda*G`SZap0nw7%oD`<$14a7fnD-7)1qYMy1; z9muv4==gKeHa5hrP|G(b*yTSj{h+6m(g%9VH?NGTbLxa&Ig3=$-iCaKufSnROr3wD z$TI9B@9}n5tDwMPwUs$6a>yuhiQSP*u?O9>!2OL>09n!Yy<;&o>496a4psL<_B^nw z>^eUu{q|7eOM#fWadXEd);ff~V1z4jxo*CZ0qvq&3BL>fiJu`{@lCOhiqnVvRUy+M zwz98U%~sdvOaT&#IqWhX3VL z$Xn4Ph73i_t+M2wyeCw^DYEbkZDuW-5O_DU=Z`)SPvA$}rmB!fUSscN$?Nx-vQBGT zPw;}QD%LvJ8tl?dlhGx-U!Std(oxd)XB&-@p(wTYfUE&43{7?5$KzrM3k z;EvpO&eF)fkUz5OoI$~z;7wpkU-6q{`kOBR+p+fqhvFv_Jf>GWs=(!5@Dd0Wp4~3L zUE}eG6#7NJ%6mzu(6y6auZ3gmUJJ)%_yR3G8Td{&Cj6y=>R#IJJU0|tcW$e(w!9H~ zE({IoJ*|b4Z&!svPtdCDZ#spJ;1BV&IQe!MJ1*VTp62(dV56~xCvh(O9FASafoZ;! z>vCV}BK9J#{EClt&X-a%lBp}NP8x?IS_Y&p!fwvu>z{dbvV(QU==|EfQ|{7p7l?Oy zsyU;BA2)XlKan%#nMf?=L|@2cU5RoQ=MQRS_=e~A#ybOFc(jO^@Y^RR`*)xly_y;9 zFH)a+bh7gH|M>a6td-hE=%V}x`zR}OCi5aZsA&s6l=&21l=+jnI+Z8kx4Tp6uO&OK zQui>w6FY8H56fAz)GT}hKT#{m-w?U&`PR&{YE`Q3^<+2YBCDR?o2qy_*>#q>yPNXx zsGMP_POno_!R-XWWtzP+W1Z6%?`6yh_K>TcRe^P?ma#fg5myzy^sCaF{K@pbKr-F? z_N|;pc#657DERNFc%ie0zI*7C_Pc0*X(jwDdWA&~Cb3(NGI*}+BhdFD;cdq6m9+u< zl%#jSWBb7izO?kVw?8DO1Rt_j5N>-R-ql7d*kl#&U9QHinW@GtU9VK{Hs$O+tlVn~ z)!5!PHEvCX);Z9tj;_zc$@u$P;Oq3DhOR&Gd!?N>wTgPpN?FYes)h1*{mXGxa6?OivK6+ZL#l zxZO3A(UU!as-}uDi+krQwWdKS&OR+}nl)xIdy=w_77KkNmk&ZOCN^+RWHsf^Gg{c| zzt~ePuox|0@r?#gFMg_YhP}a6@Uo6;7+x&+(DPsMLVVc*RSB*?)lsMdO>N~!t#Z2D`YpGL~g9s$p6 z;32+24G1ifCuJnRS}E}SLOg+8C@_mZ8XrSt$4nJU-=*IJywA4ZeY`iyJjgdZSuJfe zwe49fZAcyZpAA0Q-`le|uJ$aJF*kYmC9j)w>6`iv;f0Q~kWKJIQ$^w;vq;s?^C*}3 zx|-TNHmH^^SJi^Qc=nE(js_KqcV1D|^(--Y?n%j}EAFTHeXR3>?!v8{@#%9kE)zPMAH+}Jr8qzOr`X;{%)WrU6DnR-2Zu%D*$?E|kvm;r1 zoyInSzdfVbC$e5a`;N%I7vibf29a64U5ix^Uj4qnWR6$$;fm+?3jM>=CGbGWnnE>R z#F$x~-V^Db5$Vnm?Ra(inlfu&=DrJ+a~bdax;m_1xBNC${7VgKW5{#z zJ9hFiwVrgfa*QhEyMSL0bZNt;qB4%9V^jfqnd?WS2PA#jJo=#hWt;^Fx8=-bSEw<_ z95dv}nfH-L?378HuKP%*UANFmFF3g%qsGd*kIptA?R3;D)imp0YErWb7ExB$llP8R z)~Ghp<|&u7FYhv@R0U}l(kFe%`x-ZC9@54ji|N}?1&fv*RAX1ZqQ)+Bs{%PwyYkoQ z8l8KK`SpaJQ4Y!$#Gm$5OMlY7vtk8Uv23HWtu^Irl^x3ut3GwgclyB?=Z9$d?ORRI$_-T!bs6jca`D zy^S>vN36Im89TZQu^WvgC(4cu6;ifv24^MD^DT9$g7i}4+laEXS0M8*kMPIo?2+jK z{9D*F%@x4|vIZ`N7qNGqypLa5c!i&rW zv9X`uY*JSA>%0yqI)${Gmx~un!ta5cn6O2Sm3Fn9#P$^b@tl30>PQaUrP+6`CEiZx zJpJZRnFrUhnL_vY>!1bAOMIuUoBLCFPhI!bpH5GpOC(Byi;9L6>*1JCQO;ahh%T~( zeWeLQKNUWhVloe##BWkJZ&Ayi9nk@f_CQU@EP8yiH?=C$Z8%gBGu?r*Nl z1LYdiuVc5cmYk|13vJ}}m~{4pUkloDNZZWQbHXb^YkWK8TaQ`hveeT1WR6Z9ui--P z$Pj~aqw>>#RPzJ8Jzbef^S7rYn7a5H?vaQlrEO!}SF6OfVO{1N`4y{J6CdN<4ZeSV zCY%Tz==!Iz%R4T?hiKUVPod9kMYnX5Hkg_0j*B3UV8fIe8 zs@>qqJ9#;J=O*p5xB{AZO7=mb#0o50?w0j4ls>G&)q^s}*r!kLEe?CW7%Xw&7si$} zp2m;+UFND+xu{z&-~3LEFLyqV?&sC=b3nB&S{_x>hSay~2BbYHFYVDr32it+Sv8LO zWwIve{yJaSC}oUJiM74*4e5t6o^U{XJzw~`@BwpK#`p)tztHmsHFzo=qc>HbnszE} z3l5_{q#bYz-%Y1I;zPt1aR)mtQCsaf+d7hF@kt*X$scwbKC6unF0_7+zIjTWe&pVV z?K^Bb>|Sh8exVzQ=V6Y0DQu#VzUx{On9vE+t4e~}kGCp298;Gx0q;{C(6;!sgdYr* z4FNa&kPW5UvZ0WvRH`By68gtJ-3rYQLW57ml<%u~`u>ppCvb=#2Dro)gdUIEfsEl{ z89T7XBD4)1UYlNaAi2VUon-P`s!CSWslbY6Rkor-m9JQ%Dpzb)!4><}RD9eaeB9Oe zxT8JIs-AJrM*fRmCjO!Es-B58tT8pnkW@8(3iv1D-&;KGmf4Nd?wP$clE6Z3LZ`I$T?Pnwn`hIjpEq&I~XDu-2G!OM#{Z9MtqJ&egYw6p1bQ@ORmD}t! z#0!1uyL-?_P8$3D*XefV1BY%)>ZM=#;COsSFJo*k42W+uBsi1)1aHF2f;-`7X(K&$ zOELssFP?utaUjCCpIZm)-nbfj(|xsSTtDsLb9UipD~|YiSo@=^kHta?*;~Ro_hBE( zwGx}Q1X>gNTmr2vf!3BlYfGTDCD7UuXl)6!Mm$}JJ)G(cw3dO^@V~{N=h@J6BlH|+ zt($A>A{IT9CVGdQ>xtxEct&Vd;1YU`DCH2EjRe@UqOIB}K2V|m1hhh%5tCoq6*?4t zh-_A$K}PIZI-Ye<6?|)o${m%lh<+9QrD_ES@!s*dKdq_F9b39OH@tKp_nuJ{OD^4) zd!P85QFF!n+w`fL1?*B{Pm%9?wt^@F(10`I{f=Fb>!W2^o?LoEp=A8lI&r) zz`MjCc-i+9zwvDCza2m(u|Lp|8=eQ9XuJzfwOsfJc+Ia_T9#In1VzsSH$G$aq@?ha ztYdmjoD6?1QB!Rh%G&$xE4S=`Uw)`#9W4Eky1HNLh>pTJ>c@fUjCxwfCFla*)O)YR z$JgMZnDo`zzD%7hvXU}S8A=U<^ALJe2)QWo@hNzHw!IeCFY2r>sa8>Mx>K(=+82Xu z!u?Qti}gA-_&L_6*^MLDF^krxJi%P}fwS1ya#<3%^%yv-e?-#q-y_x|trH54z|T&t z4-T+D#`kFY24$F2tqTVEmHi(tI(;BqQ6u!_!!8|zo^PksnvAQT@p(0kQ%`6dy3jgE zOufH99Az&@4QX8=HPh8c*~ifrHp*CJ55MWN{hw*NIPitmSFch{<=9}?eZ`k}b*wU8 zudBj8?C2V&iiri@8&i=hunpJ~x{>`TrvqLV9dl=OVn}r1llC$ia)0vV*F>>LWF3sG z-`gtdAhhU)X1o$BAnRcyOFIr?sJ88u@m1mr(XtDgJ|5>ZC+n%!dI5Bv&bgAAebD&o zLwcVJy51>$n1!JYwk};P`us0m;eB6wb z&9B?gZeK!)e(5L!Cyi4w9$Cj?vM!#kUE1pHxSECKO$cRhs~$@k;h?^V@sr>=u-{s?4Ziu9tX;tG1FB;=P*RD8KcgPKh)9Uz=#>TgdGX+5dTY zC$diVOSO#KcUe+oo5(p?6JzR2FQ(TYYfu09SbJ*nYss!OeyG`RCA)~%NR3N)jl-Oc zs*n0olfu64S_?nj*RfZ`Uc0~)Q~hDaSR(RE(JeHY7TU9iOFrPsp7` zBj|3v(2~rx(A}J;pg&^gg`Q&SXHN(|ppOt^X%yO`u8tERrX-%1t;9b>iG8RydM~>b zeWqB(0jv&b_c(3B7o+)`@;oJFW&MzK8-G}}@RYzUun6qJUp7oL?R&t=Oc!w$*ac}b zCcef1@95mbmM(Bq!8^ckWRK7{@6leD8keVWnUhwf3Xrjl-IKua4DOSd1bxo{=aZO= z8EQ8B-1w)C+-Ku^RM=UA9eJJxURnFt+tTZ1j&;*3x`3>mF}0~f^I`(~ztqx?L?)cj z9iKrTkv)mVae@OGdq{aiFYq+CHGVBXmm(hQoo&fl78{$Ib~f27h;PNYy6syYZE-82OYI+vfMk9A5>$}+pa}dDV>v( z*pch!;@hc81=z>0&~a;lm})-E+=XK5)~^b#p*{SrfB${b&;ycx04}G%iEjsa_xHpI zwS1L4-i=ElS;70f!|B&-|DmjHGwrw+)8hHr6*y(*JAQ}0M#&tb}&y3SytMg09k-;#Xs(c;VA+T0d=S?tIAUq??m z%=$rj9%5n-y$fxON?&=}(!NL9Zua=KZXmSQF`qe9wC(YGIv8}SxY1|4KGU}_dv$z@ z*>t)1Y;WY)(iC&d5s+F!=pG9^ALM?w+A5)16=4DJw-S%lcChF7X@w>O+_1)4o?Uor&PEJ<$ z>ev=J-w`k#n555Sh1OmBoX`rm6g{|-dzT%WW(L$_`E=$5U);Y~aZlK9F||Df3htS+ zDfl^~tSDK#saNLC9od8-H>e!{v=%wSIHZ0|{rX)arKVc%8=L2B+Ot^N2S=WkP3W;t zp{L>l(0x%irv7!ohr;unJ_WGJ%fLyw zr4xvrpwAOpG3ww88sDgGvejy#j(3=t_=ToFo`h1f}**1k;_ z=Y%a;OnRF5UUU{CeV#9{Rz;ERVHqpy^=#ou`i-(~MRhxjS=wUXI!1f-$92{OI%}KB zeubl~i*$78*`v`2^Dbvc59RT4LLP#v)98ybe$jJf+~~|PiS0rD_<_sOJz3Rv6{#5G z7Qb!DXgZ~=2b`-Q?%7kFYU6wx-w(a6{l(uB7=`wkH@)5pJvD(E6Ip0kjm8b5TN5 z4Y`NOr4N&JmVV&fU(0yw2Gq_G>Bx?OOC=o|L3TV{2VL}6tAG;O0UEUIxW^6u4$@9G zFE=)CGSYdu@p#3Y+z7dmQ_gA=_@wX7P=dar)KyY9rUvVDUm{;}_zWo%Q~&$$h%{S% zY}qs-P5W_?RqG^;`S!{_na~eo@yNJsUeox1*P@JH_~}D?D#!J3bmIsxK^rAvGmGy* z`{LK&``iCvKYXgD0iW2LvR`y)iYd4n``q-XbzVC(?vYq{^7d#~(e^03R~jnnQ}PQ8 z!WW`D9tss*zeuN@#hoQZ*Dtf)!EMox^cgwIx_K|R>v?FS-+nLV{SEuw8!Gxq$bR>Q ziZ&1vqW9YTwb>3cRP^=ANNz}Ipv0mrkB)PUsYCBs^i?agz=L0teO20D2 z9QY%0RD4Dkh6?{N3cRwvnYG5L_dNroWl7`gj;0gJ(qG{phe$tS=>iVuw-#FNyGrCk z$n?m2oy2%XvaAEA(L3L0P|?jE)7Y5JWW=u*;e zxFVpsFX0{?`X9CaY4%&eRgL&D@PSGF;nUTZa@7fRKh_@J1-I|WJHPnM)`gkBiDGMn zC+{VWeQ&ksK;*l-iPvMUqVUe#t|IZ3{OAexx{R1gv9|XvXvjVHhlbp5H}U@&*mwWu zSK#g6${Dzry6D$2)%9ph-T#xAx@sNy=!}n{2kzL+83=g0e%a~fLhzBtK{|V44;lZ) z?rO6`UeO<~^^g+xkE8evNL)w{`6g>E=?d8W}~ofRtfvgZFG-U0Y+> zi0=Sp%H><&$oG$|^Vs@&pl*ciAbrME?G=_?UOQdxsFN2eHR*$`4<}L6dHN?s>~8#S|#$rF*m*ZKvMPzQ|LXMxB7)O9JUg+Y?Z#(1Y=0#-eiA)S<671s@mHpoYhUFd z@l~=nP#P}3aWDdmtV8Z}FZ;6%yo1~B*o}kT7vr}qVGTRQ_h{J+MUOS))^4-ji&yc! zsXF0-4uAUPyze*N3tw(BRl3(vX85^ZS8>0t|A3r12t=~rrSR{f^4^8Nm-lLZ-93MB zu&!4Zvets5x}!05Pif_N3x<>UoVAesGHcI;b@0?Z;Jp4L@(MnWhFU`X%tf3vl=dGw zm-Y4}|C+3|9TV~A%bLo1E9)w_3%0vhw}0qdXs%apRA--2iC2jwfiUBJcP@^v``YR62c zzbX1h;(`k#HYaF(Kgsv++Ldp)6DKv(QMK|;^lSR* zDj+tI_wFxZR}c$j{qL7!LvrqPdh{7(JUltsJPW(xq6U3$7Wk9+pJwpk2`lBeIYIo? z^|{5N0(IxoKF4M80`D|Ui-$v1@mIemI4Cys`yUt6X3ltUZV>zv&!};>atioHnad4-AEwyCC;g=<$j#BfgI3Rqr%#*q&N8T$Mhos+v}h z88FtDSIx<(@lALCd?1~BI~gCy>`NbZRK?fb!f#0DrQc{trj~_uojE!1Lpdp9<@5c@ zCEJEc4nl=VqK`_S(=aw%5ucn5R&^A!~xr3^Hb{ zc^LXUDEsz~fZ&sKkKhyfY+Om(LWB3wKD1HR&3DrnS5~L9l*upmFSQ~E%DXAULcKFJ zM(u2xb@nm(sciC`eJnNWa^Do@1bX$&XP?NB*csxQ<6}M5O^Jemrq+UKP4mY7g7lq6 zCw1qK{an2B>|=3t_Az4}X=eCkJ|lL%Gg)#pnv)^mpbe9 z{K@wt8lG19)$rU2o%~pNo9f*)?MI=7b?zF0EB>m6YvZEv8sFF2e2f3r6ubn57D8FC z-LugKpOGe%hwTE|vTE@zqS- zEa%28KlId}r^oxF>D7Lfx-Q^N)tu{<`My+gz4qr=0g`a|p&?dLo; zvC-J~=^t=U-=^FkuzaL*YBr4eP86|~#ZJzz=b4w4n|%=ChqX=K#U6A1ZkJBMjlN&M zBKoCfPqeWY{H%d6*DSymT&~L(i~YoY*&Fc-%<;8@&!}4S8I|62b+Tn&^h+(PqU<$^ z|Ml35z2m8eKwMKp-DP&&fY{^I&EK(U9#VDqfs6}WlD@UJSI>>3WmC{kJD=;iz=|8A zz3=wC2<>o2iT92ZFgq8Cu9x2aHa6YfAvu4Qik`f8yk>%Oij5?+c=Nd2Tf|;Fk@wY) z7Af`Ub;|i@gUA!;aHjIf+R?L(vl{3H&}NC;%l9bjgQS!8IV-JmWZG1r;Zw?e!Ak2K z7*RJw8Z!T+_NG~BRym=yj;qj}7j*Y5c(bx+c@^RjA$ZoV7cywfWGPpMPOi*C*sj{9u$Jgt3S zr_jCllA)i{%xgCV!#CU%obsSPFAx~bcMM-)uQ-asw<}j|_F2_*_s~E~t~Hc-3H?6d z57qXbt)|=!ANP(88o0v1lR`J7~b6cw!j%r<=dvrwiXP^A3 zpy=9CUeDQ7iBY;zbj#c!smJ*{XtH&x)H93p+&R9S7*=174qc9X@O(Kjbou;8lsW$p z*Q|W=Si7&fa?)=V{k95=VQiy|H)6H@LGn1PwSEv-LMMCeAj# z%>SU0zNDZ08gd$3IL@%H42isvJx%5ayJ+8FxMJTR_{XR1*Xs)KP8FL{#-3iT?fp)PNHn#x!(NUFyhaOFDRcH zZlizBu5u2i3Yy?vm|Sl5fOYqyRoBV=#Ma)B&^oXmr^Qp{uC~SXe#T9~_zj<}5xD1l z(UnWQnj4C@Tv?ME*GOBT5a&9f(`Q+COk`|p2pNaGWu9iW28kDv*dh05^YpR$Z@Kad z*z@kx48QK{Be(4#9;uLcB*mIiXz#mvzISYT;8|75*_P&tR?c<$L&#*M&udS^XI5U281hKG^e*c z%XwYzw2p>Wx7fF`cZ6;2?r3WzCd^SKya7!(DBBKwRpsgHxc?qsLL|$aivM2DR-%{4 z*-DXPI;I7GgT&MeK1TX=EILeKy|T|v``FrIQ`+>e&q&SbwcwnyiT_E2T`6&i zT_s{;9;)J3cv9tkC&ya8T5|H`*yY3{J`a)%ygh_lG<^qu;>5*g;df(P!1uie_@>!O@#f z*Qb;j4Oe-d6j>(gqWJo12f4c!UPP`<%?#+ZJS4mt*@yn{0P*zjZ{~GvmuY=Y^d|0| zYK*DR?vWT@flGKVUN|%Wd~&~lNeATsphqYYRZfU!VzC{-4y?T*}5*x?7WWYOg?ibl3@Q)mij8Db`%?E9o z-;HhRt&O6m5T_x!1#`$AeGQ8$rae*sV{JMW1T8bSL_fQz0!DI$4&nfjI8PD~{yS+63S0XM@dSEO1$q+ei#{VV;J8-i zPUuE-K=@&HOqFe$axXG?CAL3zR>R-XkaGN9uFkW=vdQThmIL?Z~eHaZ}H)r(6#*votQ19EGt%)7vsS%!^OaZ@m}09&X~pLtm)z9H*O z&XF7Q_dv=$U-~@Y0_#pYfnkp64~ZT*J$kl+r|{j54RQvv)KGInp{H-E7a2Rp{)Qe5*je+d$yh{^Hf^Y_m9}Ln1vS6Lsd_y^cnVaEDU|u5r76H9>f(6s5)XpH_yqb zt<}n}(jvD8)S-slFE5pGWft5twU+sd5VIHw@9UrQo!bXQW(n=V%kz+Pt|=~H%UX9Y z)%}1uyiec1lm#bl@m-+1_RYCwcyV~vzJB^+oiSBd{3YlGsrmKbjB^tOyTylEVvK{I zz-8(@C;zu58>PK#q4l}g_d8`>5j>+u^ou-0mhA*D#a%`F`Xdi&oV)q<;Mdak#S`1- zmOVqQM~Ht3IKE`MOVbZ#kI+8IU~$GA(NReIseN(Gx%F5XG%k)aTn?(f+Ly3Nbbd6OJBaJ zUhtHLr|0wrz7QD%PpiOl4%s6(qprvk8(-d@mqcf_aHer-;ca(JIrQ08?m(?bC$izV zad{Pe$G^Qy-z_n_TB(1Nxk{)UcK*IV+EgK-KeT3VILe;qa_$F}zK~g(9((mUVWCBp z`o?=zMv>q~Jp=rua|VR2ZmQ=FfT~5~^u7ghR{dLgxii^ZkyD%#7rJ@w+spBx{t!7( z5(`P&*RN9EIi3Gf1B@3|o5X1h($;$JkMG>ax#*kfuV3{g1udEB&Y3qEV`tt(fAxzB zqpI$Q%BVGIpEg=naj{`ZHgrAb11Qh`ftU7n|5n@WIj@kv)ppVr*%zWFcBU4GtV4nNM^eH2(kKC-6jcr~H%2=pYhEc@-qN}uex3lHdX z&(J(Fu_SfsI>~vZn7Uw_&>wn{$+|c-wctO4wa`WCVik_u_F`7(UGmenajv}Lrh1`K z^8@H`8~%;+33yq8&mO-IHb5_JrMhkdj{ETgK&#|=(6{jPBCwPo_Bn!&CA%} zQzd(-gKc_3zteO_8+l$9+7mhv87}i}%j5Es+!R`9smCf$LCLEQP!S6v9+nEZEeaMw;{7chH<`A=5@3=#HJv&13V-|AAk>$bF6z+ zx2#eobAUgc@g@Ra2)Di13y!(thC2q3`yw~O0cf)bdCl4`GClfB?wQArH0L(Uc1cC# zj*u67MUPMKm?^j|mbCy`|IF#ePnmN4zb@qdzipOm8f{KBp1|*4!V_`#MYe4J(Ag5~ z2oLjGB={3v7rYsR_+SRD7?}9e-#cc!gg*JKqpTtKv+tvGi9J_In-112Ki^{0dHEe9 zxIkWO+6h8Z&ewCmnE0Js<`)W<}R!4n~0g<{{*}mB~D@NxF%1> zYW=^uX3a=WSJ*wC)?hb)bW=vo@{Q_;VxK3EUqhYbD;d?ln71d92|kfwVo!?A!MZ5+ zhu(vV?As@{M=@)W8R^CLcoO_V)2uN8><)KB?wKd-HTsZ@Luf(tst~wfeJB4II_e3# zP4I;6Gc0MczaY9CGCCkSlU=Sl)S0dp8xKB{{UaZ79*ZSErj~CD={TLc)=T^N_I0cc zzY=@ld4GU$`^=*K0r3ah z`)7}7UvQE2KOEABky8&M(^jih$nx^`LgknqnEiLn6%N%PxnK>u+yi)i@ z&oy|8seiaoe0-zbohCSvbFG4-Jbs`D;`!$`PDU@{OpV-CM+~ivr)~I-fJNfArkL#4 zflvI7j<_nQkr)^Dc|`w{yEbL-@mB7EuZ*eoZ)FB-pP#*MoJ7Y~89BF7BJx<~_4pji zcuFmM8=gMfp4(x@R?In_a(%YJ`kzP>@eP)R3ioC8|CAyA9yjk8SSx>S;-vi^VlvCD zxyo(Gw<%{5?0ur=qHu=1?z^KqdSC5E0`1IF2e}?89G3=c6aQfE{(eKD!V!)5PK79|iUR=kMHl9O$zRxm%k> zpMgIIWPb^n;7LEA?{`P0=AN1Jl0PJErr&>G&On^0opV3jPFC)T*X^YDzU52L_)T)o zQ~ppxF8K#pQ{dlAMgNumO(k;#4RwRRO1a}1d(xr{@Rk8rr}IVR`CsN!_#d3fdU)FX z5qp1bV)zDlbC%{$cWPF&aq?a2>?kxgn!neA?dZFl2OI_mcf-@g;)BNrdj-6wwfjR4;-CLnuVDCB?`g{*s zLk9GmOY9(Yu;M-9#BNpPJ@~P@!_e-n!2DHJ(vbU~o3#Co9nbrt0*BDZ$AJS{)91%n zFaHW&{NM?j7Z1w3%$cbM7)ychpYWdWVE1kCP%q;@f_^`eM z&73DRvuIh`y~XwgElP>(;hvVw;tM( z&4z4WL*Jt3iw|o*JVp!>JcL|(uLC_^#xz^T6b>L4rZX1CK&*|{CHM`99znbfYw0b| z_8es_DQqsr3*3d`SCM-@ELrs40^lwao&e4>+9kF;IPt%+Hp5r@Wj`?Y8nA(<;a7lH zU<=x?IlF7{OA`ajIp%VnKtt}&%j9gMtVve?!-whrjQ;ZDdg&VAW9;C_lizFPe`p<% zz6B2K|5jjVChaxa7dYZJ91e6bCF?fpQn{XY&cDahlc^B;zI@Nwqy`#t|F8yqwpjdQ zJ@{HUC;XzE@s~NUVb%ARXd6yo5E~AdM(UHITgjfgjjOFIz)|WPXt)l)q_tL;+4Nhk z?^;=UeV_$sm zSJ1_uUNn+gHha;%yizBo#`=5CLD_!gSK7C+)7N>Tr@Q2iokrQWlXKOaBNe@GfVD~Xjy&jpfu_V*xqo6lI>k!#$NA`w z0lxp9@2UK@;*)=v-)Yf-F|)bnrl0qlNfRI1Ep@B$shVm5dF<1u4KgP1!kNTB6@lm52aaJp%;*=}?GCuy_GYLjynYLVS0eE;rQcALJXj1ioUjTaRh zOC_%HHMNbcxet7dz=c!p_8fr=zu*GfwxEf-nnL(x1t(&Uf)7te1$yBObCy~qcLTKv!>_`ZHM4?f}l z=sB~(PbYZf?I(=?DRRX*xOnX&N#P65h`hLBICo^l?%eQ-J=itSKmQA;Si>@28{k$T#x~XDp z4RZlW56#~O&7TWjK=X>Hp!fiGj$lj$T z_W88cN7uqCf}2K5wnK|5cO;V4X@L^riH!`t49g}h7W{GU_9|>Rhr|WQnjrg2*m1i_ zFQgq^AC{?>|C_T!)@IqqW4%L{_OX}eN1rc2{|RK8@u72HAZukMYh@5$=TvO-(1^Xf z6FLQT?e))!d8WP0Q~2RlYad)x;5T`<;j9EMw{461%mHFhIFEy$5*z-JE2JE@D*q#9 z=yrTl#Ul5`R}3HJ&<8y1BRx*OS7IbH4~lPU=u&HMY^Xx|W9{(^9_0TT=lzx9LuJgC zzu^ozfW7{=qpO(qI^SvA(fh^hZ^758!Gb)Tj1tN6@V z+crpSiF>8Pwr3|7C3*jwkVCB$o&6s9p9|~{us`h6cTQ}Ee)i-4a-b78i(dEVOGS^4 z_Cf>bMh&??%KVp`Yp}V{<;>0KOyJ4fFFMm(hqcbc-YGWth2ZKQ(U+kCL+eY78Co!* zg-&Q;DYS4mw9qwz7Cwe9{GZ=CMHhCRSr?w)eX=h6#?g~?;s5+P@O~^^SbTNj4{feM zEDvx7VRv;iJ`#jl%D>IaBY)GFJii7k`mpp2W9l z`9q@;`yG_;hTQePg?@uK;#-_zesM;W7zJ~Jj=6G02bc@!+~$KL;*+UvG38#2V$CNH zsHx1?L0}n6S@FAwe_QC=C*>$#LRmk3$p4h{@{KR&`gzn5JxlHcdw4`0@%dQwCa`Zy zJ)@KV<nCmZvm;8}lEmO*ohdO?!y3ox3Up%keX9QouYuAr~C*|HG*}wD}h3u(3 zz@49#k7q0A148sIFjn$DJ8cdt;0Sd5zr?+NcwE(W@4ZJFy*Ah*xGf6Q~voU_l~Yp=cbT5GSh z_SzR<^EB{Dy=N|C%d>t5_JWsFk90ARKq`Rbv|e8fJ;PHn6%=#5PvH#WKFjPbd&c`mRGYdn$R zpQ;ptFIZ(JxQ1=pO_6M z=yt~ZV~yFPRkOXzn{(sPuJRr>Sy+gHYs~)lfx+v2aefKzZiDAQ9$Z(`*LXT}L}#P~wtv@`5KYZw8>2n^NZD6}L%Z%%f3)tSKk%+e z8}n@6^X)t6b`N^;ActprCJHp8cI6kSlg&Gt3^LCreu)2LA-NLEqJ29?;otIkoQ*C1 zS+1tTbcTH-?b;Bhu+3WR}yyR@-cRIg9`p!OfJx}+R$MazV z4%B(<`dc%+J8OC%g^xpcv1r>(HNWo0;O z;Uq2zo7$s-M`tQefyd1++|6!Cb`I6u_#uVP^sM z%^BXM&q>5>ENDq#cX}ro8m>vQC+mv5Jz3-sQonM0vaU{sQgv5^1h?Ry0ru0=cSN9x z?5*^ntaqI^*1(CpI>}F)!yo=5Z!h+m>(zJ0X-a-I=7#mh!{e0?rcnKrO@VTnCuPY0 zQu0-uC)(6A^p_FkfT<%FjLrWt;`%_KVPAC2ep5njs~*U{nwCyoC1r zd}s6>-`Ko6@tyfOU^f2B&I$LK^?88FALcy^s!#04w&yHZ{w>_751I$S&NxT%c*#Nf z7r|$^kiJ!EUNFw#+C$%b5MH3Yb+W4~U%$1}2`A}?erIuk-(&~v`lMsQ!^!XNYfhNI z-e~O|oLN{)KWZ6^TE;@Q59Z3XK&<&j?#x1+b6u>?97zrbf56dn>-FrZy6?}9xG z`Iaa0XM6ZZSKRzibe-%N=RaZlz1R9VBIt+1?)UIx=9cgvIVJe@Uvhj|MfCb;(WMzZ z0|u>=O=g`ftN1k+NAw$fO^CgAE)fk7ltohZv zo_WR`BUkR`#_X3aA4HCNzDME%p$pdNwtec*_q6HSsHmGU3g(Sbjt{kZ{9b+zUsQB_ zx~~U0;uQWt4c-5B}}I^|O7;+;Q@T@1LW-VYptSh8C%Wgk3`YAD7VA<*Nd*J_TJU- z?xVb0&$}KQL(tJS(o@kkLL7%b20c*Mo>=meANoFLsK%1td4lWzh$a7|hUmKrnhb@t{wz z*Y9-SZQyCfSc7zLBu7u~+D|^q!)`uS@L=`mICRC?g0gD`dLJwt_10unuCFvku4$|5 zS-A`hw@W@J%w0q7b^Gnb=)=}Q!8t13ns|kLq`8Dxt=GV$^?H$jVBy|l%%_l!}?*emg)O_a&uKy!y z{-lQMeNpq$^<4iYY7V`|^?vX%hwG1G5#TYVI2JcP{_hnJ9a>@b2!^3+%stT&pV}GW zH_Gopevj~boZkU{Kj(LR`*S|vCC|xoz5u@xe#9O6Lj1z~O8G^A?;yW1%8&D(KKST^ z>h{rx0_yScD>%aM8Ge*2crjsiJh1(E;i$$y;{_j`)z&^apOHrz1IDP#%PSVNdBqMh z&lTsn&hn2LW2Ml+tE6X$7ec=W&<)pmd=cBh?her(Iki5b=Zd>ZfAe{*>r}Rt=P~eD zlHL)PE{UDnw^r*I^1zCxespjFbTMXgzginwI{i!J*6y46J!H!gZw_q^QdVmk;`2=( z?=Dn*=q~t7ckhrqnf6}F9CI=3Q9 zE}Og4z0Zf-I(9vs9=7!gwvs8ZAy3mkmd$6^GQl#-9;-6(sb1kyvL9J|D!quY(3&D? zakU?HgxtEWPvUTwwUeE$+`fNRujiBbd3omH9=ZJK!;JqFIg~GhChOi=PzD{FRO4US zvsF_`;{H3ly?*36>bbzTO?%$8&LCcDWvugX7yk9tcE7_;pW2m6QOtWR|QW+WB?XW$JvGG#9yHt~MWKuip*V*PVd zL}PubvTG?5kMA?h!bz-xHKMMw)Sc-hzR9l+oheqsi{C;180KjBLI z(PIg0VXI7d5_WKB^GYObT;}7fv$oOC*7Qwr!QRgO%G{catNb_UXq+=y$$653m#g9; zChLow*aJm52U$`;tj|iF<2Fl~R?!H3_C?zccP%4s3qJ#UlJVWO3opKHd|xe`u-4vS z$As%lbqP1oQQ_w=-oPN8Adv(VN3B>y*}GEo0*=)Y@`9ZBTedg&_Y0K|8_fo<2kmjGHj^IX;H#A6TAzx-_1I@g7ZHs zp5E*{THU0!gn!l%A+FW8g9onKGg2iRr_)#YwbVM|L!pP%%8I8{KGkrfDRoWL_ik^r zcGqXp15u;38+#B3_s5|2IFPwXhe{3P|p*M0b7#pW97^-eZxAkDK z`vaq=x$X0vPy64!_UTKjNp-X~iFTmxYUDuh`tvhp$xXlqj#QrdO5{sXefk!-$w$c9 zxa+J9&&J-`zJW*Ru>jt`0UwaHRmVEBS;4RW`d;^Xb`f@L>UmAiERN`#&DjMVK=-!I zT)*9XRoOS;*K1i9B+*&dL!<4YPt{}F^4d{-su#P(J<#nVcAc`&l&*-dx6p^2jkW)+ z^=Efu*ICEDt+-v|mDeWCdC+)OEp=QPE&_(s}lU_4C4s@Yq&0A3|ERKxW9mB+C!x?cjW6WBi<34b9R&~HpT4y zT;+4Smn*Rb**#qG17}CT^#Sn$Y;((*Q$^qx9gKY$qgRHsRvcK4&FSyVJe6xMvOYG- zWYI-TEA5>h9R=6y*^y62@Q_}OPq(z0bmO1tfDA-0eDzUy@SF*bc|#j~c`#l(BaBUgk$Oln`A5Ka6!_TtC;Af%f(^Ljiw|?%z%|Hqbu40WpuISDkD*uY z^Z4Jp!RhB2H`%q?DBs2ZHam`tGw&bgd24)s!M*UX+qlP{UogxSpMAkF&jz{2k8d95 zyDxa({XJ#Y^Sp~P==Ht_x&I>1L_6K^Vt7vmqO?N)rn-~%Rr zUkEuq{A7>xDgNj6w=UPV=0du++N2g$H%YewhrtaU8SL+j8Fq8ZY;MH4t1R_}QeR4ZPdxngZnw`K-+E2$I6t8sVvlV*d~5enir0C3CkNLFocM;9 ze8r_7%}L4yJ}8dJnPKE>)YwB0D;3w!T+Cd}i|dFrx^W%1Uo&%DM^6Px$ox^AUyVHV zNv5M`nw7v5ck9Kj8ZZSstGdJMw@2p&`V8jApZAZ~96%fT8Y>!9fm>`1Lx7`N^Nl$n>|{)kh~a z+LVE3Z_%1ux;}jW1Nd_@@O=lK-#6HU&F?PGF@3<4{V_gH(V}c6=vks+Vv3i=xi5

n1K4jGXz0eKzu4>sJ>VONsQnj8(?|wcKefjx#G!qeRN04po zVR#-H^#5H%+f&vs=$`9yZ9xlKvsxcXls2i`+BYWao~$=nFty&No}FHAuFn5O_zS1C zquldT;Lo)qc=PcC>`mZmLo|6va!q}S3qN%uF0M3|(cL*bRiEh3>Ap?uBX{FfGPDIg z(XThm*6#c0AM4()$-ZL8sLmW86C6piWi)S`{8Rh+3(?3-{XC|AqR+Vbf}jOxJD1<0 zzjhSe=Lz4YS?SwPlVFv-yON z^|8f=d_)_BmzjK2z&)dlP2(hcB|6-=#wmx_`V6*kaIQ6l#wpI;IsSLqF@uK-Z`OC( z@w}1m6g%`=Dmxo~gpb-WOUL+)-~E+vZSBIt(!qZ5g!G1JA?wTw&>9GevJR^e%+@vv-fcf=i#crL`K7y_phebg^pSpOu4*sWTGnrN z|53E<^GR#3Jl)q$JBpPw_}8lIhfR$w0}d#Y;oa@tyBgkUeIUHAr%&p?WTN$tOvi(8 zlLyOW4j1rtSbag?y zv(AsS=X#7g*Ph+2byzg{8#`yQA(cz#W0}yB&VXX|G+yHZYdIIX=CA*aLDnJ7#gV6k#mi@bDGy`p#q@qLp7+^&tn0_?yxv zk3Pn7;|FiR-fro`p0$%lBXPl?F<`FNi1yU3MeAE9!5lo^c)krhA3o9lfB)an|35j=|EJ$q|MTc+ zO!Gpx7vBJGd|Z}hSIS@L;avRJ;yn6rN09zSqK9+%e)`inKGVY(^whJ5W`*?&_`&~4 z{;rF3dvd(lxcE0PBRf3)HEeBvoT=LJ;l}K`>%kNH#Q!@dPmV<=h((s{5`1@SM0)ED{lqJ13;G}idtZmx}>w;;oTL1$PO@ZUG7C(xE+ z5>K>$^Ru>n);V6=nVyBTJ#fsf=h%y2bs*rEe)Lh+gtu8A3NfErTd~m@# zz$VMt%xeyvHlcNR`jBbLl~oJ_dx>vwaYe4zd+||LZ9-_ zL@~3`|0+Fm{}oHGbf?N>&Qnesa4!3ELUmzFFC)fkFaHOjF>*8< z(fcy)$r~|d%c?Epn)ZvDp{uNKwuF75e)g=HC0`eBffn$GDCQHJxotCH(m&L5!R5vW zG;HgI=Infg?s}oeUg)*Ar=hzS8t#P-d!gOv!iIj$CvaCFUlsb#gR;|GTJK@Lv4;tJ zV~NgthSvnG?U~rxXa+b&I6Ktm?hDdB{{-Fwa#Sgn*~Uo#V_?m`|7SUM`~LaItn1`k zH^wAQkKPCNoMTS2^s*N_x%)h#=Oe&D*$?R%*I++5@84`rAK1*fU45e;>OW+Pn%gqx z^zSzxYU$+vIc8q-!0vxzTu4035VbK|B;@+uW}UwD%* zORkDrqr?j;*Ai_;Q`b&(4XwbR@GJD@mnOQRW7s|RTsDE9w$ynf8FK#bD?-hW|7dg5 zJ@*Hl?|i0f=uV^kmG9*@9kqBo%z5jFiNksT_cVDD6q_u%2Y2`w-?8yEHa-G+qCd90>GxjQIUXD{#z0PiMReotlPhx|mF1#`O<{RQ|-uskdmD1nl9p}mFJIa4l zF;kKqv3ORoF{O%0)LCJt@@so~)WPlR&^vWz>|KoYJ}XmgjyGg`nHMKtrG1jxBZ*Bu zs8}lx{gPcXlDeIkOm=4@8L_ffmY`OEs=f ze|(N7bM}5=6koK?z2zJfaMe$H0nHUBxk&OnG(!9d`Q>i*Vx(G`=g9ZpU(S41oLr=| z&m2sc&$loiw0ClYzLi`<)&*4;<8@cAPCXxaPWAovN!91}wd=gRc`Uv;NAnk0#KRT; zqP){K&V{jhNb7puc`|`KZ}iKK8GXi9R|Oxb&YQc(dM>a!t=*TeJvrAWuUWSw^Ixt7 z*IKJveNSc3`$Q{s*bUE9j^*0yf4vl&r+-b>EOp!b#|m55QR7FqgAQADMzGE*2w`)G zKtm_3$r+b1J1%#b&FMd$#~G&)T-z# z;pF?3ztZzf>CD{qvSsOPomfRjxpFDypP9>epsR5{H*-~E;nK&Ji*$D9U5o59dwz4m zY+0yh13VKBC@Z@0b48cG^Bvh{?RW@gSH4*LPhNk<>=>ZuGAGJxf1qdS2I5m16T1$= z2g$r4Hr@7dFS6@G?kAW->f=3LJgMw8H`I@q;q;vc6@P&|z9W!u2A8F3KF!@BPP#CYh&a5hv%f<7avOm+anpTL#|Nn0@R*(V=)wV|MhRCBt#)(1R!1x=wAK z=e6b7b<2nke)O}q^1i!y$*@n~wc53i)^L5-U8S*1m@nkc>a2@J=!5yf544XWX@2?k zz;f1HMGf7}Cb0Z=?zOJc{t3-Tiwowx#seC)I*DLBl|BOhvvIgFa#YAZEFOZssxy?? zf3qk9Zl&v(8(DknTqD{VS3gx2+v*57DjY0Yl&O7mQ3hOG==HhW?X&E}``?lt=Hlwd zisO!mw@HrHA!n0qe?P(a&L}fo`Z?aZ{jS1c$;3fl;XRDAv;Bd>5yc*e57ZguJ}%sq zG=HCk$8B=gcE4_6Z;IyodadUwze23cKK4h_C)RmYoSQUZb(1V}m^G#5U}N_69o{$9 zz(;Js=k_&b*YB|9Cd>T#4O<3#mD;7v373x*-qR}>#m9iH7kHuz3-{#lvT@B%!ESXd z)`Ijo*Z*w3jltp-d<*xt6`8rCN4!D;RL z)&9u)XkRo*`$4sTx#orF4ST->{!Y1e>bby|k>BnzXcV0x(0mj7u=~oJ9P^f3*&akN*hpM;6OtJ7Ht%9YK0dDCfQeZQ6eo!PAF zj``vL*O5!VsPJ3J1PE8}O+|IW>A&1KC?s~@;AJKBp-ip|i+ya48_l_yU+3Vegu70x5UN7YB%x6a>g zg>EyFhr~yc8*jrg(Qvf*x))`afF#-Bef z7@4mba-U|LpPkc{YVh64vuGV)CixL3Sws9}kbK{v!Mm}|f#1PL>Dy84qntG~1YI!y zu2!AGgZeH!P|o(h>Ky2wxV!Y&i8EgF`YnH`hmTEX=087yk1r^WaYlTg$FBC~_x&0p zbkk~U<2y{OfV;0`^)0LoZ@J5q-ojp#TMiRrj*XRdW7YO`riOK66kbpV{^IrIr)Dhs zG=7Y0$gV#gk`3jmo1v@ViT_uw>isTrVTb1RX<@q;W&deG*@4I}5?DUAec=kTec{qS zT(_+@eBG{EkXTEdOW9Mpbi|y2U+WR*{cPFPG6wg4rS8<=|eZrSn`W){f z(ArMbS-2KB1DhZHXX)D|u{ixi*Q<>fLo9`K$F-QOlz+ij`Z94+bF&AO?_!93fH!y8 z_kon}vz*_YA7hn0;Mo9cp-;P)RNrIA4k-VD=B98!jL8z|gIr_8h3(Zhwp_+Eu$Q+E zJ6{7jaTM4urTo48zm)%B{@3%rmFp$MD-r8tGvAg$bNOwQ#rG1kzfdn9>JbA+T@z@N!|Kc0i zSZ}1R>hzt^!?5D>e6p{Tcb>cg`hLGXzoA6m)Mn*tMQ*v@T_*Zu&2)rsLVQ!e9%^*q zx4+AO|0Epy;hTI*UI7aWIM5g>&N-~Q_1vxREv*X_*H*y02lWj+`bGL$27D24TLn(a zDYJ)XRXi)_SsBk58*^*VLe@7$+2X88y_Qox%ts#u92Q)*p*omQa5{SjOe0oJ9!QH-r*hn^lea? zjS=h;$dOd!qpp0EJcMSoUIgAitb#pGl%xDg_)foIh1P>PdB?pUStc7rkoA0|r8Dv= z&I>Jb&@r5mil-z;k%Ljkxb<_^l4(ANHg+!!QO@6dcjQy(8Jx|^-r#G=e{-6Dam!!u z3!HWixmC|`ip#&3@?YBBRY+c3_VdU-$9>%C{bV8+lt>}W636aQq0k1*iMk|c8<*_AMY@J2JkGL zLk~X2Afo@7`OJ?2=Eq*so*@n}ga09uVm+{{;;H67^pf6lL($#YV+qr~kn$yz4?-8m zXoFlxzNeb!v8Ly&uhgo-Z>1{j7zHU)ha5xSbafTJi)ifCrsCM7x76=M54__*ynguY zD&>{X*vdu*t*zdI?2#_I1^KfD`9oY;88Kz$#FSO_BCC3lL99Qb*nsM=CC9P-qDV<3=FZP-B((Pgu%j0?QjH{fDZ=mepH?cGQ1b7SKOTDHBxPrX%t=@0vUYM9i zKhOQVH-qH+)VHw+dI{fZ{S;dSJuc#%%JH6f=n}1+RL+yJ<9RthA7PFWpSdWO;hL|b z=gQVW&&fVF1}9-ozj>B&Hr~Ut>Dsl_Z0G;OZ^t6h<0l^LD1Msr`Bl;<=yzz9@l!5AnWpqjts1j(&Tv^BTV)cQZR|&vNubL# zAu|o*S2A=R-!$^g3ZuBi0?G?lv+Q*-RlF<2SJ5|H8Ek-E=t%p(K}furaklmx(FV^C z%(Ra~W%JABjl;<}o8?R=`Ww-{l#QMH$Ku$$g}+=bO~*{jjhqLQ+TvT`OlLUx!G-YK z2fYgyr8Ct5-pZlZ!Xe7d^gg;?IC%GmGvnah$1{bljos31W|D5Pt7MT^@BTbTSE51D z9_8&?xvL0z_y+TRy=kWGKCJ<1<0P6yx7>jKY-yPJY~!Yg&Dzkj$Z&QJv0E8SKV{^_ z*yhCqO9t9~PEliXT2#|VyS`9cgMRsnvX`k|%M;i?GgG_L*|o+1W|zJ-R>YR1;^^wD zy!~1YrK%>e@R+8>-LfPzHuJPe^TGz^|SSjJb+XB##y+l=+_GRca80r zp?|aV%^YNotrlLeFSz)?hC{54i;sDBT&Cf}?9#l;;nI^6cFr;1y6As7bETQ_r~`(T zRWsM2xncP#-;_&+%v8>mALGi~6Q1e)Omt07P}he5EhC>NAz5xGe6Hof9?)05jZmtJjn{RHg)A)0+GyhL56X@fNGQrBlKkRnr=N}%Wf9*YO z@G{96@v^OXylm?vFOz>|#&ZQdURGx15cSN|ruf)Q_!1w;oRC3UE1{2D{oBU5BBvi= z-Bdk^zX#Bf)fO9f;SoP7(m|(3~pt-kmWPs!ooF;K9KXxJ)O|(dT18;13#Kh_X+fZK5PYA z1C~>6=CQT%{`VTw@J#j0GzR!(b2)>K$mXH5&XAin|A6zX7w_W5E!c7F#k~q|hc`W& z&b)d9dO&+-f!&v>dEX~{)ZhY>i55Mtn3BQmS#xV+_J95=JwP1tP56H*d7d`isnxG^ z58g?**1TlUIeJAfwKC2*=CKfU^viAw|JX*pxkvC_GOy37XI*RK0Yo376YwB=MGbwj zc^PCk)){Bg@6z9T)%qCF@u%d>g`WH$to&PRRsK}|3a9H5n~Q@rE;0p>$FYji1P~u6cnT;*UON{8q1?i@tmW z`SB<;#`oyhfuV9}j56N$;Z)xDoV)!uskpC<{8hv0fhM(6(o;$K3w*<|h~4Ym&UHrJ zT5W5tL(BJo%Wmb2xAJtJTW1Sr+9H1=siI4}6@!U>RF-Opbd#rb#`B_8A9KPzQv%r2 z{jP4r`cgWsf3O(1I)H2h=u1@3?K7@RGJM1Ofwy`YhkF%PzPmdmc=>KxL9k^(Ss|G*NgqxI{iYB7 zCIc<@feWtC?C?OF2@X`C4{c4v(P69Dr!*{D=2?||55N$_XD%75`;yrEpr84EQzD## z&rI{y!^@XNGVIH~B-EUlyPR{4&P_%9)v*A$u2@^GJ!`4j1RtG)U6~wr*E`m0}0CacYC`UUbRxuuBIs+@~`U>G;;)1>gF<>eaclHn;WkKBwJ2w{*_E zU;Q59e|1r_Q^UipZywCtLGQy1wRJsxz@zG&uxq z%I*M6;T(MmNAE?GmWK<^$OhK;lezR5&t35BamH^XYGiZD9vix}V6SaIe(1X`k zQ+IcE^Sqk>uj_Oja%CR^|!*;*23#einBe{KC$oXd7iX902<`}9`>M$z70H6 z^F5$5(D|R!J7(d3ADriDeJ;M2&bYW|4}aB?47?xQgSYqOf9Qrj+0@8GxnUW8+gL?I zMtL@vW6H_6FxL6jHsPQD5z5YEoOi{NKHz{EzK3$!p~7ZP5;MEV|Vk zvoaC-%*+!^g`=6`!cpqHP4v^4jeJD)r(Z!=dG(!>c$eDkp>KLmJ{WZ2thH~!Yi7(l z0$tB$uNsGs4_cee=|(<&`Ic&k>o`}sRkW`mBi$-tKJ+@d@3!eV`0`_aJjS~{;MZx) zF3{TjB%4mowvyAoGtg`oHern)x?nkS>o)A7!<@n5tufv`Q*B5;Ys~)ObdAeotkpC< zn7VE3TNHTF@hA6`Y+pzGOwWlNj_fC1=j!{fuk-xG5p+=b5v3o8Y#-4Zy*?Jzj-YSd zP3-B&tKH&9`Tkm+iO{Q<)jQ3-@HF|DqT8V-H%{w!pHqyYa#U=84!_dq@4C5`SIwud z({%I`ef;O4%;~<7^qp2e2;jFKarNb*`S9Hm;8dP(_!t6OZ_$7BS*>ZNoZ%$d>j29K(|LRlUxoOf$KPja3;A~Z2Ks*+eH>Qoz-Kc4g{%dl zv^j;2k%Lt^S`Zy*E<(#?*6$8J?Ae5Q^e}x)71yD?u(gMlXKMGCC|ACzC5NG87Gtxm zSK~Wi`4=#~dvm(umz-BPMot6r#O%zD5yQlu5%whP%swKzo@FoWdyY+&ADfiBFVe29 zQl*zFHt<9|II*_$Z&DXPmQ+{XxF8!v_G6n0$p7uq&fK%uXMRHz8iDS1X%2~2nQK;V zah}4S=qNb)G4IH^7tP?D8&Qtem#=W|3l8x7Si&?ti7lzzK1(aFNz((7b4=PXKIyCn z?zj{Ek8?{@KUcdBAdg(BVv_E7&YUS&psiiJ3!@X1am8o!A0IhE`~Q6-Fv=Eu$~BXC zi10$+iF>qTd&-y!{PWv1p0qQP-WitMiD=$)EfLRZ%!ZdmH|u)V7d78bF=nT-3E275 zn7um|1AmH#S-0)=&q(g1{C|rdyCIWW9}lKFuLwGsMeGr4Qf>!grjTnL;Dfjf@f-BX zfc7&c%)Fatn@gID>bsRi%va4P%~y24oLsGAorwE>fuKgy*ht#hVe9r3|{Sz$I#atdgYo_mV-+-HqHeM7TF?0joUcWKF zJnx6||L58jz2?S?G8cGtRMoz~y2;wPUti|Rq3oM{Kb@B)razlrZ%&rNr=NuGnCCW6 zXcyzdS`XT1FJh{^(amX{XHOvagD-j=>!W%2kg+YGe^+B0SjjKSuMVBCc~W1=)5GBb zHeXp|_SZ#{4IaLyz?}!<5%DO-ZZan&-&6pz;+dOj*(Z=DCY5~WF~!g!E8paOi{?$N z;<6EFH(%ZbbMj7SxQniAyq_KW;=|x+)_paMfxC8k06iX=EO>+`@l(Nv?vW9FxH0pW zjYtkER#NnzFvW8`c*`t2%Orp8dRKmID}U2R?YX_s#JjhvUg)se;wf!{$=&QL~KPuuq0amz->SgVLfU+>#P4w4on7TkdF^7;d+R*-@vW4N zSUmL=($?<})7Gu0YU>np!Sdh!LL0AS;1i(?^3CNvm;D^xa3mqscz>^P0XUkJEX+O0x z-m{&GHof^>DmjL}9QSx%B*Qw7{dqz9j6cfeV^rQA%w?*bA7e&(2M+&Kos9X3zoQo4^gzXI-6J=jPD2eLppNw+Z<3Z+{C%WKdt{sc zZ(%wCPYK_*@@vZUN%t!df1i_|Q<@wbmrb8Pvav|!$0{#-1U|V*HZl5#T^2otwKUJf zKf5#sBm;9gTWW7#^D~$^}v5`^U`{L{7AYlDgSENxt84i{p9vfkQ4t;uaLin z-2UXxUv!1FDb|w*f_|51-K=qw%oDyCgRo%34^ZlS)W#WGdnDzAjXL9Z9Eju!8sAF}zU48Lh>XnZqa>E}J0Ipd@b`7`e- z&8uzlU8LsWCm{a|{c`Z9htsE-cv`tZZnt^Nf;=ltf5pHNUpm`L){m$?Tqr<8Pp|N(a}uI1$$5_8c3n$%(-O4`W8>VE{{W4|9<+rK!q7 z#x*+%eaEFUXkCa7^N7n&kKZL6=w8n~n?+!{EJ0t|^j^4{)b)@@wHeDhOLcCP@RZJQ z74AHrS?a=4kd{42+%sz!^(tV#PWbX^HOZqQ8;;oX)em|EZBVgii&U2w__)!3JFx>ts&oO52lb z4f>kP?JT)F+5Rj(8?`GRyL{-v7q)NfGaN)Fz(-4n0rK;#Oy{^G6U%bGZSm(^J8C0e zN2wjNbQ2P8gcEQa$9JOJmpj z?nRE2)wbFEfRg2sFEyTADTi*WyuJ0+mhUcP?$;B;-DZ7UVft3e&+zlBpZLeg^$=W& zU8jWqq3vxZazY>WJ<9$h@!4l|hOEX*W5AdNQqJXznXiGC^Mgy##p1CF$*JsX+Go8Y z&bs4D&T-2ud`kO!@#EXGTCl@$ei(h!o?mj35Z8Yfb!BRy@yw6;J;$6|f3`iFxR$m! z!{ncOud(fgwqx_Cy^?bbu)QZt<$QNv>G|K6ekB1W?~3f-YB zRZ$SqUI661AAPEfeIECvpZ#qJJI;OJypFitQp-!gQ!lt>&dqqfU)5=hA?IZX=fEB8 z8Z~nrV?NPr7EDA;O!?~UK zLXzmti@0hI48%p_W$OFnJk5hkFo3sR%y&QWy0T4|z(ZIYWFJWtH#MdEqmnn}E%Qv> zy1zqDIQrR;;tx_qmoJX}1^D?XJm{xaC`PinrI9i=uKS9`*u(t7wQSz_U`vKLTb_DL zZsd$o>}K-&hhml4Y-&{^sys}ohKA74`Pt*M!KwUAll+A9w!~YV5#lWFcJ;L8-T&7_ z=E3Z7=>wS;+&K|>I6Iyy`?C;nY~lkU+Vqo0JE-}BJqg2OdL+PzG5zm;)zSA?YZt4`0ITn&mrSY6CzkQCrGSX{~7=PD8w(c6|rKO?NUwk&y zvWj@41b9fST1;$603TurV^tzwBL8(Cps#(Y>nPuG`Qo8=Yd_!AQw)xjpL~l+$fw_u zpdO9!s`|zHR^R2-RYFX-{s*`!ZZiOF3GOQ9j_NOSzrB3%URgt5oLs8aoPSvDN5?J66rs0uvPYxHKi|%e^IKyYTWo6ga}M9$ymR>O zAG*v(KB9v1u3}=B3^?c9b6a0${G7X}VjK7mj?YzRM7D>78_ML$2lC4M^U4=^<;$m)_vOkbO$Jya zw*I8y9D?(Mx%>FWO22mxPR=jz?!i^1FIOhB5gHL}Nn>$k@tHK><$M=5P)~Um{-khz zKJv_#7mnh&dbE#cl(Bt~_>6JvIx)&H;KZU3lV)SQD>|Dp9q@z-=2BreR!Ck}`>ge4iqnt80e9<$lHb?%_kVQPp2_rrIaHx0y%GY}bX zsW@YTc@kJxVG1^FjYvM}-oI&UFn1r=v^9{sXIvgv9KrH+o$=DGk^a>MfX7?^B}jJb2%rUhOZA(_f!g zZ+VZ!q4|_okFRTMCWk}63!i^2^^{N6Q<43Rcq?N)@3I~Ez6T0S^T1Ch7FL)yygCZH zw)!XQ=sGdJfv&A4UK?;oOLL zUJk~5TTM<6KGmM(%<0S-t>2kHspVm0D!C7z@QsqABbq)`XxDSB+vdyfA^ZO1`WO8> z*_JU*(pM(^c&2#U{}ouA&Gbx*L*jXVUM?aCLZbBcA|$2j`D8@mTiT8h!zpdTAo z+JPMj`_e{q4S07KSMBF)=PDVs8vRVRV4amDo?PeImTQ(ZJV7o4{LZhm2koA{IOh!| z-F1NT$Q9pOQ)S~|^A`EJGkHK${dnRVm*@rfn>@O$;Y&a|{7 zP0L?9ZN8_XZP~Y*+p^2&FEXL#Wc8mdt9XKBM)1N_?Zj-hM%Q&JcW66&TRyRikS&~l zo^j<$bKg(*&n8zU?I75OR*bc@v}IfVTDFh$3vWFI4AyQ`2TjSYrsGV^VdkM}RvE+=qaBr{I3)2R+O4;bz@&4sb84B8p~Lxg}GGl%62I^BRWiV7KC#4ImsN`hse~JD|msm z7i+$#4|cv3wIf@6fv$O0&x>pBh(4R!Ig6CK!9&#L&levMy=v_t8}%UH4Zgxy&%=K3 zDtjY{P0a9YMFu;^@Wlt#JR6R7p^w*p!)$8Ie)Z~9-7BG#b7Zmel||j)(y#LyQr8vY zw`MJWH}#>zXC< zdhaVC(NHW7e9QPtod@}Q=rl>y2K_GPNj}i zr&F&&FN}-v&q?p2|94xyqqekmi71}B4too21-VzO4zWR|fLKL$uFcR_jVtr5tXDXe z-xVCr7UL(LYW+9E)_3L3JNRqu2xB7NN_?Z{p~gbvpfM2doGIo}eU+VDu!GlTD-ym@s$@t@-lDmI@z| zW9yJ1Yxeo>Yx^>>SG)&jL4GCM_qY4E?`#ij-`rj@3p_5Z&__!v(9C<#%Zd5>e-?+w z{&*bzE~fmeqA}tSgW$LTy3={0(o5Fjhn0>Sj>TQwEm~@6SUS1lBg3irP3V5e>_BU^ zf6>^b@Azf6#*Q(wbNN4W zj4XehK0f$pEuZ!HtMV!_$2GU2%5Pgcij7vjs(s4YG4YY~#CVf@MzXtumngJ%gK*&a zk;>)Yu(@~t?w8%sF}GYLI#YV$9mTueOT7X0-HXe9-~7nxFYq2uf0?Qaz?-aXUwnvI zcdG|T9wa?o27kKMWztu5qrdp!LlK)RVZq6B5%@CbGWRp@LegV2X1(8OH(wa>&lxO+ z5A)39!(}t@;b5KR!^d8yo)X?mwnV@19h-xt+KpZN!+D-uiA>7nJh|fW0@2lsIuq|T zH)oCiQr$V7Px@YNpF4f71Gf6c>@nqzw{ini@khmY;Z`+l{)*K@CqDCMJ+m#?>)=W)s_54PJLvK08wB8D{P(Tn63aWcQLxif4W z=|cD}@+n^rv^t*6fk+IvbU^4pYY?joYW*<=ow4SaRR=tW`H9Veag!}$67Q)F+S>nCd8dMw$0n#Ush@Px+i*GbF#J za@*t6tRBLKE4?<>dGRRxTQM+pPYt@7?AA7y2{cxM4KRRh%=(jnq3-I6$a1?EY`Lkc zNA7DKkXy_Bv9yw-lbO~A;5Z;$+qD5}1jQlGw2wsVTdff`Znk>RF3%U?@VpM48Gab4 zPR+wK%Gz2wb@XQ!kM>+g-(P|!Y>i4^vTWJwmO(ibvCRV71 zybC^jKy~DDDIMI3{QnMqgGO*ijwIIAQSfSWx|{+R(sy;{ZV;S@v=*1I5Pjsy+_{>%E`vUf+7xy}t7X@8f-_c_A4odzQ@y-OJcpo#{P%$llx=*B({N4+0k-&^k93ao2FrQ;B2&e4!mW z-@qR106w%5yB8Z@8qe+84*vQJ(JI#`fWdylc)B@Kzw3KMaqWz0lh@u6_-$Hi81Yov zn2ZM^*7G6RsjdH|LhE5;{WjuHgT=;uj%`{tL;a89gVH-;wjeD1qwlyQzQjzMCG zf92x*B3lphcNE>p#4>z$6h28l9_Dd1^EqnkXKlb$yijvfc|6O92G}2htmRk2uauwU zvGR4*U>k|sz2@W!M-D7x9kQ_Y(8m(B+3+UuZs3rTT*SOwNWX5V-S@G*Hul?nK96Ug zlaAt*D^{Fq-gnp=U-@)i`P2D-QC|5E@&CNM@@Me>BYEYC>waYpV=<$QMy3REHmm4e z^qX?nn!c~{8b`(}oRfcfdm4^;d6C3VN}$zNZ(oCAR#$6G+lUCrO5Fn_`zDc-I5Jodx$j-lXd6meZ+jXihpE)k=)<^G$W1f^hF!9 zKY0Td7|d_Zo0h-srglWics)_L(e%!BbB;@s(Ld-|q0^T+0MYL?BF zeh0bwxK>l{P5c$?*Tz;$W2%3$zC4&Iw+CDmAUjsh7Ry1IKE^+g`#wE(BCjuFueF`_>;V$K&`$#R_qM^St6jYC&FN-Ce!ZzYxGWzYMb1z^=Y)|r znLP&`wv8N5DuaHN2Or;+yEf1|lRe9=#0)vmF!Jx+wK={DZCV>K^_6c+F^VY3x%<<&;0JQV^`~$_>tx-ds}Ln+n(NR`=$0}Z=_wua55hPFj^fN zIBKlTVIg~OBljxDbELN3op0g{0RijxfFQ-@5B} zWcRpn@o(HfU+SFA<4x~Zzl)A$v(P5GpgpVfh;(7$Kg#)K_n{L5yKrW0w{IY9$4IS+_1a$pG_aJ->8pM@%HYcv*1lUCIH^b zYq3iSZ#F+RaeE%#WQTTbLOZgzx%=6Vefp2Y8T4XtHYS>N&MUUE2Hd%J*$2Q6y3DO} zB&&Yw_KCQ0+R`2#6Q_UPTH*J?dDINrHHVAy<41*a(Ld!v-+aIXzHrFj#hG^p)*L!x z!u{?yH>gxFR2=;&o*&^~dn%@U9#?LmBG|KWh=Y zk0^FN&yk6P^3Ac9V2xnyjLO-e|H8k@0^^La)uJ`>Ak5EyuQ8`T!My$-Q#p86UQW1$ z^;QS%=r7L{5+}fW=E>w3cj0{FcGc??g`2?B8S#DDwOacEp0B5~zuV~yH|hUi=fy`fX7H>!@aFCX zW3Cd%enUR)>$mty8z+AK!`SldIJ%^NSzIwSz40FWyv<|8NOxc}=_hwE<4&CGQu1ML z>Us(Ni2sst*raMQ6WQ_TxA7(aDQ5&3a%(>d-Hg%auapaqA#!tHY2zPjeQPwvOI6>UDEIG{^HJ>>OIZ$;OGKcUL4q(1UUwOUxv7yT^_ocEP!BDOet zg#R{&DRgU+zt$nIZA7XPX2kb_LlM7#kN} z^zQ&acx?9GW^-gB^NR5|7h{uRtWwtzuS8z*6!tXL=ZqC9SD4O=2x$JGF22K;puCN(uN`A8JjWCxlY->H|0wX*rEff@wTjk^*5-qaO6%FO zUh1&2s%K{ZZu2=N#(9K~>wEN@2g!Np+LHTzD|#D3=OQNnJmeeM`($ITy(2r`IPq$W zbM`x5bIe;$={z@Ry*;)9#e0D{kg8=L05M_2hmAX>_RNMefa}E`hw)pF9z`9BeM%); zo5+#oOLcH|#VX?$Eh}!HwU1wA$tOm<^Htr0rHljXLni_vo+knjo8g_89LQ*%GslH5 z&3(-&>Rm^FYoz~e0X9pAz=qBo>IF8&O6!~mHi6z|RMT{fyu+0x*!+wbKbvZ-N`RbzXoF+(<#KBEpPla2L#SMCG*LM^T>Ay#Nf1vSU@tIn1AzPEiSL6Ia$JmWo zFKk`&5;E^$WFBJ;4-Tj58ti$1@@t5;L{nD&PrQ8QA4^Xym9C79?ge zFkx3#%qeM!d~Q*1?@9teSaC}LIb?KGx zg~84_5H^|c z#Ydcv7la16QszL1{wuh!g8nXQ2t|v6CVd5G z)zaRYE9WdvUrBy<+wRLxyZT|_{iz$mO{va*4dGAorH7B$_RfV{tQwIUG$n4Iyaa3}iq+azJIpb6OhaPNXW>qsd&SPgef- ztQr`X-oyCeD;OA;uGe$^P3%D>x5|!!u~f%Z-HexiaNbRZU|I0YI3d@OQB9I3%!4!P z_nD0PBsy-Kc<~4D=jQ0cTBpDBrf871wT~yq^8)#_h<{NqmhfHbkEO{}-P=~aU`z20 zfv4@q-L^#QYKyuT{;_p~$58Z9Xk7er-`fxJAHBwxT=Nq8(8JKUoojF3)Ap+IC1tBV z{&oo3RZe(&rY~39UuOaO-wJs9HT3ry_4kFFiQ}w_!N;hlz{&^mm(}ll`$6)uj0v~W zacnEVBU124AFbiX4=<wK@n?@3%a7H$1o)9{df?v~$aMBMYQE zu3YmHy5qy>juyvnCHNjW;PC-;rtaehqtKD?hi&B1 z+yro-A?}^yMA{vXDjBS5IGi)otHh{QgLj*jcX>}xd%H(CV(s% zFtRU(@KyO^0dRsX0Gr8+s>h2Zh!0##qu5ybDIbgQ%~oVO@fhe#RRi!J)!9D}y5l)@ zg;Up}lO`HudwEg1tX;pOf9eX1XDHvAvlaUc`!?YxL1*CX?v3R({$IJz>=}rPtx#J| zapHTkxxUEu!x&kAsK!e3nX7!70j}hgwKj5MuT79T4(gnu z=5HTfUOF#(O#Y0P25grj=d+*ovmxOyb?IkA7PfuaP1Et_!L%5d7E_-+V^KJ3zWdnz z(jxY<0%uEw&Z43|_UNiV+UJT~uNLg--bj2}|Ep7F=VLd3j}DtM;rufEB*J&9?flR- z^12NdXS2X<_Yw<#(ER(tQx323Z)ATioC)XuoA9>&KM`-%-q%I0A&oQqBAUYyc+WOx zPFo)PoYplqH%!STaWkgpw_$J6SZri}0Q*p0)3`!wyHs8{Y%Y#3w)?`9h271?nR8NH zm2a6ciP)#1uLkG~+R$D?>{ixx#Wlds(i7wHTI_R_``p~+E!UY(seZ}_Q>#83qK@c{ z{Xao7<1WpZrOmtN?vFyRsRnccd{Vi-Y5be}=v$IGFu=YR=2?ygCT&@=DUla|b;)Fo z7uF>@U$d`wFK5{8V4foTd*3zwi#fNhK4Z$)OpqslJ@>VTm>cMl*piRu;+Rw>p2@z} zyN{SOzJ+EV$&^mo)PmAGb7eEMc`BQe_*?Hle;K-Kx3}h=at;G)D(eeU4kN|j0H^fa zr^P$vCnjIt0By`@L-FdfdpKp!^VVjinWFwO`CDXzvv}BP9)!=W#y6_^o5k;IN93zu zeh#oN$89&#`yxEe^6b|?V&f-ipT56_t!ybQTXg;|Xy{-nV?H5Vg2Tv)QS`ax;BOr|8!@WF z8SB_E_$;;Y*N2hQ__3h1D%nU`>jsB6sdzsKo~o1wqldm&JkhUSaFELMM4<2RO#b^M`a`PL91v_nzz*GRiYEQY_22Av z^CFrt^{+Ob%~G~Nb69P%M=20nVJ?*(z;{9ZhrBwlg=THsSmVSqzZXn`M{vDgm|VN# zGtV-%+pOM!T}gHje2n#ih39+Zn@E`8>)fx6--5mg%&vd!*RKn1bgMdzAK+#L7v*l% zfAyoQxBwU@eokN4>N~CnyZVfDJ(I_q>&O#lM7xa9Gvv82ajPfz+R4vmpQruy-Dv&S ziAg%apI?-3g?(a~B)?^i6WLh9L^jpX%J~{5)_fDZx&hhs=b3YyhyRNFBBt4S?8@%R za*dR`PP(D?PZBpx{_7h%utyh9WSyT~*&Pc^Wb0m`zT(#I=*wK&P0V?vsXNsO-EIY* zEtBW93hpuwZq-!^oZ)Rn@BsQG*qO7(M7KFVPZ#?ZJ-Jks>pOkMmS;BQ`dq&s`K!L3 zZ>%g*UzgF(bpf}p(RsiLY^kKRLzG(B!NKJ7>!c@VU(2hnF9#FwjOSnx9E?kQp5E7L z^*rGA^t{Bfj)|<=j)EJt&z@Ui_Nd>*T|2m4f8!$Kzi~bITQxyEZL6_xm;}6^ayzqQ zdHoF2&+={fda>169D%>^KpE|2T-~3&@>cKDv)uipPKbW$+_cE*sLDA++pBvhH;Efy z6`XnpKj3^4i=VQgN@RN{FyiMEjFa&39z4CMI<1~IFY!H%n^WxSj~YAaV3uCnsngXb zs?{ICq_P@2m6^n~Y^@r*$ceG58-q??o<4Tb81SDwcFE|}vBO7}KXy)g!r~`)t(qfx z^o4R`h#rxp-aXPUa(W~B#qnw5`dSS-DE#c1Wkt~XDB}ywbH71G^9^&Bxisk2or!2&EQM>DxL)gH7y$ajWsEI(2XZeWS#=o4D3at0=S!@;~U)mlLM)G_wGTm0whM=p*@ zE{cbWj*x*t`3jJcVfhLq^UyQ8L^}i5L5I>2IXh+`0FBJWM#(&dr(A$6R9tnXY#rP; zFHs&m^pPc}5vToiv$u8x`w=<8)B8U8we-FVpGxmLGOjc0@HHq7*O%)5-PbPoYBsw# zfUp0~Y*z4zA31GrJoT5`&|@g$T>J4*J`7H8GVZ~_dVB8vB>`hPfa^l)NCoPH(O0tv z@Wa)x&RYXbl>kSxbELH?eHwnK+f4#KbzizLN!{7(Uk(EA?(8o7Q-kTb=rBLb-Uhz= zmK57@8;tMI#`*p}^r<a z>Ot0m-&~B5=3ms9b8XxoG)TRB)lYEg6TXp4_vQL6xi$a|RP~$g`mdN0uC4K}WY_cF z2W)3hz5w`wz(-u@#0byk@;|_T^v#JuPnR6cjX7gogO0QE7GS$Yy~xs3|~0A`xan#{-v_}7U;wIZ zJMH@W!495X*a^Lm`y5*~dn(Ktr+B;xpR|)FluC3FM&n~QSR*YbGs_5yboU@3j z2v2nVWzeNz=i`?@mAdZWM^pG8Q#U>Rl+ON1T|sWp=oO(HP8i!wOBPs~8i%Gr^;Gosyz8LTxgV0j=6Z59DcBwA`54AC5y+4{6?cWPAJp_S)DJ+YojY zRbxCec$V=8w($Kxn+-4)u)viicfm(!2oUIq}M?x)OEN8m~ zSE8w_M#QVay#CI;=e;*$yh*EVyZ_|czd85g+_Wmb6nZV1JJLq%gO5;r9~Rp+Bn!-+U{UxPbLVuKIlo zI**h0xn3|_2^Y;_3)Da98Gg0N1N-dQLpm!o&L;-#xNu*i_>?#1&Tl|Ac^4zfUidNw zv3%}o{L|M?U4sAX#FM&)wts*)V(1dSi?nGKt^F3PvjNlx_QmwR#s>Zlntay4`hKRoUlr-x!##q2+#}eEbm(3N4fp?PxQ}5qt+!vOOZCKmuHnA4t@Iu0 z4eE21HC)8e=`y1`4G43<= z;-|3RQo1{90%cxx>hCfBksiLoD(F2P)WNINZb*Nv55Jv`xqKA*bOd!-Xx;tI~Y zeV1_P%?0S*CpXCb@33)0?kgwdzOsV$loug4)*J!a|Lui;ed;=%s~G-Tv1$$ahx~pK z{NAJTQ9a0Z{R^X&!cdQR2i1sDnA+bN^4%;dGvah&jD2Cb^>OHW@TavI-nmiZf$m$N zyBCUoK+nBI?GMYbc0%958Tvg?^{&?I4}St#UM*gY#lvf`w|lux-2jHcy96mJxk5dz03HVif?Kq9j)HM`dIyz0Qhi^ufAcgq095-3aqKAY&zzc`qb1` zRnDo8k>5(+uUIK9+~=S>rS8c{>;2oD)|r*kV)@m%9?pbFE~qMT@f6C5wz>GG$V2~F zi+42XzEktAQ*w6zPJo^}1aL;Nh2|PPw=E&(9Qr+)+`y-7$8VqnO#dR-pC83 zo=0s(&#vL#fIRUonHq;f=QqYE&R#?GVE&v=JKwzsS?GDHx?7xNp?Ae03-nvYdfe=* zkk2u3TI6BdzVHyv&Pt@TFYe7>CTHmjig_O*)#!=kofYbrg^D4~ZYTzVJ8k zv0UoD47`I4`PA>(UVb_@f%}9}mrmMe_2g#BGhz9i8GH|#Yf$#K2fAhbL`oWr#>efkh@kMoD&G{e8x&9>0@q1A=S$6f_Qa<|66rMG%k>4A| z8ARIrzk$*x9SNRmq{r3y`)7Zbz&N9K@r+o$ zPfKedntx?qMOjf-*7q8ezIy*7b8KKwA>YNN`aoLekj9@;JAQ5&a!v4`?_umMKu%sG zeE4gO`9j*a)0_d_^mY19pgLnsB_Oge^^MdssS8qPl8k0M63>6fjBP%K-S_{kS+{Oo zZcVH`)YBj7?s6Ax>D%h=Zf`%)+ZzgQUMF(*`e(T!%w1yn zYr8FL$u7(4*_qDmvHVNyY)b5GJSrPbd%MkTXH#Nl<5Af#w-SrwF0r%m*x7hgHq5QW zlH7JS9y=S4%7(e^(wErTc04=ek_6rAMHhtD357R49_z z(zC~{@{+A240e zEe^D`cSO2E?y8PZ`!N9lMIE85Kh)cMqNiW@qLHIrouRIN;qUD}+S}GC_93@^%FW*$ zfp>MNy*ES*p zC7SS}o~hQ0`}(_kLw)XIs`$=GU!S}EWP8-~NViA2`uclMw9~-tD_aNY4MW4M@A(nn zU`lpCn=y#^jQhnnM1rEO8cveOFhWznEHDXN0zL&ED0g_mFv9Q)gU7-+*^B!+ z+Mqu@#eFCHid(+bQq|PBPc+uFZhNk3-+oc&Z`kIqlBDKfgHKv~&5|_M)r-p3ZPj() zC${QZNm^PCNm^AYNkgMbq@}T%xDPb75^3?*A0k?bw8&Z0*dn=WN;OIS`<1({xiK^O0vEO%~1`!cQ)HjPN1XK-ZqPRcO9}N|2%9NNEPjRrVzpWTLf(HAx z{#d`P<6hk`dY?CpzIQ-h0$PCvkr0W(?-X9CJycuY>FfN{$F4kf!t+<2pBy@K$oF32 zy~OwN*U;KvoY{J2{V&BYU(Gl2k40`q{`JRi{a6?Y13R;0*jn3W%su0`UH?0_zW!kM z2`YrtH=<%mCQEt;(hH5KdzJB$)Ib;l%; z5gF5A(-CK;S#25i_eqnrf>8{W(qyk-DBMjF{*Fx*^3U41YOfGaz5{Evk$M~C$q$Xx z#YIp6&kTknA?p_!s_(JQ?GwKZ%uL2Gr@=myjkO@K9oPZv1WJKjfDY_lpR<0|`qk^# z;PI*uBZeVPJL!UeP1!EUF7gXR^~M5m8uL(4{NdLMkRr}Av&R!6`@AT6;(=&=;$c;- z*qm*o|CutR(=qp<2DpdBee}7<*9%dAd5wM)V+ZCt-)bSA!~AFB6nGd}Ma2L6I92(o z5YJ#v+<`ddh|`ET9f*^T^V!gYD8r+MCen0R9L2%V0H(3d;z(y_C>Uw$4_QIdVugnW zFw1vzcOP38{`vZ&9|>-QD#?tL=E~5&jlRa}%BH4P=+ve(9@{!N{FlSSm(LC3b|+~D zja!EC?p?!Rw-C`3Ok77JD&y4S=}wo|d-v{Lm&?n+F7F^B+irM+1#M4;>e4XozDpU9 zk)GagWo1}Jm!9vQyL|cFyBuDS_oOE_4HJ{R!W)M1x{!f#mv1j8MccDDuiQiJL+vm} z@7|eI9xm4{?_KXL=1)w{FD|8)7Uw4uGB`UYub?bdR*;v2PC*9ZyIih8Q(!^a!GNmp zp5eA@dNi4g>W*YwG00D}Ex zbuyll^I|e?mf&jA;nD)h7gaPp8r278(Ov`6jiw2x0R9B8G%kno&|D5_Ty_b=Uc<|^ zEqPEoW?oiO5ZR6((rbfR>=@ZOBpA(@<;o12|v`J7ZCDH2HWKR^$NQ;&b3+}`k2 zlyLGY4^XibrND4Fx}?HW;UgWfj=+&H*B843fyK1=bID|Qk8)E^GCR(XI-fLN6<#2F zgFW8wAgfpmnch+1ivek@{F0xfQfN*XPXEsR)ZBI3E$f<`TwYl|N`mF%@SAgSM;@gh z!>Np%AHoB=BgcwAIQZ=hc`0dWIDK|S%>hU5n>TN!KEVQq40;|$V;q&P+*Fsc4!HcO zn|TUMZwPFo0#HB3<4{>)T6~nW~)Do0b-wyJERIn`q2SOpX(Q0Q%`UI zoE(vd;Q)hrLE_+79f0{2Gh&$aA;i5DD)^>d?paFS)YnozY6QDHDm-p=0aHG6NY4(0 z?fxO-qX(GrotpN5idUMC5M^ZX(&~e9v-AsCR0rVpCwElW)7we%LkT#{AvI0fetGv@bL88Pz{%UPx~rYVY*=qi`jWDlfS+ zmP-h@qBF|B(xz$8tYqhO>M5{>ET`lRRZwIG)9C#y7(yeadC=vwXYE z@<_T%D>w6(v|P&!|4dlv%Wy+{wk*EI&*2uo-r`Ao0HW#;i^hWRqY6$7&~Gc&Vl zeJ*HkH8Twz1jSAUs**+{47sGC^*;m~^77fzVbG8$vBPxdZ6uBQh)J&i7kHI6RyjaP;$Io54%u!K zR^FC}9s6;)yt@~TdR2L%vS(S0n9dz8k14wH9#qEWS;028d3bSb^B)I*8!H4q;x)E8 zdj1j5{~wR6?RjtgV*QRgopbl_AOlA>Uz#s#;L95L-Pgd_=2I1nT2O+YaTt^U{UXr; zO2G3(i5MsW&nYBEKnZxhC~+2)FwU3&C0t}&0wqi`ra%cd7&D-RdBy@LVUe)}3Y->< zwV(ti!v#ukGm1b78bb#qR4{5m34X?5P(pyw0ZQm$#6SrN#t0~3lyMf6FwU3&C0t}& z0wqi`ra%cd7&D-RdBy@LVUe)}3j9DY)`Ak83>PTD%_ssTXbc^cP{F7LCHNVKK?wmy z2PmP35d$S87$cyBQN~$N!Z>3BlyH%836wC&m;xo-V9bCL<{1m1ghj>@DDa|StOX@F z87@$Qn^6Qx&=@)>p@LBhO7JrdgAxLa4p2f5BL+%HFh)QLql~klgmK0MDB&XG5-4Gk zF$GGv!I%Lh%rh2135$#+P~cAlV=XAb$#8)Z+>9bng2vE6_0Q2jP72HVUNbJB@MUdu z(EA8-MdUJ4ui$uj)uO1(bMAX1>wI~Mws~%8TtsO>jA?FCma!FCLkBnnY z@($w~t#c(KKGN-`-~WV;xdrNC2zd^fX|1VL3AlZrb8lbyH_n3!mPK~c5Sx6ygGcD5 PAD+dYrPndYP2v9qM$<^l literal 0 HcmV?d00001 diff --git a/gui.pnproj b/gui.pnproj index d687d797..47a1608a 100644 --- a/gui.pnproj +++ b/gui.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/source/homebrewboot/BootHomebrew.c b/source/homebrewboot/BootHomebrew.c deleted file mode 100644 index 8e5d1e12..00000000 --- a/source/homebrewboot/BootHomebrew.c +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "../lstub.h" - -#include "fatmounter.h" -#include "dolloader.h" -#include "elfloader.h" - -void *innetbuffer = NULL; - -int AllocHomebrewMemory(u32 filesize) { - - innetbuffer = malloc(filesize); - - if (!innetbuffer) - return -1; - - return 1; - -} - -void FreeHomebrewBuffer() { - free(innetbuffer); - innetbuffer = NULL; -} - -void CopyHomebrewMemory(u32 read, u8 *temp, u32 len) { - - memcpy(((u8 *) innetbuffer)+read, temp, len); - -} - -int BootHomebrew(char * path) { - loadStub(); - if (Set_Stub_Split(0x00010001,"UNEO")<0) - Set_Stub_Split(0x00010001,"ULNR"); - void *buffer = NULL; - u32 filesize = 0; - entrypoint entry; - u32 cpu_isr; - - FILE * file = fopen(path, "rb"); - if (!file) SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - - fseek (file, 0, SEEK_END); - filesize = ftell(file); - rewind(file); - - buffer = malloc(filesize); - - if (fread (buffer, 1, filesize, file) != filesize) { - fclose (file); - free(buffer); - SDCard_deInit(); - USBDevice_deInit(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - fclose (file); - - struct __argv args; - bzero(&args, sizeof(args)); - args.argvMagic = ARGV_MAGIC; - args.length = strlen(path) + 2; - args.commandLine = (char*)malloc(args.length); - if (!args.commandLine) SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - strcpy(args.commandLine, path); - args.commandLine[args.length - 1] = '\0'; - args.argc = 1; - args.argv = &args.commandLine; - args.endARGV = args.argv + 1; - - int ret = valid_elf_image(buffer); - if (ret == 1) - entry = (entrypoint) load_elf_image(buffer); - else - entry = (entrypoint) load_dol(buffer, &args); - - free(buffer); - - if (!entry) { - SDCard_deInit(); - USBDevice_deInit(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - - SDCard_deInit(); - USBDevice_deInit(); - - WPAD_Flush(0); - WPAD_Disconnect(0); - WPAD_Shutdown(); - - SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); - _CPU_ISR_Disable (cpu_isr); - __exception_closeall(); - entry(); - _CPU_ISR_Restore (cpu_isr); - - return 0; -} - -int BootHomebrewFromMem() { - loadStub(); - if (Set_Stub_Split(0x00010001,"UNEO")<0) - Set_Stub_Split(0x00010001,"ULNR"); - entrypoint entry; - u32 cpu_isr; - - if (!innetbuffer) { - SDCard_deInit(); - USBDevice_deInit(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - - struct __argv args; - - int ret = valid_elf_image(innetbuffer); - if (ret == 1) - entry = (entrypoint) load_elf_image(innetbuffer); - else - entry = (entrypoint) load_dol(innetbuffer, &args); - - free(innetbuffer); - - if (!entry) { - SDCard_deInit(); - USBDevice_deInit(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - - SDCard_deInit(); - USBDevice_deInit(); - - WPAD_Flush(0); - WPAD_Disconnect(0); - WPAD_Shutdown(); - - SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); - _CPU_ISR_Disable (cpu_isr); - __exception_closeall(); - entry(); - _CPU_ISR_Restore (cpu_isr); - - return 0; -} diff --git a/source/homebrewboot/BootHomebrew.cpp b/source/homebrewboot/BootHomebrew.cpp new file mode 100644 index 00000000..d33ede39 --- /dev/null +++ b/source/homebrewboot/BootHomebrew.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbloader/usbstorage2.h" +#include "dolloader.h" +#include "fatmounter.h" +#include "sys.h" + +extern const u8 app_booter_dol[]; +extern const u32 app_booter_dol_size; + +static u8 *homebrewbuffer = (u8 *) 0x92000000; +static int homebrewsize = 0; +static std::vector Arguments; + +void AddBootArgument(const char * argv) +{ + std::string arg(argv); + Arguments.push_back(arg); +} + +int CopyHomebrewMemory(u8 *temp, u32 pos, u32 len) +{ + homebrewsize += len; + memcpy((homebrewbuffer)+pos, temp, len); + + return 1; +} + +void FreeHomebrewBuffer() +{ + homebrewbuffer = (u8 *)0x92000000; + homebrewsize = 0; +} + +static int SetupARGV(struct __argv * args) +{ + if(!args) + return -1; + + bzero(args, sizeof(struct __argv)); + args->argvMagic = ARGV_MAGIC; + + u32 stringlength = 1; + + /** Append Arguments **/ + for(u32 i = 0; i < Arguments.size(); i++) + { + stringlength += Arguments[i].size()+1; + } + + args->length = stringlength; + args->commandLine = (char*) malloc(args->length); + if (!args->commandLine) + return -1; + + u32 argc = 0; + u32 position = 0; + + /** Append Arguments **/ + for(u32 i = 0; i < Arguments.size(); i++) + { + strcpy(&args->commandLine[position], Arguments[i].c_str()); + position += Arguments[i].size()+1; + argc++; + } + + args->argc = argc; + + args->commandLine[args->length - 1] = '\0'; + args->argv = &args->commandLine; + args->endARGV = args->argv + 1; + + Arguments.clear(); + + return 0; +} + +int BootHomebrew() +{ + if(homebrewsize <= 0) + Sys_BackToLoader(); + + SDCard_deInit(); + USBDevice_deInit(); + USBStorage2_Deinit(); + + struct __argv args; + SetupARGV(&args); + + u32 cpu_isr; + + entrypoint entry = (entrypoint) load_dol(app_booter_dol, &args); + + if (!entry) + Sys_BackToLoader(); + + SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); + _CPU_ISR_Disable (cpu_isr); + __exception_closeall(); + entry(); + _CPU_ISR_Restore (cpu_isr); + + return 0; +} + +int BootHomebrew(const char * filepath) +{ + FILE * file = fopen(filepath, "rb"); + if(!file) + Sys_BackToLoader(); + + fseek(file, 0, SEEK_END); + + int size = ftell(file); + rewind(file); + + homebrewsize = fread(homebrewbuffer, 1, size, file); + fclose(file); + + AddBootArgument(filepath); + + return BootHomebrew(); +} diff --git a/source/homebrewboot/BootHomebrew.h b/source/homebrewboot/BootHomebrew.h index 60f64427..b58b3c07 100644 --- a/source/homebrewboot/BootHomebrew.h +++ b/source/homebrewboot/BootHomebrew.h @@ -1,13 +1,11 @@ #ifndef _BOOTHOMEBREW_H_ #define _BOOTHOMEBREW_H_ - -int BootHomebrew(char * path); -int BootHomebrewFromMem(); -void CopyHomebrewMemory(u32 read, u8 *temp, u32 len); -int AllocHomebrewMemory(u32 filesize); +int BootHomebrew(); +int BootHomebrew(const char * filepath); +int CopyHomebrewMemory(u8 *temp, u32 pos, u32 len); +void AddBootArgument(const char * arg); void FreeHomebrewBuffer(); -void AddBootArgument(const char * argv); -extern u32 homebrewsize; +int LoadHomebrew(const char * filepath); #endif diff --git a/source/homebrewboot/HomebrewBrowse.cpp b/source/homebrewboot/HomebrewBrowse.cpp index a78b965c..1a21c8e7 100644 --- a/source/homebrewboot/HomebrewBrowse.cpp +++ b/source/homebrewboot/HomebrewBrowse.cpp @@ -41,7 +41,6 @@ extern char wiiloadVersion[2]; extern u8 shutdown; extern u8 reset; extern struct SSettings Settings; -extern void *innetbuffer; /*** Variables used elsewhere ***/ u8 boothomebrew = 0; @@ -794,124 +793,118 @@ int MenuHomebrewBrowse() { int choice = WindowPrompt(filesizetxt, temp, tr("OK"), tr("Cancel")); - if (choice == 1) { + if (choice == 1) + { + u32 read = 0; + u8 *temp = NULL; + int len = NETWORKBLOCKSIZE; + temp = (u8 *) malloc(infilesize); - int res = AllocHomebrewMemory(infilesize); + bool error = false; + u8 *ptr = temp; + while (read < infilesize) { - if (res < 0) { - CloseConnection(); - WindowPrompt(tr("Not enough free memory."), 0, tr("OK")); - } else { - u32 read = 0; - u8 *temp = NULL; - int len = NETWORKBLOCKSIZE; - temp = (u8 *) malloc(infilesize); + ShowProgress(tr("Receiving file from:"), GetIncommingIP(), NULL, read, infilesize, true); - bool error = false; - u8 *ptr = temp; - while (read < infilesize) { + if (infilesize - read < (u32) len) + len = infilesize-read; + else + len = NETWORKBLOCKSIZE; - ShowProgress(tr("Receiving file from:"), GetIncommingIP(), NULL, read, infilesize, true); + int result = network_read(ptr, len); - if (infilesize - read < (u32) len) - len = infilesize-read; - else - len = NETWORKBLOCKSIZE; - - int result = network_read(ptr, len); - - if (result < 0) { - WindowPrompt(tr("Error while transfering data."), 0, tr("OK")); - error = true; - break; - } - if (!result) { - break; - } - - ptr += result; - - read += result; + if (result < 0) { + WindowPrompt(tr("Error while transfering data."), 0, tr("OK")); + error = true; + break; + } + if (!result) { + break; } - - char filename[101]; - if (!error) { - - network_read((u8*) &filename, 100); - - // Do we need to unzip this thing? - if (wiiloadVersion[0] > 0 || wiiloadVersion[1] > 4) { - // We need to unzip... - if (temp[0] == 'P' && temp[1] == 'K' && temp[2] == 0x03 && temp[3] == 0x04) { - // It's a zip file, unzip to the apps directory - - // Zip archive, ask for permission to install the zip - char zippath[255]; - sprintf((char *) &zippath, "%s%s", Settings.homebrewapps_path, filename); - - FILE *fp = fopen(zippath, "wb"); - if (fp != NULL) - { - fwrite(temp, 1, infilesize, fp); - fclose(fp); - - // Now unzip the zip file... - unzFile uf = unzOpen(zippath); - if (uf==NULL) { - error = true; - } else { - extractZip(uf,0,1,0, Settings.homebrewapps_path); - unzCloseCurrentFile(uf); - - remove(zippath); - - // Reload this menu here... - menu = MENU_HOMEBREWBROWSE; - break; - } - } else { - error = true; - } - } else if (uncfilesize != 0) { // if uncfilesize == 0, it's not compressed - // It's compressed, uncompress - u8 *unc = (u8 *) malloc(uncfilesize); - uLongf f = uncfilesize; - error = uncompress(unc, &f, temp, infilesize) != Z_OK; - uncfilesize = f; - homebrewsize = uncfilesize; - - free(temp); - temp = unc; - } - } - - if (!error && strstr(filename,".zip") == NULL) { - innetbuffer = temp; + ptr += result; - } - } - - ProgressStop(); + read += result; + } - if (error || read != infilesize) { - WindowPrompt(tr("Error:"), tr("No data could be read."), tr("OK")); - FreeHomebrewBuffer(); + char filename[101]; + if (!error) { + + network_read((u8*) &filename, 100); + + // Do we need to unzip this thing? + if (wiiloadVersion[0] > 0 || wiiloadVersion[1] > 4) { + + // We need to unzip... + if (temp[0] == 'P' && temp[1] == 'K' && temp[2] == 0x03 && temp[3] == 0x04) { + // It's a zip file, unzip to the apps directory + + // Zip archive, ask for permission to install the zip + char zippath[255]; + sprintf((char *) &zippath, "%s%s", Settings.homebrewapps_path, filename); + + FILE *fp = fopen(zippath, "wb"); + if (fp != NULL) + { + fwrite(temp, 1, infilesize, fp); + fclose(fp); + + // Now unzip the zip file... + unzFile uf = unzOpen(zippath); + if (uf==NULL) { + error = true; + } else { + extractZip(uf,0,1,0, Settings.homebrewapps_path); + unzCloseCurrentFile(uf); + + remove(zippath); + + // Reload this menu here... + menu = MENU_HOMEBREWBROWSE; + break; + } + } else { + error = true; + } + } else if (uncfilesize != 0) { // if uncfilesize == 0, it's not compressed + // It's compressed, uncompress + u8 *unc = (u8 *) malloc(uncfilesize); + uLongf f = uncfilesize; + error = uncompress(unc, &f, temp, infilesize) != Z_OK; + uncfilesize = f; + infilesize = f; + + free(temp); + temp = unc; + } + } + + if (!error && strstr(filename,".zip") == NULL) { + CopyHomebrewMemory(temp, 0, infilesize); + free(temp); + } + } + + ProgressStop(); + + if (error || read != infilesize) { + WindowPrompt(tr("Error:"), tr("No data could be read."), tr("OK")); + FreeHomebrewBuffer(); + } else { + if (strstr(filename,".dol") || strstr(filename,".DOL") + || strstr(filename,".elf") || strstr(filename,".ELF")) + { + boothomebrew = 2; + AddBootArgument( filename ); + menu = MENU_EXIT; + CloseConnection(); + break; + } else if (strstr(filename,".zip")) { + WindowPrompt(tr("Success:"), tr("Uploaded ZIP file installed to homebrew directory."), tr("OK")); + CloseConnection(); } else { - if (strstr(filename,".dol") || strstr(filename,".DOL") - || strstr(filename,".elf") || strstr(filename,".ELF")) { - boothomebrew = 2; - AddBootArgument( filename ); - menu = MENU_EXIT; - CloseConnection(); - break; - } else if (strstr(filename,".zip")) { - WindowPrompt(tr("Success:"), tr("Uploaded ZIP file installed to homebrew directory."), tr("OK")); - CloseConnection(); - } else { - FreeHomebrewBuffer(); - WindowPrompt(tr("ERROR:"), tr("Not a DOL/ELF file."), tr("OK")); - } + FreeHomebrewBuffer(); + WindowPrompt(tr("ERROR:"), tr("Not a DOL/ELF file."), tr("OK")); } } } diff --git a/source/homebrewboot/dolloader.c b/source/homebrewboot/dolloader.c index 5462e1b4..5edce25d 100644 --- a/source/homebrewboot/dolloader.c +++ b/source/homebrewboot/dolloader.c @@ -20,7 +20,7 @@ typedef struct _dolheader { u32 entry_point; } dolheader; -u32 load_dol(void *dolstart, struct __argv *argv) { +u32 load_dol(const void *dolstart, struct __argv *argv) { u32 i; dolheader *dolfile; @@ -28,15 +28,13 @@ u32 load_dol(void *dolstart, struct __argv *argv) { dolfile = (dolheader *) dolstart; for (i = 0; i < 7; i++) { if ((!dolfile->text_size[i]) || (dolfile->text_start[i] < 0x100)) continue; - VIDEO_WaitVSync(); ICInvalidateRange ((void *) dolfile->text_start[i],dolfile->text_size[i]); - memmove ((void *) dolfile->text_start[i],dolstart+dolfile->text_pos[i],dolfile->text_size[i]); + memcpy((void *) dolfile->text_start[i],dolstart+dolfile->text_pos[i],dolfile->text_size[i]); } for (i = 0; i < 11; i++) { if ((!dolfile->data_size[i]) || (dolfile->data_start[i] < 0x100)) continue; - VIDEO_WaitVSync(); - memmove ((void *) dolfile->data_start[i],dolstart+dolfile->data_pos[i],dolfile->data_size[i]); + memcpy((void *) dolfile->data_start[i],dolstart+dolfile->data_pos[i],dolfile->data_size[i]); DCFlushRangeNoSync ((void *) dolfile->data_start[i],dolfile->data_size[i]); } @@ -45,7 +43,7 @@ u32 load_dol(void *dolstart, struct __argv *argv) { if (argv && argv->argvMagic == ARGV_MAGIC) { void *new_argv = (void *)(dolfile->entry_point + 8); - memmove(new_argv, argv, sizeof(*argv)); + memcpy(new_argv, argv, sizeof(*argv)); DCFlushRange(new_argv, sizeof(*argv)); } diff --git a/source/homebrewboot/dolloader.h b/source/homebrewboot/dolloader.h index 288d9f5a..af644414 100644 --- a/source/homebrewboot/dolloader.h +++ b/source/homebrewboot/dolloader.h @@ -5,10 +5,10 @@ extern "C" { #endif - extern void __exception_closeall(); - typedef void (*entrypoint) (void); +extern void __exception_closeall(); +typedef void (*entrypoint) (void); - u32 load_dol(void *dolstart, struct __argv *argv); +u32 load_dol(const void *dolstart, struct __argv *argv); #ifdef __cplusplus diff --git a/source/homebrewboot/elf_abi.h b/source/homebrewboot/elf_abi.h deleted file mode 100644 index 994912ca..00000000 --- a/source/homebrewboot/elf_abi.h +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright (c) 1995, 1996, 2001, 2002 - * Erik Theisen. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This is the ELF ABI header file - * formerly known as "elf_abi.h". - */ - -#ifndef _ELF_ABI_H -#define _ELF_ABI_H - -#include - -/* - * This version doesn't work for 64-bit ABIs - Erik. - */ - -/* - * These typedefs need to be handled better. - */ -typedef u32 Elf32_Addr; /* Unsigned program address */ -typedef u32 Elf32_Off; /* Unsigned file offset */ -typedef s32 Elf32_Sword; /* Signed large integer */ -typedef u32 Elf32_Word; /* Unsigned large integer */ -typedef u16 Elf32_Half; /* Unsigned medium integer */ - -/* e_ident[] identification indexes */ -#define EI_MAG0 0 /* file ID */ -#define EI_MAG1 1 /* file ID */ -#define EI_MAG2 2 /* file ID */ -#define EI_MAG3 3 /* file ID */ -#define EI_CLASS 4 /* file class */ -#define EI_DATA 5 /* data encoding */ -#define EI_VERSION 6 /* ELF header version */ -#define EI_OSABI 7 /* OS/ABI specific ELF extensions */ -#define EI_ABIVERSION 8 /* ABI target version */ -#define EI_PAD 9 /* start of pad bytes */ -#define EI_NIDENT 16 /* Size of e_ident[] */ - -/* e_ident[] magic number */ -#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */ -#define ELFMAG1 'E' /* e_ident[EI_MAG1] */ -#define ELFMAG2 'L' /* e_ident[EI_MAG2] */ -#define ELFMAG3 'F' /* e_ident[EI_MAG3] */ -#define ELFMAG "\177ELF" /* magic */ -#define SELFMAG 4 /* size of magic */ - -/* e_ident[] file class */ -#define ELFCLASSNONE 0 /* invalid */ -#define ELFCLASS32 1 /* 32-bit objs */ -#define ELFCLASS64 2 /* 64-bit objs */ -#define ELFCLASSNUM 3 /* number of classes */ - -/* e_ident[] data encoding */ -#define ELFDATANONE 0 /* invalid */ -#define ELFDATA2LSB 1 /* Little-Endian */ -#define ELFDATA2MSB 2 /* Big-Endian */ -#define ELFDATANUM 3 /* number of data encode defines */ - -/* e_ident[] OS/ABI specific ELF extensions */ -#define ELFOSABI_NONE 0 /* No extension specified */ -#define ELFOSABI_HPUX 1 /* Hewlett-Packard HP-UX */ -#define ELFOSABI_NETBSD 2 /* NetBSD */ -#define ELFOSABI_LINUX 3 /* Linux */ -#define ELFOSABI_SOLARIS 6 /* Sun Solaris */ -#define ELFOSABI_AIX 7 /* AIX */ -#define ELFOSABI_IRIX 8 /* IRIX */ -#define ELFOSABI_FREEBSD 9 /* FreeBSD */ -#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX */ -#define ELFOSABI_MODESTO 11 /* Novell Modesto */ -#define ELFOSABI_OPENBSD 12 /* OpenBSD */ -/* 64-255 Architecture-specific value range */ - -/* e_ident[] ABI Version */ -#define ELFABIVERSION 0 - -/* e_ident */ -#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ - (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ - (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ - (ehdr).e_ident[EI_MAG3] == ELFMAG3) - -/* ELF Header */ -typedef struct elfhdr { - unsigned char e_ident[EI_NIDENT]; /* ELF Identification */ - Elf32_Half e_type; /* object file type */ - Elf32_Half e_machine; /* machine */ - Elf32_Word e_version; /* object file version */ - Elf32_Addr e_entry; /* virtual entry point */ - Elf32_Off e_phoff; /* program header table offset */ - Elf32_Off e_shoff; /* section header table offset */ - Elf32_Word e_flags; /* processor-specific flags */ - Elf32_Half e_ehsize; /* ELF header size */ - Elf32_Half e_phentsize; /* program header entry size */ - Elf32_Half e_phnum; /* number of program header entries */ - Elf32_Half e_shentsize; /* section header entry size */ - Elf32_Half e_shnum; /* number of section header entries */ - Elf32_Half e_shstrndx; /* section header table's "section - header string table" entry offset */ -} Elf32_Ehdr; - -/* e_type */ -#define ET_NONE 0 /* No file type */ -#define ET_REL 1 /* relocatable file */ -#define ET_EXEC 2 /* executable file */ -#define ET_DYN 3 /* shared object file */ -#define ET_CORE 4 /* core file */ -#define ET_NUM 5 /* number of types */ -#define ET_LOOS 0xfe00 /* reserved range for operating */ -#define ET_HIOS 0xfeff /* system specific e_type */ -#define ET_LOPROC 0xff00 /* reserved range for processor */ -#define ET_HIPROC 0xffff /* specific e_type */ - -/* e_machine */ -#define EM_NONE 0 /* No Machine */ -#define EM_M32 1 /* AT&T WE 32100 */ -#define EM_SPARC 2 /* SPARC */ -#define EM_386 3 /* Intel 80386 */ -#define EM_68K 4 /* Motorola 68000 */ -#define EM_88K 5 /* Motorola 88000 */ -#if 0 -#define EM_486 6 /* RESERVED - was Intel 80486 */ -#endif -#define EM_860 7 /* Intel 80860 */ -#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */ -#define EM_S370 9 /* IBM System/370 Processor */ -#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ -#if 0 -#define EM_SPARC64 11 /* RESERVED - was SPARC v9 -64-bit unoffical */ -#endif -/* RESERVED 11-14 for future use */ -#define EM_PARISC 15 /* HPPA */ -/* RESERVED 16 for future use */ -#define EM_VPP500 17 /* Fujitsu VPP500 */ -#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */ -#define EM_960 19 /* Intel 80960 */ -#define EM_PPC 20 /* PowerPC */ -#define EM_PPC64 21 /* 64-bit PowerPC */ -#define EM_S390 22 /* IBM System/390 Processor */ -/* RESERVED 23-35 for future use */ -#define EM_V800 36 /* NEC V800 */ -#define EM_FR20 37 /* Fujitsu FR20 */ -#define EM_RH32 38 /* TRW RH-32 */ -#define EM_RCE 39 /* Motorola RCE */ -#define EM_ARM 40 /* Advanced Risc Machines ARM */ -#define EM_ALPHA 41 /* Digital Alpha */ -#define EM_SH 42 /* Hitachi SH */ -#define EM_SPARCV9 43 /* SPARC Version 9 */ -#define EM_TRICORE 44 /* Siemens TriCore embedded processor */ -#define EM_ARC 45 /* Argonaut RISC Core */ -#define EM_H8_300 46 /* Hitachi H8/300 */ -#define EM_H8_300H 47 /* Hitachi H8/300H */ -#define EM_H8S 48 /* Hitachi H8S */ -#define EM_H8_500 49 /* Hitachi H8/500 */ -#define EM_IA_64 50 /* Intel Merced */ -#define EM_MIPS_X 51 /* Stanford MIPS-X */ -#define EM_COLDFIRE 52 /* Motorola Coldfire */ -#define EM_68HC12 53 /* Motorola M68HC12 */ -#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -#define EM_PCP 55 /* Siemens PCP */ -#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -#define EM_STARCORE 58 /* Motorola Start*Core processor */ -#define EM_ME16 59 /* Toyota ME16 processor */ -#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -#define EM_X86_64 62 /* AMD x86-64 */ -#define EM_PDSP 63 /* Sony DSP Processor */ -/* RESERVED 64,65 for future use */ -#define EM_FX66 66 /* Siemens FX66 microcontroller */ -#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -#define EM_SVX 73 /* Silicon Graphics SVx */ -#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -#define EM_VAX 75 /* Digital VAX */ -#define EM_CHRIS 76 /* Axis Communications embedded proc. */ -#define EM_JAVELIN 77 /* Infineon Technologies emb. proc. */ -#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -#define EM_MMIX 80 /* Donald Knuth's edu 64-bit proc. */ -#define EM_HUANY 81 /* Harvard University mach-indep objs */ -#define EM_PRISM 82 /* SiTera Prism */ -#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -#define EM_FR30 84 /* Fujitsu FR30 */ -#define EM_D10V 85 /* Mitsubishi DV10V */ -#define EM_D30V 86 /* Mitsubishi DV30V */ -#define EM_V850 87 /* NEC v850 */ -#define EM_M32R 88 /* Mitsubishi M32R */ -#define EM_MN10300 89 /* Matsushita MN10200 */ -#define EM_MN10200 90 /* Matsushita MN10200 */ -#define EM_PJ 91 /* picoJava */ -#define EM_NUM 92 /* number of machine types */ - -/* Version */ -#define EV_NONE 0 /* Invalid */ -#define EV_CURRENT 1 /* Current */ -#define EV_NUM 2 /* number of versions */ - -/* Section Header */ -typedef struct { - Elf32_Word sh_name; /* name - index into section header - string table section */ - Elf32_Word sh_type; /* type */ - Elf32_Word sh_flags; /* flags */ - Elf32_Addr sh_addr; /* address */ - Elf32_Off sh_offset; /* file offset */ - Elf32_Word sh_size; /* section size */ - Elf32_Word sh_link; /* section header table index link */ - Elf32_Word sh_info; /* extra information */ - Elf32_Word sh_addralign; /* address alignment */ - Elf32_Word sh_entsize; /* section entry size */ -} Elf32_Shdr; - -/* Special Section Indexes */ -#define SHN_UNDEF 0 /* undefined */ -#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */ -#define SHN_LOPROC 0xff00 /* reserved range for processor */ -#define SHN_HIPROC 0xff1f /* specific section indexes */ -#define SHN_LOOS 0xff20 /* reserved range for operating */ -#define SHN_HIOS 0xff3f /* specific semantics */ -#define SHN_ABS 0xfff1 /* absolute value */ -#define SHN_COMMON 0xfff2 /* common symbol */ -#define SHN_XINDEX 0xffff /* Index is an extra table */ -#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */ - -/* sh_type */ -#define SHT_NULL 0 /* inactive */ -#define SHT_PROGBITS 1 /* program defined information */ -#define SHT_SYMTAB 2 /* symbol table section */ -#define SHT_STRTAB 3 /* string table section */ -#define SHT_RELA 4 /* relocation section with addends*/ -#define SHT_HASH 5 /* symbol hash table section */ -#define SHT_DYNAMIC 6 /* dynamic section */ -#define SHT_NOTE 7 /* note section */ -#define SHT_NOBITS 8 /* no space section */ -#define SHT_REL 9 /* relation section without addends */ -#define SHT_SHLIB 10 /* reserved - purpose unknown */ -#define SHT_DYNSYM 11 /* dynamic symbol table section */ -#define SHT_INIT_ARRAY 14 /* Array of constructors */ -#define SHT_FINI_ARRAY 15 /* Array of destructors */ -#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -#define SHT_GROUP 17 /* Section group */ -#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ -#define SHT_NUM 19 /* number of section types */ -#define SHT_LOOS 0x60000000 /* Start OS-specific */ -#define SHT_HIOS 0x6fffffff /* End OS-specific */ -#define SHT_LOPROC 0x70000000 /* reserved range for processor */ -#define SHT_HIPROC 0x7fffffff /* specific section header types */ -#define SHT_LOUSER 0x80000000 /* reserved range for application */ -#define SHT_HIUSER 0xffffffff /* specific indexes */ - -/* Section names */ -#define ELF_BSS ".bss" /* uninitialized data */ -#define ELF_COMMENT ".comment" /* version control information */ -#define ELF_DATA ".data" /* initialized data */ -#define ELF_DATA1 ".data1" /* initialized data */ -#define ELF_DEBUG ".debug" /* debug */ -#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */ -#define ELF_DYNSTR ".dynstr" /* dynamic string table */ -#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */ -#define ELF_FINI ".fini" /* termination code */ -#define ELF_FINI_ARRAY ".fini_array" /* Array of destructors */ -#define ELF_GOT ".got" /* global offset table */ -#define ELF_HASH ".hash" /* symbol hash table */ -#define ELF_INIT ".init" /* initialization code */ -#define ELF_INIT_ARRAY ".init_array" /* Array of constuctors */ -#define ELF_INTERP ".interp" /* Pathname of program interpreter */ -#define ELF_LINE ".line" /* Symbolic line numnber information */ -#define ELF_NOTE ".note" /* Contains note section */ -#define ELF_PLT ".plt" /* Procedure linkage table */ -#define ELF_PREINIT_ARRAY ".preinit_array" /* Array of pre-constructors */ -#define ELF_REL_DATA ".rel.data" /* relocation data */ -#define ELF_REL_FINI ".rel.fini" /* relocation termination code */ -#define ELF_REL_INIT ".rel.init" /* relocation initialization code */ -#define ELF_REL_DYN ".rel.dyn" /* relocaltion dynamic link info */ -#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */ -#define ELF_REL_TEXT ".rel.text" /* relocation code */ -#define ELF_RODATA ".rodata" /* read-only data */ -#define ELF_RODATA1 ".rodata1" /* read-only data */ -#define ELF_SHSTRTAB ".shstrtab" /* section header string table */ -#define ELF_STRTAB ".strtab" /* string table */ -#define ELF_SYMTAB ".symtab" /* symbol table */ -#define ELF_SYMTAB_SHNDX ".symtab_shndx"/* symbol table section index */ -#define ELF_TBSS ".tbss" /* thread local uninit data */ -#define ELF_TDATA ".tdata" /* thread local init data */ -#define ELF_TDATA1 ".tdata1" /* thread local init data */ -#define ELF_TEXT ".text" /* code */ - -/* Section Attribute Flags - sh_flags */ -#define SHF_WRITE 0x1 /* Writable */ -#define SHF_ALLOC 0x2 /* occupies memory */ -#define SHF_EXECINSTR 0x4 /* executable */ -#define SHF_MERGE 0x10 /* Might be merged */ -#define SHF_STRINGS 0x20 /* Contains NULL terminated strings */ -#define SHF_INFO_LINK 0x40 /* sh_info contains SHT index */ -#define SHF_LINK_ORDER 0x80 /* Preserve order after combining*/ -#define SHF_OS_NONCONFORMING 0x100 /* Non-standard OS specific handling */ -#define SHF_GROUP 0x200 /* Member of section group */ -#define SHF_TLS 0x400 /* Thread local storage */ -#define SHF_MASKOS 0x0ff00000 /* OS specific */ -#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */ -/* specific section attributes */ - -/* Section Group Flags */ -#define GRP_COMDAT 0x1 /* COMDAT group */ -#define GRP_MASKOS 0x0ff00000 /* Mask OS specific flags */ -#define GRP_MASKPROC 0xf0000000 /* Mask processor specific flags */ - -/* Symbol Table Entry */ -typedef struct elf32_sym { - Elf32_Word st_name; /* name - index into string table */ - Elf32_Addr st_value; /* symbol value */ - Elf32_Word st_size; /* symbol size */ - unsigned char st_info; /* type and binding */ - unsigned char st_other; /* 0 - no defined meaning */ - Elf32_Half st_shndx; /* section header index */ -} Elf32_Sym; - -/* Symbol table index */ -#define STN_UNDEF 0 /* undefined */ - -/* Extract symbol info - st_info */ -#define ELF32_ST_BIND(x) ((x) >> 4) -#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf) -#define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) -#define ELF32_ST_VISIBILITY(x) ((x) & 0x3) - -/* Symbol Binding - ELF32_ST_BIND - st_info */ -#define STB_LOCAL 0 /* Local symbol */ -#define STB_GLOBAL 1 /* Global symbol */ -#define STB_WEAK 2 /* like global - lower precedence */ -#define STB_NUM 3 /* number of symbol bindings */ -#define STB_LOOS 10 /* reserved range for operating */ -#define STB_HIOS 12 /* system specific symbol bindings */ -#define STB_LOPROC 13 /* reserved range for processor */ -#define STB_HIPROC 15 /* specific symbol bindings */ - -/* Symbol type - ELF32_ST_TYPE - st_info */ -#define STT_NOTYPE 0 /* not specified */ -#define STT_OBJECT 1 /* data object */ -#define STT_FUNC 2 /* function */ -#define STT_SECTION 3 /* section */ -#define STT_FILE 4 /* file */ -#define STT_NUM 5 /* number of symbol types */ -#define STT_TLS 6 /* Thread local storage symbol */ -#define STT_LOOS 10 /* reserved range for operating */ -#define STT_HIOS 12 /* system specific symbol types */ -#define STT_LOPROC 13 /* reserved range for processor */ -#define STT_HIPROC 15 /* specific symbol types */ - -/* Symbol visibility - ELF32_ST_VISIBILITY - st_other */ -#define STV_DEFAULT 0 /* Normal visibility rules */ -#define STV_INTERNAL 1 /* Processor specific hidden class */ -#define STV_HIDDEN 2 /* Symbol unavailable in other mods */ -#define STV_PROTECTED 3 /* Not preemptible, not exported */ - - -/* Relocation entry with implicit addend */ -typedef struct { - Elf32_Addr r_offset; /* offset of relocation */ - Elf32_Word r_info; /* symbol table index and type */ -} Elf32_Rel; - -/* Relocation entry with explicit addend */ -typedef struct { - Elf32_Addr r_offset; /* offset of relocation */ - Elf32_Word r_info; /* symbol table index and type */ - Elf32_Sword r_addend; -} Elf32_Rela; - -/* Extract relocation info - r_info */ -#define ELF32_R_SYM(i) ((i) >> 8) -#define ELF32_R_TYPE(i) ((unsigned char) (i)) -#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t)) - -/* Program Header */ -typedef struct { - Elf32_Word p_type; /* segment type */ - Elf32_Off p_offset; /* segment offset */ - Elf32_Addr p_vaddr; /* virtual address of segment */ - Elf32_Addr p_paddr; /* physical address - ignored? */ - Elf32_Word p_filesz; /* number of bytes in file for seg. */ - Elf32_Word p_memsz; /* number of bytes in mem. for seg. */ - Elf32_Word p_flags; /* flags */ - Elf32_Word p_align; /* memory alignment */ -} Elf32_Phdr; - -/* Segment types - p_type */ -#define PT_NULL 0 /* unused */ -#define PT_LOAD 1 /* loadable segment */ -#define PT_DYNAMIC 2 /* dynamic linking section */ -#define PT_INTERP 3 /* the RTLD */ -#define PT_NOTE 4 /* auxiliary information */ -#define PT_SHLIB 5 /* reserved - purpose undefined */ -#define PT_PHDR 6 /* program header */ -#define PT_TLS 7 /* Thread local storage template */ -#define PT_NUM 8 /* Number of segment types */ -#define PT_LOOS 0x60000000 /* reserved range for operating */ -#define PT_HIOS 0x6fffffff /* system specific segment types */ -#define PT_LOPROC 0x70000000 /* reserved range for processor */ -#define PT_HIPROC 0x7fffffff /* specific segment types */ - -/* Segment flags - p_flags */ -#define PF_X 0x1 /* Executable */ -#define PF_W 0x2 /* Writable */ -#define PF_R 0x4 /* Readable */ -#define PF_MASKOS 0x0ff00000 /* OS specific segment flags */ -#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */ -/* specific segment flags */ -/* Dynamic structure */ -typedef struct { - Elf32_Sword d_tag; /* controls meaning of d_val */ - union { - Elf32_Word d_val; /* Multiple meanings - see d_tag */ - Elf32_Addr d_ptr; /* program virtual address */ - } d_un; -} Elf32_Dyn; - -extern Elf32_Dyn _DYNAMIC[]; - -/* Dynamic Array Tags - d_tag */ -#define DT_NULL 0 /* marks end of _DYNAMIC array */ -#define DT_NEEDED 1 /* string table offset of needed lib */ -#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */ -#define DT_PLTGOT 3 /* address PLT/GOT */ -#define DT_HASH 4 /* address of symbol hash table */ -#define DT_STRTAB 5 /* address of string table */ -#define DT_SYMTAB 6 /* address of symbol table */ -#define DT_RELA 7 /* address of relocation table */ -#define DT_RELASZ 8 /* size of relocation table */ -#define DT_RELAENT 9 /* size of relocation entry */ -#define DT_STRSZ 10 /* size of string table */ -#define DT_SYMENT 11 /* size of symbol table entry */ -#define DT_INIT 12 /* address of initialization func. */ -#define DT_FINI 13 /* address of termination function */ -#define DT_SONAME 14 /* string table offset of shared obj */ -#define DT_RPATH 15 /* string table offset of library -search path */ -#define DT_SYMBOLIC 16 /* start sym search in shared obj. */ -#define DT_REL 17 /* address of rel. tbl. w addends */ -#define DT_RELSZ 18 /* size of DT_REL relocation table */ -#define DT_RELENT 19 /* size of DT_REL relocation entry */ -#define DT_PLTREL 20 /* PLT referenced relocation entry */ -#define DT_DEBUG 21 /* bugger */ -#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */ -#define DT_JMPREL 23 /* add. of PLT's relocation entries */ -#define DT_BIND_NOW 24 /* Process relocations of object */ -#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -#define DT_RUNPATH 29 /* Library search path */ -#define DT_FLAGS 30 /* Flags for the object being loaded */ -#define DT_ENCODING 32 /* Start of encoded range */ -#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -#define DT_NUM 34 /* Number used. */ -#define DT_LOOS 0x60000000 /* reserved range for OS */ -#define DT_HIOS 0x6fffffff /* specific dynamic array tags */ -#define DT_LOPROC 0x70000000 /* reserved range for processor */ -#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */ - -/* Dynamic Tag Flags - d_un.d_val */ -#define DF_ORIGIN 0x01 /* Object may use DF_ORIGIN */ -#define DF_SYMBOLIC 0x02 /* Symbol resolutions starts here */ -#define DF_TEXTREL 0x04 /* Object contains text relocations */ -#define DF_BIND_NOW 0x08 /* No lazy binding for this object */ -#define DF_STATIC_TLS 0x10 /* Static thread local storage */ - -/* Standard ELF hashing function */ -unsigned long elf_hash(const unsigned char *name); - -#define ELF_TARG_VER 1 /* The ver for which this code is intended */ - -/* - * XXX - PowerPC defines really don't belong in here, - * but we'll put them in for simplicity. - */ - -/* Values for Elf32/64_Ehdr.e_flags. */ -#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ - -/* Cygnus local bits below */ -#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib -flag */ - -/* PowerPC relocations defined by the ABIs */ -#define R_PPC_NONE 0 -#define R_PPC_ADDR32 1 /* 32bit absolute address */ -#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -#define R_PPC_ADDR16 3 /* 16bit absolute address */ -#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -#define R_PPC_ADDR14_BRTAKEN 8 -#define R_PPC_ADDR14_BRNTAKEN 9 -#define R_PPC_REL24 10 /* PC relative 26 bit */ -#define R_PPC_REL14 11 /* PC relative 16 bit */ -#define R_PPC_REL14_BRTAKEN 12 -#define R_PPC_REL14_BRNTAKEN 13 -#define R_PPC_GOT16 14 -#define R_PPC_GOT16_LO 15 -#define R_PPC_GOT16_HI 16 -#define R_PPC_GOT16_HA 17 -#define R_PPC_PLTREL24 18 -#define R_PPC_COPY 19 -#define R_PPC_GLOB_DAT 20 -#define R_PPC_JMP_SLOT 21 -#define R_PPC_RELATIVE 22 -#define R_PPC_LOCAL24PC 23 -#define R_PPC_UADDR32 24 -#define R_PPC_UADDR16 25 -#define R_PPC_REL32 26 -#define R_PPC_PLT32 27 -#define R_PPC_PLTREL32 28 -#define R_PPC_PLT16_LO 29 -#define R_PPC_PLT16_HI 30 -#define R_PPC_PLT16_HA 31 -#define R_PPC_SDAREL16 32 -#define R_PPC_SECTOFF 33 -#define R_PPC_SECTOFF_LO 34 -#define R_PPC_SECTOFF_HI 35 -#define R_PPC_SECTOFF_HA 36 -/* Keep this the last entry. */ -#define R_PPC_NUM 37 - -/* The remaining relocs are from the Embedded ELF ABI, and are not - in the SVR4 ELF ABI. */ -#define R_PPC_EMB_NADDR32 101 -#define R_PPC_EMB_NADDR16 102 -#define R_PPC_EMB_NADDR16_LO 103 -#define R_PPC_EMB_NADDR16_HI 104 -#define R_PPC_EMB_NADDR16_HA 105 -#define R_PPC_EMB_SDAI16 106 -#define R_PPC_EMB_SDA2I16 107 -#define R_PPC_EMB_SDA2REL 108 -#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -#define R_PPC_EMB_MRKREF 110 -#define R_PPC_EMB_RELSEC16 111 -#define R_PPC_EMB_RELST_LO 112 -#define R_PPC_EMB_RELST_HI 113 -#define R_PPC_EMB_RELST_HA 114 -#define R_PPC_EMB_BIT_FLD 115 -#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ - -/* Diab tool relocations. */ -#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ - -/* This is a phony reloc to handle any old fashioned TOC16 references - that may still be in object files. */ -#define R_PPC_TOC16 255 - -#endif /* _ELF_H */ - diff --git a/source/homebrewboot/elfloader.c b/source/homebrewboot/elfloader.c deleted file mode 100644 index af9354ab..00000000 --- a/source/homebrewboot/elfloader.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2001 William L. Pitts - * Modifications (c) 2004 Felix Domke - * All rights reserved. - * - * Redistribution and use in source and binary forms are freely - * permitted provided that the above copyright notice and this - * paragraph and the following disclaimer are duplicated in all - * such forms. - * - * This software is provided "AS IS" and without any express or - * implied warranties, including, without limitation, the implied - * warranties of merchantability and fitness for a particular - * purpose. - */ - -#include -#include - -#include - -#include "elf_abi.h" - -/* ====================================================================== - * Determine if a valid ELF image exists at the given memory location. - * First looks at the ELF header magic field, the makes sure that it is - * executable and makes sure that it is for a PowerPC. - * ====================================================================== */ -s32 valid_elf_image (void *addr) { - Elf32_Ehdr *ehdr; /* Elf header structure pointer */ - - ehdr = (Elf32_Ehdr *) addr; - - if (!IS_ELF (*ehdr)) - return 0; - - if (ehdr->e_type != ET_EXEC) - return -1; - - if (ehdr->e_machine != EM_PPC) - return -1; - - return 1; -} - - -/* ====================================================================== - * A very simple elf loader, assumes the image is valid, returns the - * entry point address. - * ====================================================================== */ -u32 load_elf_image (void *addr) { - Elf32_Ehdr *ehdr; - Elf32_Shdr *shdr; - u8 *strtab = 0; - u8 *image; - int i; - - ehdr = (Elf32_Ehdr *) addr; - /* Find the section header string table for output info */ - shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + - (ehdr->e_shstrndx * sizeof (Elf32_Shdr))); - - if (shdr->sh_type == SHT_STRTAB) - strtab = (u8 *) (addr + shdr->sh_offset); - - /* Load each appropriate section */ - for (i = 0; i < ehdr->e_shnum; ++i) { - shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + - (i * sizeof (Elf32_Shdr))); - - if (!(shdr->sh_flags & SHF_ALLOC) - || shdr->sh_addr == 0 || shdr->sh_size == 0) { - continue; - } - - shdr->sh_addr &= 0x3FFFFFFF; - shdr->sh_addr |= 0x80000000; - - if (strtab) { - /*printf ("%sing section %s @ 0x%08x (0x%08x bytes)\n", - (shdr->sh_type == SHT_NOBITS) ? - "clear" : "load", - &strtab[shdr->sh_name], - (u32) shdr->sh_addr, - (u32) shdr->sh_size);*/ - } - - if (shdr->sh_type == SHT_NOBITS) { - memset ((void *) shdr->sh_addr, 0, shdr->sh_size); - } else { - image = (u8 *) addr + shdr->sh_offset; - memcpy ((void *) shdr->sh_addr, - (const void *) image, - shdr->sh_size); - } - DCFlushRangeNoSync ((void *) shdr->sh_addr, shdr->sh_size); - } - - return (ehdr->e_entry & 0x3FFFFFFF) | 0x80000000; -} - diff --git a/source/homebrewboot/elfloader.h b/source/homebrewboot/elfloader.h deleted file mode 100644 index 725aed7e..00000000 --- a/source/homebrewboot/elfloader.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _ELFLOADER_H_ -#define _ELFLOADER_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - - s32 valid_elf_image (void *addr); - u32 load_elf_image (void *addr); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/libfat/bit_ops.h b/source/libfat/bit_ops.h index 8a5fb3e1..762be0b3 100644 --- a/source/libfat/bit_ops.h +++ b/source/libfat/bit_ops.h @@ -3,7 +3,7 @@ Functions for dealing with conversion of data between types Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -26,8 +26,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BIT_OPS_H -#define __BIT_OPS_H +#ifndef _BIT_OPS_H +#define _BIT_OPS_H #include diff --git a/source/libfat/cache.h b/source/libfat/cache.h new file mode 100644 index 00000000..c6e48d07 --- /dev/null +++ b/source/libfat/cache.h @@ -0,0 +1,130 @@ +/* + cache.h + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _CACHE_H +#define _CACHE_H + +#include "common.h" +#include "disc.h" + +#define PAGE_SECTORS 64 +#define CACHE_PAGE_SIZE (BYTES_PER_READ * PAGE_SECTORS) + +typedef struct { + sec_t sector; + unsigned int count; + unsigned int last_access; + bool dirty; + uint8_t* cache; +} CACHE_ENTRY; + +typedef struct { + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + CACHE_ENTRY* cacheEntries; +} CACHE; + +/* +Read data from a sector in the cache +If the sector is not in the cache, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size); + +bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the cache +If the sector is not in the cache, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the cache, zeroing the sector first +If the sector is not in the cache, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +/* +Read several sectors from the cache +*/ +bool _FAT_cache_readSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the cache +*/ +static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, sec_t sector) { + return _FAT_cache_readPartialSector (cache, buffer, sector, 0, BYTES_PER_READ); +} + +/* +Write a full sector to the cache +*/ +static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec_t sector) { + return _FAT_cache_writePartialSector (cache, buffer, sector, 0, BYTES_PER_READ); +} + +bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the cache +*/ +bool _FAT_cache_flush (CACHE* cache); + +/* +Clear out the contents of the cache without writing any dirty sectors first +*/ +void _FAT_cache_invalidate (CACHE* cache); + +CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition); + +void _FAT_cache_destructor (CACHE* cache); + +#endif // _CACHE_H + diff --git a/source/libfat/common.h b/source/libfat/common.h index 85d15db9..98845e05 100644 --- a/source/libfat/common.h +++ b/source/libfat/common.h @@ -3,7 +3,7 @@ Common definitions and included files for the FATlib Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -26,23 +26,54 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __COMMON_H -#define __COMMON_H +#ifndef _COMMON_H +#define _COMMON_H #define BYTES_PER_READ 512 -#include "fat.h" #include #include +#include "libfat/fat.h" +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif // Platform specific includes -#include -#include -#include +#if defined(__gamecube__) || defined (__wii__) + #include + #include + #include +#elif defined(NDS) + #include + #include + #include +#elif defined(GBA) + #include + #include +#endif + // Platform specific options -#define DEFAULT_CACHE_PAGES 4 -#define DEFAULT_SECTORS_PAGE 64 -#define USE_LWP_LOCK -#define USE_RTC_TIME +#if defined (__wii__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (__gamecube__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (NDS) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 8 + #define USE_RTC_TIME +#elif defined (GBA) + #define DEFAULT_CACHE_PAGES 2 + #define DEFAULT_SECTORS_PAGE 8 + #define LIMIT_SECTORS 128 +#endif #endif // _COMMON_H diff --git a/source/libfat/directory.c b/source/libfat/directory.c index 279a361d..0e7b5ede 100644 --- a/source/libfat/directory.c +++ b/source/libfat/directory.c @@ -329,7 +329,8 @@ bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) { } lfn[lfnPos] = '\0'; // Set end of lfn to null character lfnChkSum = entryData[LFN_offset_checkSum]; - } if (lfnChkSum != entryData[LFN_offset_checkSum]) { + } + if (lfnChkSum != entryData[LFN_offset_checkSum]) { lfnExists = false; } if (lfnExists) { @@ -417,6 +418,49 @@ bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) { return true; } +bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label) { + DIR_ENTRY entry; + DIR_ENTRY_POSITION entryEnd; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + int i; + bool end; + + _FAT_directory_getRootEntry(partition, &entry); + + entryEnd = entry.dataEnd; + + // Make sure we are using the correct root directory, in case of FAT32 + if (entryEnd.cluster == FAT16_ROOT_DIR_CLUSTER) { + entryEnd.cluster = partition->rootDirCluster; + } + + label[0]='\0'; + label[11]='\0'; + end = false; + //this entry should be among the first 3 entries in the root directory table, if not, then system can have trouble displaying the right volume label + while(!end) { + if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { + end = true; + } + + if(!_FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, + entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE)) + { //error reading + return false; + } + if (entryData[DIR_ENTRY_attributes] == ATTRIB_VOL && entryData[0] != DIR_ENTRY_FREE) { + for (i = 0; i < 11; i++) { + label[i] = entryData[DIR_ENTRY_name + i]; + } + return true; + } else if (entryData[0] == DIR_ENTRY_LAST) { + end = true; + } + } + return false; +} + bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) { DIR_ENTRY_POSITION entryStart = entry->dataStart; DIR_ENTRY_POSITION entryEnd = entry->dataEnd; diff --git a/source/libfat/directory.h b/source/libfat/directory.h index 02bd82ff..93429217 100644 --- a/source/libfat/directory.h +++ b/source/libfat/directory.h @@ -4,7 +4,7 @@ a FAT partition Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -27,8 +27,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __DIRECTORY_H -#define __DIRECTORY_H +#ifndef _DIRECTORY_H +#define _DIRECTORY_H #include @@ -132,7 +132,7 @@ Returns true on success, false on failure */ bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd); -/* +/* Changes the current directory to the one specified by path Returns true on success, false on failure */ @@ -156,10 +156,10 @@ bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t d /* Get the start cluster of a file from it's entry data */ -uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData); +uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData); -/* -Fill in the file name and entry data of DIR_ENTRY* entry. +/* +Fill in the file name and entry data of DIR_ENTRY* entry. Assumes that the entry's dataStart and dataEnd are correct Returns true on success, false on failure */ @@ -170,4 +170,9 @@ Fill in a stat struct based on a file entry */ void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st); +/* +Get volume label +*/ +bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label); + #endif // _DIRECTORY_H diff --git a/source/libfat/disc.h b/source/libfat/disc.h new file mode 100644 index 00000000..5c955f90 --- /dev/null +++ b/source/libfat/disc.h @@ -0,0 +1,110 @@ +/* + disc.h + Interface to the low level disc functions. Used by the higher level + file system code. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef _DISC_H +#define _DISC_H + +#include "common.h" + +/* +A list of all default devices to try at startup, +terminated by a {NULL,NULL} entry. +*/ +typedef struct { + const char* name; + const DISC_INTERFACE* (*getInterface)(void); +} INTERFACE_ID; +extern const INTERFACE_ID _FAT_disc_interfaces[]; + +/* +Check if a disc is inserted +Return true if a disc is inserted and ready, false otherwise +*/ +static inline bool _FAT_disc_isInserted (const DISC_INTERFACE* disc) { + return disc->isInserted(); +} + +/* +Read numSectors sectors from a disc, starting at sector. +numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, +else it is at least 1 +sector is 0 or greater +buffer is a pointer to the memory to fill +*/ +static inline bool _FAT_disc_readSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer) { + return disc->readSectors (sector, numSectors, buffer); +} + +/* +Write numSectors sectors to a disc, starting at sector. +numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, +else it is at least 1 +sector is 0 or greater +buffer is a pointer to the memory to read from +*/ +static inline bool _FAT_disc_writeSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, const void* buffer) { + return disc->writeSectors (sector, numSectors, buffer); +} + +/* +Reset the card back to a ready state +*/ +static inline bool _FAT_disc_clearStatus (const DISC_INTERFACE* disc) { + return disc->clearStatus(); +} + +/* +Initialise the disc to a state ready for data reading or writing +*/ +static inline bool _FAT_disc_startup (const DISC_INTERFACE* disc) { + return disc->startup(); +} + +/* +Put the disc in a state ready for power down. +Complete any pending writes and disable the disc if necessary +*/ +static inline bool _FAT_disc_shutdown (const DISC_INTERFACE* disc) { + return disc->shutdown(); +} + +/* +Return a 32 bit value unique to each type of interface +*/ +static inline uint32_t _FAT_disc_hostType (const DISC_INTERFACE* disc) { + return disc->ioType; +} + +/* +Return a 32 bit value that specifies the capabilities of the disc +*/ +static inline uint32_t _FAT_disc_features (const DISC_INTERFACE* disc) { + return disc->features; +} + +#endif // _DISC_H diff --git a/source/libfat/fat.h b/source/libfat/fat.h index d7b11176..b0ffc13a 100644 --- a/source/libfat/fat.h +++ b/source/libfat/fat.h @@ -1,8 +1,10 @@ /* fat.h Simple functionality for startup, mounting and unmounting of FAT-based devices. - - Copyright (c) 2006 Michael "Chishm" Chisholm + + Copyright (c) 2006 - 2009 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -34,8 +36,24 @@ extern "C" { #endif +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + #include -#include + +#if defined(__gamecube__) || defined (__wii__) +# include +#else +# ifdef NDS +# include "nds/disc_io.h" +# else +# include "disc_io.h" +# endif +#endif /* Initialise any inserted block-devices. @@ -53,7 +71,7 @@ extern bool fatInitDefault (void); /* Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". You can then access the filesystem using "name:/". -This will mount the active partition or the first valid partition on the disc, +This will mount the active partition or the first valid partition on the disc, and will use a cache size optimized for the host system. */ extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); @@ -67,12 +85,18 @@ cacheSize specifies the number of pages to allocate for the cache. This will not startup the disc, so you need to call interface->startup(); first. */ extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); + /* Unmount the partition specified by name. If there are open files, it will attempt to synchronise them to disc. */ extern void fatUnmount (const char* name); +/* +Get Volume Label +*/ +extern void fatGetVolumeLabel (const char* name, char *label); + #ifdef __cplusplus } #endif diff --git a/source/libfat/fat_cache.c b/source/libfat/fat_cache.c index 422915b8..3fe34537 100644 --- a/source/libfat/fat_cache.c +++ b/source/libfat/fat_cache.c @@ -37,8 +37,8 @@ #include #include "common.h" -#include "fat_cache.h" -#include "disc_fat.h" +#include "cache.h" +#include "disc.h" #include "mem_allocate.h" #include "bit_ops.h" @@ -127,7 +127,7 @@ static CACHE_ENTRY* _FAT_cache_getPage(CACHE *cache,sec_t sector) cacheEntries[i].last_access = accessTime(); return &(cacheEntries[i]); } - + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accesssector > sector) { - + secs_to_write = entry->sector - sector; - + _FAT_disc_writeSectors(cache->disc,sector,secs_to_write,src); src += (secs_to_write*BYTES_PER_READ); sector += secs_to_write; numSectors -= secs_to_write; } - + sec = sector - entry->sector; secs_to_write = entry->count - sec; @@ -327,7 +327,7 @@ bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, cons numSectors -= secs_to_write; entry->dirty = true; - + } else { _FAT_disc_writeSectors(cache->disc,sector,numSectors,src); numSectors=0; diff --git a/source/libfat/disc_fat.c b/source/libfat/fat_disc.c similarity index 73% rename from source/libfat/disc_fat.c rename to source/libfat/fat_disc.c index c17ca4c5..1fac0ed9 100644 --- a/source/libfat/disc_fat.c +++ b/source/libfat/fat_disc.c @@ -2,9 +2,9 @@ disc.c Interface to the low level disc functions. Used by the higher level file system code. - + Copyright (c) 2008 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -27,7 +27,7 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "disc_fat.h" +#include "disc.h" /* The list of interfaces consists of a series of name/interface pairs. @@ -39,15 +39,16 @@ The list is terminated by a NULL/NULL entry. */ /* ====================== Wii ====================== */ +#if defined (__wii__) #include -#include "usbloader/usbstorage2.h" +#include #include static const DISC_INTERFACE* get_io_wiisd (void) { return &__io_wiisd; } static const DISC_INTERFACE* get_io_usbstorage (void) { - return &__io_usbstorage2; + return &__io_usbstorage; } static const DISC_INTERFACE* get_io_gcsda (void) { @@ -63,5 +64,42 @@ const INTERFACE_ID _FAT_disc_interfaces[] = { {"carda", get_io_gcsda}, {"cardb", get_io_gcsdb}, {NULL, NULL} -}; +}; + +/* ==================== Gamecube ==================== */ +#elif defined (__gamecube__) +#include + +static const DISC_INTERFACE* get_io_gcsda (void) { + return &__io_gcsda; +} +static const DISC_INTERFACE* get_io_gcsdb (void) { + return &__io_gcsdb; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"carda", get_io_gcsda}, + {"cardb", get_io_gcsdb}, + {NULL, NULL} +}; + +/* ====================== NDS ====================== */ +#elif defined (NDS) +#include + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"fat", dldiGetInternal}, + {NULL, NULL} +}; + +/* ====================== GBA ====================== */ +#elif defined (GBA) +#include + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"fat", discGetInterface}, + {NULL, NULL} +}; + +#endif diff --git a/source/libfat/fatdir.c b/source/libfat/fatdir.c index 5a555e38..55faa0b9 100644 --- a/source/libfat/fatdir.c +++ b/source/libfat/fatdir.c @@ -36,7 +36,7 @@ #include "fatdir.h" -#include "fat_cache.h" +#include "cache.h" #include "file_allocation_table.h" #include "partition.h" #include "directory.h" diff --git a/source/libfat/fatdir.h b/source/libfat/fatdir.h index 1b47a914..426dd30b 100644 --- a/source/libfat/fatdir.h +++ b/source/libfat/fatdir.h @@ -1,11 +1,11 @@ /* fatdir.h - - Functions used by the newlib disc stubs to interface with + + Functions used by the newlib disc stubs to interface with this library Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -29,8 +29,8 @@ */ -#ifndef __FATDIR_H -#define __FATDIR_H +#ifndef _FATDIR_H +#define _FATDIR_H #include #include diff --git a/source/libfat/fatfile.c b/source/libfat/fatfile.c index ba8dddb5..0892f960 100644 --- a/source/libfat/fatfile.c +++ b/source/libfat/fatfile.c @@ -39,7 +39,7 @@ #include #include -#include "fat_cache.h" +#include "cache.h" #include "file_allocation_table.h" #include "bit_ops.h" #include "filetime.h" diff --git a/source/libfat/file_allocation_table.h b/source/libfat/file_allocation_table.h index de500496..560c616d 100644 --- a/source/libfat/file_allocation_table.h +++ b/source/libfat/file_allocation_table.h @@ -4,7 +4,7 @@ a FAT partition Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -27,8 +27,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __FAT_H -#define __FAT_H +#ifndef _FAT_H +#define _FAT_H #include "common.h" #include "partition.h" @@ -36,7 +36,7 @@ #define CLUSTER_EOF_16 0xFFFF #define CLUSTER_EOF 0x0FFFFFFF #define CLUSTER_FREE 0x00000000 -#define CLUSTER_ROOT 0x00000000 +#define CLUSTER_ROOT 0x00000000 #define CLUSTER_FIRST 0x00000002 #define CLUSTER_ERROR 0xFFFFFFFF @@ -58,8 +58,8 @@ uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster); unsigned int _FAT_fat_freeClusterCount (PARTITION* partition); static inline sec_t _FAT_fat_clusterToSector (PARTITION* partition, uint32_t cluster) { - return (cluster >= CLUSTER_FIRST) ? - ((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart : + return (cluster >= CLUSTER_FIRST) ? + ((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart : partition->rootDirStart; } diff --git a/source/libfat/filetime.h b/source/libfat/filetime.h index 8ffd539a..3bfd8ed8 100644 --- a/source/libfat/filetime.h +++ b/source/libfat/filetime.h @@ -3,7 +3,7 @@ Conversion of file time and date values to various other types Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -26,8 +26,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __FILETIME_H -#define __FILETIME_H +#ifndef _FILETIME_H +#define _FILETIME_H #include "common.h" #include diff --git a/source/libfat/libfat.c b/source/libfat/libfat.c index 0b3cbd06..71af3689 100644 --- a/source/libfat/libfat.c +++ b/source/libfat/libfat.c @@ -36,7 +36,7 @@ #include "fatdir.h" #include "lock.h" #include "mem_allocate.h" -#include "disc_fat.h" +#include "disc.h" static const devoptab_t dotab_fat = { "fat", @@ -69,17 +69,17 @@ bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSec devoptab_t* devops; char* nameCopy; + if(!name || !interface) + return false; + if(!interface->startup()) return false; - if(!interface->isInserted()) { - interface->shutdown(); + if(!interface->isInserted()) return false; - } devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1); if (!devops) { - interface->shutdown(); return false; } // Use the space allocated at the end of the devoptab struct for storing the name @@ -89,7 +89,6 @@ bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSec partition = _FAT_partition_constructor (interface, cacheSize, SectorsPerPage, startSector); if (!partition) { _FAT_mem_free (devops); - interface->shutdown(); return false; } @@ -111,7 +110,9 @@ bool fatMountSimple (const char* name, const DISC_INTERFACE* interface) { void fatUnmount (const char* name) { devoptab_t *devops; PARTITION* partition; - const DISC_INTERFACE *disc; + + if(!name) + return; devops = (devoptab_t*)GetDeviceOpTab (name); if (!devops) { @@ -128,10 +129,8 @@ void fatUnmount (const char* name) { } partition = (PARTITION*)devops->deviceData; - disc = partition->disc; _FAT_partition_destructor (partition); _FAT_mem_free (devops); - disc->shutdown(); } bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) { @@ -194,4 +193,49 @@ bool fatInitDefault (void) { return fatInit (DEFAULT_CACHE_PAGES, true); } +void fatGetVolumeLabel (const char* name, char *label) { + devoptab_t *devops; + PARTITION* partition; + char *buf; + int namelen,i; + if(!name || !label) + return; + + namelen = strlen(name); + buf=(char*)_FAT_mem_allocate(sizeof(char)*namelen+2); + strcpy(buf,name); + + if (name[namelen-1] == '/') { + buf[namelen-1]='\0'; + namelen--; + } + + if (name[namelen-1] != ':') { + buf[namelen]=':'; + buf[namelen+1]='\0'; + } + + devops = (devoptab_t*)GetDeviceOpTab(buf); + + for(i=0;buf[i]!='\0' && buf[i]!=':';i++); + if (!devops || strncasecmp(buf,devops->name,i)) { + free(buf); + return; + } + + free(buf); + + // Perform a quick check to make sure we're dealing with a libfat controlled device + if (devops->open_r != dotab_fat.open_r) { + return; + } + + partition = (PARTITION*)devops->deviceData; + + if(!_FAT_directory_getVolumeLabel(partition, label)) { + strncpy(label,partition->label,11); + label[11]='\0'; + } + if(!strncmp(label, "NO NAME", 7)) label[0]='\0'; +} diff --git a/source/libfat/libfatversion.h b/source/libfat/libfatversion.h new file mode 100644 index 00000000..40c50463 --- /dev/null +++ b/source/libfat/libfatversion.h @@ -0,0 +1,10 @@ +#ifndef __LIBFATVERSION_H__ +#define __LIBFATVERSION_H__ + +#define _LIBFAT_MAJOR_ 1 +#define _LIBFAT_MINOR_ 0 +#define _LIBFAT_PATCH_ 7 + +#define _LIBFAT_STRING "libFAT Release 1.0.7" + +#endif // __LIBFATVERSION_H__ diff --git a/source/libfat/lock.h b/source/libfat/lock.h index 73b8902b..a93194c2 100644 --- a/source/libfat/lock.h +++ b/source/libfat/lock.h @@ -26,8 +26,8 @@ */ -#ifndef __LOCK_H -#define __LOCK_H +#ifndef _LOCK_H +#define _LOCK_H #include "common.h" diff --git a/source/libfat/mem_allocate.h b/source/libfat/mem_allocate.h index 2d38f9ce..451cf3b1 100644 --- a/source/libfat/mem_allocate.h +++ b/source/libfat/mem_allocate.h @@ -5,7 +5,7 @@ malloc is unavailable Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -28,8 +28,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __MEM_ALLOCATE_H_ -#define __MEM_ALLOCATE_H_ +#ifndef _MEM_ALLOCATE_H +#define _MEM_ALLOCATE_H #include @@ -38,7 +38,6 @@ static inline void* _FAT_mem_allocate (size_t size) { } static inline void* _FAT_mem_align (size_t size) { - return memalign (32, size); } diff --git a/source/libfat/partition.c b/source/libfat/partition.c index 955468de..dc1a29c9 100644 --- a/source/libfat/partition.c +++ b/source/libfat/partition.c @@ -205,11 +205,17 @@ PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cach return NULL; } - _FAT_startSector = startSector; - // Init the partition lock _FAT_lock_init(&partition->lock); + _FAT_startSector = startSector; + + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT16_volumeLabel), 11); + else + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT32_volumeLabel), 11); + partition->label[11] = '\0'; + // Set partition's disc interface partition->disc = disc; diff --git a/source/libfat/partition.h b/source/libfat/partition.h index 5c9595e0..52e6648d 100644 --- a/source/libfat/partition.h +++ b/source/libfat/partition.h @@ -4,7 +4,7 @@ on various block devices. Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -27,11 +27,11 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __PARTITION_H -#define __PARTITION_H +#ifndef _PARTITION_H +#define _PARTITION_H #include "common.h" -#include "fat_cache.h" +#include "cache.h" #include "lock.h" // Device name @@ -67,6 +67,7 @@ typedef struct { struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files mutex_t lock; // A lock for partition operations bool readOnly; // If this is set, then do not try writing to the disc + char label[12]; // Volume label } PARTITION; /* @@ -75,7 +76,7 @@ Mount the supplied device and return a pointer to the struct necessary to use it PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage, sec_t startSector); /* -Dismount the device and free all structures used. +Dismount the device and free all structures used. Will also attempt to synchronise all open files to disc. */ void _FAT_partition_destructor (PARTITION* partition); diff --git a/source/libntfs/acls.c b/source/libntfs/acls.c index 4bfd3652..76cc6ce5 100644 --- a/source/libntfs/acls.c +++ b/source/libntfs/acls.c @@ -563,7 +563,8 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end) &((const char*)pacl)[offace]; acesz = le16_to_cpu(pace->size); if (((offace + acesz) > end) - || !ntfs_valid_sid(&pace->sid)) + || !ntfs_valid_sid(&pace->sid) + || ((ntfs_sid_size(&pace->sid) + 8) != (int)acesz)) ok = FALSE; offace += acesz; } @@ -614,7 +615,6 @@ BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) * old revision and no DACL though SE_DACL_PRESENT is set */ if ((attrsz >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) - && (ntfs_attr_size(securattr) <= attrsz) && (phead->revision == SECURITY_DESCRIPTOR_REVISION) && (offowner >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) && ((offowner + 2) < attrsz) @@ -622,14 +622,15 @@ BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) && ((offgroup + 2) < attrsz) && (!offdacl || ((offdacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) - && (offdacl < attrsz))) + && (offdacl+sizeof(ACL) < attrsz))) && (!offsacl || ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) - && (offsacl < attrsz))) + && (offsacl+sizeof(ACL) < attrsz))) && !(phead->owner & const_cpu_to_le32(3)) && !(phead->group & const_cpu_to_le32(3)) && !(phead->dacl & const_cpu_to_le32(3)) && !(phead->sacl & const_cpu_to_le32(3)) + && (ntfs_attr_size(securattr) <= attrsz) && ntfs_valid_sid((const SID*)&securattr[offowner]) && ntfs_valid_sid((const SID*)&securattr[offgroup]) /* @@ -707,10 +708,12 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, if (ntfs_same_sid(&pnewace->sid, ownersid)) { memcpy(&pnewace->sid, usid, usidsz); acesz = usidsz + 8; + pnewace->size = cpu_to_le16(acesz); } if (ntfs_same_sid(&pnewace->sid, groupsid)) { memcpy(&pnewace->sid, gsid, gsidsz); acesz = gsidsz + 8; + pnewace->size = cpu_to_le16(acesz); } if (pnewace->mask & GENERIC_ALL) { pnewace->mask &= ~GENERIC_ALL; @@ -2985,8 +2988,10 @@ static int build_std_permissions(const char *securattr, if (offdacl) { acecnt = le16_to_cpu(pacl->ace_count); offace = offdacl + sizeof(ACL); - } else + } else { acecnt = 0; + offace = 0; + } for (nace = 0; nace < acecnt; nace++) { pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; if (!(pace->flags & INHERIT_ONLY_ACE)) { @@ -3255,8 +3260,10 @@ static int build_ownadmin_permissions(const char *securattr, if (offdacl) { acecnt = le16_to_cpu(pacl->ace_count); offace = offdacl + sizeof(ACL); - } else + } else { acecnt = 0; + offace = 0; + } firstapply = TRUE; isforeign = 3; for (nace = 0; nace < acecnt; nace++) { diff --git a/source/libntfs/acls.h b/source/libntfs/acls.h index 13e5dbd1..8a83d32d 100644 --- a/source/libntfs/acls.h +++ b/source/libntfs/acls.h @@ -29,8 +29,6 @@ * should be moved to some config file */ -#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */ -#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */ #define BUFSZ 1024 /* buffer size to read mapping file */ #define MAPPINGFILE ".NTFS-3G/UserMapping" /* default mapping file */ #define LINESZ 120 /* maximum useful size of a mapping line */ diff --git a/source/libntfs/attrib.c b/source/libntfs/attrib.c index 41e4dcf0..123c9a91 100644 --- a/source/libntfs/attrib.c +++ b/source/libntfs/attrib.c @@ -44,6 +44,7 @@ #include #endif +#include "param.h" #include "compat.h" #include "attrib.h" #include "attrlist.h" @@ -64,8 +65,6 @@ #include "misc.h" #include "efs.h" -#define STANDARD_COMPRESSION_UNIT 4 - ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') }; ntfschar STREAM_SDS[] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), @@ -463,9 +462,15 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, * inode (for named data streams). The compression mark * may change any time, the compression state can only * change when stream is wiped out. + * + * Also prevent compression on NTFS version < 3.0 + * or cluster size > 4K or compression is disabled */ a->flags &= ~ATTR_COMPRESSION_MASK; - if (na->ni->flags & FILE_ATTR_COMPRESSED) + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)) a->flags |= ATTR_IS_COMPRESSED; } @@ -606,6 +611,11 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", (unsigned long long)na->ni->mft_no, na->type); + /* avoid multiple full runlist mappings */ + if (NAttrFullyMapped(na)) { + ret = 0; + goto out; + } ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) goto out; @@ -679,8 +689,10 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) (long long)highest_vcn, (long long)last_vcn); goto err_out; } - if (errno == ENOENT) + if (errno == ENOENT) { + NAttrSetFullyMapped(na); ret = 0; + } err_out: ntfs_attr_put_search_ctx(ctx); out: @@ -1187,24 +1199,49 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, if ((na->data_flags & ATTR_COMPRESSION_MASK) && (need < na->compression_block_clusters)) { /* - * for a compressed file, be sure to allocate the full hole. - * We may need space to decompress existing compressed data. + * for a compressed file, be sure to allocate the full + * compression block, as we may need space to decompress + * existing compressed data. + * So allocate the space common to compression block + * and existing hole. */ - rlc = ntfs_cluster_alloc(vol, (*rl)->vcn, (*rl)->length, + VCN alloc_vcn; + + if ((from_vcn & -na->compression_block_clusters) <= (*rl)->vcn) + alloc_vcn = (*rl)->vcn; + else + alloc_vcn = from_vcn & -na->compression_block_clusters; + need = (alloc_vcn | (na->compression_block_clusters - 1)) + + 1 - alloc_vcn; + if (need > (*rl)->length) { + ntfs_log_error("Cannot allocate %lld clusters" + " within a hole of %lld\n", + (long long)need, + (long long)(*rl)->length); + errno = EIO; + goto err_out; + } + rlc = ntfs_cluster_alloc(vol, alloc_vcn, need, lcn_seek_from, DATA_ZONE); } else rlc = ntfs_cluster_alloc(vol, from_vcn, need, lcn_seek_from, DATA_ZONE); if (!rlc) goto err_out; + if (na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) + na->compressed_size += need << vol->cluster_size_bits; *rl = ntfs_runlists_merge(na->rl, rlc); /* - * For a compressed attribute, we must be sure there is an - * available entry, so reserve it before it gets too late. + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. */ - if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) - *rl = ntfs_rl_extend(*rl,1); + if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) { + runlist_element *oldrl = na->rl; + na->rl = *rl; + *rl = ntfs_rl_extend(na,*rl,2); + if (!*rl) na->rl = oldrl; /* restore to original if failed */ + } if (!*rl) { eo = errno; ntfs_log_perror("Failed to merge runlists"); @@ -1215,8 +1252,9 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, errno = eo; goto err_out; } + na->unused_runs = 2; na->rl = *rl; - if (*update_from == -1) + if ((*update_from == -1) || (from_vcn < *update_from)) *update_from = from_vcn; *rl = ntfs_attr_find_vcn(na, cur_vcn); if (!*rl) { @@ -1266,6 +1304,314 @@ err_out: static int stuff_hole(ntfs_attr *na, const s64 pos); +/* + * Split an existing hole for overwriting with data + * The hole may have to be split into two or three parts, so + * that the overwritten part fits within a single compression block + * + * No cluster allocation is needed, this will be done later in + * standard hole filling, hence no need to reserve runs for + * future needs. + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * (or the full block, if it was a full hole) + * -1 if there were an error + */ + +static int split_compressed_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from) +{ + int compressed_part; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + + compressed_part + = na->compression_block_clusters; + /* reserve entries in runlist if we have to split */ + if (rl->length > na->compression_block_clusters) { + *prl = ntfs_rl_extend(na,*prl,2); + if (!*prl) { + compressed_part = -1; + } else { + rl = *prl; + na->unused_runs = 2; + } + } + if (*prl && (rl->length > na->compression_block_clusters)) { + /* + * Locate the update part relative to beginning of + * current run + */ + int beginwrite = (pos >> cluster_size_bits) - rl->vcn; + s32 endblock = (((pos + count - 1) >> cluster_size_bits) + | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + + compressed_part = na->compression_block_clusters + - (rl->length & (na->compression_block_clusters - 1)); + if ((beginwrite + compressed_part) >= na->compression_block_clusters) + compressed_part = na->compression_block_clusters; + /* + * if the run ends beyond end of needed block + * we have to split the run + */ + if (endblock < rl[0].length) { + runlist_element *xrl; + int n; + + /* + * we have to split into three parts if the run + * does not end within the first compression block. + * This means the hole begins before the + * compression block. + */ + if (endblock > na->compression_block_clusters) { + if (na->unused_runs < 2) { +ntfs_log_error("No free run, case 1\n"); + } + na->unused_runs -= 2; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[2] = *xrl; + xrl--; + } while (xrl != rl); + rl[1].length = na->compression_block_clusters; + rl[2].length = rl[0].length - endblock; + rl[0].length = endblock + - na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[2].lcn = LCN_HOLE; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[2].vcn = rl[1].vcn + + na->compression_block_clusters; + rl = ++(*prl); + } else { + /* + * split into two parts and use the + * first one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 2\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + if (beginwrite < endblock) { + /* we will write into the first part of hole */ + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + } else { + /* we will write into the second part of hole */ +// impossible ? + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + rl = ++(*prl); + } + } + } else { + if (rl[1].length) { + runlist_element *xrl; + int n; + + /* + * split into two parts and use the + * last one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 4\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + } else { + rl[2].lcn = rl[1].lcn; + rl[2].vcn = rl[1].vcn; + rl[2].length = rl[1].length; + } + rl[1].vcn -= na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[1].length = na->compression_block_clusters; + rl[0].length -= na->compression_block_clusters; + if (pos >= (rl[1].vcn << cluster_size_bits)) { + rl = ++(*prl); + } + } + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + } + return (compressed_part); +} + +/* + * Borrow space from adjacent hole for appending data + * The hole may have to be split so that the end of hole is not + * affected by cluster allocation and overwriting + * Cluster allocation is needed for the overwritten compression block + * + * Must always leave two unused entries in the runlist + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * -1 if there were an error + */ + +static int borrow_from_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from, BOOL wasnonresident) +{ + int compressed_part = 0; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + s32 endblock; + long long allocated; + runlist_element *zrl; + int irl; + BOOL undecided; + BOOL nothole; + + /* check whether the compression block is fully allocated */ + endblock = (((pos + count - 1) >> cluster_size_bits) | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + + undecided = (allocated < endblock) && (zrl->lcn == LCN_RL_NOT_MAPPED); + nothole = (allocated >= endblock) || (zrl->lcn != LCN_HOLE); + + if (undecided || nothole) { + runlist_element *orl = na->rl; + s64 olcn = (*prl)->lcn; + /* + * Map the full runlist (needed to compute the + * compressed size), unless the runlist has not + * yet been created (data just made non-resident) + */ + irl = *prl - na->rl; + if (!NAttrBeingNonResident(na) + && ntfs_attr_map_whole_runlist(na)) { + rl = (runlist_element*)NULL; + } else { + /* + * Mapping the runlist may cause its relocation, + * and relocation may be at the same place with + * relocated contents. + * Have to find the current run again when this + * happens. + */ + if ((na->rl != orl) || ((*prl)->lcn != olcn)) { + zrl = &na->rl[irl]; + while (zrl->length && (zrl->lcn != olcn)) + zrl++; + *prl = zrl; + } + if (!(*prl)->length) { + ntfs_log_error("Mapped run not found," + " inode %lld lcn 0x%llx\n", + (long long)na->ni->mft_no, + (long long)olcn); + rl = (runlist_element*)NULL; + } else { + rl = ntfs_rl_extend(na,*prl,2); + na->unused_runs = 2; + } + } + *prl = rl; + if (rl && undecided) { + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) + && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + } + } + /* + * compression block not fully allocated and followed + * by a hole : we must allocate in the hole. + */ + if (rl && (allocated < endblock) && (zrl->lcn == LCN_HOLE)) { + s64 xofs; + + /* + * split the hole if not fully needed + */ + if ((allocated + zrl->length) > endblock) { + runlist_element *xrl; + + *prl = ntfs_rl_extend(na,*prl,1); + if (*prl) { + /* beware : rl was reallocated */ + rl = *prl; + zrl = &rl[irl]; + na->unused_runs = 0; + xrl = zrl; + while (xrl->length) xrl++; + do { + xrl[1] = *xrl; + } while (xrl-- != zrl); + zrl->length = endblock - allocated; + zrl[1].length -= zrl->length; + zrl[1].vcn = zrl->vcn + zrl->length; + } + } + if (*prl) { + if (wasnonresident) + compressed_part = na->compression_block_clusters + - zrl->length; + xofs = 0; + if (ntfs_attr_fill_hole(na, + zrl->length << cluster_size_bits, + &xofs, &zrl, update_from)) + compressed_part = -1; + else { + /* go back to initial cluster, now reallocated */ + while (zrl->vcn > (pos >> cluster_size_bits)) + zrl--; + *prl = zrl; + } + } + } + if (!*prl) { + ntfs_log_error("No elements to borrow from a hole\n"); + compressed_part = -1; + } else + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + return (compressed_part); +} + /** * ntfs_attr_pwrite - positioned write to an ntfs attribute * @na: ntfs attribute to write to @@ -1301,9 +1647,9 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) unsigned int undo_initialized_size : 1; unsigned int undo_data_size : 1; } need_to = { 0, 0 }; - BOOL makingnonresident = FALSE; BOOL wasnonresident = FALSE; BOOL compressed; + BOOL updatemap; ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " "0x%llx.\n", (long long)na->ni->mft_no, na->type, @@ -1317,6 +1663,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) vol = na->ni->vol; compressed = (na->data_flags & ATTR_COMPRESSION_MASK) != const_cpu_to_le16(0); + na->unused_runs = 0; /* prepare overflow checks */ /* * Encrypted attributes are only supported in raw mode. We return * access denied, which is what Windows NT4 does, too. @@ -1338,23 +1685,14 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) goto errno_set; /* If this is a compressed attribute it needs special treatment. */ wasnonresident = NAttrNonResident(na) != 0; - makingnonresident = wasnonresident /* yes : already changed */ - && !pos && (count == na->initialized_size); /* - * Writing to compressed files is currently restricted - * to appending data. However we have to accept - * recursive write calls to make the attribute non resident. - * These are writing at position 0 up to initialized_size. - * Compression is also restricted to data streams. - * Only ATTR_IS_COMPRESSED compression mode is supported. + * Compression is restricted to data streams and + * only ATTR_IS_COMPRESSED compression mode is supported. */ if (compressed && ((na->type != AT_DATA) || ((na->data_flags & ATTR_COMPRESSION_MASK) - != ATTR_IS_COMPRESSED) - || ((pos != na->initialized_size) - && (pos || (count != na->initialized_size))))) { - // TODO: Implement writing compressed attributes! (AIA) + != ATTR_IS_COMPRESSED))) { errno = EOPNOTSUPP; goto errno_set; } @@ -1384,7 +1722,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * so truncate the requested count if needed (big buffers). */ if (compressed) { - fullcount = na->data_size - pos; + fullcount = (pos | (na->compression_block_size - 1)) + 1 - pos; if (count > fullcount) count = fullcount; } @@ -1428,6 +1766,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) } /* Handle writes beyond initialized_size. */ + if (pos + count > na->initialized_size) { if (ntfs_attr_map_whole_runlist(na)) goto err_out; @@ -1438,9 +1777,10 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * before it gets too late. */ if (compressed) { - na->rl = ntfs_rl_extend(na->rl,2); + na->rl = ntfs_rl_extend(na,na->rl,2); if (!na->rl) goto err_out; + na->unused_runs = 2; } /* Set initialized_size to @pos + @count. */ ctx = ntfs_attr_get_search_ctx(na->ni, NULL); @@ -1458,8 +1798,10 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) ctx->attr->initialized_size = cpu_to_sle64(pos + count); /* fix data_size for compressed files */ - if (compressed) + if (compressed) { + na->data_size = pos + count; ctx->attr->data_size = ctx->attr->initialized_size; + } if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, ctx->mrec)) { /* @@ -1473,6 +1815,19 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) goto err_out; } na->initialized_size = pos + count; +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if ((compressed || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } +#endif ntfs_attr_put_search_ctx(ctx); ctx = NULL; /* @@ -1497,7 +1852,6 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) } goto err_out; } - ofs = pos - (rl->vcn << vol->cluster_size_bits); /* * Determine if there is compressed data in the current * compression block (when appending to an existing file). @@ -1518,49 +1872,51 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) if ((rl->lcn == (LCN)LCN_HOLE) && wasnonresident) { if (rl->length < na->compression_block_clusters) + /* + * the needed block is in a hole smaller + * than the compression block : we can use + * it fully + */ compressed_part = na->compression_block_clusters - rl->length; else { - compressed_part - = na->compression_block_clusters; - if (rl->length > na->compression_block_clusters) { - rl[2].lcn = rl[1].lcn; - rl[2].vcn = rl[1].vcn; - rl[2].length = rl[1].length; - rl[1].vcn -= compressed_part; - rl[1].lcn = LCN_HOLE; - rl[1].length = compressed_part; - rl[0].length -= compressed_part; - ofs -= rl->length << vol->cluster_size_bits; - rl++; - } + /* + * the needed block is in a hole bigger + * than the compression block : we must + * split the hole and use it partially + */ + compressed_part = split_compressed_hole(na, + &rl, pos, count, &update_from); } - /* normal hole filling will do later */ - } else - if ((rl->lcn >= 0) && (rl[1].lcn == (LCN)LCN_HOLE)) { - s64 xofs; + } else { + if (rl->lcn >= 0) { + /* + * the needed block contains data, make + * sure the full compression block is + * allocated. Borrow from hole if needed + */ + compressed_part = borrow_from_hole(na, + &rl, pos, count, &update_from, + wasnonresident); + } + } - if (wasnonresident) - compressed_part = na->compression_block_clusters - - rl[1].length; - rl++; - xofs = 0; - if (ntfs_attr_fill_hole(na, - rl->length << vol->cluster_size_bits, - &xofs, &rl, &update_from)) - goto err_out; - /* the fist allocated cluster was not merged */ - if (!xofs) - rl--; - } + if (compressed_part < 0) + goto err_out; + + /* just making non-resident, so not yet compressed */ + if (NAttrBeingNonResident(na) + && (compressed_part < na->compression_block_clusters)) + compressed_part = 0; } + ofs = pos - (rl->vcn << vol->cluster_size_bits); /* * Scatter the data from the linear data buffer to the volume. Note, a * partial final vcn is taken care of by the @count capping of write * length. */ - for (hole_end = 0; count; rl++, ofs = 0, hole_end = 0) { + for (hole_end = 0; count; rl++, ofs = 0) { if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { @@ -1641,7 +1997,8 @@ retry: if (compressed) { written = ntfs_compressed_pwrite(na, rl, wpos, ofs, to_write, - rounding, b, compressed_part); + rounding, cb, compressed_part, + &update_from); } else { written = ntfs_pwrite(vol->dev, wpos, rounding, cb); @@ -1654,7 +2011,8 @@ retry: if (compressed) { written = ntfs_compressed_pwrite(na, rl, wpos, ofs, to_write, - to_write, b, compressed_part); + to_write, b, compressed_part, + &update_from); } else written = ntfs_pwrite(vol->dev, wpos, to_write, b); @@ -1682,10 +2040,17 @@ retry: done: if (ctx) ntfs_attr_put_search_ctx(ctx); - /* Update mapping pairs if needed. */ - if ((update_from != -1) - || (compressed && !makingnonresident)) - if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) { + /* + * Update mapping pairs if needed. + * For a compressed file, we try to make a partial update + * of the mapping list. This makes a difference only if + * inode extents were needed. + */ + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); + if (updatemap) + if (ntfs_attr_update_mapping_pairs(na, + (update_from < 0 ? 0 : update_from))) { /* * FIXME: trying to recover by goto rl_err_out; * could cause driver hang by infinite looping. @@ -1749,8 +2114,10 @@ err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ - if (update_from != -1) - ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); + if (updatemap) + ntfs_attr_update_mapping_pairs(na, 0); /* Restore original data_size if needed. */ if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size)) ntfs_log_perror("Failed to restore data_size"); @@ -1762,7 +2129,8 @@ errno_set: int ntfs_attr_pclose(ntfs_attr *na) { - s64 written, ofs; + s64 ofs; + int failed; BOOL ok = TRUE; VCN update_from = -1; ntfs_volume *vol; @@ -1782,6 +2150,7 @@ int ntfs_attr_pclose(ntfs_attr *na) goto errno_set; } vol = na->ni->vol; + na->unused_runs = 0; compressed = (na->data_flags & ATTR_COMPRESSION_MASK) != const_cpu_to_le16(0); /* @@ -1797,15 +2166,25 @@ int ntfs_attr_pclose(ntfs_attr *na) if (!compressed || !NAttrNonResident(na)) goto out; + /* safety check : no recursion on close */ + if (NAttrComprClosing(na)) { + errno = EIO; + ntfs_log_error("Bad ntfs_attr_pclose" + " recursion on inode %lld\n", + (long long)na->ni->mft_no); + goto out; + } + NAttrSetComprClosing(na); /* - * For a compressed attribute, we must be sure there is an - * available entry, so reserve it before it gets too late. + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. */ if (ntfs_attr_map_whole_runlist(na)) goto err_out; - na->rl = ntfs_rl_extend(na->rl,1); + na->rl = ntfs_rl_extend(na,na->rl,2); if (!na->rl) goto err_out; + na->unused_runs = 2; /* Find the runlist element containing the terminal vcn. */ rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits); if (!rl) { @@ -1826,10 +2205,16 @@ int ntfs_attr_pclose(ntfs_attr *na) * length. */ compressed_part = 0; - if ((rl->lcn >= 0) && (rl[1].lcn == (LCN)LCN_HOLE)) - compressed_part - = na->compression_block_clusters - rl[1].length; - else + if (rl->lcn >= 0) { + runlist_element *xrl; + + xrl = rl; + do { + xrl++; + } while (xrl->lcn >= 0); + compressed_part = (-xrl->length) + & (na->compression_block_clusters - 1); + } else if (rl->lcn == (LCN)LCN_HOLE) { if (rl->length < na->compression_block_clusters) compressed_part @@ -1883,26 +2268,32 @@ int ntfs_attr_pclose(ntfs_attr *na) } retry: - written = 0; + failed = 0; + if (update_from < 0) update_from = 0; if (!NVolReadOnly(vol)) { - - written = ntfs_compressed_close(na, rl, ofs); - /* If everything ok, update progress counters and continue. */ - if (!written) - goto done; + failed = ntfs_compressed_close(na, rl, ofs, &update_from); +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->compressed_size; + set_nino_flag(na->ni,KnownSize); + } +#endif } + if (failed) { /* If the syscall was interrupted, try again. */ - if (written == (s64)-1 && errno == EINTR) - goto retry; - if (!written) - errno = EIO; - goto rl_err_out; - -done: + if (errno == EINTR) + goto retry; + else + goto rl_err_out; + } if (ctx) ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ - if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) { + if (NAttrFullyMapped(na)) + if (ntfs_attr_update_mapping_pairs(na, update_from)) { /* * FIXME: trying to recover by goto rl_err_out; * could cause driver hang by infinite looping. @@ -1925,7 +2316,8 @@ err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ - ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); + if (NAttrFullyMapped(na)) + ntfs_attr_update_mapping_pairs(na, 0); errno = eo; errno_set: ok = FALSE; @@ -2195,38 +2587,24 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, errno = ENOENT; return -1; } - } else if (name && !ntfs_names_are_equal(name, name_len, - (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), - a->name_length, ic, upcase, upcase_len)) { + } else { register int rc; - - rc = ntfs_names_collate(name, name_len, - (ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, 1, IGNORE_CASE, - upcase, upcase_len); - /* - * If @name collates before a->name, there is no - * matching attribute. - */ - if (rc == -1) { - errno = ENOENT; - return -1; - } + if (name && ((rc = ntfs_names_full_collate(name, + name_len, (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, ic, + upcase, upcase_len)))) { + /* + * If @name collates before a->name, + * there is no matching attribute. + */ + if (rc < 0) { + errno = ENOENT; + return -1; + } /* If the strings are not equal, continue search. */ - if (rc) - continue; - rc = ntfs_names_collate(name, name_len, - (ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, 1, CASE_SENSITIVE, - upcase, upcase_len); - if (rc == -1) { - errno = ENOENT; - return -1; + continue; } - if (rc) - continue; } /* * The names match or @name not present and attribute is @@ -2506,38 +2884,22 @@ find_attr_list_attr: if (name == AT_UNNAMED) { if (al_name_len) goto not_found; - } else if (name && !ntfs_names_are_equal(al_name, al_name_len, - name, name_len, ic, vol->upcase, - vol->upcase_len)) { - register int rc; + } else { + int rc; - rc = ntfs_names_collate(name, name_len, al_name, - al_name_len, 1, IGNORE_CASE, - vol->upcase, vol->upcase_len); - /* - * If @name collates before al_name, there is no - * matching attribute. - */ - if (rc == -1) - goto not_found; - /* If the strings are not equal, continue search. */ - if (rc) - continue; - /* - * FIXME: Reverse engineering showed 0, IGNORE_CASE but - * that is inconsistent with ntfs_attr_find(). The - * subsequent rc checks were also different. Perhaps I - * made a mistake in one of the two. Need to recheck - * which is correct or at least see what is going - * on... (AIA) - */ - rc = ntfs_names_collate(name, name_len, al_name, - al_name_len, 1, CASE_SENSITIVE, - vol->upcase, vol->upcase_len); - if (rc == -1) - goto not_found; - if (rc) + if (name && ((rc = ntfs_names_full_collate(name, + name_len, al_name, al_name_len, ic, + vol->upcase, vol->upcase_len)))) { + + /* + * If @name collates before al_name, + * there is no matching attribute. + */ + if (rc < 0) + goto not_found; + /* If the strings are not equal, continue search. */ continue; + } } /* * The names match or @name not present and attribute is @@ -3280,9 +3642,12 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, goto put_err_out; } } - if (type == AT_DATA && name == AT_UNNAMED) { + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30 + : type == AT_DATA && name == AT_UNNAMED) { ni->data_size = size; ni->allocated_size = (size + 7) & ~7; + set_nino_flag(ni,KnownSize); } ntfs_inode_mark_dirty(ni); ntfs_attr_put_search_ctx(ctx); @@ -3454,7 +3819,6 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) { ntfs_inode *base_ni, *ni; ATTR_TYPES type; - int err; if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) { errno = EINVAL; @@ -3479,7 +3843,7 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) if (ntfs_attrlist_entry_add(ni, ctx->attr)) ntfs_log_trace("Rollback failed. Leaving inconstant " "metadata.\n"); - err = EIO; + errno = EIO; return -1; } ntfs_inode_mark_dirty(ni); @@ -3715,6 +4079,9 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, add_attr_record: if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) && ((type == AT_DATA) || ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30)))) data_flags = ATTR_IS_COMPRESSED; @@ -4199,6 +4566,13 @@ int ntfs_attr_make_non_resident(ntfs_attr *na, - 1) & ~(vol->cluster_size - 1); if (new_allocated_size > 0) { + if ((a->flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) { + /* must allocate full compression blocks */ + new_allocated_size = ((new_allocated_size - 1) + | ((1L << (STANDARD_COMPRESSION_UNIT + + vol->cluster_size_bits)) - 1)) + 1; + } /* Start by allocating clusters to hold the attribute value. */ rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> vol->cluster_size_bits, -1, DATA_ZONE); @@ -4211,6 +4585,7 @@ int ntfs_attr_make_non_resident(ntfs_attr *na, * we can use ntfs_attr_pwrite(). */ NAttrSetNonResident(na); + NAttrSetBeingNonResident(na); na->rl = rl; na->allocated_size = new_allocated_size; na->data_size = na->initialized_size = le32_to_cpu(a->value_length); @@ -4345,6 +4720,8 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); * @newsize: new size (in bytes) to which to resize the attribute * * Change the size of a resident, open ntfs attribute @na to @newsize bytes. + * Can also be used to force an attribute non-resident. In this case, the + * size cannot be changed. * * On success return 0 * On error return values are: @@ -4355,7 +4732,8 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); * ERANGE - @newsize is not valid for the attribute type of @na. * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. */ -static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) +static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, + BOOL force_non_resident) { ntfs_attr_search_ctx *ctx; ntfs_volume *vol; @@ -4393,7 +4771,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) * attribute non-resident if the attribute type supports it. If it is * smaller we can go ahead and attempt the resize. */ - if (newsize < vol->mft_record_size) { + if ((newsize < vol->mft_record_size) && !force_non_resident) { /* Perform the resize of the attribute record. */ if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, newsize))) { @@ -4403,10 +4781,21 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) if ((na->data_flags & ATTR_COMPRESSION_MASK) || NAttrSparse(na)) na->compressed_size = na->allocated_size; - if (na->type == AT_DATA && na->name == AT_UNNAMED) { + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { na->ni->data_size = na->data_size; - na->ni->allocated_size = na->allocated_size; - NInoFileNameSetDirty(na->ni); + if (((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size + = na->compressed_size; + else + na->ni->allocated_size + = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + if (na->type == AT_DATA) + NInoFileNameSetDirty(na->ni); } goto resize_done; } @@ -4422,6 +4811,21 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) if (!ntfs_attr_make_non_resident(na, ctx)) { ntfs_inode_mark_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); + /* + * do not truncate when forcing non-resident, this + * could cause the attribute to be made resident again, + * so size changes are not allowed. + */ + if (force_non_resident) { + ret = 0; + if (newsize != na->data_size) { + ntfs_log_error("Cannot change size when" + " forcing non-resident\n"); + errno = EIO; + ret = STATUS_ERROR; + } + return (ret); + } /* Resize non-resident attribute */ return ntfs_attr_truncate(na, newsize); } else if (errno != ENOSPC && errno != EPERM) { @@ -4460,10 +4864,17 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) ntfs_attr_close(tna); continue; } + if (((tna->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + && ntfs_attr_pclose(tna)) { + err = errno; + ntfs_attr_close(tna); + goto put_err_out; + } ntfs_inode_mark_dirty(tna->ni); ntfs_attr_close(tna); ntfs_attr_put_search_ctx(ctx); - return ntfs_resident_attr_resize(na, newsize); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); } /* Check whether error occurred. */ if (errno != ENOENT) { @@ -4483,7 +4894,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) ntfs_log_perror("Could not free space in MFT record"); return -1; } - return ntfs_resident_attr_resize(na, newsize); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); } /* @@ -4522,7 +4933,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) ntfs_attr_put_search_ctx(ctx); if (ntfs_inode_add_attrlist(ni)) return -1; - return ntfs_resident_attr_resize(na, newsize); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); } /* Allocate new mft record. */ ni = ntfs_mft_record_alloc(vol, ni); @@ -4543,7 +4954,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) ntfs_attr_put_search_ctx(ctx); /* Try to perform resize once again. */ - return ntfs_resident_attr_resize(na, newsize); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); resize_done: /* @@ -4564,11 +4975,39 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) int ret; ntfs_log_enter("Entering\n"); - ret = ntfs_resident_attr_resize_i(na, newsize); + ret = ntfs_resident_attr_resize_i(na, newsize, FALSE); ntfs_log_leave("\n"); return ret; } +/* + * Force an attribute to be made non-resident without + * changing its size. + * + * This is particularly needed when the attribute has no data, + * as the non-resident variant requires more space in the MFT + * record, and may imply expelling some other attribute. + * + * As a consequence the existing ntfs_attr_search_ctx's have to + * be closed or reinitialized. + * + * returns 0 if successful, + * < 0 if failed, with errno telling why + */ + +int ntfs_attr_force_non_resident(ntfs_attr *na) +{ + int res; + + res = ntfs_resident_attr_resize_i(na, na->data_size, TRUE); + if (!res && !NAttrNonResident(na)) { + res = -1; + errno = EIO; + ntfs_log_error("Failed to force non-resident\n"); + } + return (res); +} + /** * ntfs_attr_make_resident - convert a non-resident to a resident attribute * @na: open ntfs attribute to make resident @@ -4602,7 +5041,7 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) if (sle64_to_cpu(a->lowest_vcn)) { ntfs_log_trace("Eeek! Should be called for the first extent of the " "attribute. Aborting...\n"); - err = EINVAL; + errno = EINVAL; return -1; } @@ -4679,6 +5118,9 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) */ if (!na->data_size && (na->type == AT_DATA) + && (na->ni->vol->major_ver >= 3) + && NVolCompression(na->ni->vol) + && (na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) && (na->ni->flags & FILE_ATTR_COMPRESSED)) { a->flags |= ATTR_IS_COMPRESSED; na->data_flags = a->flags; @@ -4851,7 +5293,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, * allocated size in the index. */ if (na->type == AT_DATA && na->name == AT_UNNAMED) { - if (sparse) + if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) na->ni->allocated_size = na->compressed_size; else na->ni->allocated_size = na->allocated_size; @@ -4878,9 +5320,10 @@ static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn) const runlist_element *stop_rl; int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1; BOOL finished_build; + BOOL first_updated = FALSE; retry: - if (!na || !na->rl || from_vcn) { + if (!na || !na->rl) { errno = EINVAL; ntfs_log_perror("%s: na=%p", __FUNCTION__, na); return -1; @@ -4912,6 +5355,8 @@ retry: CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { a = ctx->attr; m = ctx->mrec; + if (!a->lowest_vcn) + first_updated = TRUE; /* * If runlist is updating not from the beginning, then set * @stop_vcn properly, i.e. to the lowest vcn of record that @@ -5061,6 +5506,34 @@ retry: ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__); goto put_err_out; } + /* + * If the base extent was skipped in the above process, + * we still may have to update the sizes. + */ + if (!first_updated) { + le16 spcomp; + + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + a = ctx->attr; + a->allocated_size = cpu_to_sle64(na->allocated_size); + spcomp = na->data_flags + & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + if (spcomp) + a->compressed_size = cpu_to_sle64(na->compressed_size); + if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) { + na->ni->allocated_size + = (spcomp + ? na->compressed_size + : na->allocated_size); + NInoFileNameSetDirty(na->ni); + } + } else { + ntfs_log_error("Failed to update sizes in base extent\n"); + goto put_err_out; + } + } /* Deallocate not used attribute extents and return with success. */ if (finished_build) { @@ -5238,8 +5711,19 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) } /* The first cluster outside the new allocation. */ - first_free_vcn = (newsize + vol->cluster_size - 1) >> - vol->cluster_size_bits; + if (na->data_flags & ATTR_COMPRESSION_MASK) + /* + * For compressed files we must keep full compressions blocks, + * but currently we do not decompress/recompress the last + * block to truncate the data, so we may leave more allocated + * clusters than really needed. + */ + first_free_vcn = (((newsize - 1) + | (na->compression_block_size - 1)) + 1) + >> vol->cluster_size_bits; + else + first_free_vcn = (newsize + vol->cluster_size - 1) >> + vol->cluster_size_bits; /* * Compare the new allocation with the old one and only deallocate * clusters if there is a change. @@ -5305,9 +5789,17 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) ctx->attr->initialized_size = cpu_to_sle64(newsize); } /* Update data size in the index. */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - na->ni->data_size = na->data_size; - NInoFileNameSetDirty(na->ni); + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } } /* If the attribute now has zero size, make it resident. */ @@ -5496,9 +5988,17 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) na->data_size = newsize; ctx->attr->data_size = cpu_to_sle64(newsize); /* Update data size in the index. */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - na->ni->data_size = na->data_size; - NInoFileNameSetDirty(na->ni); + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } } /* Set the inode dirty so it is written out later. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); @@ -5598,7 +6098,7 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) */ if (na->data_flags & ATTR_IS_ENCRYPTED) { errno = EACCES; - ntfs_log_info("Failed to truncate encrypted attribute"); + ntfs_log_trace("Cannot truncate encrypted attribute\n"); goto out; } /* @@ -5612,8 +6112,7 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) != const_cpu_to_le16(0); if (compressed && NAttrNonResident(na) - && (((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED) - || (newsize && (newsize < na->data_size)))) { + && ((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)) { errno = EOPNOTSUPP; ntfs_log_perror("Failed to truncate compressed attribute"); goto out; @@ -5621,16 +6120,16 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) if (NAttrNonResident(na)) { /* * For compressed data, the last block must be fully - * allocated, and we do not known the size of compression + * allocated, and we do not know the size of compression * block until the attribute has been made non-resident. * Moreover we can only process a single compression * block at a time (from where we are about to write), * so we silently do not allocate more. * - * Note : do not request truncate on compressed files + * Note : do not request upsizing of compressed files * unless being able to face the consequences ! */ - if (compressed && newsize) + if (compressed && newsize && (newsize > na->data_size)) fullsize = (na->initialized_size | (na->compression_block_size - 1)) + 1; else diff --git a/source/libntfs/attrib.h b/source/libntfs/attrib.h index bcdb0117..43ab7f53 100644 --- a/source/libntfs/attrib.h +++ b/source/libntfs/attrib.h @@ -189,6 +189,7 @@ struct _ntfs_attr { u32 compression_block_size; u8 compression_block_size_bits; u8 compression_block_clusters; + s8 unused_runs; /* pre-reserved entries available */ }; /** @@ -198,6 +199,9 @@ struct _ntfs_attr { typedef enum { NA_Initialized, /* 1: structure is initialized. */ NA_NonResident, /* 1: Attribute is not resident. */ + NA_BeingNonResident, /* 1: Attribute is being made not resident. */ + NA_FullyMapped, /* 1: Attribute has been fully mapped */ + NA_ComprClosing, /* 1: Compressed attribute is being closed */ } ntfs_attr_state_bits; #define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state) @@ -212,6 +216,18 @@ typedef enum { #define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) #define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) +#define NAttrBeingNonResident(na) test_nattr_flag(na, BeingNonResident) +#define NAttrSetBeingNonResident(na) set_nattr_flag(na, BeingNonResident) +#define NAttrClearBeingNonResident(na) clear_nattr_flag(na, BeingNonResident) + +#define NAttrFullyMapped(na) test_nattr_flag(na, FullyMapped) +#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped) +#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped) + +#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) +#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) +#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) + #define GenNAttrIno(func_name, flag) \ extern int NAttr##func_name(ntfs_attr *na); \ extern void NAttrSet##func_name(ntfs_attr *na); \ @@ -288,6 +304,7 @@ extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type); int ntfs_attr_make_non_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx); +int ntfs_attr_force_non_resident(ntfs_attr *na); extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, diff --git a/source/libntfs/attrib_frag.c b/source/libntfs/attrib_frag.c index 7f5a659e..5c3c4a1d 100644 --- a/source/libntfs/attrib_frag.c +++ b/source/libntfs/attrib_frag.c @@ -818,7 +818,7 @@ static s64 ntfs_attr_getfragments_i(ntfs_attr *na, const s64 pos, s64 count, u64 s64 br, to_read, ofs, total, total2, max_read, max_init; ntfs_volume *vol; runlist_element *rl; -// u16 efs_padding_length; + //u16 efs_padding_length; /* Sanity checking arguments is done in ntfs_attr_pread(). */ diff --git a/source/libntfs/cache.c b/source/libntfs/cache.c index 6455b1f8..dd147672 100644 --- a/source/libntfs/cache.c +++ b/source/libntfs/cache.c @@ -1,378 +1,609 @@ -/* - cache.c - The cache is not visible to the user. It should be flushed - when any file is closed or changes are made to the filesystem. +/** + * cache.c : deal with LRU caches + * + * Copyright (c) 2008-2009 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ - This cache implements a least-used-page replacement policy. This will - distribute sectors evenly over the pages, so if less than the maximum - pages are used at once, they should all eventually remain in the cache. - This also has the benefit of throwing out old sectors, so as not to keep - too many stale pages around. - - Copyright (c) 2006 Michael "Chishm" Chisholm - Copyright (c) 2009 shareese, rodries - Copyright (c) 2010 Dimok - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H #include -#include +#endif -//#include "common.h" +#include "types.h" +#include "security.h" #include "cache.h" -#include "bit_ops.h" -//#include "disc.h" +#include "misc.h" +#include "logging.h" -#include "mem_allocate.h" -#include -//#include "bit_ops.h" -//#include "file_allocation_table.h" +/* + * General functions to deal with LRU caches + * + * The cached data have to be organized in a structure in which + * the first fields must follow a mandatory pattern and further + * fields may contain any fixed size data. They are stored in an + * LRU list. + * + * A compare function must be provided for finding a wanted entry + * in the cache. Another function may be provided for invalidating + * an entry to facilitate multiple invalidation. + * + * These functions never return error codes. When there is a + * shortage of memory, data is simply not cached. + * When there is a hashing bug, hashing is dropped, and sequential + * searches are used. + */ -#define CACHE_FREE UINT_MAX +/* + * Enter a new hash index, after a new record has been inserted + * + * Do not call when a record has been modified (with no key change) + */ -NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition) { - NTFS_CACHE* cache; - unsigned int i; - NTFS_CACHE_ENTRY* cacheEntries; - - if(numberOfPages==0 || sectorsPerPage==0) return NULL; - - if (numberOfPages < 4) { - numberOfPages = 4; - } - - if (sectorsPerPage < 32) { - sectorsPerPage = 32; - } - - cache = (NTFS_CACHE*) ntfs_alloc (sizeof(NTFS_CACHE)); - if (cache == NULL) { - return NULL; - } - - cache->disc = discInterface; - cache->endOfPartition = endOfPartition; - cache->numberOfPages = numberOfPages; - cache->sectorsPerPage = sectorsPerPage; - - - cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc ( sizeof(NTFS_CACHE_ENTRY) * numberOfPages); - if (cacheEntries == NULL) { - ntfs_free (cache); - return NULL; - } - - for (i = 0; i < numberOfPages; i++) { - cacheEntries[i].sector = CACHE_FREE; - cacheEntries[i].count = 0; - cacheEntries[i].last_access = 0; - cacheEntries[i].dirty = false; - cacheEntries[i].cache = (uint8_t*) ntfs_align ( sectorsPerPage * BYTES_PER_READ ); - } - - cache->cacheEntries = cacheEntries; - - return cache; -} - -void _NTFS_cache_destructor (NTFS_CACHE* cache) { - unsigned int i; - - if(cache==NULL) return; - - // Clear out cache before destroying it - _NTFS_cache_flush(cache); - - // Free memory in reverse allocation order - for (i = 0; i < cache->numberOfPages; i++) { - ntfs_free (cache->cacheEntries[i].cache); - } - ntfs_free (cache->cacheEntries); - ntfs_free (cache); -} - -static u32 accessCounter = 0; - -static u32 accessTime(){ - accessCounter++; - return accessCounter; -} - -static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache,sec_t sector) +static void inserthashindex(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current) { - unsigned int i; - NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; - unsigned int numberOfPages = cache->numberOfPages; - unsigned int sectorsPerPage = cache->sectorsPerPage; + int h; + struct HASH_ENTRY *link; + struct HASH_ENTRY *first; - bool foundFree = false; - unsigned int oldUsed = 0; - unsigned int oldAccess = UINT_MAX; - - for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { - cacheEntries[i].last_access = accessTime(); - return &(cacheEntries[i]); - } - - if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; - cacheEntries[oldUsed].dirty = false; - } - sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size - sec_t next_page = sector + sectorsPerPage; - if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; - - if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; - - cacheEntries[oldUsed].sector = sector; - cacheEntries[oldUsed].count = next_page-sector; - cacheEntries[oldUsed].last_access = accessTime(); - - return &(cacheEntries[oldUsed]); -} - -static NTFS_CACHE_ENTRY* _NTFS_cache_findPage(NTFS_CACHE *cache, sec_t sector, sec_t count) { - - unsigned int i; - NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; - unsigned int numberOfPages = cache->numberOfPages; - NTFS_CACHE_ENTRY *entry = NULL; - sec_t lowest = UINT_MAX; - - for(i=0;i cacheEntries[i].sector) { - intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; + if (cache->dohash) { + h = cache->dohash(current); + if ((h >= 0) && (h < cache->max_hash)) { + /* get a free link and insert at top of hash list */ + link = cache->free_hash; + if (link) { + cache->free_hash = link->next; + first = cache->first_hash[h]; + if (first) + link->next = first; + else + link->next = NULL; + link->entry = current; + cache->first_hash[h] = link; } else { - intersect = cacheEntries[i].sector - sector < count; + ntfs_log_error("No more hash entries," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; } - - if ( intersect && (cacheEntries[i].sector < lowest)) { - lowest = cacheEntries[i].sector; - entry = &cacheEntries[i]; - } - } - } - - return entry; -} - -bool _NTFS_cache_readSectors(NTFS_CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) -{ - sec_t sec; - sec_t secs_to_read; - NTFS_CACHE_ENTRY *entry; - uint8_t *dest = buffer; - - while(numSectors>0) { - entry = _NTFS_cache_getPage(cache,sector); - if(entry==NULL) return false; - - sec = sector - entry->sector; - secs_to_read = entry->count - sec; - if(secs_to_read>numSectors) secs_to_read = numSectors; - - memcpy(dest,entry->cache + (sec*BYTES_PER_READ),(secs_to_read*BYTES_PER_READ)); - - dest += (secs_to_read*BYTES_PER_READ); - sector += secs_to_read; - numSectors -= secs_to_read; - } - - return true; -} - -/* -Reads some data from a cache page, determined by the sector number -*/ - -bool _NTFS_cache_readPartialSector (NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) -{ - sec_t sec; - NTFS_CACHE_ENTRY *entry; - - if (offset + size > BYTES_PER_READ) return false; - - entry = _NTFS_cache_getPage(cache,sector); - if(entry==NULL) return false; - - sec = sector - entry->sector; - memcpy(buffer,entry->cache + ((sec*BYTES_PER_READ) + offset),size); - - return true; -} - -bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { - uint8_t buf[4]; - if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; - - switch(num_bytes) { - case 1: *value = buf[0]; break; - case 2: *value = u8array_to_u16(buf,0); break; - case 4: *value = u8array_to_u32(buf,0); break; - default: return false; - } - return true; -} - -/* -Writes some data to a cache page, making sure it is loaded into memory first. -*/ - -bool _NTFS_cache_writePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) -{ - sec_t sec; - NTFS_CACHE_ENTRY *entry; - - if (offset + size > BYTES_PER_READ) return false; - - entry = _NTFS_cache_getPage(cache,sector); - if(entry==NULL) return false; - - sec = sector - entry->sector; - memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size); - - entry->dirty = true; - return true; -} - -bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { - uint8_t buf[4] = {0, 0, 0, 0}; - - switch(size) { - case 1: buf[0] = value; break; - case 2: u16_to_u8array(buf, 0, value); break; - case 4: u32_to_u8array(buf, 0, value); break; - default: return false; - } - - return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size); -} - -/* -Writes some data to a cache page, zeroing out the page first -*/ - -bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) -{ - sec_t sec; - NTFS_CACHE_ENTRY *entry; - - if (offset + size > BYTES_PER_READ) return false; - - entry = _NTFS_cache_getPage(cache,sector); - if(entry==NULL) return false; - - sec = sector - entry->sector; - memset(entry->cache + (sec*BYTES_PER_READ),0,BYTES_PER_READ); - memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size); - - entry->dirty = true; - return true; -} - -bool _NTFS_cache_writeSectors (NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) -{ - sec_t sec; - sec_t secs_to_write; - NTFS_CACHE_ENTRY* entry; - const uint8_t *src = buffer; - - while(numSectors>0) - { - entry = _NTFS_cache_findPage(cache,sector,numSectors); - - if(entry!=NULL) { - - if ( entry->sector > sector) { - - secs_to_write = entry->sector - sector; - - cache->disc->writeSectors(sector,secs_to_write,src); - src += (secs_to_write*BYTES_PER_READ); - sector += secs_to_write; - numSectors -= secs_to_write; - } - - sec = sector - entry->sector; - secs_to_write = entry->count - sec; - - if(secs_to_write>numSectors) secs_to_write = numSectors; - - memcpy(entry->cache + (sec*BYTES_PER_READ),src,(secs_to_write*BYTES_PER_READ)); - - src += (secs_to_write*BYTES_PER_READ); - sector += secs_to_write; - numSectors -= secs_to_write; - - entry->dirty = true; - } else { - cache->disc->writeSectors(sector,numSectors,src); - numSectors=0; + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; } } - return true; } /* -Flushes all dirty pages to disc, clearing the dirty flag. -*/ -bool _NTFS_cache_flush (NTFS_CACHE* cache) { - unsigned int i; - if(cache==NULL) return true; + * Drop a hash index when a record is about to be deleted + */ - for (i = 0; i < cache->numberOfPages; i++) { - if (cache->cacheEntries[i].dirty) { - if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { - return false; +static void drophashindex(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *current, int hash) +{ + struct HASH_ENTRY *link; + struct HASH_ENTRY *previous; + + if (cache->dohash) { + if ((hash >= 0) && (hash < cache->max_hash)) { + /* find the link and unlink */ + link = cache->first_hash[hash]; + previous = (struct HASH_ENTRY*)NULL; + while (link && (link->entry != current)) { + previous = link; + link = link->next; + } + if (link) { + if (previous) + previous->next = link->next; + else + cache->first_hash[hash] = link->next; + link->next = cache->free_hash; + cache->free_hash = link; + } else { + ntfs_log_error("Bad hash list," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } +} + +/* + * Fetch an entry from cache + * + * returns the cache entry, or NULL if not available + * The returned entry may be modified, but not freed + */ + +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * locate the entry if present + */ + h = cache->dohash(wanted); + link = cache->first_hash[h]; + while (link && compare(link->entry, wanted)) + link = link->next; + if (link) + current = link->entry; + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list if no hash table + * or if hashing has just failed + */ + current = cache->most_recent_entry; + while (current + && compare(current, wanted)) { + current = current->next; + } + } + if (current) { + previous = current->previous; + cache->hits++; + if (previous) { + /* + * found and not at head of list, unlink from current + * position and relink as head of list + */ + previous->next = current->next; + if (current->next) + current->next->previous + = current->previous; + else + cache->oldest_entry + = current->previous; + current->next = cache->most_recent_entry; + current->previous + = (struct CACHED_GENERIC*)NULL; + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; } } - cache->cacheEntries[i].dirty = false; + cache->reads++; } - - return true; + return (current); } -void _NTFS_cache_invalidate (NTFS_CACHE* cache) { - unsigned int i; - if(cache==NULL) - return; +/* + * Enter an inode number into cache + * returns the cache entry or NULL if not possible + */ - _NTFS_cache_flush(cache); - for (i = 0; i < cache->numberOfPages; i++) { - cache->cacheEntries[i].sector = CACHE_FREE; - cache->cacheEntries[i].last_access = 0; - cache->cacheEntries[i].count = 0; - cache->cacheEntries[i].dirty = false; +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *before; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link && compare(link->entry, item)) + link = link->next; + if (link) { + current = link->entry; + } + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list to locate the end, + * and find out whether the entry is already in list + * As we normally go to the end, no statistics is + * kept. + */ + current = cache->most_recent_entry; + while (current + && compare(current, item)) { + current = current->next; + } + } + + if (!current) { + /* + * Not in list, get a free entry or reuse the + * last entry, and relink as head of list + * Note : we assume at least three entries, so + * before, previous and first are different when + * an entry is reused. + */ + + if (cache->free_entry) { + current = cache->free_entry; + cache->free_entry = cache->free_entry->next; + if (item->varsize) { + current->variable = ntfs_malloc( + item->varsize); + } else + current->variable = (void*)NULL; + current->varsize = item->varsize; + if (!cache->oldest_entry) + cache->oldest_entry = current; + } else { + /* reusing the oldest entry */ + current = cache->oldest_entry; + before = current->previous; + before->next = (struct CACHED_GENERIC*)NULL; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + if (cache->dofree) + cache->dofree(current); + cache->oldest_entry = current->previous; + if (item->varsize) { + if (current->varsize) + current->variable = realloc( + current->variable, + item->varsize); + else + current->variable = ntfs_malloc( + item->varsize); + } else { + if (current->varsize) + free(current->variable); + current->variable = (void*)NULL; + } + current->varsize = item->varsize; + } + current->next = cache->most_recent_entry; + current->previous = (struct CACHED_GENERIC*)NULL; + if (cache->most_recent_entry) + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + memcpy(current->fixed, item->fixed, cache->fixed_size); + if (item->varsize) { + if (current->variable) { + memcpy(current->variable, + item->variable, item->varsize); + } else { + /* + * no more memory for variable part + * recycle entry in free list + * not an error, just uncacheable + */ + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + current = (struct CACHED_GENERIC*)NULL; + } + } else { + current->variable = (void*)NULL; + current->varsize = 0; + } + if (cache->dohash && current) + inserthashindex(cache,current); + } + cache->writes++; + } + return (current); +} + +/* + * Invalidate a cache entry + * The entry is moved to the free entry list + * A specific function may be called for entry deletion + */ + +static void do_invalidate(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current, int flags) +{ + struct CACHED_GENERIC *previous; + + previous = current->previous; + if ((flags & CACHE_FREE) && cache->dofree) + cache->dofree(current); + /* + * Relink into free list + */ + if (current->next) + current->next->previous = current->previous; + else + cache->oldest_entry = current->previous; + if (previous) + previous->next = current->next; + else + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + if (current->variable) + free(current->variable); + current->varsize = 0; + } + + +/* + * Invalidate entries in cache + * + * Several entries may have to be invalidated (at least for inodes + * associated to directories which have been renamed), a different + * compare function may be provided to select entries to invalidate + * + * Returns the number of deleted entries, this can be used by + * the caller to signal a cache corruption if the entry was + * supposed to be found. + */ + +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, cache_compare compare, + int flags) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct CACHED_GENERIC *next; + struct HASH_ENTRY *link; + int count; + int h; + + current = (struct CACHED_GENERIC*)NULL; + count = 0; + if (cache) { + if (!(flags & CACHE_NOHASH) && cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link) { + if (compare(link->entry, item)) + link = link->next; + else { + current = link->entry; + link = link->next; + if (current) { + drophashindex(cache,current,h); + do_invalidate(cache, + current,flags); + count++; + } + } + } + } + if ((flags & CACHE_NOHASH) || !cache->dohash) { + /* + * Search sequentially in LRU list + */ + current = cache->most_recent_entry; + previous = (struct CACHED_GENERIC*)NULL; + while (current) { + if (!compare(current, item)) { + next = current->next; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + do_invalidate(cache,current,flags); + current = next; + count++; + } else { + previous = current; + current = current->next; + } + } + } + } + return (count); +} + +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags) +{ + int count; + + count = 0; + if (cache) { + if (cache->dohash) + drophashindex(cache,item,cache->dohash(item)); + do_invalidate(cache,item,flags); + count++; + } + return (count); +} + +/* + * Free memory allocated to a cache + */ + +static void ntfs_free_cache(struct CACHE_HEADER *cache) +{ + struct CACHED_GENERIC *entry; + + if (cache) { + for (entry=cache->most_recent_entry; entry; entry=entry->next) { + if (cache->dofree) + cache->dofree(entry); + if (entry->variable) + free(entry->variable); + } + free(cache); } } + +/* + * Create a cache + * + * Returns the cache header, or NULL if the cache could not be created + */ + +static struct CACHE_HEADER *ntfs_create_cache(const char *name, + cache_free dofree, cache_hash dohash, + int full_item_size, + int item_count, int max_hash) +{ + struct CACHE_HEADER *cache; + struct CACHED_GENERIC *pc; + struct CACHED_GENERIC *qc; + struct HASH_ENTRY *ph; + struct HASH_ENTRY *qh; + struct HASH_ENTRY **px; + size_t size; + int i; + + size = sizeof(struct CACHE_HEADER) + item_count*full_item_size; + if (max_hash) + size += item_count*sizeof(struct HASH_ENTRY) + + max_hash*sizeof(struct HASH_ENTRY*); + cache = (struct CACHE_HEADER*)ntfs_malloc(size); + if (cache) { + /* header */ + cache->name = name; + cache->dofree = dofree; + if (dohash && max_hash) { + cache->dohash = dohash; + cache->max_hash = max_hash; + } else { + cache->dohash = (cache_hash)NULL; + cache->max_hash = 0; + } + cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC); + cache->reads = 0; + cache->writes = 0; + cache->hits = 0; + /* chain the data entries, and mark an invalid entry */ + cache->most_recent_entry = (struct CACHED_GENERIC*)NULL; + cache->oldest_entry = (struct CACHED_GENERIC*)NULL; + cache->free_entry = &cache->entry[0]; + pc = &cache->entry[0]; + for (i=0; i<(item_count - 1); i++) { + qc = (struct CACHED_GENERIC*)((char*)pc + + full_item_size); + pc->next = qc; + pc->variable = (void*)NULL; + pc->varsize = 0; + pc = qc; + } + /* special for the last entry */ + pc->next = (struct CACHED_GENERIC*)NULL; + pc->variable = (void*)NULL; + pc->varsize = 0; + + if (max_hash) { + /* chain the hash entries */ + ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size); + cache->free_hash = ph; + for (i=0; i<(item_count - 1); i++) { + qh = &ph[1]; + ph->next = qh; + ph = qh; + } + /* special for the last entry */ + if (item_count) { + ph->next = (struct HASH_ENTRY*)NULL; + } + /* create and initialize the hash indexes */ + px = (struct HASH_ENTRY**)&ph[1]; + cache->first_hash = px; + for (i=0; ifree_hash = (struct HASH_ENTRY*)NULL; + cache->first_hash = (struct HASH_ENTRY**)NULL; + } + } + return (cache); +} + +/* + * Create all LRU caches + * + * No error return, if creation is not possible, cacheing will + * just be not available + */ + +void ntfs_create_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + /* inode cache */ + vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL, + ntfs_dir_inode_hash, sizeof(struct CACHED_INODE), + CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE); +#endif +#if CACHE_NIDATA_SIZE + /* idata cache */ + vol->nidata_cache = ntfs_create_cache("nidata", + ntfs_inode_nidata_free, ntfs_inode_nidata_hash, + sizeof(struct CACHED_NIDATA), + CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE); +#endif +#if CACHE_LOOKUP_SIZE + /* lookup cache */ + vol->lookup_cache = ntfs_create_cache("lookup", + (cache_free)NULL, ntfs_dir_lookup_hash, + sizeof(struct CACHED_LOOKUP), + CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE); +#endif + vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL, + (cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0); +#if CACHE_LEGACY_SIZE + vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL, + (cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0); +#endif +} + +/* + * Free all LRU caches + */ + +void ntfs_free_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + ntfs_free_cache(vol->xinode_cache); +#endif +#if CACHE_NIDATA_SIZE + ntfs_free_cache(vol->nidata_cache); +#endif +#if CACHE_LOOKUP_SIZE + ntfs_free_cache(vol->lookup_cache); +#endif + ntfs_free_cache(vol->securid_cache); +#if CACHE_LEGACY_SIZE + ntfs_free_cache(vol->legacy_cache); +#endif +} diff --git a/source/libntfs/cache.h b/source/libntfs/cache.h index d45208bf..67e4f9da 100644 --- a/source/libntfs/cache.h +++ b/source/libntfs/cache.h @@ -1,136 +1,119 @@ /* - NTFS_CACHE.h - The NTFS_CACHE is not visible to the user. It should be flushed - when any file is closed or changes are made to the filesystem. + * cache.h : deal with indexed LRU caches + * + * Copyright (c) 2008-2009 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ - This NTFS_CACHE implements a least-used-page replacement policy. This will - distribute sectors evenly over the pages, so if less than the maximum - pages are used at once, they should all eventually remain in the NTFS_CACHE. - This also has the benefit of throwing out old sectors, so as not to keep - too many stale pages around. +#ifndef _NTFS_CACHE_H_ +#define _NTFS_CACHE_H_ - Copyright (c) 2006 Michael "Chishm" Chisholm - Copyright (c) 2009 shareese, rodries +#include "volume.h" - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: +struct CACHED_GENERIC { + struct CACHED_GENERIC *next; + struct CACHED_GENERIC *previous; + void *variable; + size_t varsize; + union { + /* force alignment for pointers and u64 */ + u64 u64align; + void *ptralign; + } fixed[0]; +} ; - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products derived - from this software without specific prior written permission. +struct CACHED_INODE { + struct CACHED_INODE *next; + struct CACHED_INODE *previous; + const char *pathname; + size_t varsize; + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; +} ; - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ +struct CACHED_NIDATA { + struct CACHED_NIDATA *next; + struct CACHED_NIDATA *previous; + const char *pathname; /* not used */ + size_t varsize; /* not used */ + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; + ntfs_inode *ni; +} ; -#ifndef _CACHE_H -#define _CACHE_H +struct CACHED_LOOKUP { + struct CACHED_LOOKUP *next; + struct CACHED_LOOKUP *previous; + const char *name; + size_t namesize; + /* above fields must match "struct CACHED_GENERIC" */ + u64 parent; + u64 inum; +} ; -//#include "common.h" -//#include "disc.h" +enum { + CACHE_FREE = 1, + CACHE_NOHASH = 2 +} ; -#include -#include -#include -#include -#include +typedef int (*cache_compare)(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *item); +typedef void (*cache_free)(const struct CACHED_GENERIC *cached); +typedef int (*cache_hash)(const struct CACHED_GENERIC *cached); -#define BYTES_PER_READ 512 +struct HASH_ENTRY { + struct HASH_ENTRY *next; + struct CACHED_GENERIC *entry; +} ; -typedef struct { - sec_t sector; - unsigned int count; - u64 last_access; - bool dirty; - u8* cache; -} NTFS_CACHE_ENTRY; +struct CACHE_HEADER { + const char *name; + struct CACHED_GENERIC *most_recent_entry; + struct CACHED_GENERIC *oldest_entry; + struct CACHED_GENERIC *free_entry; + struct HASH_ENTRY *free_hash; + struct HASH_ENTRY **first_hash; + cache_free dofree; + cache_hash dohash; + unsigned long reads; + unsigned long writes; + unsigned long hits; + int fixed_size; + int max_hash; + struct CACHED_GENERIC entry[0]; +} ; -typedef struct { - const DISC_INTERFACE* disc; - sec_t endOfPartition; - unsigned int numberOfPages; - unsigned int sectorsPerPage; - NTFS_CACHE_ENTRY* cacheEntries; -} NTFS_CACHE; + /* cast to generic, avoiding gcc warnings */ +#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr)) -/* -Read data from a sector in the NTFS_CACHE -If the sector is not in the NTFS_CACHE, it will be swapped in -offset is the position to start reading from -size is the amount of data to read -Precondition: offset + size <= BYTES_PER_READ -*/ -//bool _NTFS_cache_readPartialSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector, unsigned int offset, size_t size); +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, + cache_compare compare); +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare); +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare, int flags); +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags); -//bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* NTFS_CACHE, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); +void ntfs_create_lru_caches(ntfs_volume *vol); +void ntfs_free_lru_caches(ntfs_volume *vol); -/* -Write data to a sector in the NTFS_CACHE -If the sector is not in the NTFS_CACHE, it will be swapped in. -When the sector is swapped out, the data will be written to the disc -offset is the position to start writing to -size is the amount of data to write -Precondition: offset + size <= BYTES_PER_READ -*/ -//bool _NTFS_cache_writePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); - -//bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* NTFS_CACHE, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); - -/* -Write data to a sector in the NTFS_CACHE, zeroing the sector first -If the sector is not in the NTFS_CACHE, it will be swapped in. -When the sector is swapped out, the data will be written to the disc -offset is the position to start writing to -size is the amount of data to write -Precondition: offset + size <= BYTES_PER_READ -*/ -//bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); - -/* -Read several sectors from the NTFS_CACHE -*/ -bool _NTFS_cache_readSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer); - -/* -Read a full sector from the NTFS_CACHE -*/ -//static inline bool _NTFS_cache_readSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector) { -// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); -//} - -/* -Write a full sector to the NTFS_CACHE -*/ -//static inline bool _NTFS_cache_writeSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector) { -// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); -//} - -bool _NTFS_cache_writeSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer); - -/* -Write any dirty sectors back to disc and clear out the contents of the NTFS_CACHE -*/ -bool _NTFS_cache_flush (NTFS_CACHE* NTFS_CACHE); - -/* -Clear out the contents of the NTFS_CACHE without writing any dirty sectors first -*/ -void _NTFS_cache_invalidate (NTFS_CACHE* NTFS_CACHE); - -NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition); - -void _NTFS_cache_destructor (NTFS_CACHE* NTFS_CACHE); - -#endif // _CACHE_H +#endif /* _NTFS_CACHE_H_ */ diff --git a/source/libntfs/cache2.c b/source/libntfs/cache2.c new file mode 100644 index 00000000..872f1a57 --- /dev/null +++ b/source/libntfs/cache2.c @@ -0,0 +1,374 @@ +/* + cache.c + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + Copyright (c) 2010 Dimok + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "cache2.h" +#include "bit_ops.h" +#include "mem_allocate.h" + +#define CACHE_FREE UINT_MAX + +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize) { + NTFS_CACHE* cache; + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries; + + if(numberOfPages==0 || sectorsPerPage==0) return NULL; + + if (numberOfPages < 4) { + numberOfPages = 4; + } + + if (sectorsPerPage < 32) { + sectorsPerPage = 32; + } + + cache = (NTFS_CACHE*) ntfs_alloc (sizeof(NTFS_CACHE)); + if (cache == NULL) { + return NULL; + } + + cache->disc = discInterface; + cache->endOfPartition = endOfPartition; + cache->numberOfPages = numberOfPages; + cache->sectorsPerPage = sectorsPerPage; + cache->sectorSize = sectorSize; + + + cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc ( sizeof(NTFS_CACHE_ENTRY) * numberOfPages); + if (cacheEntries == NULL) { + ntfs_free (cache); + return NULL; + } + + for (i = 0; i < numberOfPages; i++) { + cacheEntries[i].sector = CACHE_FREE; + cacheEntries[i].count = 0; + cacheEntries[i].last_access = 0; + cacheEntries[i].dirty = false; + cacheEntries[i].cache = (uint8_t*) ntfs_align ( sectorsPerPage * cache->sectorSize ); + } + + cache->cacheEntries = cacheEntries; + + return cache; +} + +void _NTFS_cache_destructor (NTFS_CACHE* cache) { + unsigned int i; + + if(cache==NULL) return; + + // Clear out cache before destroying it + _NTFS_cache_flush(cache); + + // Free memory in reverse allocation order + for (i = 0; i < cache->numberOfPages; i++) { + ntfs_free (cache->cacheEntries[i].cache); + } + ntfs_free (cache->cacheEntries); + ntfs_free (cache); +} + +static u32 accessCounter = 0; + +static u32 accessTime(){ + accessCounter++; + return accessCounter; +} + +static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache,sec_t sector) +{ + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + unsigned int sectorsPerPage = cache->sectorsPerPage; + + bool foundFree = false; + unsigned int oldUsed = 0; + unsigned int oldAccess = UINT_MAX; + + for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { + cacheEntries[i].last_access = accessTime(); + return &(cacheEntries[i]); + } + + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; + cacheEntries[oldUsed].dirty = false; + } + sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size + sec_t next_page = sector + sectorsPerPage; + if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; + + if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; + + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = next_page-sector; + cacheEntries[oldUsed].last_access = accessTime(); + + return &(cacheEntries[oldUsed]); +} + +static NTFS_CACHE_ENTRY* _NTFS_cache_findPage(NTFS_CACHE *cache, sec_t sector, sec_t count) { + + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + NTFS_CACHE_ENTRY *entry = NULL; + sec_t lowest = UINT_MAX; + + for(i=0;i cacheEntries[i].sector) { + intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; + } else { + intersect = cacheEntries[i].sector - sector < count; + } + + if ( intersect && (cacheEntries[i].sector < lowest)) { + lowest = cacheEntries[i].sector; + entry = &cacheEntries[i]; + } + } + } + + return entry; +} + +bool _NTFS_cache_readSectors(NTFS_CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) +{ + sec_t sec; + sec_t secs_to_read; + NTFS_CACHE_ENTRY *entry; + uint8_t *dest = buffer; + + while(numSectors>0) { + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_read = entry->count - sec; + if(secs_to_read>numSectors) secs_to_read = numSectors; + + memcpy(dest,entry->cache + (sec*cache->sectorSize),(secs_to_read*cache->sectorSize)); + + dest += (secs_to_read*cache->sectorSize); + sector += secs_to_read; + numSectors -= secs_to_read; + } + + return true; +} + +/* +Reads some data from a cache page, determined by the sector number +*/ + +bool _NTFS_cache_readPartialSector (NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->sectorSize) + offset),size); + + return true; +} + +bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { + uint8_t buf[4]; + if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; + + switch(num_bytes) { + case 1: *value = buf[0]; break; + case 2: *value = u8array_to_u16(buf,0); break; + case 4: *value = u8array_to_u32(buf,0); break; + default: return false; + } + return true; +} + +/* +Writes some data to a cache page, making sure it is loaded into memory first. +*/ + +bool _NTFS_cache_writePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { + uint8_t buf[4] = {0, 0, 0, 0}; + + switch(size) { + case 1: buf[0] = value; break; + case 2: u16_to_u8array(buf, 0, value); break; + case 4: u32_to_u8array(buf, 0, value); break; + default: return false; + } + + return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size); +} + +/* +Writes some data to a cache page, zeroing out the page first +*/ + +bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->sectorSize),0,cache->sectorSize); + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _NTFS_cache_writeSectors (NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) +{ + sec_t sec; + sec_t secs_to_write; + NTFS_CACHE_ENTRY* entry; + const uint8_t *src = buffer; + + while(numSectors>0) + { + entry = _NTFS_cache_findPage(cache,sector,numSectors); + + if(entry!=NULL) { + + if ( entry->sector > sector) { + + secs_to_write = entry->sector - sector; + + cache->disc->writeSectors(sector,secs_to_write,src); + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + } + + sec = sector - entry->sector; + secs_to_write = entry->count - sec; + + if(secs_to_write>numSectors) secs_to_write = numSectors; + + memcpy(entry->cache + (sec*cache->sectorSize),src,(secs_to_write*cache->sectorSize)); + + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + + entry->dirty = true; + + } else { + cache->disc->writeSectors(sector,numSectors,src); + numSectors=0; + } + } + return true; +} + +/* +Flushes all dirty pages to disc, clearing the dirty flag. +*/ +bool _NTFS_cache_flush (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) return true; + + for (i = 0; i < cache->numberOfPages; i++) { + if (cache->cacheEntries[i].dirty) { + if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { + return false; + } + } + cache->cacheEntries[i].dirty = false; + } + + return true; +} + +void _NTFS_cache_invalidate (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) + return; + + _NTFS_cache_flush(cache); + for (i = 0; i < cache->numberOfPages; i++) { + cache->cacheEntries[i].sector = CACHE_FREE; + cache->cacheEntries[i].last_access = 0; + cache->cacheEntries[i].count = 0; + cache->cacheEntries[i].dirty = false; + } +} diff --git a/source/libntfs/cache2.h b/source/libntfs/cache2.h new file mode 100644 index 00000000..21daca7c --- /dev/null +++ b/source/libntfs/cache2.h @@ -0,0 +1,135 @@ +/* + NTFS_CACHE.h + The NTFS_CACHE is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This NTFS_CACHE implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the NTFS_CACHE. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _CACHE2_H +#define _CACHE2_H + +//#include "common.h" +//#include "disc.h" + +#include +#include +#include +#include +#include + +typedef struct { + sec_t sector; + unsigned int count; + u64 last_access; + bool dirty; + u8* cache; +} NTFS_CACHE_ENTRY; + +typedef struct { + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + sec_t sectorSize; + NTFS_CACHE_ENTRY* cacheEntries; +} NTFS_CACHE; + +/* +Read data from a sector in the NTFS_CACHE +If the sector is not in the NTFS_CACHE, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_readPartialSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector, unsigned int offset, size_t size); + +//bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* NTFS_CACHE, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the NTFS_CACHE +If the sector is not in the NTFS_CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_writePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +//bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* NTFS_CACHE, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the NTFS_CACHE, zeroing the sector first +If the sector is not in the NTFS_CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +/* +Read several sectors from the NTFS_CACHE +*/ +bool _NTFS_cache_readSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the NTFS_CACHE +*/ +//static inline bool _NTFS_cache_readSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector) { +// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +//} + +/* +Write a full sector to the NTFS_CACHE +*/ +//static inline bool _NTFS_cache_writeSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector) { +// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +//} + +bool _NTFS_cache_writeSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the NTFS_CACHE +*/ +bool _NTFS_cache_flush (NTFS_CACHE* NTFS_CACHE); + +/* +Clear out the contents of the NTFS_CACHE without writing any dirty sectors first +*/ +void _NTFS_cache_invalidate (NTFS_CACHE* NTFS_CACHE); + +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize); + +void _NTFS_cache_destructor (NTFS_CACHE* NTFS_CACHE); + +#endif // _CACHE_H + diff --git a/source/libntfs/collate.c b/source/libntfs/collate.c index 20e5db1e..5f7a015a 100644 --- a/source/libntfs/collate.c +++ b/source/libntfs/collate.c @@ -23,32 +23,23 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif - +#ifdef HAVE_STDLIB_H +#include +#endif #ifdef HAVE_STRING_H #include #endif +#ifdef HAVE_ERRNO_H +#include +#endif +#include "attrib.h" +#include "index.h" #include "collate.h" #include "debug.h" #include "unistr.h" #include "logging.h" -BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr) -{ - /* - * FIXME: At the moment we only support COLLATION_BINARY, - * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return false - * for everything else. - * JPA added COLLATION_NTOFS_SECURITY_HASH - */ - if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG - && cr != COLLATION_FILE_NAME - && cr != COLLATION_NTOFS_SECURITY_HASH - && cr != COLLATION_NTOFS_ULONGS) - return FALSE; - return TRUE; -} - /** * ntfs_collate_binary - Which of two binary objects should be listed first * @vol: unused @@ -177,15 +168,15 @@ static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unus { int rc; u32 d1, d2; - const u32 *p1, *p2; + const le32 *p1, *p2; ntfs_log_trace("Entering.\n"); if (data1_len != data2_len || data1_len != 8) { ntfs_log_error("data1_len or/and data2_len not equal to 8.\n"); return NTFS_COLLATION_ERROR; } - p1 = (const u32*)data1; - p2 = (const u32*)data2; + p1 = (const le32*)data1; + p2 = (const le32*)data2; d1 = le32_to_cpup(p1); d2 = le32_to_cpup(p2); if (d1 < d2) @@ -228,89 +219,53 @@ static int ntfs_collate_file_name(ntfs_volume *vol, const void *data1, const int data1_len __attribute__((unused)), const void *data2, const int data2_len __attribute__((unused))) { + const FILE_NAME_ATTR *file_name_attr1; + const FILE_NAME_ATTR *file_name_attr2; int rc; ntfs_log_trace("Entering.\n"); - rc = ntfs_file_values_compare(data1, data2, NTFS_COLLATION_ERROR, - IGNORE_CASE, vol->upcase, vol->upcase_len); - if (!rc) - rc = ntfs_file_values_compare(data1, data2, - NTFS_COLLATION_ERROR, CASE_SENSITIVE, - vol->upcase, vol->upcase_len); + file_name_attr1 = (const FILE_NAME_ATTR*)data1; + file_name_attr2 = (const FILE_NAME_ATTR*)data2; + rc = ntfs_names_full_collate( + (ntfschar*)&file_name_attr1->file_name, + file_name_attr1->file_name_length, + (ntfschar*)&file_name_attr2->file_name, + file_name_attr2->file_name_length, + CASE_SENSITIVE, vol->upcase, vol->upcase_len); ntfs_log_trace("Done, returning %i.\n", rc); return rc; } -typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, const int, - const void *, const int); - -static ntfs_collate_func_t ntfs_do_collate0x0[3] = { - ntfs_collate_binary, - ntfs_collate_file_name, - NULL/*ntfs_collate_unicode_string*/, -}; - -static ntfs_collate_func_t ntfs_do_collate0x1[4] = { - ntfs_collate_ntofs_ulong, - NULL/*ntfs_collate_ntofs_sid*/, - ntfs_collate_ntofs_security_hash, - ntfs_collate_ntofs_ulongs -}; - -/** - * ntfs_collate - collate two data items using a specified collation rule - * @vol: ntfs volume to which the data items belong - * @cr: collation rule to use when comparing the items - * @data1: first data item to collate - * @data1_len: length in bytes of @data1 - * @data2: second data item to collate - * @data2_len: length in bytes of @data2 +/* + * Get a pointer to appropriate collation function. * - * Collate the two data items @data1 and @data2 using the collation rule @cr - * and return -1, 0, or 1 if @data1 is found, respectively, to collate before, - * to match, or to collate after @data2. - * - * For speed we use the collation rule @cr as an index into two tables of - * function pointers to call the appropriate collation function. - * - * Return NTFS_COLLATION_ERROR if error occurred. + * Returns NULL if the needed function is not implemented */ -int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, - const void *data1, const int data1_len, - const void *data2, const int data2_len) -{ - int i; - ntfs_log_trace("Entering.\n"); - if (!vol || !data1 || !data2 || data1_len < 0 || data2_len < 0) { - ntfs_log_error("Invalid arguments passed.\n"); - return NTFS_COLLATION_ERROR; +COLLATE ntfs_get_collate_function(COLLATION_RULES cr) +{ + COLLATE collate; + + switch (cr) { + case COLLATION_BINARY : + collate = ntfs_collate_binary; + break; + case COLLATION_FILE_NAME : + collate = ntfs_collate_file_name; + break; + case COLLATION_NTOFS_SECURITY_HASH : + collate = ntfs_collate_ntofs_security_hash; + break; + case COLLATION_NTOFS_ULONG : + collate = ntfs_collate_ntofs_ulong; + break; + case COLLATION_NTOFS_ULONGS : + collate = ntfs_collate_ntofs_ulongs; + break; + default : + errno = EOPNOTSUPP; + collate = (COLLATE)NULL; + break; } - /* - * FIXME: At the moment we only support COLLATION_BINARY, - * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return error - * for everything else. - * JPA added COLLATION_NTOFS_SECURITY_HASH - * JPA added COLLATION_NTOFS_ULONGS - */ - if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG - && cr != COLLATION_FILE_NAME - && cr != COLLATION_NTOFS_SECURITY_HASH - && cr != COLLATION_NTOFS_ULONGS) - goto err; - i = le32_to_cpu(cr); - if (i < 0) - goto err; - if (i <= 0x02) - return ntfs_do_collate0x0[i](vol, data1, data1_len, - data2, data2_len); - if (i < 0x10) - goto err; - i -= 0x10; - if (i <= 3) - return ntfs_do_collate0x1[i](vol, data1, data1_len, - data2, data2_len); -err: - ntfs_log_debug("Unknown collation rule.\n"); - return NTFS_COLLATION_ERROR; + return (collate); } diff --git a/source/libntfs/collate.h b/source/libntfs/collate.h index 9c0ec9bc..fe383835 100644 --- a/source/libntfs/collate.h +++ b/source/libntfs/collate.h @@ -29,9 +29,6 @@ #define NTFS_COLLATION_ERROR -2 -extern BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr); -extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, - const void *data1, const int data1_len, - const void *data2, const int data2_len); +extern COLLATE ntfs_get_collate_function(COLLATION_RULES); #endif /* _NTFS_COLLATE_H */ diff --git a/source/libntfs/compat.c b/source/libntfs/compat.c index e9b3d5df..63114a48 100644 --- a/source/libntfs/compat.c +++ b/source/libntfs/compat.c @@ -77,7 +77,7 @@ int ffs(int x) #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; -static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.7 2009/03/27 09:09:59 jpandre Exp $"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; #endif /* LIBC_SCCS and not lint */ /* @@ -164,7 +164,7 @@ int daemon(int nochdir, int noclose) { #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93"; -static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.7 2009/03/27 09:09:59 jpandre Exp $"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; #endif /* LIBC_SCCS and not lint */ /* diff --git a/source/libntfs/compress.c b/source/libntfs/compress.c index 4db1826d..fbd30ba9 100644 --- a/source/libntfs/compress.c +++ b/source/libntfs/compress.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2006 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2009 Jean-Pierre Andre + * Copyright (c) 2009-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -118,7 +118,7 @@ static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, BOOL done; const unsigned char *key; int c; - unsigned int mxi; + unsigned long mxi; unsigned int mxl; mxl = (1 << (16 - pctx->nbt)) + 2; @@ -147,16 +147,21 @@ static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, } } if (!done) { - register unsigned int i; + register unsigned long i; register const unsigned char *p1,*p2; i = 1; - p1 = key; - p2 = &pctx->inbuf[pp]; mxi = NTFS_SB_SIZE - r; - do { - } while ((p1[i] == p2[i]) && (++i < mxi)); - less = (i < mxi) && (p1[i] < p2[i]); + if (mxi < 2) + less = FALSE; + else { + p1 = key; + p2 = &pctx->inbuf[pp]; + /* this loop has a significant impact on performances */ + do { + } while ((p1[i] == p2[i]) && (++i < mxi)); + less = (i < mxi) && (p1[i] < p2[i]); + } if (i >= THRESHOLD) { if (i > pctx->match_length) { pctx->match_position = @@ -174,7 +179,8 @@ static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, pctx->rson[i] = r; else pctx->lson[i] = r; - pctx->dad[pp] = NIL; /* remove pp */ + /* remove pp */ + pctx->dad[pp] = NIL; done = TRUE; pctx->match_length = mxl; } @@ -196,7 +202,8 @@ static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, * or zero if there was a bug */ -static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, unsigned int rr, int dd) +static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, + unsigned int rr, int dd) { unsigned int bestlen = 0; @@ -214,7 +221,8 @@ static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, unsigned int r goto bug; } if (((rr + bestlen) < NTFS_SB_SIZE)) { - while ((unsigned int)(1 << pctx->nbt) <= (rr - 1)) + while ((unsigned int)(1 << pctx->nbt) + <= (rr - 1)) pctx->nbt++; ntfs_new_node(pctx,rr); if (pctx->match_length > bestlen) @@ -247,7 +255,8 @@ bug : * or zero if there was an error */ -static unsigned int ntfs_compress_block(const char *inbuf, unsigned int size, char *outbuf) +static unsigned int ntfs_compress_block(const char *inbuf, + unsigned int size, char *outbuf) { struct COMPRESS_CONTEXT *pctx; char *ptag; @@ -284,7 +293,8 @@ static unsigned int ntfs_compress_block(const char *inbuf, unsigned int size, ch outbuf[xout++] = inbuf[rr]; ntag++; } else { - while ((unsigned int)(1 << pctx->nbt) <= (rr - 1)) + while ((unsigned int)(1 << pctx->nbt) + <= (rr - 1)) pctx->nbt++; q = (pctx->match_position << (16 - pctx->nbt)) + pctx->match_length - THRESHOLD; @@ -732,7 +742,15 @@ do_next_cb: na->data_size = na->initialized_size = na->allocated_size; do { br = ntfs_attr_pread(na, ofs, to_read, b); - if (br < 0) { + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read an" + " uncompressed cluster," + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)ofs); + errno = EIO; + } err = errno; na->data_size = tdata_size; na->initialized_size = tinitialized_size; @@ -783,7 +801,15 @@ do_next_cb: br = ntfs_attr_pread(na, (vcn << vol->cluster_size_bits) + (cb_pos - cb), to_read, cb_pos); - if (br < 0) { + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read a" + " compressed cluster, " + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)(vcn << vol->cluster_size_bits)); + errno = EIO; + } err = errno; na->data_size = tdata_size; na->initialized_size = tinitialized_size; @@ -881,11 +907,11 @@ static u32 read_clusters(ntfs_volume *vol, const runlist_element *rl, * Returns the amount of data written */ -static int write_clusters(ntfs_volume *vol, const runlist_element *rl, - s64 offs, int to_write, const char *outbuf) +static s32 write_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, s32 to_write, const char *outbuf) { - int count; - int put, xput; + s32 count; + s32 put, xput; s64 xpos; BOOL first; const char *xoutbuf; @@ -926,17 +952,17 @@ static int write_clusters(ntfs_volume *vol, const runlist_element *rl, * or -2 if there were an irrecoverable error (errno set) */ -static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl, - s64 offs, unsigned int insz, const char *inbuf) +static s32 ntfs_comp_set(ntfs_attr *na, runlist_element *rl, + s64 offs, u32 insz, const char *inbuf) { ntfs_volume *vol; char *outbuf; char *pbuf; - unsigned int compsz; - int written; - int rounded; + u32 compsz; + s32 written; + s32 rounded; unsigned int clsz; - unsigned int p; + u32 p; unsigned int sz; unsigned int bsz; BOOL fail; @@ -1002,7 +1028,10 @@ static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl, rounded = ((compsz - 1) | (clsz - 1)) + 1; written = write_clusters(vol, rl, offs, rounded, outbuf); if (written != rounded) { -// previously written text has been spoilt, should return a specific error + /* + * TODO : previously written text has been + * spoilt, should return a specific error + */ ntfs_log_error("error writing compressed data\n"); errno = EIO; written = -2; @@ -1016,25 +1045,297 @@ static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl, } /* - * Free unneeded clusters after compression + * Check the validity of a compressed runlist + * The check starts at the beginning of current run and ends + * at the end of runlist + * errno is set if the runlist is not valid + */ + +static BOOL valid_compressed_run(ntfs_attr *na, runlist_element *rl, + BOOL fullcheck, const char *text) +{ + runlist_element *xrl; + const char *err; + BOOL ok = TRUE; + + xrl = rl; + while (xrl->vcn & (na->compression_block_clusters - 1)) + xrl--; + err = (const char*)NULL; + while (xrl->length) { + if ((xrl->vcn + xrl->length) != xrl[1].vcn) + err = "Runs not adjacent"; + if (xrl->lcn == LCN_HOLE) { + if ((xrl->vcn + xrl->length) + & (na->compression_block_clusters - 1)) { + err = "Invalid hole"; + } + if (fullcheck && (xrl[1].lcn == LCN_HOLE)) { + err = "Adjacent holes"; + } + } + if (err) { + ntfs_log_error("%s at %s index %ld inode %lld\n", + err, text, (long)(xrl - na->rl), + (long long)na->ni->mft_no); + errno = EIO; + ok = FALSE; + err = (const char*)NULL; + } + xrl++; + } + return (ok); +} + +/* + * Free unneeded clusters after overwriting compressed data * - * This generally requires an empty slot at the end of runlist, + * This generally requires one or two empty slots at the end of runlist, * but we do not want to reallocate the runlist here because * there are many pointers to it. - * So the empty slot has to be reserved beforehand + * So the empty slots have to be reserved beforehand + * + * Returns zero unless some error occurred (described by errno) + * + * +======= start of block =====+ + * 0 |A chunk may overflow | <-- rl usedcnt : A + B + * |A on previous block | then B + * |A | + * +-- end of allocated chunk --+ freelength : C + * |B | (incl overflow) + * +== end of compressed data ==+ + * |C | <-- freerl freecnt : C + D + * |C chunk may overflow | + * |C on next block | + * +-- end of allocated chunk --+ + * |D | + * |D chunk may overflow | + * 15 |D on next block | + * +======== end of block ======+ + * + */ + +static int ntfs_compress_overwr_free(ntfs_attr *na, runlist_element *rl, + s32 usedcnt, s32 freecnt, VCN *update_from) +{ + BOOL beginhole; + BOOL mergeholes; + s32 oldlength; + s32 freelength; + s64 freelcn; + s64 freevcn; + runlist_element *freerl; + ntfs_volume *vol; + s32 carry; + int res; + + vol = na->ni->vol; + res = 0; + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + /* can merge with hole before ? */ + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + /* truncate current run, carry to subsequent hole */ + carry = freelength; + oldlength = rl->length; + if (mergeholes) { + /* merging with a hole before */ + freerl = rl; + } else { + rl->length -= freelength; /* warning : can be zero */ + freerl = ++rl; + } + if (!mergeholes && (usedcnt || beginhole)) { + s32 freed; + runlist_element *frl; + runlist_element *erl; + int holes = 0; + BOOL threeparts; + + /* free the unneeded clusters from initial run, then freerl */ + threeparts = (freelength > freecnt); + freed = 0; + frl = freerl; + if (freelength) { + res = ntfs_cluster_free_basic(vol,freelcn, + (threeparts ? freecnt : freelength)); + if (!res) + freed += (threeparts ? freecnt : freelength); + if (!usedcnt) { + holes++; + freerl--; + freerl->length += (threeparts + ? freecnt : freelength); + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + } + } + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + if (!res) { + freed += frl->length; + frl->lcn = LCN_HOLE; + frl->length += carry; + carry = 0; + holes++; + } + } else { + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + if (!res) { + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + frl++; + } + na->compressed_size -= freed << vol->cluster_size_bits; + switch (holes) { + case 0 : + /* there are no hole, must insert one */ + /* space for hole has been prereserved */ + if (freerl->lcn == LCN_HOLE) { + if (threeparts) { + erl = freerl; + while (erl->length) + erl++; + do { + erl[2] = *erl; + } while (erl-- != freerl); + + freerl[1].length = freelength - freecnt; + freerl->length = freecnt; + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[2].lcn = LCN_HOLE; + freerl[2].vcn = freerl[1].vcn + + freerl[1].length; + freerl->vcn = freevcn; + } else { + freerl->vcn = freevcn; + freerl->length += freelength; + } + } else { + erl = freerl; + while (erl->length) + erl++; + if (threeparts) { + do { + erl[2] = *erl; + } while (erl-- != freerl); + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = oldlength - usedcnt - freecnt; + } else { + do { + erl[1] = *erl; + } while (erl-- != freerl); + } + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + break; + case 1 : + /* there is a single hole, may have to merge */ + freerl->vcn = freevcn; + if (freerl[1].lcn == LCN_HOLE) { + freerl->length += freerl[1].length; + erl = freerl; + do { + erl++; + *erl = erl[1]; + } while (erl->length); + } + break; + default : + /* there were several holes, must merge them */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + if (freerl[holes].lcn == LCN_HOLE) { + freerl->length += freerl[holes].length; + holes++; + } + erl = freerl; + do { + erl++; + *erl = erl[holes - 1]; + } while (erl->length); + break; + } + } else { + s32 freed; + runlist_element *frl; + runlist_element *xrl; + + freed = 0; + frl = freerl--; + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + freerl->length += frl->length; + freed += frl->length; + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + frl++; + } else { + freerl->length += freecnt - freed; + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + /* remove unneded runlist entries */ + xrl = freerl; + /* group with next run if also a hole */ + if (frl->length && (frl->lcn == LCN_HOLE)) { + xrl->length += frl->length; + frl++; + } + while (frl->length) { + *++xrl = *frl++; + } + *++xrl = *frl; /* terminator */ + na->compressed_size -= freed << vol->cluster_size_bits; + } + return (res); +} + + +/* + * Free unneeded clusters after compression + * + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand * * Returns zero unless some error occurred (described by errno) */ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, - s64 used, s64 reserved) + s64 used, s64 reserved, BOOL appending, + VCN *update_from) { - int freecnt; - int usedcnt; + s32 freecnt; + s32 usedcnt; int res; s64 freelcn; s64 freevcn; - int freelength; + s32 freelength; BOOL mergeholes; BOOL beginhole; ntfs_volume *vol; @@ -1044,9 +1345,11 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, vol = na->ni->vol; freecnt = (reserved - used) >> vol->cluster_size_bits; usedcnt = (reserved >> vol->cluster_size_bits) - freecnt; + if (rl->vcn < *update_from) + *update_from = rl->vcn; /* skip entries fully used, if any */ while (rl->length && (rl->length < usedcnt)) { - usedcnt -= rl->length; + usedcnt -= rl->length; /* must be > 0 */ rl++; } if (rl->length) { @@ -1056,62 +1359,93 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, * The required entry has been prereserved when * mapping the runlist. */ + /* get the free part in initial run */ freelcn = rl->lcn + usedcnt; freevcn = rl->vcn + usedcnt; - freelength = rl->length - usedcnt; /* new count of allocated clusters */ - rl->length = usedcnt; /* warning : can be zero */ if (!((freevcn + freecnt) & (na->compression_block_clusters - 1))) { - beginhole = !usedcnt && !rl->vcn; - mergeholes = !usedcnt - && rl[0].vcn - && (rl[-1].lcn == LCN_HOLE); - if (mergeholes) { - freerl = rl; - freerl->length = freecnt; - } else - freerl = ++rl; - if ((freelength > 0) - && !mergeholes - && (usedcnt || beginhole)) { + if (!appending) + res = ntfs_compress_overwr_free(na,rl, + usedcnt,freecnt,update_from); + else { + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + if (mergeholes) { + s32 carry; + + /* shorten the runs which have free space */ + carry = freecnt; + freerl = rl; + while (freerl->length < carry) { + carry -= freerl->length; + freerl++; + } + freerl->length = carry; + freerl = rl; + } else { + rl->length = usedcnt; /* can be zero ? */ + freerl = ++rl; + } + if ((freelength > 0) + && !mergeholes + && (usedcnt || beginhole)) { /* * move the unused part to the end. Doing so, * the vcn will be out of order. This does * not harm, the vcn are meaningless now, and * only the lcn are meaningful for freeing. */ - /* locate current end */ - while (rl->length) - rl++; - /* new terminator relocated */ - rl[1].vcn = rl->vcn; - rl[1].lcn = LCN_ENOENT; - rl[1].length = 0; - /* hole, currently allocated */ - rl->vcn = freevcn; - rl->lcn = freelcn; - rl->length = freelength; - } - /* free the hole */ - res = ntfs_cluster_free_from_rl(vol,freerl); - if (!res) { - if (mergeholes) { - /* merge with adjacent hole */ - freerl--; - freerl->length += freecnt; + /* locate current end */ + while (rl->length) + rl++; + /* new terminator relocated */ + rl[1].vcn = rl->vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + /* hole, currently allocated */ + rl->vcn = freevcn; + rl->lcn = freelcn; + rl->length = freelength; } else { - if (beginhole) + /* why is this different from the begin hole case ? */ + if ((freelength > 0) + && !mergeholes + && !usedcnt) { freerl--; - /* mark hole as free */ - freerl->lcn = LCN_HOLE; - freerl->vcn = freevcn; - freerl->length = freecnt; + freerl->length = freelength; + if (freerl->vcn < *update_from) + *update_from + = freerl->vcn; + } + } + /* free the hole */ + res = ntfs_cluster_free_from_rl(vol,freerl); + if (!res) { + na->compressed_size -= freecnt + << vol->cluster_size_bits; + if (mergeholes) { + /* merge with adjacent hole */ + freerl--; + freerl->length += freecnt; + } else { + if (beginhole) + freerl--; + /* mark hole as free */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + /* and set up the new end */ + freerl[1].lcn = LCN_ENOENT; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = 0; } - /* and set up the new end */ - freerl[1].lcn = LCN_ENOENT; - freerl[1].vcn = freevcn + freecnt; - freerl[1].length = 0; } } else { ntfs_log_error("Bad end of a compression block set\n"); @@ -1130,7 +1464,7 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, */ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, - s64 offs, u32 compsz, int pos, + s64 offs, u32 compsz, s32 pos, BOOL appending, char *outbuf, s64 to_write, const void *b) { int fail = 1; @@ -1147,7 +1481,10 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, compbuf = (char*)ntfs_malloc(compsz); if (compbuf) { /* must align to full block for decompression */ - decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1; + if (appending) + decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1; + else + decompsz = na->compression_block_size; got = read_clusters(na->ni->vol, rl, offs, compsz, compbuf); if ((got == compsz) @@ -1171,7 +1508,8 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, */ static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, - const char *outbuf, int count, BOOL compress) + const char *outbuf, s32 count, BOOL compress, + BOOL appending, VCN *update_from) { int rounded; int written; @@ -1183,9 +1521,11 @@ static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, compress = FALSE; if ((written >= 0) && ntfs_compress_free(na,rl,offs + written, - offs + na->compression_block_size)) + offs + na->compression_block_size, appending, + update_from)) written = -1; - } + } else + written = 0; if (!compress) { clsz = 1 << na->ni->vol->cluster_size_bits; rounded = ((count - 1) | (clsz - 1)) + 1; @@ -1204,32 +1544,55 @@ static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, * it has to be reserved beforehand. * * Returns the size of uncompressed data written, - * or zero if an error occurred. + * or negative if an error occurred. * When the returned size is less than requested, new clusters have * to be allocated before the function is called again. */ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, s64 offs, s64 to_write, s64 rounded, - const void *b, int compressed_part) + const void *b, int compressed_part, + VCN *update_from) { ntfs_volume *vol; runlist_element *brl; /* entry containing the beginning of block */ int compression_length; s64 written; s64 to_read; + s64 to_flush; s64 roffs; s64 got; s64 start_vcn; s64 nextblock; + s64 endwrite; u32 compsz; char *inbuf; char *outbuf; BOOL fail; BOOL done; BOOL compress; + BOOL appending; - written = 0; /* default return */ + if (!valid_compressed_run(na,wrl,FALSE,"begin compressed write")) { + return (-1); + } + if ((*update_from < 0) + || (compressed_part < 0) + || (compressed_part > (int)na->compression_block_clusters)) { + ntfs_log_error("Bad update vcn or compressed_part %d for compressed write\n", + compressed_part); + errno = EIO; + return (-1); + } + /* make sure there are two unused entries in runlist */ + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed write\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + written = -1; /* default return */ vol = na->ni->vol; compression_length = na->compression_block_clusters; compress = FALSE; @@ -1243,8 +1606,10 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, */ nextblock = ((offs + (wrl->vcn << vol->cluster_size_bits)) | (na->compression_block_size - 1)) + 1; - if ((offs + to_write + (wrl->vcn << vol->cluster_size_bits)) - >= nextblock) { + /* determine whether we are appending to file */ + endwrite = offs + to_write + (wrl->vcn << vol->cluster_size_bits); + appending = endwrite >= na->initialized_size; + if (endwrite >= nextblock) { /* it is time to compress */ compress = TRUE; /* only process what we can */ @@ -1265,6 +1630,8 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, /* find the beginning of block */ start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; while (brl->vcn && (brl->vcn > start_vcn)) { /* jumping back a hole means big trouble */ if (brl->lcn == (LCN)LCN_HOLE) { @@ -1284,14 +1651,24 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, * Decompress the data and append */ compsz = compressed_part << vol->cluster_size_bits; -// improve the needed size outbuf = (char*)ntfs_malloc(na->compression_block_size); if (outbuf) { - to_read = offs - roffs; + if (appending) { + to_read = offs - roffs; + to_flush = to_read + to_write; + } else { + to_read = na->data_size + - (brl->vcn << vol->cluster_size_bits); + if (to_read > na->compression_block_size) + to_read = na->compression_block_size; + to_flush = to_read; + } if (!ntfs_read_append(na, brl, roffs, compsz, - to_read, outbuf, to_write, b)) { + (s32)(offs - roffs), appending, + outbuf, to_write, b)) { written = ntfs_flush(na, brl, roffs, - outbuf, to_read + to_write, compress); + outbuf, to_flush, compress, appending, + update_from); if (written >= 0) { written = to_write; done = TRUE; @@ -1302,9 +1679,9 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, } else { if (compress && !fail) { /* - * we are filling up a block, read the full set of blocks - * and compress it - */ + * we are filling up a block, read the full set + * of blocks and compress it + */ inbuf = (char*)ntfs_malloc(na->compression_block_size); if (inbuf) { to_read = offs - roffs; @@ -1326,7 +1703,8 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, && !ntfs_compress_free(na,brl, written + roffs, na->compression_block_size - + roffs)) { + + roffs, + appending, update_from)) { done = TRUE; written = to_write; } @@ -1354,18 +1732,22 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, } } } + if ((written >= 0) + && !valid_compressed_run(na,wrl,TRUE,"end compressed write")) + written = -1; return (written); } /* * Close a file written compressed. * This compresses the last partial compression block of the file. - * An empty runlist slot has to be reserved beforehand. + * Two empty runlist slots have to be reserved beforehand. * * Returns zero if closing is successful. */ -int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) +int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs, + VCN *update_from) { ntfs_volume *vol; runlist_element *brl; /* entry containing the beginning of block */ @@ -1379,6 +1761,18 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) BOOL fail; BOOL done; + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed close\n"); + errno = EIO; + return (-1); + } + if (*update_from < 0) { + ntfs_log_error("Bad update vcn for compressed close\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; vol = na->ni->vol; compression_length = na->compression_block_clusters; done = FALSE; @@ -1390,7 +1784,10 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) if (inbuf) { start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) & -compression_length; - to_read = offs + ((wrl->vcn - start_vcn) << vol->cluster_size_bits); + if (start_vcn < *update_from) + *update_from = start_vcn; + to_read = offs + ((wrl->vcn - start_vcn) + << vol->cluster_size_bits); brl = wrl; fail = FALSE; while (brl->vcn && (brl->vcn > start_vcn)) { @@ -1403,7 +1800,8 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) } if (!fail) { /* roffs can be an offset from another uncomp block */ - roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits; + roffs = (start_vcn - brl->vcn) + << vol->cluster_size_bits; if (to_read) { got = read_clusters(vol, brl, roffs, to_read, inbuf); @@ -1414,7 +1812,8 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) /* free the unused clusters */ && !ntfs_compress_free(na,brl, written + roffs, - na->compression_block_size + roffs)) { + na->compression_block_size + roffs, + TRUE, update_from)) { done = TRUE; } else /* if compression failed, leave uncompressed */ @@ -1426,5 +1825,7 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) free(inbuf); } } + if (done && !valid_compressed_run(na,wrl,TRUE,"end compressed close")) + done = FALSE; return (!done); } diff --git a/source/libntfs/compress.h b/source/libntfs/compress.h index 809c3c93..c2569321 100644 --- a/source/libntfs/compress.h +++ b/source/libntfs/compress.h @@ -31,9 +31,11 @@ extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos, s64 offs, s64 to_write, s64 rounded, - const void *b, int compressed_part); + const void *b, int compressed_part, + VCN *update_from); -extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, s64 offs); +extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, + s64 offs, VCN *update_from); #endif /* defined _NTFS_COMPRESS_H */ diff --git a/source/libntfs/config.h b/source/libntfs/config.h index 67109a10..ba0168e1 100644 --- a/source/libntfs/config.h +++ b/source/libntfs/config.h @@ -1,45 +1,43 @@ -/* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ -/* Define if building universal (internal helper macro) */ -/* #undef AC_APPLE_UNIVERSAL_BUILD */ - -//#define DEBUG /* Define to 1 if debug should be enabled */ -#ifdef DEBUG -#define ENABLE_DEBUG -#define NTFS_ENABLE_LOG -#endif +#undef ENABLE_DEBUG + +/* Define to 1 if the nfconv patch should be enabled */ +#undef ENABLE_NFCONV /* Define to 1 if using internal fuse */ -/* #undef FUSE_INTERNAL */ +#undef FUSE_INTERNAL /* Define to 1 if you have the `atexit' function. */ #define HAVE_ATEXIT 1 /* Define to 1 if you have the `basename' function. */ -/* #undef HAVE_BASENAME */ +#undef HAVE_BASENAME /* Define to 1 if you have the header file. */ -/* #undef HAVE_BYTESWAP_H */ +#undef HAVE_BYTESWAP_H + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME /* Define to 1 if you have the header file. */ #define HAVE_CTYPE_H 1 /* Define to 1 if you have the `daemon' function. */ -/* #undef HAVE_DAEMON */ +#undef HAVE_DAEMON /* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ +#undef HAVE_DLFCN_H /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ -/* #undef HAVE_DOPRNT */ +#undef HAVE_DOPRNT /* Define to 1 if you have the `dup2' function. */ -/* #undef HAVE_DUP2 */ +#undef HAVE_DUP2 /* Define to 1 if you have the header file. */ -/* #undef HAVE_ENDIAN_H */ +#undef HAVE_ENDIAN_H /* Define to 1 if you have the header file. */ #define HAVE_ERRNO_H 1 @@ -48,10 +46,10 @@ #define HAVE_FCNTL_H 1 /* Define to 1 if you have the `fdatasync' function. */ -/* #undef HAVE_FDATASYNC */ +#undef HAVE_FDATASYNC /* Define to 1 if you have the header file. */ -/* #undef HAVE_FEATURES_H */ +#undef HAVE_FEATURES_H /* Define to 1 if you have the `ffs' function. */ #define HAVE_FFS 1 @@ -60,7 +58,7 @@ #define HAVE_FORK 1 /* Define to 1 if you have the `getmntent' function. */ -/* #undef HAVE_GETMNTENT */ +#undef HAVE_GETMNTENT /* Define to 1 if you have the header file. */ #define HAVE_GETOPT_H 1 @@ -68,8 +66,11 @@ /* Define to 1 if you have the `getopt_long' function. */ #define HAVE_GETOPT_LONG 1 +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + /* Define to 1 if you have the `hasmntopt' function. */ -/* #undef HAVE_HASMNTOPT */ +#undef HAVE_HASMNTOPT /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 @@ -78,19 +79,19 @@ #define HAVE_LIBGEN_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_LIBINTL_H */ +#undef HAVE_LIBINTL_H /* Define to 1 if you have the header file. */ #define HAVE_LIMITS_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_FD_H */ +#undef HAVE_LINUX_FD_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_HDREG_H */ +#undef HAVE_LINUX_HDREG_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_MAJOR_H */ +#undef HAVE_LINUX_MAJOR_H /* Define to 1 if you have the header file. */ #define HAVE_LOCALE_H 1 @@ -111,25 +112,25 @@ #define HAVE_MEMMOVE 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_MEMORY_H */ +#undef HAVE_MEMORY_H /* Define to 1 if you have the `memset' function. */ #define HAVE_MEMSET 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_MNTENT_H */ +#undef HAVE_MNTENT_H /* Define to 1 if you have the `realpath' function. */ -/* #undef HAVE_REALPATH */ +#undef HAVE_REALPATH /* Define to 1 if you have the `regcomp' function. */ -/* #undef HAVE_REGCOMP */ +#undef HAVE_REGCOMP /* Define to 1 if you have the `setlocale' function. */ #define HAVE_SETLOCALE 1 /* Define to 1 if you have the `setxattr' function. */ -/* #undef HAVE_SETXATTR */ +#undef HAVE_SETXATTR /* Define to 1 if `stat' has the bug that it succeeds when given the zero-length file name argument. */ @@ -169,7 +170,7 @@ #define HAVE_STRFTIME 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_STRINGS_H */ +#undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 @@ -187,42 +188,44 @@ #define HAVE_STRTOUL 1 /* Define to 1 if `st_atim' is member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_ATIM */ +#undef HAVE_STRUCT_STAT_ST_ATIM + +/* Define to 1 if `st_atimensec' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMENSEC /* Define to 1 if `st_atimespec' is member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */ +#undef HAVE_STRUCT_STAT_ST_ATIMESPEC /* Define to 1 if `st_blocks' is member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_BLOCKS 1 +#undef HAVE_STRUCT_STAT_ST_BLOCKS /* Define to 1 if `st_rdev' is member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_RDEV 1 +#undef HAVE_STRUCT_STAT_ST_RDEV /* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ -#define HAVE_ST_BLOCKS 1 -#define HAVE_STRUCT_STAT_ST_BLOCKS 1 +#undef HAVE_ST_BLOCKS /* Define to 1 if you have the `sysconf' function. */ -/* #undef HAVE_SYSCONF */ +#define HAVE_SYSCONF 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYSLOG_H */ +#undef HAVE_SYSLOG_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_BYTEORDER_H */ +#undef HAVE_SYS_BYTEORDER_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_ENDIAN_H */ +#undef HAVE_SYS_ENDIAN_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IOCTL_H */ +#undef HAVE_SYS_IOCTL_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_MKDEV_H */ +#undef HAVE_SYS_MKDEV_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_MOUNT_H */ +#undef HAVE_SYS_MOUNT_H /* Define to 1 if you have the header file. */ #define HAVE_SYS_PARAM_H 1 @@ -234,13 +237,13 @@ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SYSMACROS_H */ +#undef HAVE_SYS_SYSMACROS_H /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_VFS_H */ +#undef HAVE_SYS_VFS_H /* Define to 1 if you have the header file. */ #define HAVE_TIME_H 1 @@ -249,13 +252,16 @@ #define HAVE_UNISTD_H 1 /* Define to 1 if you have the `utime' function. */ -/* #undef HAVE_UTIME */ +#undef HAVE_UTIME + +/* Define to 1 if you have the `utimensat' function. */ +#undef HAVE_UTIMENSAT /* Define to 1 if you have the header file. */ #define HAVE_UTIME_H 1 /* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ -/* #undef HAVE_UTIME_NULL */ +#undef HAVE_UTIME_NULL /* Define to 1 if you have the `vprintf' function. */ #define HAVE_VPRINTF 1 @@ -264,27 +270,23 @@ #define HAVE_WCHAR_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_WINDOWS_H */ +#undef HAVE_WINDOWS_H /* Define to 1 if the system has the type `_Bool'. */ -#define HAVE__BOOL 1 +#undef HAVE__BOOL /* Don't update /etc/mtab */ -/* #undef IGNORE_MTAB */ +#undef IGNORE_MTAB /* Define to 1 if `lstat' dereferences a symlink specified with a trailing slash. */ -/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */ - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#define LT_OBJDIR ".libs/" +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK /* Define to 1 if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ +#undef NO_MINUS_C_MINUS_O /* Don't use default IO ops */ -/* #undef NO_NTFS_DEVICE_DEFAULT_IO_OPS */ +#undef NO_NTFS_DEVICE_DEFAULT_IO_OPS /* Name of package */ #define PACKAGE "ntfs-3g" @@ -296,16 +298,16 @@ #define PACKAGE_NAME "ntfs-3g" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "ntfs-3g 2009.4.4AR.16" +#define PACKAGE_STRING "ntfs-3g 2010.8.8" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "ntfs-3g" /* Define to the version of this package. */ -#define PACKAGE_VERSION "2009.4.4AR.16" +#define PACKAGE_VERSION "2010.8.8" /* POSIX ACL support */ -/* #undef POSIXACLS */ +#undef POSIXACLS /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 @@ -331,59 +333,42 @@ # define __EXTENSIONS__ 1 #endif - /* Version number of package */ -#define VERSION "2009.4.4AR.16" +#define VERSION "2010.8.8" /* Define to 1 if this is a Windows OS */ -/* #undef WINDOWS */ +#undef WINDOWS -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel). */ -#if defined AC_APPLE_UNIVERSAL_BUILD -# if defined __BIG_ENDIAN__ -# define WORDS_BIGENDIAN 1 -# endif -#else -# ifndef WORDS_BIGENDIAN -# define WORDS_BIGENDIAN 1 -# endif -#endif +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#define WORDS_BIGENDIAN 1 /* Define to 1 if your processor stores words with the least significant byte first (like Intel and VAX, unlike Motorola and SPARC). */ -/* #undef WORDS_LITTLEENDIAN */ +#undef WORDS_LITTLEENDIAN /* Number of bits in a file offset, on hosts where this is settable. */ -/* #undef _FILE_OFFSET_BITS */ +#define _FILE_OFFSET_BITS 64 + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif /* Define for large files, on AIX-style hosts. */ -/* #undef _LARGE_FILES */ - -/* Define to 1 if on MINIX. */ -/* #undef _MINIX */ - -/* Define to 2 if the system does not provide POSIX.1 features except with - this defined. */ -/* #undef _POSIX_1_SOURCE */ - -/* Define to 1 if you need to in order for `stat' and other things to work. */ -/* #undef _POSIX_SOURCE */ +#define _LARGE_FILES 1 /* Required define if using POSIX threads */ -/* #undef _REENTRANT */ - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ +#undef _REENTRANT /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus -/* #undef inline */ +#define inline __inline__ #endif /* Define to `long int' if does not define. */ -/* #undef off_t */ +#undef off_t /* Define to `unsigned int' if does not define. */ -/* #undef size_t */ +#undef size_t diff --git a/source/libntfs/device_io.h b/source/libntfs/device_io.h index b8701590..fad4d85f 100644 --- a/source/libntfs/device_io.h +++ b/source/libntfs/device_io.h @@ -28,6 +28,18 @@ #ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS +#ifndef __CYGWIN32__ + +#ifndef GEKKO +/* Not on Cygwin; use standard Unix style low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_unix_io_ops +#else +/* Wii i/o device. */ +#define ntfs_device_default_io_ops ntfs_device_gekko_io_ops +#endif + +#else /* __CYGWIN32__ */ + #ifndef HDIO_GETGEO # define HDIO_GETGEO 0x301 /** @@ -53,8 +65,10 @@ struct hd_geometry { # define BLKBSZSET 0x40041271 #endif -/* On Nintendo GameCube/Wii; use Gekko low level device operations. */ -#define ntfs_device_default_io_ops ntfs_device_gekko_io_ops +/* On Cygwin; use Win32 low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_win32_io_ops + +#endif /* __CYGWIN32__ */ /* Forward declaration. */ diff --git a/source/libntfs/dir.c b/source/libntfs/dir.c index 297f10f1..2372f3f8 100644 --- a/source/libntfs/dir.c +++ b/source/libntfs/dir.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005-2007 Yura Pakhuchiy - * Copyright (c) 2008-2009 Jean-Pierre Andre + * Copyright (c) 2008-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -44,6 +44,7 @@ #include #endif +#include "param.h" #include "types.h" #include "debug.h" #include "attrib.h" @@ -55,9 +56,11 @@ #include "ntfstime.h" #include "lcnalloc.h" #include "logging.h" +#include "cache.h" #include "misc.h" #include "security.h" #include "reparse.h" +#include "object_id.h" #ifdef HAVE_SETXATTR #include @@ -85,6 +88,29 @@ ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'), #if CACHE_INODE_SIZE +/* + * Pathname hashing + * + * Based on first char and second char (which may be '\0') + */ + +int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached) +{ + const char *path; + const unsigned char *name; + + path = (const char*)cached->variable; + if (!path) { + ntfs_log_error("Bad inode cache entry\n"); + return (-1); + } + name = (const unsigned char*)strrchr(path,'/'); + if (!name) + name = (const unsigned char*)path; + return (((name[0] << 1) + name[1] + strlen((const char*)name)) + % (2*CACHE_INODE_SIZE)); +} + /* * Pathname comparing for entering/fetching from cache */ @@ -103,21 +129,90 @@ static int inode_cache_compare(const struct CACHED_GENERIC *cached, * related to a renamed directory * inode numbers are also checked, as deleting a long name may * imply deleting a short name and conversely + * + * Only use associated with a CACHE_NOHASH flag */ static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached, const struct CACHED_GENERIC *wanted) { int len; + BOOL different; + const struct CACHED_INODE *w; + const struct CACHED_INODE *c; - len = strlen(wanted->variable); - return (!cached->variable - || ((((const struct CACHED_INODE*)wanted)->inum - != MREF(((const struct CACHED_INODE*)cached)->inum)) - && (strncmp((const char*)cached->variable, - (const char*)wanted->variable,len) - || ((((const char*)cached->variable)[len] != '\0') - && (((const char*)cached->variable)[len] != '/'))))); + w = (const struct CACHED_INODE*)wanted; + c = (const struct CACHED_INODE*)cached; + if (w->pathname) { + len = strlen(w->pathname); + different = !cached->variable + || ((w->inum != MREF(c->inum)) + && (strncmp(c->pathname, w->pathname, len) + || ((c->pathname[len] != '\0') + && (c->pathname[len] != '/')))); + } else + different = !c->pathname + || (w->inum != MREF(c->inum)); + return (different); +} + +#endif + +#if CACHE_LOOKUP_SIZE + +/* + * File name comparing for entering/fetching from lookup cache + */ + +static int lookup_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (c->namesize != w->namesize) + || memcmp(c->name, w->name, c->namesize)); +} + +/* + * Inode number comparing for invalidating lookup cache + * + * All entries with designated inode number are invalidated + * + * Only use associated with a CACHE_NOHASH flag + */ + +static int lookup_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (MREF(c->inum) != MREF(w->inum))); +} + +/* + * Lookup hashing + * + * Based on first, second and and last char + */ + +int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached) +{ + const unsigned char *name; + int count; + unsigned int val; + + name = (const unsigned char*)cached->variable; + count = cached->varsize; + if (!name || !count) { + ntfs_log_error("Bad lookup cache entry\n"); + return (-1); + } + val = (name[0] << 2) + (name[1] << 1) + name[count - 1] + count; + return (val % (2*CACHE_LOOKUP_SIZE)); } #endif @@ -147,8 +242,8 @@ static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached, * If the volume is mounted with the case sensitive flag set, then we only * allow exact matches. */ -u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, - const int uname_len) +u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len) { VCN vcn; u64 mref = 0; @@ -158,6 +253,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_ALLOCATION *ia; + IGNORE_CASE_BOOL case_sensitivity; u8 *index_end; ntfs_attr *ia_na; int eo, rc; @@ -182,6 +278,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, "%lld", (unsigned long long)dir_ni->mft_no); goto put_err_out; } + case_sensitivity = (NVolCaseSensitive(vol) ? CASE_SENSITIVE : IGNORE_CASE); /* Get to the index root value. */ ir = (INDEX_ROOT*)((u8*)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); @@ -226,10 +323,10 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. */ - rc = ntfs_names_collate(uname, uname_len, + rc = ntfs_names_full_collate(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - IGNORE_CASE, vol->upcase, vol->upcase_len); + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); /* * If uname collates before the name of the current entry, there * is definitely no such name in this index but we might need to @@ -238,19 +335,6 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, if (rc == -1) break; /* The names are not equal, continue the search. */ - if (rc) - continue; - /* - * Names match with case insensitive comparison, now try the - * case sensitive comparison, which is required for proper - * collation. - */ - rc = ntfs_names_collate(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - CASE_SENSITIVE, vol->upcase, vol->upcase_len); - if (rc == -1) - break; if (rc) continue; /* @@ -381,10 +465,10 @@ descend_into_child_node: * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. */ - rc = ntfs_names_collate(uname, uname_len, + rc = ntfs_names_full_collate(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - IGNORE_CASE, vol->upcase, vol->upcase_len); + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); /* * If uname collates before the name of the current entry, there * is definitely no such name in this index but we might need to @@ -395,20 +479,6 @@ descend_into_child_node: /* The names are not equal, continue the search. */ if (rc) continue; - /* - * Names match with case insensitive comparison, now try the - * case sensitive comparison, which is required for proper - * collation. - */ - rc = ntfs_names_collate(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - CASE_SENSITIVE, vol->upcase, vol->upcase_len); - if (rc == -1) - break; - if (rc) - continue; - mref = le64_to_cpu(ie->indexed_file); free(ia); ntfs_attr_close(ia_na); @@ -463,6 +533,123 @@ close_err_out: goto eo_put_err_out; } +/* + * Lookup a file in a directory from its UTF-8 name + * + * The name is first fetched from cache if one is defined + * + * Returns the inode number + * or -1 if not possible (errno tells why) + */ + +u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name) +{ + int uname_len; + ntfschar *uname = (ntfschar*)NULL; + u64 inum; + char *cached_name; + const char *const_name; + + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + const_name = cached_name; + } else { + cached_name = (char*)NULL; + const_name = name; + } + if (const_name) { +#if CACHE_LOOKUP_SIZE + + /* + * fetch inode from cache + */ + + if (dir_ni->vol->lookup_cache) { + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + + item.name = const_name; + item.namesize = strlen(const_name) + 1; + item.parent = dir_ni->mft_no; + cached = (struct CACHED_LOOKUP*)ntfs_fetch_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) { + inum = cached->inum; + if (inum == (u64)-1) + errno = ENOENT; + } else { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(name, &uname); + if (uname_len >= 0) { + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + item.inum = inum; + /* enter into cache, even if not found */ + ntfs_enter_cache(dir_ni->vol->lookup_cache, + GENERIC(&item), + lookup_cache_compare); + free(uname); + } else + inum = (s64)-1; + } + } else +#endif + { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(cached_name, &uname); + if (uname_len >= 0) + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + else + inum = (s64)-1; + } + if (cached_name) + free(cached_name); + } else + inum = (s64)-1; + return (inum); +} + +/* + * Update a cache lookup record when a name has been defined + * + * The UTF-8 name is required + */ + +void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, u64 inum) +{ +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + char *cached_name; + + if (dir_ni->vol->lookup_cache) { + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + item.name = cached_name; + } else { + cached_name = (char*)NULL; + item.name = name; + } + if (item.name) { + item.namesize = strlen(item.name) + 1; + item.parent = dir_ni->mft_no; + item.inum = inum; + cached = (struct CACHED_LOOKUP*)ntfs_enter_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) + cached->inum = inum; + if (cached_name) + free(cached_name); + } + } +#endif +} + /** * ntfs_pathname_to_inode - Find the inode which represents the given pathname * @vol: An ntfs volume obtained from ntfs_mount @@ -560,9 +747,46 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, q = strchr(p, PATH_SEP); if (q != NULL) { *q = '\0'; - /* q++; JPA */ } - +#if CACHE_INODE_SIZE + /* + * fetch inode for partial path from cache + */ + cached = (struct CACHED_INODE*)NULL; + if (!parent) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + if (cached) { + inum = cached->inum; + } + } + /* + * if not in cache, translate, search, then + * insert into cache if found + */ + if (!cached) { + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); + if (!parent && (inum != (u64) -1)) { + item.inum = inum; + ntfs_enter_cache(vol->xinode_cache, + GENERIC(&item), + inode_cache_compare); + } + } +#else len = ntfs_mbstoucs(p, &unicode); if (len < 0) { ntfs_log_perror("Could not convert filename to Unicode:" @@ -573,32 +797,6 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, err = ENAMETOOLONG; goto close; } -#if CACHE_INODE_SIZE - /* - * fetch inode for partial path from cache - * if not available, compute and store into cache - */ - if (parent) - inum = ntfs_inode_lookup_by_name(ni, unicode, len); - else { - item.pathname = fullname; - item.varsize = strlen(fullname) + 1; - cached = (struct CACHED_INODE*)ntfs_fetch_cache( - vol->xinode_cache, GENERIC(&item), - inode_cache_compare); - if (cached) { - inum = cached->inum; - } else { - inum = ntfs_inode_lookup_by_name(ni, unicode, len); - if (inum != (u64) -1) { - item.inum = inum; - ntfs_enter_cache(vol->xinode_cache, - GENERIC(&item), - inode_cache_compare); - } - } - } -#else inum = ntfs_inode_lookup_by_name(ni, unicode, len); #endif if (inum == (u64) -1) { @@ -691,6 +889,10 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, { FILE_NAME_ATTR *fn = &ie->key.file_name; unsigned dt_type; + BOOL metadata; + ntfschar *loname; + int res; + MFT_REF mref; ntfs_log_trace("Entering.\n"); @@ -710,9 +912,38 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, dt_type = NTFS_DT_UNKNOWN; else dt_type = NTFS_DT_REG; - return filldir(dirent, fn->file_name, fn->file_name_length, - fn->file_name_type, *pos, - le64_to_cpu(ie->indexed_file), dt_type); + + /* return metadata files and hidden files if requested */ + mref = le64_to_cpu(ie->indexed_file); + metadata = (MREF(mref) != FILE_root) && (MREF(mref) < FILE_first_user); + if ((!metadata && (NVolShowHidFiles(dir_ni->vol) + || !(fn->file_attributes & FILE_ATTR_HIDDEN))) + || (NVolShowSysFiles(dir_ni->vol) && (NVolShowHidFiles(dir_ni->vol) + || metadata))) { + if (NVolCaseSensitive(dir_ni->vol)) { + res = filldir(dirent, fn->file_name, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + } else { + loname = (ntfschar*)ntfs_malloc(2*fn->file_name_length); + if (loname) { + memcpy(loname, fn->file_name, + 2*fn->file_name_length); + ntfs_name_locase(loname, fn->file_name_length, + dir_ni->vol->locase, + dir_ni->vol->upcase_len); + res = filldir(dirent, loname, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + free(loname); + } else + res = -1; + } + } else + res = 0; + return (res); } /** @@ -1196,6 +1427,9 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); if (!ni) return NULL; +#if CACHE_NIDATA_SIZE + ntfs_inode_invalidate(dir_ni->vol, ni->mft_no); +#endif /* * Create STANDARD_INFORMATION attribute. * JPA Depending on available inherited security descriptor, @@ -1210,10 +1444,10 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, err = errno; goto err_out; } - si->creation_time = utc2ntfs(ni->creation_time); - si->last_data_change_time = utc2ntfs(ni->last_data_change_time); - si->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - si->last_access_time = utc2ntfs(ni->last_access_time); + si->creation_time = ni->creation_time; + si->last_data_change_time = ni->last_data_change_time; + si->last_mft_change_time = ni->last_mft_change_time; + si->last_access_time = ni->last_access_time; if (securid) { set_nino_flag(ni, v3_Extensions); ni->owner_id = si->owner_id = 0; @@ -1226,7 +1460,21 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, si->file_attributes = FILE_ATTR_SYSTEM; ni->flags = FILE_ATTR_SYSTEM; } + ni->flags |= FILE_ATTR_ARCHIVE; + if (NVolHideDotFiles(dir_ni->vol) + && (name_len > 1) + && (name[0] == const_cpu_to_le16('.')) + && (name[1] != const_cpu_to_le16('.'))) + ni->flags |= FILE_ATTR_HIDDEN; + /* + * Set compression flag according to parent directory + * unless NTFS version < 3.0 or cluster size > 4K + * or compression has been disabled + */ if ((dir_ni->flags & FILE_ATTR_COMPRESSED) + && (dir_ni->vol->major_ver >= 3) + && NVolCompression(dir_ni->vol) + && (dir_ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) && (S_ISREG(type) || S_ISDIR(type))) ni->flags |= FILE_ATTR_COMPRESSED; /* Add STANDARD_INFORMATION to inode. */ @@ -1355,12 +1603,18 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, fn->file_attributes = FILE_ATTR_SYSTEM; else fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); - fn->data_size = cpu_to_sle64(ni->data_size); - fn->allocated_size = cpu_to_sle64(ni->allocated_size); + fn->file_attributes |= FILE_ATTR_ARCHIVE; + fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN; + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + else { + fn->data_size = cpu_to_sle64(ni->data_size); + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + } memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); /* Add FILE_NAME attribute to inode. */ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { @@ -1521,12 +1775,18 @@ int ntfs_delete(ntfs_volume *vol, const char *pathname, BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; BOOL case_sensitive_match = TRUE; int err = 0; +#if CACHE_NIDATA_SIZE + int i; +#endif #if CACHE_INODE_SIZE struct CACHED_INODE item; const char *p; u64 inum = (u64)-1; int count; #endif +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP lkitem; +#endif ntfs_log_trace("Entering.\n"); @@ -1640,8 +1900,37 @@ search: * case there are no reference to this inode left, so we should free all * non-resident attributes and mark all MFT record as not in use. */ +#if CACHE_LOOKUP_SIZE + /* invalidate entry in lookup cache */ + lkitem.name = (const char*)NULL; + lkitem.namesize = 0; + lkitem.inum = ni->mft_no; + lkitem.parent = dir_ni->mft_no; + ntfs_invalidate_cache(vol->lookup_cache, GENERIC(&lkitem), + lookup_cache_inv_compare, CACHE_NOHASH); +#endif #if CACHE_INODE_SIZE inum = ni->mft_no; + if (pathname) { + /* invalide cache entry, even if there was an error */ + /* Remove leading /'s. */ + p = pathname; + while (*p == PATH_SEP) + p++; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",pathname); + item.pathname = p; + item.varsize = strlen(p); + } else { + item.pathname = (const char*)NULL; + item.varsize = 0; + } + item.inum = inum; + count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item), + inode_cache_inv_compare, CACHE_NOHASH); + if (pathname && !count) + ntfs_log_error("Could not delete inode cache entry for %s\n", + pathname); #endif if (ni->mrec->link_count) { ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); @@ -1656,6 +1945,13 @@ search: */ err = errno; } + if (ntfs_delete_object_id_index(ni)) { + /* + * Failed to remove the object id index : proceed anyway + * This is not a critical error. + */ + err = errno; + } ntfs_attr_reinit_search_ctx(actx); while (!ntfs_attrs_walk(actx)) { if (actx->attr->non_resident) { @@ -1684,12 +1980,31 @@ search: "Probably leaving inconsistent metadata.\n"); } /* All extents should be attached after attribute walk. */ +#if CACHE_NIDATA_SIZE + /* + * Disconnect extents before deleting them, so they are + * not wrongly moved to cache through the chainings + */ + for (i=ni->nr_extents-1; i>=0; i--) { + ni->extent_nis[i]->base_ni = (ntfs_inode*)NULL; + ni->extent_nis[i]->nr_extents = 0; + if (ntfs_mft_record_free(ni->vol, ni->extent_nis[i])) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + } + free(ni->extent_nis); + ni->nr_extents = 0; + ni->extent_nis = (ntfs_inode**)NULL; +#else while (ni->nr_extents) if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { err = errno; ntfs_log_error("Failed to free extent MFT record. " "Leaving inconsistent metadata.\n"); } +#endif if (ntfs_mft_record_free(ni->vol, ni)) { err = errno; ntfs_log_error("Failed to free base MFT record. " @@ -1705,24 +2020,6 @@ out: err = errno; if (ntfs_inode_close(ni) && !err) err = errno; -#if CACHE_INODE_SIZE - if (pathname) { - /* invalide cache entry, even if there was an error */ - /* Remove leading /'s. */ - p = pathname; - while (*p == PATH_SEP) - p++; - if (p[0] && (p[strlen(p)-1] == PATH_SEP)) - ntfs_log_error("Unnormalized path %s\n",pathname); - item.pathname = p; - item.inum = inum; - count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item), - inode_cache_inv_compare); - if (!count) - ntfs_log_error("Could not delete inode cache entry for %s\n", - pathname); - } -#endif if (err) { errno = err; ntfs_log_debug("Could not delete file: %s\n", strerror(errno)); @@ -1783,14 +2080,17 @@ static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, fn->file_name_length = name_len; fn->file_name_type = nametype; fn->file_attributes = ni->flags; - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT; - fn->allocated_size = cpu_to_sle64(ni->allocated_size); - fn->data_size = cpu_to_sle64(ni->data_size); - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + } else { + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + fn->data_size = cpu_to_sle64(ni->data_size); + } + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); /* Add FILE_NAME attribute to index. */ if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, @@ -1829,6 +2129,45 @@ int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) return (ntfs_link_i(ni, dir_ni, name, name_len, FILE_NAME_POSIX)); } +/* + * Get a parent directory from an inode entry + * + * This is only used in situations where the path used to access + * the current file is not known for sure. The result may be different + * from the path when the file is linked in several parent directories. + * + * Currently this is only used for translating ".." in the target + * of a Vista relative symbolic link + */ + +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) +{ + ntfs_inode *dir_ni = (ntfs_inode*)NULL; + u64 inum; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + if (ni->mft_no != FILE_root) { + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ((ntfs_inode*)NULL); + + if (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + inum = le64_to_cpu(fn->parent_directory); + if (inum != (u64)-1) { + dir_ni = ntfs_inode_open(ni->vol, MREF(inum)); + } + } + ntfs_attr_put_search_ctx(ctx); + } + return (dir_ni); +} + #ifdef HAVE_SETXATTR #define MAX_DOS_NAME_LENGTH 12 @@ -1876,65 +2215,107 @@ static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) } +/* + * Get a long name for a file in designated directory + * + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) + */ + +static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname) +{ + size_t outsize = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + /* first search for WIN32 or DOS+WIN32 names */ + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type & FILE_NAME_WIN32) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a WIN32 or WIN32+DOS name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + /* if not found search for POSIX names */ + if (!outsize) { + ntfs_attr_reinit_search_ctx(ctx); + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type == FILE_NAME_POSIX) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a POSIX name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + } + ntfs_attr_put_search_ctx(ctx); + return (outsize); +} + + /* * Get the ntfs DOS name into an extended attribute */ -int ntfs_get_ntfs_dos_name(const char *path, - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size) { int outsize = 0; char *outname = (char*)NULL; - ntfs_inode *dir_ni = NULL; u64 dnum; - char *dirname; - const char *rdirname; - char *p; int doslen; ntfschar dosname[MAX_DOS_NAME_LENGTH]; - /* get the parent directory */ - dirname = strdup(path); - if (dirname) { - p = strrchr(dirname,'/'); - if (p) { - *p++ = 0; - rdirname = (dirname[0] ? dirname : "/"); - dir_ni = ntfs_pathname_to_inode(ni->vol, NULL, rdirname); - dnum = dir_ni->mft_no; - free(dirname); - } - } - if (dir_ni) { - doslen = get_dos_name(ni, dnum, dosname); - if (doslen > 0) { - /* - * Found a DOS name for the entry, make - * uppercase and encode into the buffer - * if there is enough space - */ - ntfs_name_upcase(dosname, doslen, - ni->vol->upcase, ni->vol->upcase_len); - if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { - ntfs_log_error("Cannot represent dosname in current locale.\n"); - outsize = -errno; - } else { - outsize = strlen(outname); - if (value && (outsize <= (int)size)) - memcpy(value, outname, outsize); - else - if (size && (outsize > (int)size)) - outsize = -ERANGE; - free(outname); - } - } else { - if (doslen == 0) - errno = ENODATA; + dnum = dir_ni->mft_no; + doslen = get_dos_name(ni, dnum, dosname); + if (doslen > 0) { + /* + * Found a DOS name for the entry, make + * uppercase and encode into the buffer + * if there is enough space + */ + ntfs_name_upcase(dosname, doslen, + ni->vol->upcase, ni->vol->upcase_len); + if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { + ntfs_log_error("Cannot represent dosname in current locale.\n"); outsize = -errno; + } else { + outsize = strlen(outname); + if (value && (outsize <= (int)size)) + memcpy(value, outname, outsize); + else + if (size && (outsize > (int)size)) + outsize = -ERANGE; + free(outname); } - ntfs_inode_close(dir_ni); - } else + } else { + if (doslen == 0) + errno = ENODATA; outsize = -errno; + } return (outsize); } @@ -2020,7 +2401,6 @@ static int set_namespace(ntfs_inode *ni, ntfs_inode *dir_ni, */ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, - const char *fullpath, ntfschar *shortname, int shortlen, ntfschar *longname, int longlen, ntfschar *deletename, int deletelen, BOOL existed) @@ -2068,17 +2448,22 @@ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, longname, longlen, FILE_NAME_WIN32_AND_DOS) >= 0)) res = 0; - ntfs_inode_close(ni); - ntfs_inode_close(dir_ni); + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); + if (ntfs_inode_close_in_dir(ni,dir_ni) && !res) + res = -1; + if (ntfs_inode_close(dir_ni) && !res) + res = -1; } - } else + } else { if (!ntfs_link_i(ni, dir_ni, shortname, shortlen, FILE_NAME_DOS) /* make sure a new link was recorded */ && (le16_to_cpu(ni->mrec->link_count) > linkcount)) { /* delete the existing long name or short name */ - if (!ntfs_delete(vol, fullpath, ni, dir_ni, - deletename, deletelen)) { +// is it ok to not provide the path ? + if (!ntfs_delete(vol, (char*)NULL, ni, dir_ni, + deletename, deletelen)) { /* delete closes the inodes, so have to open again */ dir_ni = ntfs_inode_open(vol, dnum); if (dir_ni) { @@ -2088,15 +2473,20 @@ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, longname, longlen, FILE_NAME_WIN32)) res = 0; - ntfs_inode_close(ni); + if (ntfs_inode_close_in_dir(ni, + dir_ni) + && !res) + res = -1; } - ntfs_inode_close(dir_ni); + if (ntfs_inode_close(dir_ni) && !res) + res = -1; } } } else { - ntfs_inode_close(ni); + ntfs_inode_close_in_dir(ni,dir_ni); ntfs_inode_close(dir_ni); } + } return (res); } @@ -2111,8 +2501,8 @@ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, * The inode of the file is always closed */ -int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, - int flags, ntfs_inode *ni) +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags) { int res = 0; int longlen = 0; @@ -2125,11 +2515,7 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, u64 dnum; BOOL closed = FALSE; ntfschar *shortname = NULL; - ntfschar *longname = NULL; - ntfs_inode *dir_ni = NULL; - char *dirname = (char*)NULL; - const char *rdirname; - char *p; + ntfschar longname[NTFS_MAX_NAME_LEN]; vol = ni->vol; fnum = ni->mft_no; @@ -2141,28 +2527,17 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, shortlen = ntfs_mbstoucs(newname, &shortname); /* make sure the short name has valid chars */ if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) { - ntfs_inode_close(ni); + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); res = -errno; return res; } - /* get the parent directory */ - dirname = strdup(path); - if (dirname) { - p = strrchr(dirname,'/'); - if (p) { - *p++ = 0; - longlen = ntfs_mbstoucs(p, &longname); - /* make sure the long name had valid chars */ - if (!ntfs_forbidden_chars(longname,longlen)) { - rdirname = (dirname[0] ? dirname : "/"); - dir_ni = ntfs_pathname_to_inode(vol, NULL, rdirname); - } - } - } - if (dir_ni) { - dnum = dir_ni->mft_no; + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { oldlen = get_dos_name(ni, dnum, oldname); - if (oldlen >= 0) { + if ((oldlen >= 0) + && !ntfs_forbidden_chars(longname, longlen)) { if (oldlen > 0) { if (flags & XATTR_CREATE) { res = -1; @@ -2175,7 +2550,6 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, res = 0; else { res = set_dos_name(ni, dir_ni, - path, shortname, shortlen, longname, longlen, oldname, oldlen, TRUE); @@ -2186,7 +2560,7 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, res = -1; errno = ENODATA; } else { - res = set_dos_name(ni, dir_ni, path, + res = set_dos_name(ni, dir_ni, shortname, shortlen, longname, longlen, longname, longlen, FALSE); @@ -2195,17 +2569,15 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, } } else res = -1; - if (!closed) - ntfs_inode_close(dir_ni); } else { res = -1; - errno = EINVAL; + errno = ENOENT; } - free(dirname); - free(longname); free(shortname); - if (!closed) - ntfs_inode_close(ni); + if (!closed) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } return (res ? -1 : 0); } @@ -2213,7 +2585,7 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, * Delete the ntfs DOS name */ -int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni) +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) { int res; int oldnametype; @@ -2223,27 +2595,13 @@ int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni) ntfs_volume *vol; BOOL deleted = FALSE; ntfschar shortname[MAX_DOS_NAME_LENGTH]; - ntfschar *longname = NULL; - ntfs_inode *dir_ni = NULL; - char *dirname = (char*)NULL; - const char *rdirname; - char *p; + ntfschar longname[NTFS_MAX_NAME_LEN]; res = -1; vol = ni->vol; - /* get the parent directory */ - dirname = strdup(path); - if (dirname) { - p = strrchr(dirname,'/'); - if (p) { - *p++ = 0; - longlen = ntfs_mbstoucs(p, &longname); - rdirname = (dirname[0] ? dirname : "/"); - dir_ni = ntfs_pathname_to_inode(vol, NULL, rdirname); - } - } - if (dir_ni) { - dnum = dir_ni->mft_no; + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { shortlen = get_dos_name(ni, dnum, shortname); if (shortlen >= 0) { /* migrate the long name as Posix */ @@ -2278,8 +2636,8 @@ int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni) */ errno = EIO; ntfs_log_error("Could not change" - " DOS name of %s to Posix\n", - path); + " DOS name of inode %lld to Posix\n", + (long long)ni->mft_no); } break; default : @@ -2288,13 +2646,14 @@ int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni) break; } } - if (!deleted) - ntfs_inode_close(dir_ni); + } else { + errno = ENOENT; + res = -1; + } + if (!deleted) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); } - if (!deleted) - ntfs_inode_close(ni); - free(longname); - free(dirname); return (res); } diff --git a/source/libntfs/dir.h b/source/libntfs/dir.h index 49f706fb..56e76fe7 100644 --- a/source/libntfs/dir.h +++ b/source/libntfs/dir.h @@ -61,6 +61,9 @@ extern ntfschar NTFS_INDEX_R[3]; extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, const int uname_len); +extern u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name); +extern void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, + u64 inum); extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, const char *pathname); @@ -104,12 +107,22 @@ typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name, extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, void *dirent, ntfs_filldir_t filldir); -int ntfs_get_ntfs_dos_name(const char *path, - char *value, size_t size, ntfs_inode *ni); -int ntfs_set_ntfs_dos_name(const char *path, - const char *value, size_t size, int flags, - ntfs_inode *ni); -int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni); +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni); + +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_INODE_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached); +extern int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached); + +#endif #endif /* defined _NTFS_DIR_H */ diff --git a/source/libntfs/efs.c b/source/libntfs/efs.c index e7a134a2..6ccec20a 100644 --- a/source/libntfs/efs.c +++ b/source/libntfs/efs.c @@ -4,7 +4,7 @@ * This module is part of ntfs-3g library * * Copyright (c) 2009 Martin Bene - * Copyright (c) 2009 Jean-Pierre Andre + * Copyright (c) 2009-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -73,8 +73,7 @@ static ntfschar logged_utility_stream_name[] = { * Get the ntfs EFS info into an extended attribute */ -int ntfs_get_efs_info(const char *path, - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size) { EFS_ATTR_HEADER *efs_info; s64 attr_size = 0; @@ -102,43 +101,133 @@ int ntfs_get_efs_info(const char *path, } else { if (efs_info) { free(efs_info); - ntfs_log_info("Bad efs_info for file %s\n",path); + ntfs_log_error("Bad efs_info for inode %lld\n", + (long long)ni->mft_no); } else { - ntfs_log_info("Could not get efsinfo" - " for file %s\n", path); + ntfs_log_error("Could not get efsinfo" + " for inode %lld\n", + (long long)ni->mft_no); } errno = EIO; attr_size = 0; } } else { errno = ENODATA; - ntfs_log_info("File %s is not encrypted",path); + ntfs_log_trace("Inode %lld is not encrypted\n", + (long long)ni->mft_no); } } return (attr_size ? (int)attr_size : -errno); } +/* + * Fix all encrypted AT_DATA attributes of an inode + * + * The fix may require making an attribute non resident, which + * requires more space in the MFT record, and may cause some + * attribute to be expelled and the full record to be reorganized. + * When this happens, the search for data attributes has to be + * reinitialized. + * + * Returns zero if successful. + * -1 if there is a problem. + */ + +static int fixup_loop(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + ntfs_attr *na; + ATTR_RECORD *a; + BOOL restart; + BOOL first; + int cnt; + int maxcnt; + int res = 0; + + maxcnt = 0; + do { + restart = FALSE; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + res = -1; + } + cnt = 0; + while (!restart && !res + && !ntfs_attr_lookup(AT_DATA, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + cnt++; + a = ctx->attr; + na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, + (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), + a->name_length); + if (!na) { + ntfs_log_error("can't open DATA Attribute\n"); + res = -1; + } + if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) { + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + /* + * ntfs_attr_make_non_resident fails if there + * is not enough space in the MFT record. + * When this happens, force making non-resident + * so that some other attribute is expelled. + */ + if (ntfs_attr_force_non_resident(na)) { + res = -1; + } else { + /* make sure there is some progress */ + if (cnt <= maxcnt) { + errno = EIO; + ntfs_log_error("Multiple failure" + " making non resident\n"); + res = -1; + } else { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + restart = TRUE; + maxcnt = cnt; + } + } + } + if (!restart && !res + && ntfs_efs_fixup_attribute(ctx, na)) { + ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n"); + res = -1; + } + } + if (na) + ntfs_attr_close(na); + } + first = FALSE; + } while (restart && !res); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (res); +} + /* * Set the efs data from an extended attribute * Warning : the new data is not checked * Returns 0, or -1 if there is a problem */ -int ntfs_set_efs_info(const char *path __attribute__((unused)), - const char *value, size_t size, int flags, - ntfs_inode *ni) +int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size, + int flags) + { int res; int written; ntfs_attr *na; const EFS_ATTR_HEADER *info_header; - ntfs_attr_search_ctx *ctx; res = 0; if (ni && value && size) { if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) { if (ni->flags & FILE_ATTR_ENCRYPTED) { - ntfs_log_info("File %s already encrypted",path); + ntfs_log_trace("Inode %lld already encrypted\n", + (long long)ni->mft_no); errno = EEXIST; } else { /* @@ -147,8 +236,8 @@ int ntfs_set_efs_info(const char *path __attribute__((unused)), * restored as compressed. * TODO : decompress first. */ - ntfs_log_error("File %s cannot be encrypted and compressed\n", - path); + ntfs_log_error("Inode %lld cannot be encrypted and compressed\n", + (long long)ni->mft_no); errno = EIO; } return -1; @@ -207,20 +296,8 @@ int ntfs_set_efs_info(const char *path __attribute__((unused)), /* iterate over AT_DATA attributes */ /* set encrypted flag, truncate attribute to match padding bytes */ - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get ctx for efs\n"); - return (-1); - } - while (!ntfs_attr_lookup(AT_DATA, NULL, 0, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (ntfs_efs_fixup_attribute(ctx, NULL)) { - ntfs_log_error("Error in efs fixup of AT_DATA Attribute"); - ntfs_attr_put_search_ctx(ctx); - return(-1); - } - } - ntfs_attr_put_search_ctx(ctx); + if (fixup_loop(ni)) + return -1; } ni->flags |= FILE_ATTR_ENCRYPTED; NInoSetDirty(ni); @@ -247,15 +324,14 @@ int ntfs_set_efs_info(const char *path __attribute__((unused)), int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) { u64 newsize; + u64 oldsize; le16 appended_bytes; u16 padding_length; - ATTR_RECORD *a; ntfs_inode *ni; - BOOL close_na = FALSE; BOOL close_ctx = FALSE; - if (!ctx && !na) { - ntfs_log_error("neither ctx nor na specified for efs_fixup_attribute\n"); + if (!na) { + ntfs_log_error("no na specified for efs_fixup_attribute\n"); goto err_out; } if (!ctx) { @@ -264,55 +340,79 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) ntfs_log_error("Failed to get ctx for efs\n"); goto err_out; } - close_ctx=TRUE; + close_ctx = TRUE; if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); goto err_out; } - } - - a = ctx->attr; - if (!na) { - na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, - (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), - a->name_length); - if (!na) { - ntfs_log_error("can't open DATA Attribute\n"); - return (-1); + } else { + if (!NAttrNonResident(na)) { + ntfs_log_error("Cannot make non resident" + " when a context has been allocated\n"); + goto err_out; } - close_na = TRUE; - } - /* make sure size is valid for a raw encrypted stream */ - if ((na->data_size & 511) != 2) { - ntfs_log_error("Bad raw encrypted stream"); - goto err_out; - } - /* read padding length from last two bytes of attribute */ - if (ntfs_attr_pread(na, na->data_size-2, 2, &appended_bytes) != 2) { - ntfs_log_error("Error reading padding length\n"); - goto err_out; - } - padding_length = le16_to_cpu(appended_bytes); - if (padding_length > 511 || padding_length > na->data_size-2) { - errno = EINVAL; - ntfs_log_error("invalid padding length %d for data_size %lld\n", - padding_length, (long long)na->data_size); - goto err_out; - } - newsize = na->data_size - padding_length - 2; - /* truncate attribute to possibly free clusters allocated - for the last two bytes */ - if (ntfs_attr_truncate(na, na->data_size-2)) { - ntfs_log_error("Error truncating attribute\n"); - goto err_out; } - /* Encrypted AT_DATA Attributes MUST be non-resident */ + /* no extra bytes are added to void attributes */ + oldsize = na->data_size; + if (oldsize) { + /* make sure size is valid for a raw encrypted stream */ + if ((oldsize & 511) != 2) { + ntfs_log_error("Bad raw encrypted stream\n"); + goto err_out; + } + /* read padding length from last two bytes of attribute */ + if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) { + ntfs_log_error("Error reading padding length\n"); + goto err_out; + } + padding_length = le16_to_cpu(appended_bytes); + if (padding_length > 511 || padding_length > na->data_size-2) { + errno = EINVAL; + ntfs_log_error("invalid padding length %d for data_size %lld\n", + padding_length, (long long)oldsize); + goto err_out; + } + newsize = oldsize - padding_length - 2; + /* + * truncate attribute to possibly free clusters allocated + * for the last two bytes, but do not truncate to new size + * to avoid losing useful data + */ + if (ntfs_attr_truncate(na, oldsize - 2)) { + ntfs_log_error("Error truncating attribute\n"); + goto err_out; + } + } else + newsize = 0; + + /* + * Encrypted AT_DATA Attributes MUST be non-resident + * This has to be done after the attribute is resized, as + * resizing down to zero may cause the attribute to be made + * resident. + */ if (!NAttrNonResident(na) - && ntfs_attr_make_non_resident(na, ctx)) { - ntfs_log_error("Error making DATA attribute non-resident\n"); - goto err_out; + && ntfs_attr_make_non_resident(na, ctx)) { + if (!close_ctx + || ntfs_attr_force_non_resident(na)) { + ntfs_log_error("Error making DATA attribute non-resident\n"); + goto err_out; + } else { + /* + * must reinitialize context after forcing + * non-resident. We need a context for updating + * the state, and at this point, we are sure + * the context is not used elsewhere. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } } ni = na->ni; if (!na->name_len) { @@ -321,8 +421,6 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) } NInoSetDirty(ni); NInoFileNameSetDirty(ni); - if (close_na) - ntfs_attr_close(na); ctx->attr->data_size = cpu_to_le64(newsize); if (le64_to_cpu(ctx->attr->initialized_size) > newsize) @@ -333,8 +431,6 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) return (0); err_out: - if (close_na && na) - ntfs_attr_close(na); if (close_ctx && ctx) ntfs_attr_put_search_ctx(ctx); return (-1); diff --git a/source/libntfs/efs.h b/source/libntfs/efs.h index bb28e3c7..6eada067 100644 --- a/source/libntfs/efs.h +++ b/source/libntfs/efs.h @@ -21,11 +21,10 @@ #ifndef EFS_H #define EFS_H -int ntfs_get_efs_info(const char *path, - char *value, size_t size, ntfs_inode *ni); -int ntfs_set_efs_info(const char *path, - const char *value, size_t size, int flags, - ntfs_inode *ni); +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_efs_info(ntfs_inode *ni, + const char *value, size_t size, int flags); int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na); #endif /* EFS_H */ diff --git a/source/libntfs/gekko_io.c b/source/libntfs/gekko_io.c index 708f5067..48ca90d4 100644 --- a/source/libntfs/gekko_io.c +++ b/source/libntfs/gekko_io.c @@ -132,22 +132,13 @@ static int ntfs_device_gekko_io_open(struct ntfs_device *dev, int flags) fd->len = (fd->sectorCount * fd->sectorSize); fd->ino = le64_to_cpu(boot.volume_serial_number); - // If the device sector size is not 512 bytes then we cannot continue, - // gekko disc I/O works on the assumption that sectors are always 512 bytes long. - // TODO: Implement support for non-512 byte sector sizes through some fancy maths!? - if (fd->sectorSize != BYTES_PER_SECTOR) { - ntfs_log_error("Boot sector claims there is %i bytes per sector; expected %i\n", fd->sectorSize, BYTES_PER_SECTOR); - errno = EIO; - return -1; - } - // Mark the device as read-only (if required) if (flags & O_RDONLY) { NDevSetReadOnly(dev); } // Create the cache - fd->cache = _NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount); + fd->cache = _NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize); // Mark the device as open NDevSetBlock(dev); @@ -288,29 +279,31 @@ static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s return -1; } + if(offset < 0) + { + errno = EROFS; + return -1; + } + if(!count) return 0; sec_t sec_start = (sec_t) fd->startSector; sec_t sec_count = 1; - u32 buffer_offset = 0; + u32 buffer_offset = (u32) (offset % fd->sectorSize); u8 *buffer = NULL; // Determine the range of sectors required for this read if (offset > 0) { - sec_start += (sec_t) floor((f64) offset/fd->sectorSize); - buffer_offset = (u32) (offset % fd->sectorSize); + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); } - if (count > fd->sectorSize) { - sec_count = (sec_t) ceil((f64) count/fd->sectorSize); - - if(buffer_offset > 0) - sec_count += 1; + if (buffer_offset+count > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); } // If this read happens to be on the sector boundaries then do the read straight into the destination buffer - if((offset % fd->sectorSize == 0) && (count % fd->sectorSize == 0)) { + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) { // Read from the device ntfs_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count); @@ -378,29 +371,30 @@ static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, return -1; } - if(!count) + if(count < 0 || offset < 0) { + errno = EROFS; + return -1; + } + + if(count == 0) return 0; sec_t sec_start = (sec_t) fd->startSector; sec_t sec_count = 1; - u32 buffer_offset = 0; + u32 buffer_offset = (u32) (offset % fd->sectorSize); u8 *buffer = NULL; // Determine the range of sectors required for this write if (offset > 0) { - sec_start += (sec_t) floor((f64) offset/fd->sectorSize); - buffer_offset = (u32) (offset % fd->sectorSize); + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); } - if (count > fd->sectorSize) { - sec_count = (sec_t) ceil((f64) count/fd->sectorSize); - - if(buffer_offset > 0) - sec_count += 1; + if ((buffer_offset+count) > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); } // If this write happens to be on the sector boundaries then do the write straight to disc - if((offset % fd->sectorSize == 0) && (count % fd->sectorSize == 0)) { - + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) + { // Write to the device ntfs_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count); if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buf)) { @@ -408,10 +402,10 @@ static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, errno = EIO; return -1; } - // Else write from a buffer aligned to the sector boundaries - } else { - + } + else + { // Allocate a buffer to hold the write data buffer = (u8 *) ntfs_alloc(sec_count * fd->sectorSize); if (!buffer) { @@ -421,11 +415,23 @@ static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, // Read the first and last sectors of the buffer from disc (if required) // NOTE: This is done because the data does not line up with the sector boundaries, // we just read in the buffer edges where the data overlaps with the rest of the disc - if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buffer)) { - ntfs_log_perror("read failure @ sector %d\n", sec_start); - ntfs_free(buffer); - errno = EIO; - return -1; + if(buffer_offset != 0) + { + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, 1, buffer)) { + ntfs_log_perror("read failure @ sector %d\n", sec_start); + ntfs_free(buffer); + errno = EIO; + return -1; + } + } + if((buffer_offset+count) % fd->sectorSize != 0) + { + if (!ntfs_device_gekko_io_readsectors(dev, sec_start + sec_count - 1, 1, buffer + ((sec_count-1) * fd->sectorSize))) { + ntfs_log_perror("read failure @ sector %d\n", sec_start + sec_count - 1); + ntfs_free(buffer); + errno = EIO; + return -1; + } } // Copy the data into the write buffer @@ -445,8 +451,7 @@ static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, } // Mark the device as dirty (if we actually wrote anything) - if (count > 0) - NDevSetDirty(dev); + NDevSetDirty(dev); return count; } @@ -608,11 +613,6 @@ static int ntfs_device_gekko_io_ioctl(struct ntfs_device *dev, int request, void #if defined(BLKBSZSET) case BLKBSZSET: { int sectorSize = *(int*)argp; - if (sectorSize != BYTES_PER_SECTOR) { - ntfs_log_perror("Attempt to set sector size to an unsupported value (%i), ignored\n", sectorSize); - errno = EOPNOTSUPP; - return -1; - } fd->sectorSize = sectorSize; return 0; } diff --git a/source/libntfs/gekko_io.h b/source/libntfs/gekko_io.h index 5f6ba035..d5018e89 100644 --- a/source/libntfs/gekko_io.h +++ b/source/libntfs/gekko_io.h @@ -26,12 +26,10 @@ #endif #include "types.h" -#include "cache.h" +#include "cache2.h" #include #include -#define BYTES_PER_SECTOR 512 /* Forced by gekko disc i/o */ - /** * gekko_fd - Gekko device driver descriptor */ @@ -44,7 +42,7 @@ typedef struct _gekko_fd { u64 pos; /* Current position within the partition (in bytes) */ u64 len; /* Total length of partition (in bytes) */ ino_t ino; /* Device identifier */ - NTFS_CACHE *cache; /* Cache */ + NTFS_CACHE *cache; /* Cache */ u32 cachePageCount; /* The number of pages in the cache */ u32 cachePageSize; /* The number of sectors per cache page */ } gekko_fd; diff --git a/source/libntfs/index.c b/source/libntfs/index.c index f78336aa..7df0deec 100644 --- a/source/libntfs/index.c +++ b/source/libntfs/index.c @@ -38,9 +38,9 @@ #endif #include "attrib.h" -#include "collate.h" #include "debug.h" #include "index.h" +#include "collate.h" #include "mst.h" #include "dir.h" #include "logging.h" @@ -517,8 +517,13 @@ static int ntfs_ie_lookup(const void *key, const int key_len, * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. */ - rc = ntfs_collate(icx->ni->vol, icx->cr, key, key_len, &ie->key, - le16_to_cpu(ie->key_length)); + if (!icx->collate) { + ntfs_log_error("Collation function not defined\n"); + errno = EOPNOTSUPP; + return STATUS_ERROR; + } + rc = icx->collate(icx->ni->vol, key, key_len, + &ie->key, le16_to_cpu(ie->key_length)); if (rc == NTFS_COLLATION_ERROR) { ntfs_log_error("Collation error. Perhaps a filename " "contains invalid characters?\n"); @@ -697,12 +702,12 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic icx->vcn_size_bits = ni->vol->cluster_size_bits; else icx->vcn_size_bits = ni->vol->sector_size_bits; - - icx->cr = ir->collation_rule; - if (!ntfs_is_collation_rule_supported(icx->cr)) { + /* get the appropriate collation function */ + icx->collate = ntfs_get_collate_function(ir->collation_rule); + if (!icx->collate) { err = errno = EOPNOTSUPP; ntfs_log_perror("Unknown collation rule 0x%x", - (unsigned)le32_to_cpu(icx->cr)); + (unsigned)le32_to_cpu(ir->collation_rule)); goto err_out; } diff --git a/source/libntfs/index.h b/source/libntfs/index.h index 75e7b106..c0e76180 100644 --- a/source/libntfs/index.h +++ b/source/libntfs/index.h @@ -63,6 +63,9 @@ #define MAX_PARENT_VCN 32 +typedef int (*COLLATE)(ntfs_volume *vol, const void *data1, int len1, + const void *data2, int len2); + /** * struct ntfs_index_context - * @ni: inode containing the @entry described by this context @@ -116,7 +119,7 @@ typedef struct { INDEX_ENTRY *entry; void *data; u16 data_len; - COLLATION_RULES cr; + COLLATE collate; BOOL is_in_root; INDEX_ROOT *ir; ntfs_attr_search_ctx *actx; diff --git a/source/libntfs/inode.c b/source/libntfs/inode.c index f6398901..6f3fa060 100644 --- a/source/libntfs/inode.c +++ b/source/libntfs/inode.c @@ -5,6 +5,7 @@ * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2009-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -39,10 +40,13 @@ #include #endif +#include "param.h" #include "compat.h" #include "types.h" -#include "attrib.h" +#include "volume.h" +#include "cache.h" #include "inode.h" +#include "attrib.h" #include "debug.h" #include "mft.h" #include "attrlist.h" @@ -120,7 +124,7 @@ ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol) static void __ntfs_inode_release(ntfs_inode *ni) { if (NInoDirty(ni)) - ntfs_log_error("Releasing dirty inode %lld!\n", + ntfs_log_error("Releasing dirty inode %lld!\n", (long long)ni->mft_no); if (NInoAttrList(ni) && ni->attr_list) free(ni->attr_list); @@ -152,7 +156,7 @@ static void __ntfs_inode_release(ntfs_inode *ni) * Return a pointer to the ntfs_inode structure on success or NULL on error, * with errno set to the error code. */ -ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) +static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) { s64 l; ntfs_inode *ni = NULL; @@ -190,10 +194,10 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); ni->flags = std_info->file_attributes; - ni->creation_time = ntfs2utc(std_info->creation_time); - ni->last_data_change_time = ntfs2utc(std_info->last_data_change_time); - ni->last_mft_change_time = ntfs2utc(std_info->last_mft_change_time); - ni->last_access_time = ntfs2utc(std_info->last_access_time); + ni->creation_time = std_info->creation_time; + ni->last_data_change_time = std_info->last_data_change_time; + ni->last_mft_change_time = std_info->last_mft_change_time; + ni->last_access_time = std_info->last_access_time; /* JPA insert v3 extensions if present */ /* length may be seen as 72 (v1.x) or 96 (v3.x) */ lthle = ctx->attr->length; @@ -205,13 +209,13 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) ni->usn = std_info->usn; } else { clear_nino_flag(ni, v3_Extensions); - ni->owner_id = 0; - ni->security_id = 0; + ni->owner_id = const_cpu_to_le32(0); + ni->security_id = const_cpu_to_le32(0); } /* Set attribute list information. */ olderrno = errno; - if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, - ctx)) { + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { if (errno != ENOENT) goto put_err_out; /* Attribute list attribute does not present. */ @@ -239,7 +243,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) if (l != ni->attr_list_size) { errno = EIO; ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode " - "%lld", (long long)l, ni->attr_list_size, + "%lld", (long long)l, ni->attr_list_size, (long long)MREF(mref)); goto put_err_out; } @@ -266,9 +270,10 @@ get_size: ni->data_size = le32_to_cpu(ctx->attr->value_length); ni->allocated_size = (ni->data_size + 7) & ~7; } + set_nino_flag(ni,KnownSize); } ntfs_attr_put_search_ctx(ctx); -out: +out: ntfs_log_leave("\n"); return ni; @@ -304,10 +309,11 @@ err_out: * EINVAL @ni is invalid (probably it is an extent inode). * EIO I/O error while trying to write inode to disk. */ -int ntfs_inode_close(ntfs_inode *ni) + +int ntfs_inode_real_close(ntfs_inode *ni) { int ret = -1; - + if (!ni) return 0; @@ -324,7 +330,7 @@ int ntfs_inode_close(ntfs_inode *ni) /* Is this a base inode with mapped extent inodes? */ if (ni->nr_extents > 0) { while (ni->nr_extents > 0) { - if (ntfs_inode_close(ni->extent_nis[0])) { + if (ntfs_inode_real_close(ni->extent_nis[0])) { if (errno != EIO) errno = EBUSY; goto err; @@ -364,14 +370,16 @@ int ntfs_inode_close(ntfs_inode *ni) /* Ignore errors, they don't really matter. */ if (tmp_nis) base_ni->extent_nis = tmp_nis; - } else if (tmp_nis) + } else if (tmp_nis) { free(tmp_nis); + base_ni->extent_nis = (ntfs_inode**)NULL; + } /* Allow for error checking. */ i = -1; break; } - - /* + + /* * We could successfully sync, so only log this error * and try to sync other inode extents too. */ @@ -379,7 +387,7 @@ int ntfs_inode_close(ntfs_inode *ni) ntfs_log_error("Extent inode %lld was not found\n", (long long)ni->mft_no); } - + __ntfs_inode_release(ni); ret = 0; err: @@ -387,6 +395,154 @@ err: return ret; } +#if CACHE_NIDATA_SIZE + +/* + * Free an inode structure when there is not more space + * in the cache + */ + +void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached) +{ + ntfs_inode_real_close(((const struct CACHED_NIDATA*)cached)->ni); +} + +/* + * Compute a hash value for an inode entry + */ + +int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item) +{ + return (((const struct CACHED_NIDATA*)item)->inum + % (2*CACHE_NIDATA_SIZE)); +} + +/* + * inum comparing for entering/fetching from cache + */ + +static int idata_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + return (((const struct CACHED_NIDATA*)cached)->inum + != ((const struct CACHED_NIDATA*)wanted)->inum); +} + +/* + * Invalidate an inode entry when not needed anymore. + * The entry should have been synced, it may be reused later, + * if it is requested before it is dropped from cache. + */ + +void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref) +{ + struct CACHED_NIDATA item; + int count; + + item.inum = MREF(mref); + item.ni = (ntfs_inode*)NULL; + item.pathname = (const char*)NULL; + item.varsize = 0; + count = ntfs_invalidate_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare,CACHE_FREE); +} + +#endif + +/* + * Open an inode + * + * When possible, an entry recorded in the cache is reused + * + * **NEVER REOPEN** an inode, this can lead to a duplicated + * cache entry (hard to detect), and to an obsolete one being + * reused. System files are however protected from being cached. + */ + +ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) +{ + ntfs_inode *ni; +#if CACHE_NIDATA_SIZE + struct CACHED_NIDATA item; + struct CACHED_NIDATA *cached; + + /* fetch idata from cache */ + item.inum = MREF(mref); + debug_double_inode(item.inum,1); + item.pathname = (const char*)NULL; + item.varsize = 0; + cached = (struct CACHED_NIDATA*)ntfs_fetch_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare); + if (cached) { + ni = cached->ni; + /* do not keep open entries in cache */ + ntfs_remove_cache(vol->nidata_cache, + (struct CACHED_GENERIC*)cached,0); + } else { + ni = ntfs_inode_real_open(vol, mref); + } +#else + ni = ntfs_inode_real_open(vol, mref); +#endif + return (ni); +} + +/* + * Close an inode entry + * + * If cacheing is in use, the entry is synced and kept available + * in cache for further use. + * + * System files (inode < 16 or having the IS_4 flag) are protected + * against being cached. + */ + +int ntfs_inode_close(ntfs_inode *ni) +{ + int res; +#if CACHE_NIDATA_SIZE + BOOL dirty; + struct CACHED_NIDATA item; + + if (ni) { + debug_double_inode(ni->mft_no,0); + /* do not cache system files : could lead to double entries */ + if (ni->vol && ni->vol->nidata_cache + && ((ni->mft_no == FILE_root) + || ((ni->mft_no >= FILE_first_user) + && !(ni->mrec->flags & MFT_RECORD_IS_4)))) { + /* If we have dirty metadata, write it out. */ + dirty = NInoDirty(ni) || NInoAttrListDirty(ni); + if (dirty) { + res = ntfs_inode_sync(ni); + /* do a real close if sync failed */ + if (res) + ntfs_inode_real_close(ni); + } else + res = 0; + + if (!res) { + /* feed idata into cache */ + item.inum = ni->mft_no; + item.ni = ni; + item.pathname = (const char*)NULL; + item.varsize = 0; + debug_cached_inode(ni); + ntfs_enter_cache(ni->vol->nidata_cache, + GENERIC(&item), idata_cache_compare); + } + } else { + /* cache not ready or system file, really close */ + res = ntfs_inode_real_close(ni); + } + } else + res = 0; +#else + res = ntfs_inode_real_close(ni); +#endif + return (res); +} + /** * ntfs_extent_inode_open - load an extent inode and attach it to its base * @base_ni: base ntfs inode @@ -424,11 +580,11 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) ntfs_log_perror("%s", __FUNCTION__); return NULL; } - + ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", (unsigned long long)mft_no, (unsigned long long)base_ni->mft_no); - + /* Is the extent inode already open and attached to the base inode? */ if (base_ni->nr_extents > 0) { extent_nis = base_ni->extent_nis; @@ -561,11 +717,11 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); std_info->file_attributes = ni->flags; - if (test_nino_flag(ni, TimesDirty)) { - std_info->creation_time = utc2ntfs(ni->creation_time); - std_info->last_data_change_time = utc2ntfs(ni->last_data_change_time); - std_info->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - std_info->last_access_time = utc2ntfs(ni->last_access_time); + if (!test_nino_flag(ni, TimesSet)) { + std_info->creation_time = ni->creation_time; + std_info->last_data_change_time = ni->last_data_change_time; + std_info->last_mft_change_time = ni->last_mft_change_time; + std_info->last_access_time = ni->last_access_time; } /* JPA update v3.x extensions, ensuring consistency */ @@ -595,12 +751,15 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) * * Return 0 on success or -1 on error with errno set to the error code. */ -static int ntfs_inode_sync_file_name(ntfs_inode *ni) +static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) { ntfs_attr_search_ctx *ctx = NULL; ntfs_index_context *ictx; ntfs_inode *index_ni; FILE_NAME_ATTR *fn; + FILE_NAME_ATTR *fnx; + REPARSE_POINT *rpp; + le32 reparse_tag; int err = 0; ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); @@ -610,6 +769,17 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) err = errno; goto err_out; } + /* Collect the reparse tag, if any */ + reparse_tag = cpu_to_le32(0); + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + rpp = (REPARSE_POINT*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + reparse_tag = rpp->reparse_tag; + } + ntfs_attr_reinit_search_ctx(ctx); + } /* Walk through all FILE_NAME attributes and update them. */ while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + @@ -624,7 +794,10 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) */ index_ni = ni; } else - index_ni = ntfs_inode_open(ni->vol, + if (dir_ni) + index_ni = dir_ni; + else + index_ni = ntfs_inode_open(ni->vol, le64_to_cpu(fn->parent_directory)); if (!index_ni) { if (!err) @@ -639,7 +812,8 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) err = errno; ntfs_log_perror("Failed to get index ctx, inode %lld", (long long)index_ni->mft_no); - if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) err = errno; continue; } @@ -658,21 +832,34 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) continue; } /* Update flags and file size. */ - fn = (FILE_NAME_ATTR *)ictx->data; - fn->file_attributes = - (fn->file_attributes & ~FILE_ATTR_VALID_FLAGS) | + fnx = (FILE_NAME_ATTR *)ictx->data; + fnx->file_attributes = + (fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) | (ni->flags & FILE_ATTR_VALID_FLAGS); - fn->allocated_size = cpu_to_sle64(ni->allocated_size); - fn->data_size = cpu_to_sle64(ni->data_size); - if (test_nino_flag(ni, TimesDirty)) { - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fnx->data_size = fnx->allocated_size + = const_cpu_to_le64(0); + else { + fnx->allocated_size = cpu_to_sle64(ni->allocated_size); + fnx->data_size = cpu_to_sle64(ni->data_size); + } + /* update or clear the reparse tag in the index */ + fnx->reparse_point_tag = reparse_tag; + if (!test_nino_flag(ni, TimesSet)) { + fnx->creation_time = ni->creation_time; + fnx->last_data_change_time = ni->last_data_change_time; + fnx->last_mft_change_time = ni->last_mft_change_time; + fnx->last_access_time = ni->last_access_time; + } else { + fnx->creation_time = fn->creation_time; + fnx->last_data_change_time = fn->last_data_change_time; + fnx->last_mft_change_time = fn->last_mft_change_time; + fnx->last_access_time = fn->last_access_time; } ntfs_index_entry_mark_dirty(ictx); ntfs_index_ctx_put(ictx); - if ((ni != index_ni) && ntfs_inode_close(index_ni) && !err) + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) err = errno; } /* Check for real error occurred. */ @@ -714,11 +901,10 @@ err_out: * EBUSY - Inode and/or one of its extents is busy, try again later. * EIO - I/O error while writing the inode (or one of its extents). */ -int ntfs_inode_sync(ntfs_inode *ni) +static int ntfs_inode_sync_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) { int ret = 0; int err = 0; - if (!ni) { errno = EINVAL; ntfs_log_error("Failed to sync NULL inode\n"); @@ -740,7 +926,7 @@ int ntfs_inode_sync(ntfs_inode *ni) /* Update FILE_NAME's in the index. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && NInoFileNameTestAndClearDirty(ni) && - ntfs_inode_sync_file_name(ni)) { + ntfs_inode_sync_file_name(ni, dir_ni)) { if (!err || errno == EIO) { err = errno; if (err != EIO) @@ -768,8 +954,8 @@ int ntfs_inode_sync(ntfs_inode *ni) } NInoAttrListSetDirty(ni); goto sync_inode; - } - + } + if (na->data_size == ni->attr_list_size) { if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, ni->attr_list) != ni->attr_list_size) { @@ -791,7 +977,7 @@ int ntfs_inode_sync(ntfs_inode *ni) } ntfs_attr_close(na); } - + sync_inode: /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ if (NInoTestAndClearDirty(ni)) { @@ -817,8 +1003,8 @@ sync_inode: eni = ni->extent_nis[i]; if (!NInoTestAndClearDirty(eni)) continue; - - if (ntfs_mft_record_write(eni->vol, eni->mft_no, + + if (ntfs_mft_record_write(eni->vol, eni->mft_no, eni->mrec)) { if (!err || errno == EIO) { err = errno; @@ -838,11 +1024,33 @@ sync_inode: errno = err; ret = -1; } - + ntfs_log_leave("\n"); return ret; } +int ntfs_inode_sync(ntfs_inode *ni) +{ + return (ntfs_inode_sync_in_dir(ni, (ntfs_inode*)NULL)); +} + +/* + * Close an inode with an open parent inode + */ + +int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + + res = ntfs_inode_sync_in_dir(ni, dir_ni); + if (res) { + if (errno != EIO) + errno = EBUSY; + } else + res = ntfs_inode_close(ni); + return (res); +} + /** * ntfs_inode_add_attrlist - add attribute list to inode and fill it * @ni: opened ntfs inode to which add attribute list @@ -885,19 +1093,19 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) } /* Walk through all attributes. */ while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { - + int ale_size; - + if (ctx->attr->type == AT_ATTRIBUTE_LIST) { err = EIO; ntfs_log_perror("Attribute list already present"); goto put_err_out; } - + ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7; al_len += ale_size; - + aln = realloc(al, al_len); if (!aln) { err = errno; @@ -906,9 +1114,9 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) } ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); al = aln; - + memset(ale, 0, ale_size); - + /* Add attribute to attribute list. */ ale->type = ctx->attr->type; ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + @@ -975,7 +1183,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) ntfs_attr_close(na); goto remove_attrlist_record;; } - + ntfs_attr_put_search_ctx(ctx); ntfs_attr_close(na); return 0; @@ -1076,12 +1284,12 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) * find next, because we don't need such. */ while (ctx->ntfs_ino->mft_no != ni->mft_no) { -retry: +retry: if (ntfs_attr_position(AT_UNUSED, ctx)) goto put_err_out; } - if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && + if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && ctx->attr->type == AT_DATA) goto retry; @@ -1102,10 +1310,10 @@ retry: return 0; } /* - * Reposition to first attribute after $STANDARD_INFORMATION - * and $ATTRIBUTE_LIST instead of simply skipping this attribute - * because in the case when we have got only in-memory attribute - * list then ntfs_attr_lookup will fail when it tries to find + * Reposition to first attribute after $STANDARD_INFORMATION + * and $ATTRIBUTE_LIST instead of simply skipping this attribute + * because in the case when we have got only in-memory attribute + * list then ntfs_attr_lookup will fail when it tries to find * $ATTRIBUTE_LIST. */ ntfs_attr_reinit_search_ctx(ctx); @@ -1129,7 +1337,7 @@ put_err_out: */ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) { - time_t now; + ntfs_time now; if (!ni) { ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); @@ -1140,15 +1348,14 @@ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) NVolReadOnly(ni->vol) || !mask) return; - now = time(NULL); + now = ntfs_current_time(); if (mask & NTFS_UPDATE_ATIME) ni->last_access_time = now; if (mask & NTFS_UPDATE_MTIME) ni->last_data_change_time = now; if (mask & NTFS_UPDATE_CTIME) ni->last_mft_change_time = now; - - set_nino_flag(ni, TimesDirty); + NInoFileNameSetDirty(ni); NInoSetDirty(ni); } @@ -1161,7 +1368,7 @@ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) * Check if the mft record given by @mft_no and @attr contains the bad sector * list. Please note that mft record numbers describing $Badclus extent inodes * will not match the current $Badclus:$Bad check. - * + * * On success return 1 if the file is $Badclus:$Bad, otherwise return 0. * On error return -1 with errno set to the error code. */ @@ -1175,7 +1382,7 @@ int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) errno = EINVAL; return -1; } - + if (mft_no != FILE_BadClus) return 0; @@ -1209,8 +1416,7 @@ int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) * -errno if failed */ -int ntfs_inode_get_times(const char *path __attribute__((unused)), - char *value, size_t size, ntfs_inode *ni) +int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; @@ -1250,7 +1456,7 @@ int ntfs_inode_get_times(const char *path __attribute__((unused)), ret = -ERANGE; } ntfs_attr_put_search_ctx(ctx); - } + } return (ret ? ret : -errno); } @@ -1268,22 +1474,21 @@ int ntfs_inode_get_times(const char *path __attribute__((unused)), * -1 if there were an error (described by errno) */ -int ntfs_inode_set_times(const char *path __attribute__((unused)), - const char *value, size_t size, - int flags, ntfs_inode *ni) +int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, + int flags) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; FILE_NAME_ATTR *fn; const u64 *times; - le64 now; + ntfs_time now; int cnt; int ret; ret = -1; if ((size >= 8) && !(flags & XATTR_CREATE)) { times = (const u64*)value; - now = utc2ntfs(time((time_t*)NULL)); + now = ntfs_current_time(); /* update the standard information attribute */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (ctx) { @@ -1296,16 +1501,31 @@ int ntfs_inode_set_times(const char *path __attribute__((unused)), std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); /* - * Do not mark times dirty to avoid - * overwriting them when the inode is closed. + * Mark times set to avoid overwriting + * them when the inode is closed. + * The inode structure must also be updated + * (with loss of precision) because of cacheing. + * TODO : use NTFS precision in inode, and + * return sub-second times in getattr() */ + set_nino_flag(ni, TimesSet); std_info->creation_time = cpu_to_le64(times[0]); - if (size >= 16) + ni->creation_time + = std_info->creation_time; + if (size >= 16) { std_info->last_data_change_time = cpu_to_le64(times[1]); - if (size >= 24) + ni->last_data_change_time + = std_info->last_data_change_time; + } + if (size >= 24) { std_info->last_access_time = cpu_to_le64(times[2]); + ni->last_access_time + = std_info->last_access_time; + } std_info->last_mft_change_time = now; + ni->last_mft_change_time = now; ntfs_inode_mark_dirty(ctx->ntfs_ino); + NInoFileNameSetDirty(ni); /* update the file names attributes */ ntfs_attr_reinit_search_ctx(ctx); @@ -1315,10 +1535,6 @@ int ntfs_inode_set_times(const char *path __attribute__((unused)), 0, NULL, 0, ctx)) { fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); - /* - * Do not mark times dirty to avoid - * overwriting them when the inode is closed. - */ fn->creation_time = cpu_to_le64(times[0]); if (size >= 16) diff --git a/source/libntfs/inode.h b/source/libntfs/inode.h index 3abc878c..5a6f7da6 100644 --- a/source/libntfs/inode.h +++ b/source/libntfs/inode.h @@ -32,6 +32,7 @@ typedef struct _ntfs_inode ntfs_inode; #include "layout.h" #include "support.h" #include "volume.h" +#include "ntfstime.h" /** * enum ntfs_inode_state_bits - @@ -49,7 +50,8 @@ typedef enum { NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated in the index. */ NI_v3_Extensions, /* 1: JPA v3.x extensions present. */ - NI_TimesDirty, /* 1: Times need to be updated */ + NI_TimesSet, /* 1: Use times which were set */ + NI_KnownSize, /* 1: Set if sizes are meaningful */ } ntfs_inode_state_bits; #define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) @@ -135,8 +137,11 @@ struct _ntfs_inode { * These two fields are used to sync filename index and guaranteed to be * correct, however value in index itself maybe wrong (windows itself * do not update them properly). + * For directories, they hold the index size, provided the + * flag KnownSize is set. */ - s64 data_size; /* Data size of unnamed DATA attribute. */ + s64 data_size; /* Data size of unnamed DATA attribute + (or INDEX_ROOT for directories) */ s64 allocated_size; /* Allocated size stored in the filename index. (NOTE: Equal to allocated size of the unnamed data attribute for normal or @@ -149,10 +154,10 @@ struct _ntfs_inode { * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME * attribute in the index. */ - time_t creation_time; - time_t last_data_change_time; - time_t last_mft_change_time; - time_t last_access_time; + ntfs_time creation_time; + ntfs_time last_data_change_time; + ntfs_time last_mft_change_time; + ntfs_time last_access_time; /* NTFS 3.x extensions added by JPA */ /* only if NI_v3_Extensions is set in state */ le32 owner_id; @@ -177,6 +182,19 @@ extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); extern int ntfs_inode_close(ntfs_inode *ni); +extern int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_NIDATA_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_inode_real_close(ntfs_inode *ni); +extern void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref); +extern void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached); +extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item); + +#endif + extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref); @@ -195,9 +213,13 @@ extern int ntfs_inode_free_space(ntfs_inode *ni, int size); extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a); -extern int ntfs_inode_get_times(const char *path, char *value, - size_t size, ntfs_inode *ni); -extern int ntfs_inode_set_times(const char *path, const char *value, - size_t size, int flags, ntfs_inode *ni); +extern int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size); + +extern int ntfs_inode_set_times(ntfs_inode *ni, const char *value, + size_t size, int flags); + +/* debugging */ +#define debug_double_inode(num, type) +#define debug_cached_inode(ni) #endif /* defined _NTFS_INODE_H */ diff --git a/source/libntfs/lcnalloc.c b/source/libntfs/lcnalloc.c index ae678430..e84d2431 100644 --- a/source/libntfs/lcnalloc.c +++ b/source/libntfs/lcnalloc.c @@ -473,7 +473,7 @@ done_zones_check: ntfs_log_trace("Switching zone.\n"); pass = 1; if (rlpos) { - LCN tc = tc = rl[rlpos - 1].lcn + + LCN tc = rl[rlpos - 1].lcn + rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP; if (used_zone_pos) @@ -609,6 +609,42 @@ out: return ret; } +/* + * Basic cluster run free + * Returns 0 if successful + */ + +int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count) +{ + s64 nr_freed = 0; + int ret = -1; + + ntfs_log_trace("Entering.\n"); + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)lcn, (long long)count); + + if (lcn >= 0) { + update_full_status(vol,lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn, + count)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)lcn, + (long long)count); + goto out; + } + nr_freed += count; + } + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; +} + /** * ntfs_cluster_free - free clusters on an ntfs volume * @vol: mounted ntfs volume on which to free the clusters diff --git a/source/libntfs/lcnalloc.h b/source/libntfs/lcnalloc.h index e87ca43a..cbf4c5cd 100644 --- a/source/libntfs/lcnalloc.h +++ b/source/libntfs/lcnalloc.h @@ -42,6 +42,7 @@ extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone); extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl); +extern int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count); extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count); diff --git a/source/libntfs/logfile.c b/source/libntfs/logfile.c index 4a129cb4..277ad142 100644 --- a/source/libntfs/logfile.c +++ b/source/libntfs/logfile.c @@ -699,8 +699,8 @@ BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) */ int ntfs_empty_logfile(ntfs_attr *na) { - /*s64 pos, count; - char buf[NTFS_BUF_SIZE];*/ + s64 pos, count; + char buf[NTFS_BUF_SIZE]; ntfs_log_trace("Entering.\n"); @@ -713,7 +713,7 @@ int ntfs_empty_logfile(ntfs_attr *na) return -1; } - /*memset(buf, -1, NTFS_BUF_SIZE); + memset(buf, -1, NTFS_BUF_SIZE); pos = 0; while ((count = na->data_size - pos) > 0) { @@ -729,7 +729,7 @@ int ntfs_empty_logfile(ntfs_attr *na) return -1; } pos += count; - }*/ + } NVolSetLogFileEmpty(na->ni->vol); diff --git a/source/libntfs/mft.c b/source/libntfs/mft.c index ce138d2e..e93c6646 100644 --- a/source/libntfs/mft.c +++ b/source/libntfs/mft.c @@ -1478,8 +1478,7 @@ found_free_rec: ni->flags = 0; ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = - ni->last_access_time = time(NULL); - set_nino_flag(ni, TimesDirty); + ni->last_access_time = ntfs_current_time(); /* Update the default mft allocation position if it was used. */ if (!base_ni) vol->mft_data_pos = bit + 1; @@ -1781,8 +1780,7 @@ found_free_rec: ni->flags = 0; ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = - ni->last_access_time = time(NULL); - set_nino_flag(ni, TimesDirty); + ni->last_access_time = ntfs_current_time(); /* Update the default mft allocation position if it was used. */ if (!base_ni) vol->mft_data_pos = bit + 1; @@ -1862,7 +1860,11 @@ int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni) } /* Throw away the now freed inode. */ +#if CACHE_NIDATA_SIZE + if (!ntfs_inode_real_close(ni)) { +#else if (!ntfs_inode_close(ni)) { +#endif vol->free_mft_records++; return 0; } diff --git a/source/libntfs/misc.c b/source/libntfs/misc.c index 8e662a6a..b2e17cbf 100644 --- a/source/libntfs/misc.c +++ b/source/libntfs/misc.c @@ -1,7 +1,6 @@ /** * misc.c : miscellaneous : * - dealing with errors in memory allocation - * - data caching * * Copyright (c) 2008 Jean-Pierre Andre * @@ -33,7 +32,6 @@ #endif #include "types.h" -#include "security.h" #include "misc.h" #include "logging.h" @@ -61,304 +59,3 @@ void *ntfs_malloc(size_t size) ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); return p; } - -/* - * General functions to deal with LRU caches - * - * The cached data have to be organized in a structure in which - * the first fields must follow a mandatory pattern and further - * fields may contain any fixed size data. They are stored in an - * LRU list. - * - * A compare function must be provided for finding a wanted entry - * in the cache. Another function may be provided for invalidating - * an entry to facilitate multiple invalidation. - * - * These functions never return error codes. When there is a - * shortage of memory, data is simply not cached. - */ - -/* - * Fetch an entry from cache - * - * returns the cache entry, or NULL if not available - */ - -struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *wanted, cache_compare compare) -{ - struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; - - current = (struct CACHED_GENERIC*)NULL; - if (cache) { - /* - * Search sequentially in LRU list - */ - current = cache->most_recent_entry; - previous = (struct CACHED_GENERIC*)NULL; - while (current - && compare(current, wanted)) { - previous = current; - current = current->next; - } - if (current) - cache->hits++; - if (current && previous) { - /* - * found and not at head of list, unlink from current - * position and relink as head of list - */ - previous->next = current->next; - current->next = cache->most_recent_entry; - cache->most_recent_entry = current; - } - cache->reads++; - } - return (current); -} - -/* - * Enter an inode number into cache - * returns the cache entry or NULL if not possible - */ - -struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, cache_compare compare) -{ - struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; - struct CACHED_GENERIC *before; - - current = (struct CACHED_GENERIC*)NULL; - if (cache) { - - /* - * Search sequentially in LRU list to locate the end, - * and find out whether the entry is already in list - * As we normally go to the end, no statistics is - * kept. - */ - current = cache->most_recent_entry; - previous = (struct CACHED_GENERIC*)NULL; - before = (struct CACHED_GENERIC*)NULL; - while (current - && compare(current, item)) { - before = previous; - previous = current; - current = current->next; - } - - if (!current) { - /* - * Not in list, get a free entry or reuse the - * last entry, and relink as head of list - * Note : we assume at least three entries, so - * before, previous and first are different when - * an entry is reused. - */ - - if (cache->free_entry) { - current = cache->free_entry; - cache->free_entry = cache->free_entry->next; - if (item->varsize) { - current->variable = ntfs_malloc( - item->varsize); - } else - current->variable = (void*)NULL; - current->varsize = item->varsize; - } else { - before->next = (struct CACHED_GENERIC*)NULL; - current = previous; - if (item->varsize) { - if (current->varsize) - current->variable = realloc( - current->variable, - item->varsize); - else - current->variable = ntfs_malloc( - item->varsize); - } else { - if (current->varsize) - free(current->variable); - current->variable = (void*)NULL; - } - current->varsize = item->varsize; - } - current->next = cache->most_recent_entry; - cache->most_recent_entry = current; - memcpy(current->fixed, item->fixed, cache->fixed_size); - if (item->varsize) { - if (current->variable) { - memcpy(current->variable, - item->variable, item->varsize); - } else { - /* - * no more memory for variable part - * recycle entry in free list - * not an error, just uncacheable - */ - cache->most_recent_entry = current->next; - current->next = cache->free_entry; - cache->free_entry = current; - current = (struct CACHED_GENERIC*)NULL; - } - } else { - current->variable = (void*)NULL; - current->varsize = 0; - } - } - cache->writes++; - } - return (current); -} - -/* - * Invalidate entries in cache - * - * Several entries may have to be invalidated (at least for inodes - * associated to directories which have been renamed), a different - * compare function may be provided to select entries to invalidate - * - * Returns the number of deleted entries, this can be used by - * the caller to signal a cache corruption if the entry was - * supposed to be found. - */ - -int ntfs_invalidate_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, cache_compare compare) -{ - struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; - int count; - - current = (struct CACHED_GENERIC*)NULL; - count = 0; - if (cache) { - /* - * Search sequentially in LRU list - */ - current = cache->most_recent_entry; - previous = (struct CACHED_GENERIC*)NULL; - while (current) { - if (!compare(current, item)) { - /* - * Relink into free list - */ - if (previous) - previous->next = current->next; - else - cache->most_recent_entry = current->next; - current->next = cache->free_entry; - cache->free_entry = current; - if (current->variable) - free(current->variable); - current->varsize = 0; - if (previous) - current = previous->next; - else - current = cache->most_recent_entry; - count++; - } else { - previous = current; - current = current->next; - } - } - } - return (count); -} - -/* - * Free memory allocated to a cache - */ - -static void ntfs_free_cache(struct CACHE_HEADER *cache) -{ - struct CACHED_GENERIC *entry; - - if (cache) { - for (entry=cache->most_recent_entry; entry; entry=entry->next) - if (entry->variable) - free(entry->variable); - free(cache); - } -} - -/* - * Create a cache - * - * Returns the cache header, or NULL if the cache could not be created - */ - -static struct CACHE_HEADER *ntfs_create_cache(const char *name, - int full_item_size, int item_count) -{ - struct CACHE_HEADER *cache; - struct CACHED_GENERIC *p; - struct CACHED_GENERIC *q; - int i; - - cache = (struct CACHE_HEADER*) - ntfs_malloc(sizeof(struct CACHE_HEADER) - + item_count*full_item_size); - if (cache) { - cache->name = name; - cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC); - cache->reads = 0; - cache->writes = 0; - cache->hits = 0; - /* chain the entries, and mark an invalid entry */ - cache->most_recent_entry = (struct CACHED_GENERIC*)NULL; - cache->free_entry = &cache->entry[0]; - p = &cache->entry[0]; - for (i=0; i<(item_count - 1); i++) { - q = (struct CACHED_GENERIC*)((char*)p + full_item_size); - p->next = q; - p->variable = (void*)NULL; - p->varsize = 0; - p = q; - } - /* special for the last entry */ - p->next = (struct CACHED_GENERIC*)NULL; - p->variable = (void*)NULL; - p->varsize = 0; - } - return (cache); -} - -/* - * Create all LRU caches - * - * No error return, if creation is not possible, cacheing will - * just be not available - */ - -void ntfs_create_lru_caches(ntfs_volume *vol) -{ -#if CACHE_INODE_SIZE - /* inode cache */ - vol->xinode_cache = ntfs_create_cache("inode", - sizeof(struct CACHED_INODE), CACHE_INODE_SIZE); -#endif - vol->securid_cache = ntfs_create_cache("securid", - sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE); -#if CACHE_LEGACY_SIZE - vol->legacy_cache = ntfs_create_cache("legacy", - sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE); -#endif -} - -/* - * Free all LRU caches - */ - -void ntfs_free_lru_caches(ntfs_volume *vol) -{ -#if CACHE_INODE_SIZE - ntfs_free_cache(vol->xinode_cache); -#endif - ntfs_free_cache(vol->securid_cache); -#if CACHE_LEGACY_SIZE - ntfs_free_cache(vol->legacy_cache); -#endif -} diff --git a/source/libntfs/misc.h b/source/libntfs/misc.h index dbdee9a9..a03e964e 100644 --- a/source/libntfs/misc.h +++ b/source/libntfs/misc.h @@ -1,7 +1,6 @@ /* * misc.h : miscellaneous exports * - memory allocation - * - LRU caches * * Copyright (c) 2008 Jean-Pierre Andre * @@ -24,49 +23,6 @@ #ifndef _NTFS_MISC_H_ #define _NTFS_MISC_H_ -#include "volume.h" - -struct CACHED_GENERIC { - struct CACHED_GENERIC *next; - void *variable; - size_t varsize; - void *fixed[0]; -} ; - -struct CACHED_INODE { - struct CACHED_INODE *next; - const char *pathname; - size_t varsize; - /* above fields must match "struct CACHED_GENERIC" */ - u64 inum; -} ; - -typedef int (*cache_compare)(const struct CACHED_GENERIC *cached, - const struct CACHED_GENERIC *item); - -struct CACHE_HEADER { - const char *name; - struct CACHED_GENERIC *most_recent_entry; - struct CACHED_GENERIC *free_entry; - unsigned long reads; - unsigned long writes; - unsigned long hits; - int fixed_size; - struct CACHED_GENERIC entry[0]; -} ; - - /* cast to generic, avoiding gcc warnings */ -#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr)) - -struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *wanted, cache_compare compare); -struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, cache_compare compare); -int ntfs_invalidate_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, cache_compare compare); -void ntfs_create_lru_caches(ntfs_volume *vol); -void ntfs_free_lru_caches(ntfs_volume *vol); - void *ntfs_calloc(size_t size); void *ntfs_malloc(size_t size); diff --git a/source/libntfs/ntfs.c b/source/libntfs/ntfs.c index 85a01159..10451e47 100644 --- a/source/libntfs/ntfs.c +++ b/source/libntfs/ntfs.c @@ -70,7 +70,7 @@ static const devoptab_t devops_ntfs = { void ntfsInit (void) { static bool isInit = false; - + // Initialise ntfs-3g (if not already done so) if (!isInit) { isInit = true; @@ -83,9 +83,9 @@ void ntfsInit (void) #endif // Set our current local ntfs_set_locale(); - + } - + return; } @@ -97,14 +97,14 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) int partition_count = 0; sec_t part_lba = 0; int i; - + union { - u8 buffer[BYTES_PER_SECTOR]; + u8 buffer[512]; MASTER_BOOT_RECORD mbr; EXTENDED_BOOT_RECORD ebr; NTFS_BOOT_SECTOR boot; } sector; - + // Sanity check if (!interface) { errno = EINVAL; @@ -112,10 +112,10 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) } if (!partitions) return 0; - + // Initialise ntfs-3g ntfsInit(); - + // Start the device and check that it is inserted if (!interface->startup()) { errno = EIO; @@ -124,7 +124,7 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) if (!interface->isInserted()) { return 0; } - + // Read the first sector on the device if (!interface->readSectors(0, 1, §or.buffer)) { errno = EIO; @@ -135,12 +135,12 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) if (sector.mbr.signature == MBR_SIGNATURE) { memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD)); ntfs_log_debug("Valid Master Boot Record found\n"); - + // Search the partition table for all NTFS partitions (max. 4 primary partitions) for (i = 0; i < 4; i++) { partition = &mbr.partitions[i]; part_lba = le32_to_cpu(mbr.partitions[i].lba_start); - + ntfs_log_debug("Partition %i: %s, sector %d, type 0x%x\n", i + 1, partition->status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", part_lba, partition->type); @@ -151,11 +151,11 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) // Ignore empty partitions case PARTITION_TYPE_EMPTY: continue; - + // NTFS partition case PARTITION_TYPE_NTFS: { ntfs_log_debug("Partition %i: Claims to be NTFS\n", i + 1); - + // Read and validate the NTFS partition if (interface->readSectors(part_lba, 1, §or)) { if (sector.boot.oem_id == NTFS_OEM_ID) { @@ -168,28 +168,28 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) ntfs_log_debug("Partition %i: Invalid NTFS boot sector, not actually NTFS\n", i + 1); } } - + break; - + } - + // DOS 3.3+ or Windows 95 extended partition case PARTITION_TYPE_DOS33_EXTENDED: case PARTITION_TYPE_WIN95_EXTENDED: { ntfs_log_debug("Partition %i: Claims to be Extended\n", i + 1); - + // Walk the extended partition chain, finding all NTFS partitions within it sec_t ebr_lba = part_lba; sec_t next_erb_lba = 0; do { - + // Read and validate the extended boot record if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) { if (sector.ebr.signature == EBR_SIGNATURE) { ntfs_log_debug("Logical Partition @ %d: type 0x%x\n", ebr_lba + next_erb_lba, sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", sector.ebr.partition.type); - + // Get the start sector of the current partition // and the next extended boot record in the chain part_lba = ebr_lba + next_erb_lba + le32_to_cpu(sector.ebr.partition.lba_start); @@ -213,16 +213,16 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) next_erb_lba = 0; } } - + } while (next_erb_lba); - + break; - + } - + // Unknown or unsupported partition type default: { - + // Check if this partition has a valid NTFS boot record anyway, // it might be misrepresented due to a lazy partition editor if (interface->readSectors(part_lba, 1, §or)) { @@ -237,15 +237,15 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) } } } - + break; - + } - + } - + } - + // Else it is assumed this device has no master boot record } else { ntfs_log_debug("No Master Boot Record was found!\n"); @@ -262,12 +262,12 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) } } } - + } // Shutdown the device /*interface->shutdown();*/ - + // Return the found partitions (if any) if (partition_count > 0) { *partitions = (sec_t*)ntfs_alloc(sizeof(sec_t) * partition_count); @@ -276,7 +276,7 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) return partition_count; } } - + return 0; } @@ -293,7 +293,7 @@ int ntfsMountAll (ntfs_md **mounts, u32 flags) // Initialise ntfs-3g ntfsInit(); - + // Find and mount all NTFS partitions on all known devices for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { disc = &discs[i]; @@ -302,7 +302,7 @@ int ntfsMountAll (ntfs_md **mounts, u32 flags) for (j = 0, k = 0; j < partition_count; j++) { // Find the next unused mount name - do { + do { sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); if (k >= NTFS_MAX_MOUNTS) { ntfs_free(partitions); @@ -320,12 +320,12 @@ int ntfsMountAll (ntfs_md **mounts, u32 flags) mount_count++; } } - + } ntfs_free(partitions); } } - + // Return the mounts (if any) if (mount_count > 0 && mounts) { *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); @@ -334,7 +334,7 @@ int ntfsMountAll (ntfs_md **mounts, u32 flags) return mount_count; } } - + return 0; } @@ -348,7 +348,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag int partition_count = 0; char name[128]; int i, j, k; - + // Sanity check if (!interface) { errno = EINVAL; @@ -357,7 +357,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag // Initialise ntfs-3g ntfsInit(); - + // Find the specified device then find and mount all NTFS partitions on it for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { if (discs[i].interface == interface) { @@ -365,9 +365,9 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag partition_count = ntfsFindPartitions(disc->interface, &partitions); if (partition_count > 0 && partitions) { for (j = 0, k = 0; j < partition_count; j++) { - + // Find the next unused mount name - do { + do { sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); if (k >= NTFS_MAX_MOUNTS) { ntfs_free(partitions); @@ -375,7 +375,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag return -1; } } while (ntfsGetDevice(name, false)); - + // Mount the partition if (mount_count < NTFS_MAX_MOUNTS) { if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) { @@ -385,7 +385,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag mount_count++; } } - + } ntfs_free(partitions); } @@ -398,7 +398,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag errno = ENODEV; return -1; } - + // Return the mounts (if any) if (mount_count > 0 && mounts) { *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); @@ -415,13 +415,13 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe { ntfs_vd *vd = NULL; gekko_fd *fd = NULL; - + // Sanity check if (!name || !interface) { errno = EINVAL; - return -1; + return false; } - + // Initialise ntfs-3g ntfsInit(); @@ -430,20 +430,20 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe errno = EADDRINUSE; return false; } - + // Check that we can at least read from this device if (!(interface->features & FEATURE_MEDIUM_CANREAD)) { errno = EPERM; return false; } - + // Allocate the volume descriptor vd = (ntfs_vd*)ntfs_alloc(sizeof(ntfs_vd)); if (!vd) { errno = ENOMEM; return false; } - + // Setup the volume descriptor vd->id = interface->ioType; vd->flags = 0; @@ -454,7 +454,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe vd->atime = ((flags & NTFS_UPDATE_ACCESS_TIMES) ? ATIME_ENABLED : ATIME_DISABLED); vd->showHiddenFiles = (flags & NTFS_SHOW_HIDDEN_FILES); vd->showSystemFiles = (flags & NTFS_SHOW_SYSTEM_FILES); - + // Allocate the device driver descriptor fd = (gekko_fd*)ntfs_alloc(sizeof(gekko_fd)); if (!fd) { @@ -462,7 +462,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe errno = ENOMEM; return false; } - + // Setup the device driver descriptor fd->interface = interface; fd->startSector = startSector; @@ -470,7 +470,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe fd->sectorCount = 0; fd->cachePageCount = cachePageCount; fd->cachePageSize = cachePageSize; - + // Allocate the device driver vd->dev = ntfs_device_alloc(name, 0, &ntfs_device_gekko_io_ops, fd); if (!vd->dev) { @@ -478,7 +478,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe ntfs_free(vd); return false; } - + // Build the mount flags if (flags & NTFS_READ_ONLY) vd->flags |= MS_RDONLY; @@ -493,10 +493,10 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe vd->flags |= MS_RECOVER; if (flags & NTFS_IGNORE_HIBERFILE) vd->flags |= MS_IGNORE_HIBERFILE; - + if (vd->flags & MS_RDONLY) ntfs_log_debug("Mounting \"%s\" as read-only\n", name); - + // Mount the device vd->vol = ntfs_device_mount(vd->dev, vd->flags); if (!vd->vol) { @@ -511,14 +511,14 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe ntfs_free(vd); return false; } - + // Initialise the volume descriptor if (ntfsInitVolume(vd)) { ntfs_umount(vd->vol, true); ntfs_free(vd); return false; } - + // Add the device to the devoptab table if (ntfsAddDevice(name, vd)) { ntfsDeinitVolume(vd); @@ -526,7 +526,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe ntfs_free(vd); return false; } - + return true; } @@ -538,19 +538,19 @@ void ntfsUnmount (const char *name, bool force) vd = ntfsGetVolume(name); if (!vd) return; - + // Remove the device from the devoptab table ntfsRemoveDevice(name); - + // Deinitialise the volume descriptor ntfsDeinitVolume(vd); - + // Unmount the volume ntfs_umount(vd->vol, force); - + // Free the volume descriptor ntfs_free(vd); - + return; } @@ -560,13 +560,13 @@ const char *ntfsGetVolumeName (const char *name) //ntfs_attr *na = NULL; //ntfschar *ulabel = NULL; //char *volumeName = NULL; - + // Sanity check if (!name) { errno = EINVAL; return NULL; } - + // Get the devices volume descriptor vd = ntfsGetVolume(name); if (!vd) { @@ -574,15 +574,15 @@ const char *ntfsGetVolumeName (const char *name) return NULL; } return vd->vol->vol_name; -/* - +/* + // If the volume name has already been cached then just use that if (vd->name[0]) return vd->name; - + // Lock ntfsLock(vd); - + // Check if the volume name attribute exists na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); if (!na) { @@ -598,7 +598,7 @@ const char *ntfsGetVolumeName (const char *name) errno = ENOMEM; return false; } - + // Read the volume name if (ntfs_attr_pread(na, 0, na->data_size, ulabel) != na->data_size) { ntfs_free(ulabel); @@ -618,7 +618,7 @@ const char *ntfsGetVolumeName (const char *name) // If the volume name was read then cache it (for future fetches) if (volumeName) strcpy(vd->name, volumeName); - + // Close the volume name attribute if (na) ntfs_attr_close(na); @@ -626,12 +626,12 @@ const char *ntfsGetVolumeName (const char *name) // Clean up ntfs_free(volumeName); ntfs_free(ulabel); - + // Unlock ntfsUnlock(vd); - + return vd->name; -*/ +*/ } bool ntfsSetVolumeName (const char *name, const char *volumeName) @@ -640,13 +640,13 @@ bool ntfsSetVolumeName (const char *name, const char *volumeName) ntfs_attr *na = NULL; ntfschar *ulabel = NULL; int ulabel_len; - + // Sanity check if (!name) { errno = EINVAL; return false; } - + // Get the devices volume descriptor vd = ntfsGetVolume(name); if (!vd) { @@ -656,7 +656,7 @@ bool ntfsSetVolumeName (const char *name, const char *volumeName) // Lock ntfsLock(vd); - + // Convert the new volume name to unicode ulabel_len = ntfsLocalToUnicode(volumeName, &ulabel) * sizeof(ntfschar); if (ulabel_len < 0) { @@ -664,56 +664,56 @@ bool ntfsSetVolumeName (const char *name, const char *volumeName) errno = EINVAL; return false; } - + // Check if the volume name attribute exists na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); if (na) { - + // It does, resize it to match the length of the new volume name if (ntfs_attr_truncate(na, ulabel_len)) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } - + // Write the new volume name if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } - + } else { - + // It doesn't, create it now if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } - + } // Reset the volumes name cache (as it has now been changed) vd->name[0] = '\0'; - + // Close the volume name attribute if (na) ntfs_attr_close(na); - + // Sync the volume node if (ntfs_inode_sync(vd->vol->vol_ni)) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } - + // Clean up ntfs_free(ulabel); - + // Unlock ntfsUnlock(vd); - + return true; } diff --git a/source/libntfs/ntfs.h b/source/libntfs/ntfs.h index ff2af30b..d805f4c2 100644 --- a/source/libntfs/ntfs.h +++ b/source/libntfs/ntfs.h @@ -65,7 +65,7 @@ typedef struct _ntfs_md { * * @param INTERFACE The block device to search * @param PARTITIONS (out) A pointer to receive the array of partition start sectors - * + * * @return The number of entries in PARTITIONS or -1 if an error occurred (see errno) * @note The caller is responsible for freeing PARTITIONS when finished with it */ @@ -76,7 +76,7 @@ extern int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitio * * @param MOUNTS (out) A pointer to receive the array of mount descriptors * @param FLAGS Additional mounting flags. (see above) - * + * * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) * @note The caller is responsible for freeing MOUNTS when finished with it * @note All device caches are setup using default values (see above) @@ -89,7 +89,7 @@ extern int ntfsMountAll (ntfs_md **mounts, u32 flags); * @param INTERFACE The block device to mount. * @param MOUNTS (out) A pointer to receive the array of mount descriptors * @param FLAGS Additional mounting flags. (see above) - * + * * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) * @note The caller is responsible for freeing MOUNTS when finished with it * @note The device cache is setup using default values (see above) @@ -103,9 +103,9 @@ extern int ntfsMountDevice (const DISC_INTERFACE* interface, ntfs_md **mounts, u * @param INTERFACE The block device to mount * @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions) * @param CACHEPAGECOUNT The total number of pages in the device cache - * @param CACHEPAGESIZE The number of sectors per cache page + * @param CACHEPAGESIZE The number of sectors per cache page * @param FLAGS Additional mounting flags (see above) - * + * * @return True if mount was successful, false if no partition was found or an error occurred (see errno) * @note ntfsFindPartitions should be used first to locate the partitions start sector */ @@ -139,18 +139,6 @@ extern const char *ntfsGetVolumeName (const char *name); */ extern bool ntfsSetVolumeName (const char *name, const char *volumeName); -/* -typedef struct _FileInfo FileInfo; - -struct _FileInfo -{ - u64 offset[64]; - s64 sector[64]; - u64 count[64]; - u32 num; - u64 filesize; -}; -*/ typedef int (*_ntfs_frag_append_t)(void *ff, u32 offset, u32 sector, u32 count); int _NTFS_get_fragments (const char *path, _ntfs_frag_append_t append_fragment, void *callback_data); diff --git a/source/libntfs/ntfsdir.c b/source/libntfs/ntfsdir.c index 71828cc4..ad54b514 100644 --- a/source/libntfs/ntfsdir.c +++ b/source/libntfs/ntfsdir.c @@ -402,19 +402,22 @@ int ntfs_readdir_filler (DIR_ITER *dirState, const ntfschar *name, const int nam // Convert the entry name to our current local if (ntfsUnicodeToLocal(name, name_len, &entry_name, 0) < 0) { - ntfs_free(entry); return -1; } + if(dir->first && dir->first->mref == FILE_root && + MREF(mref) == FILE_root && strcmp(entry_name, "..") == 0) + { + return 0; + } + // If this is not the parent or self directory reference if ((strcmp(entry_name, ".") != 0) && (strcmp(entry_name, "..") != 0)) { // Open the entry ntfs_inode *ni = ntfs_pathname_to_inode(dir->vd->vol, dir->ni, entry_name); - if (!ni) { - ntfs_free(entry); + if (!ni) return -1; - } // Double check that this entry can be emuerated (as described by the volume descriptor) if (((ni->flags & FILE_ATTR_HIDDEN) && !dir->vd->showHiddenFiles) || @@ -429,13 +432,14 @@ int ntfs_readdir_filler (DIR_ITER *dirState, const ntfschar *name, const int nam } // Allocate a new directory entry - entry = ntfs_alloc(sizeof(ntfs_dir_entry)); + entry = (ntfs_dir_entry *) ntfs_alloc(sizeof(ntfs_dir_entry)); if (!entry) return -1; // Setup the entry entry->name = entry_name; entry->next = NULL; + entry->mref = MREF(mref); // Link the entry to the directory if (!dir->first) { diff --git a/source/libntfs/ntfsdir.h b/source/libntfs/ntfsdir.h index 1b16d2f9..0550592f 100644 --- a/source/libntfs/ntfsdir.h +++ b/source/libntfs/ntfsdir.h @@ -30,6 +30,7 @@ */ typedef struct _ntfs_dir_entry { char *name; + u64 mref; struct _ntfs_dir_entry *next; } ntfs_dir_entry; diff --git a/source/libntfs/ntfsfile.c b/source/libntfs/ntfsfile.c index 262fb877..5db85168 100644 --- a/source/libntfs/ntfsfile.c +++ b/source/libntfs/ntfsfile.c @@ -65,8 +65,8 @@ void ntfsCloseFile (ntfs_file_state *file) // Sync the file (and its attributes) to disc if(file->write) { - ntfsSync(file->vd, file->ni); ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME); + ntfsSync(file->vd, file->ni); } if (file->read) diff --git a/source/libntfs/ntfsfile.h b/source/libntfs/ntfsfile.h index 275c745a..8e5ffcc6 100644 --- a/source/libntfs/ntfsfile.h +++ b/source/libntfs/ntfsfile.h @@ -6,7 +6,7 @@ * * This program/include file 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 + * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program/include file is distributed in the hope that it will be @@ -43,7 +43,7 @@ typedef struct _ntfs_file_state { bool compressed; /* True if file data is compressed */ bool encrypted; /* True if file data is encryted */ off_t pos; /* Current position within the file (in bytes) */ - size_t len; /* Total length of the file (in bytes) */ + u64 len; /* Total length of the file (in bytes) */ struct _ntfs_file_state *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ struct _ntfs_file_state *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ } ntfs_file_state; diff --git a/source/libntfs/ntfsfile_frag.c b/source/libntfs/ntfsfile_frag.c index 8fd75404..55823789 100644 --- a/source/libntfs/ntfsfile_frag.c +++ b/source/libntfs/ntfsfile_frag.c @@ -1,6 +1,6 @@ /** * ntfsfile.c - devoptab file routines for NTFS-based devices. - * + * Copyright (c) 2010 Miigotu * Copyright (c) 2009 Rhys "Shareese" Koedijk * Copyright (c) 2006 Michael "Chishm" Chisholm * @@ -58,22 +58,29 @@ void ntfsCloseFile (ntfs_file_state *file) // Special case fix ups for compressed and/or encrypted files if (file->compressed) - ntfs_attr_pclose(file->data_na); + ntfs_attr_pclose(file->data_na); +#ifdef HAVE_SETXATTR if (file->encrypted) ntfs_efs_fixup_attribute(NULL, file->data_na); - +#endif // Close the file data attribute (if open) if (file->data_na) ntfs_attr_close(file->data_na); - + // Sync the file (and its attributes) to disc if(file->write) + { + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME); ntfsSync(file->vd, file->ni); - + } + + if (file->read) + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); + // Close the file (if open) if (file->ni) ntfsCloseEntry(file->vd, file->ni); - + // Reset the file state file->ni = NULL; file->data_na = NULL; @@ -92,7 +99,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode); ntfs_file_state* file = STATE(fileStruct); - + // Get the volume descriptor for this path file->vd = ntfsGetVolume(path); if (!file->vd) { @@ -122,7 +129,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags ntfsUnlock(file->vd); return -1; } - + // Try and find the file and (if found) ensure that it is not a directory file->ni = ntfsOpenEntry(file->vd, path); if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { @@ -131,10 +138,10 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags r->_errno = EISDIR; return -1; } - + // Are we creating this file? if (flags & O_CREAT) { - + // The file SHOULD NOT already exist if (file->ni) { ntfsCloseEntry(file->vd, file->ni); @@ -142,7 +149,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags r->_errno = EEXIST; return -1; } - + // Create the file file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL); if (!file->ni) { @@ -151,14 +158,14 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags } } - + // Sanity check, the file should be open by now if (!file->ni) { ntfsUnlock(file->vd); r->_errno = ENOENT; return -1; } - + // Open the files data attribute file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0); if(!file->data_na) { @@ -170,7 +177,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags // Determine if this files data is compressed and/or encrypted file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED); file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED); - + // We cannot read/write encrypted files if (file->encrypted) { ntfs_attr_close(file->data_na); @@ -179,7 +186,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags r->_errno = EACCES; return -1; } - + // Make sure we aren't trying to write to a read-only file if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) { ntfs_attr_close(file->data_na); @@ -188,7 +195,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags r->_errno = EROFS; return -1; } - + // Truncate the file if requested if ((flags & O_TRUNC) && file->write) { if (ntfs_attr_truncate(file->data_na, 0)) { @@ -199,14 +206,16 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags return -1; } } - + // Set the files current position and length file->pos = 0; file->len = file->data_na->data_size; + ntfs_log_trace("file->len %d\n", file->len); + // Update file times ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); - + // Insert the file into the double-linked FILO list of open files if (file->vd->firstOpenFile) { file->nextOpenFile = file->vd->firstOpenFile; @@ -217,31 +226,31 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags file->prevOpenFile = NULL; file->vd->firstOpenFile = file; file->vd->openFileCount++; - + // Unlock ntfsUnlock(file->vd); - + return (int)fileStruct; } int ntfs_close_r (struct _reent *r, int fd) { ntfs_log_trace("fd %p\n", fd); - + ntfs_file_state* file = STATE(fd); - + // Sanity check if (!file || !file->vd) { r->_errno = EBADF; return -1; } - + // Lock ntfsLock(file->vd); // Close the file ntfsCloseFile(file); - + // Remove the file from the double-linked FILO list of open files file->vd->openFileCount--; if (file->nextOpenFile) @@ -253,45 +262,45 @@ int ntfs_close_r (struct _reent *r, int fd) // Unlock ntfsUnlock(file->vd); - + return 0; } ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len); - + ntfs_file_state* file = STATE(fd); ssize_t written = 0; off_t old_pos = 0; - + // Sanity check if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } - + // Short circuit cases where we don't actually have to do anything if (!ptr || len <= 0) { return 0; } - + // Lock ntfsLock(file->vd); - + // Check that we are allowed to write to this file if (!file->write) { ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } - + // If we are in append mode, backup the current position and move to the end of the file if (file->append) { old_pos = file->pos; file->pos = file->len; } - + // Write to the files data atrribute while (len) { ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr); @@ -304,26 +313,22 @@ ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len) file->pos += ret; written += ret; } - + // If we are in append mode, restore the current position to were it was prior to this write if (file->append) { file->pos = old_pos; } - + // Mark the file for archiving (if we actually wrote something) if (written) file->ni->flags |= FILE_ATTR_ARCHIVE; - - // Update file times (if we actually wrote something) - if (written) - ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_MCTIME); - + // Update the files data length file->len = file->data_na->data_size; - - // Unlock + + // Unlock ntfsUnlock(file->vd); - + return written; } @@ -346,6 +351,9 @@ int _NTFS_get_fragments (const char *path, if (fd != (int)file) return -12; + + + // Sanity check if (!file || !file->vd || !file->ni || !file->data_na) { //r->_errno = EINVAL; @@ -373,6 +381,7 @@ int _NTFS_get_fragments (const char *path, if (file->pos + len > file->len) { r->_errno = EOVERFLOW; len = file->len - file->pos; + ntfs_log_trace("EOVERFLOW"); } */ @@ -384,6 +393,7 @@ int _NTFS_get_fragments (const char *path, s64 ret = ntfs_attr_getfragments(file->data_na, file->pos, len, offset, append_fragment, callback_data); if (ret <= 0 || ret > len) { + ntfsUnlock(file->vd); //r->_errno = errno; ret_val = -14; if (ret < 0) ret_val = ret; @@ -401,6 +411,7 @@ int _NTFS_get_fragments (const char *path, ret_val = 0; /* + //ntfs_log_trace("file->pos: %d \n", (u32)file->pos); // Update file times (if we actually read something) if (read) ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); @@ -523,7 +534,7 @@ int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len) // Update file times (if we actually changed something) if (file->len != file->data_na->data_size) - ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_MCTIME); + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_AMCTIME); // Update the files data length file->len = file->data_na->data_size; diff --git a/source/libntfs/ntfsinternal.c b/source/libntfs/ntfsinternal.c index b4d39491..fed15bce 100644 --- a/source/libntfs/ntfsinternal.c +++ b/source/libntfs/ntfsinternal.c @@ -34,7 +34,6 @@ #include #endif -#include "usbloader/usbstorage2.h" #include "ntfsinternal.h" #include "ntfsdir.h" #include "ntfsfile.h" @@ -42,11 +41,11 @@ #if defined(__wii__) #include #include - +#include const INTERFACE_ID ntfs_disc_interfaces[] = { { "sd", &__io_wiisd }, - { "usb", &__io_usbstorage2 }, + { "usb", &__io_usbstorage }, { "carda", &__io_gcsda }, { "cardb", &__io_gcsdb }, { NULL, NULL } @@ -77,7 +76,7 @@ int ntfsAddDevice (const char *name, void *deviceData) } // Allocate a devoptab for this device - dev = ntfs_alloc(sizeof(devoptab_t) + strlen(name) + 1); + dev = (devoptab_t *) ntfs_alloc(sizeof(devoptab_t) + strlen(name) + 1); if (!dev) { errno = ENOMEM; return false; @@ -304,7 +303,7 @@ ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel) } // Get the target path of this entry - target = ntfs_make_symlink(path, ni, &attr_size); + target = ntfs_make_symlink(ni, path, &attr_size); if (!target) { ntfsCloseEntry(vd, ni); return NULL; @@ -549,7 +548,6 @@ int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path) } // Update entry times - ntfsUpdateTimes(vd, ni, NTFS_UPDATE_CTIME); ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); // Sync the entry to disc @@ -816,7 +814,7 @@ int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int // do it manually by replacing non-ASCII characters with underscores if (!*outs || outs_len >= ins_len) { if (!*outs) { - *outs = ntfs_alloc(ins_len + 1); + *outs = (char *) ntfs_alloc(ins_len + 1); if (!*outs) { errno = ENOMEM; return -1; diff --git a/source/libntfs/ntfstime.h b/source/libntfs/ntfstime.h index e933b53c..426269d7 100644 --- a/source/libntfs/ntfstime.h +++ b/source/libntfs/ntfstime.h @@ -3,6 +3,7 @@ * * Copyright (c) 2005 Anton Altaparmakov * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -26,44 +27,95 @@ #ifdef HAVE_TIME_H #include #endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_GETTIMEOFDAY +#include +#endif #include "types.h" +/* + * There are four times more conversions of internal representation + * to ntfs representation than any other conversion, so the most + * efficient internal representation is ntfs representation + * (with low endianness) + */ +typedef sle64 ntfs_time; + #define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000) /** - * ntfs2utc - Convert an NTFS time to Unix time + * ntfs2timespec - Convert an NTFS time to Unix time * @ntfs_time: An NTFS time in 100ns units since 1601 * * NTFS stores times as the number of 100ns intervals since January 1st 1601 at * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD. * - * Return: n A Unix time (number of seconds since 1970) + * Return: A Unix time (number of seconds since 1970, and nanoseconds) */ -static __inline__ time_t ntfs2utc(s64 ntfs_time) +static __inline__ struct timespec ntfs2timespec(ntfs_time ntfstime) { - return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000; + struct timespec spec; + s64 cputime; + + cputime = sle64_to_cpu(ntfstime); + spec.tv_sec = (cputime - (NTFS_TIME_OFFSET)) / 10000000; + spec.tv_nsec = (cputime - (NTFS_TIME_OFFSET) + - (s64)spec.tv_sec*10000000)*100; + /* force zero nsec for overflowing dates */ + if ((spec.tv_nsec < 0) || (spec.tv_nsec > 999999999)) + spec.tv_nsec = 0; + return (spec); } /** - * utc2ntfs - Convert Linux time to NTFS time + * timespec2ntfs - Convert Linux time to NTFS time * @utc_time: Linux time to convert to NTFS * * Convert the Linux time @utc_time to its corresponding NTFS time. * * Linux stores time in a long at present and measures it as the number of - * 1-second intervals since 1st January 1970, 00:00:00 UTC. + * 1-second intervals since 1st January 1970, 00:00:00 UTC + * with a separated non-negative nanosecond value * - * NTFS uses Microsoft's standard time format which is stored in a s64 and is + * NTFS uses Microsoft's standard time format which is stored in a sle64 and is * measured as the number of 100 nano-second intervals since 1st January 1601, * 00:00:00 UTC. * - * Return: n An NTFS time (100ns units since Jan 1601) + * Return: An NTFS time (100ns units since Jan 1601) */ -static __inline__ s64 utc2ntfs(time_t utc_time) +static __inline__ ntfs_time timespec2ntfs(struct timespec spec) { - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET); + s64 units; + + units = (s64)spec.tv_sec * 10000000 + + NTFS_TIME_OFFSET + spec.tv_nsec/100; + return (cpu_to_le64(units)); +} + +/* + * Return the current time in ntfs format + */ + +static __inline__ ntfs_time ntfs_current_time(void) +{ + struct timespec now; + +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_SYS_CLOCK_GETTIME) + clock_gettime(CLOCK_REALTIME, &now); +#elif defined(HAVE_GETTIMEOFDAY) + struct timeval microseconds; + + gettimeofday(µseconds, (struct timezone*)NULL); + now.tv_sec = microseconds.tv_sec; + now.tv_nsec = microseconds.tv_usec*1000; +#else + now.tv_sec = time((time_t*)NULL); + now.tv_nsec = 0; +#endif + return (timespec2ntfs(now)); } #endif /* _NTFS_NTFSTIME_H */ diff --git a/source/libntfs/object_id.c b/source/libntfs/object_id.c new file mode 100644 index 00000000..555dd137 --- /dev/null +++ b/source/libntfs/object_id.c @@ -0,0 +1,637 @@ +/** + * object_id.c - Processing of object ids + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2009 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SETXATTR +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "lcnalloc.h" +#include "object_id.h" +#include "logging.h" +#include "misc.h" + +/* + * Endianness considerations + * + * According to RFC 4122, GUIDs should be printed with the most + * significant byte first, and the six fields be compared individually + * for ordering. RFC 4122 does not define the internal representation. + * + * Here we always copy disk images with no endianness change, + * and, for indexing, GUIDs are compared as if they were a sequence + * of four unsigned 32 bit integers. + * + * --------------------- begin from RFC 4122 ---------------------- + * Consider each field of the UUID to be an unsigned integer as shown + * in the table in section Section 4.1.2. Then, to compare a pair of + * UUIDs, arithmetically compare the corresponding fields from each + * UUID in order of significance and according to their data type. + * Two UUIDs are equal if and only if all the corresponding fields + * are equal. + * + * UUIDs, as defined in this document, can also be ordered + * lexicographically. For a pair of UUIDs, the first one follows the + * second if the most significant field in which the UUIDs differ is + * greater for the first UUID. The second precedes the first if the + * most significant field in which the UUIDs differ is greater for + * the second UUID. + * + * The fields are encoded as 16 octets, with the sizes and order of the + * fields defined above, and with each field encoded with the Most + * Significant Byte first (known as network byte order). Note that the + * field names, particularly for multiplexed fields, follow historical + * practice. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_low | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_mid | time_hi_and_version | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |clk_seq_hi_res | clk_seq_low | node (0-1) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | node (2-5) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * ---------------------- end from RFC 4122 ----------------------- + */ + +typedef struct { + GUID object_id; +} OBJECT_ID_INDEX_KEY; + +typedef struct { + le64 file_id; + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; +} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA + +struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */ + INDEX_ENTRY_HEADER header; + OBJECT_ID_INDEX_KEY key; + OBJECT_ID_INDEX_DATA data; +} ; + +static ntfschar objid_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('O') }; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Set the index for a new object id + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *object_id) +{ + struct OBJECT_ID_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; + + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_DATA)); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct OBJECT_ID_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + + memcpy(&indx.key.object_id,object_id,sizeof(GUID)); + + indx.data.file_id = file_id; + memcpy(&indx.data.birth_volume_id, + &object_id->birth_volume_id,sizeof(GUID)); + memcpy(&indx.data.birth_object_id, + &object_id->birth_object_id,sizeof(GUID)); + memcpy(&indx.data.domain_id, + &object_id->domain_id,sizeof(GUID)); + ntfs_index_ctx_reinit(xo); + return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx)); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Open the $Extend/$ObjId file and its index + * + * Return the index context if opened + * or NULL if an error occurred (errno tells why) + * + * The index has to be freed and inode closed when not needed any more. + */ + +static ntfs_index_context *open_object_id_index(ntfs_volume *vol) +{ + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xo; + + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xo = ntfs_index_ctx_get(ni, objid_index_name, 2); + if (!xo) { + ntfs_inode_close(ni); + } + } else + xo = (ntfs_index_context*)NULL; + return (xo); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Merge object_id data stored in the index into + * a full object_id struct. + * + * returns 0 if merging successful + * -1 if no data could be merged. This is generally not an error + */ + +static int merge_index_data(ntfs_inode *ni, + const OBJECT_ID_ATTR *objectid_attr, + OBJECT_ID_ATTR *full_objectid) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + ntfs_index_context *xo; + ntfs_inode *xoni; + int res; + + res = -1; + xo = open_object_id_index(ni->vol); + if (xo) { + memcpy(&key.object_id,objectid_attr,sizeof(GUID)); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + /* make sure inode numbers match */ + if (entry + && (MREF(le64_to_cpu(entry->data.file_id)) + == ni->mft_no)) { + memcpy(&full_objectid->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&full_objectid->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&full_objectid->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + res = 0; + } + } + xoni = xo->ni; + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Remove an object id index entry if attribute present + * + * Returns the size of existing object id + * (the existing object_d is returned) + * -1 if failure, explained by errno + */ + +static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, + OBJECT_ID_ATTR *old_attr) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + s64 size; + int ret; + + ret = na->data_size; + if (ret) { + /* read the existing object id attribute */ + size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr); + if (size >= (s64)sizeof(GUID)) { + memcpy(&key.object_id, + &old_attr->object_id,sizeof(GUID)); + size = sizeof(GUID); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + memcpy(&old_attr->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&old_attr->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&old_attr->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + size = sizeof(OBJECT_ID_ATTR); + if (ntfs_index_rm(xo)) + ret = -1; + } + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Update the object id and index + * + * The object_id attribute should have been created and the + * non-duplication of the GUID should have been checked before. + * + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed + */ + +static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *value, size_t size) +{ + OBJECT_ID_ATTR old_attr; + ntfs_attr *na; + int oldsize; + int written; + int res; + + res = 0; + + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + + /* remove the existing index entry */ + oldsize = remove_object_id_index(na,xo,&old_attr); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)sizeof(GUID)); + /* write the object_id in attribute */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)sizeof(GUID), + &value->object_id); + if (written != (s64)sizeof(GUID)) { + ntfs_log_error("Failed to update " + "object id\n"); + errno = EIO; + res = -1; + } + } + /* write index part if provided */ + if (!res + && ((size < sizeof(OBJECT_ID_ATTR)) + || set_object_id_index(ni,xo,value))) { + /* + * If cannot index, try to remove the object + * id and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index object id." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); +} + +/* + * Add a (dummy) object id to an inode if it does not exist + * + * returns 0 if attribute was inserted (or already present) + * -1 if adding failed (explained by errno) + */ + +static int add_object_id(ntfs_inode *ni, int flags) +{ + int res; + u8 dummy; + + res = -1; /* default return */ + if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no object id attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, AT_OBJECT_ID, + AT_UNNAMED, 0, &dummy, (s64)0); + NInoSetDirty(ni); + } else + errno = EOPNOTSUPP; + } else + errno = ENODATA; + } else { + if (flags & XATTR_CREATE) + errno = EEXIST; + else + res = 0; + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Delete an object_id index entry + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +int ntfs_delete_object_id_index(ntfs_inode *ni) +{ + ntfs_index_context *xo; + ntfs_inode *xoni; + ntfs_attr *na; + OBJECT_ID_ATTR old_attr; + int res; + + res = 0; + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + /* + * read the existing object id + * and un-index it + */ + xo = open_object_id_index(ni->vol); + if (xo) { + if (remove_object_id_index(na,xo,&old_attr) < 0) + res = -1; + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + ntfs_attr_close(na); + } + return (res); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs object id into an extended attribute + * + * If present, the object_id from the attribute and the GUIDs + * from the index are returned (formatted as OBJECT_ID_ATTR) + * + * Returns the global size (can be 0, 16 or 64) + * and the buffer is updated if it is long enough + */ + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size) +{ + OBJECT_ID_ATTR full_objectid; + OBJECT_ID_ATTR *objectid_attr; + s64 attr_size; + int full_size; + + full_size = 0; /* default to no data and some error to be defined */ + if (ni) { + objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni, + AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size); + if (objectid_attr) { + /* restrict to only GUID present in attr */ + if (attr_size == sizeof(GUID)) { + memcpy(&full_objectid.object_id, + objectid_attr,sizeof(GUID)); + full_size = sizeof(GUID); + /* get data from index, if any */ + if (!merge_index_data(ni, objectid_attr, + &full_objectid)) { + full_size = sizeof(OBJECT_ID_ATTR); + } + if (full_size <= (s64)size) { + if (value) + memcpy(value,&full_objectid, + full_size); + else + errno = EINVAL; + } + } else { + /* unexpected size, better return unsupported */ + errno = EOPNOTSUPP; + full_size = 0; + } + free(objectid_attr); + } else + errno = ENODATA; + } + return (full_size ? (int)full_size : -errno); +} + +/* + * Set the object id from an extended attribute + * + * If the size is 64, the attribute and index are set. + * else if the size is not less than 16 only the attribute is set. + * The object id index is set accordingly. + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + OBJECT_ID_INDEX_KEY key; + ntfs_inode *xoni; + ntfs_index_context *xo; + int res; + + res = 0; + if (ni && value && (size >= sizeof(GUID))) { + xo = open_object_id_index(ni->vol); + if (xo) { + /* make sure the GUID was not used somewhere */ + memcpy(&key.object_id, value, sizeof(GUID)); + if (ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + ntfs_index_ctx_reinit(xo); + res = add_object_id(ni, flags); + if (!res) { + /* update value and index */ + res = update_object_id(ni,xo, + (const OBJECT_ID_ATTR*)value, + size); + } + } else { + /* GUID is present elsewhere */ + res = -1; + errno = EEXIST; + } + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Remove the object id + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_object_id(ntfs_inode *ni) +{ + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xoni; + ntfs_index_context *xo; + int oldsize; + OBJECT_ID_ATTR old_attr; + + res = 0; + if (ni) { + /* + * open and delete the object id + */ + na = ntfs_attr_open(ni, AT_OBJECT_ID, + AT_UNNAMED,0); + if (na) { + /* first remove index (old object id needed) */ + xo = open_object_id_index(ni->vol); + if (xo) { + oldsize = remove_object_id_index(na,xo, + &old_attr); + if (oldsize < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (res + && (oldsize > (int)sizeof(GUID))) { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_object_id_index(ni, xo, + &old_attr); + ntfs_log_error( + "Failed to remove object id." + " Possible corruption.\n"); + } + } + + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ diff --git a/source/libntfs/object_id.h b/source/libntfs/object_id.h new file mode 100644 index 00000000..31af9fd8 --- /dev/null +++ b/source/libntfs/object_id.h @@ -0,0 +1,35 @@ +/* + * + * Copyright (c) 2008 Jean-Pierre Andre + * + */ + +/* + * 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef OBJECT_ID_H +#define OBJECT_ID_H + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_object_id(ntfs_inode *ni); + +int ntfs_delete_object_id_index(ntfs_inode *ni); + +#endif /* OBJECT_ID_H */ diff --git a/source/libntfs/param.h b/source/libntfs/param.h new file mode 100644 index 00000000..f21bd653 --- /dev/null +++ b/source/libntfs/param.h @@ -0,0 +1,82 @@ +/* + * param.h - Parameter values for ntfs-3g + * + * Copyright (c) 2009 Jean-Pierre Andre + * + * This program/include file 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/include file 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 (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_PARAM_H +#define _NTFS_PARAM_H + +#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */ +#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */ +#define CACHE_LOOKUP_SIZE 64 /* lookup cache, zero or >= 3 and not too big */ +#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */ +#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */ + +#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */ +#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */ + + /* default security sub-authorities */ +enum { + DEFSECAUTH1 = -1153374643, /* 3141592653 */ + DEFSECAUTH2 = 589793238, + DEFSECAUTH3 = 462843383, + DEFSECBASE = 10000 +}; + +/* + * Parameters for compression + */ + + /* default option for compression */ +#define DEFAULT_COMPRESSION FALSE + /* (log2 of) number of clusters in a compression block for new files */ +#define STANDARD_COMPRESSION_UNIT 4 + /* maximum cluster size for allowing compression for new files */ +#define MAX_COMPRESSION_CLUSTER_SIZE 4096 + +/* + * Permission checking modes for high level and low level + * + * The choices for high and low lowel are independent, they have + * no effect on the library + * + * Stick to the recommended values unless you understand the consequences + * on protection and performances. Use of cacheing is good for + * performances, but bad on security. + * + * Possible values for high level : + * 1 : no cache, kernel control (recommended) + * 4 : no cache, file system control + * 7 : no cache, kernel control for ACLs + * + * Possible values for low level : + * 2 : no cache, kernel control + * 3 : use kernel/fuse cache, kernel control + * 5 : no cache, file system control (recommended) + * 8 : no cache, kernel control for ACLs + * + * Use of options 7 and 8 requires a patch to fuse + * When Posix ACLs are selected in the configure options, a value + * of 6 is added in the mount report. + */ + +#define HPERMSCONFIG 1 +#define LPERMSCONFIG 5 + +#endif /* defined _NTFS_PARAM_H */ diff --git a/source/libntfs/reparse.c b/source/libntfs/reparse.c index 80a25acb..0f6360e1 100644 --- a/source/libntfs/reparse.c +++ b/source/libntfs/reparse.c @@ -1,8 +1,7 @@ /** * reparse.c - Processing of reparse points * - * This module is part of ntfs-3g library, but may also be - * integrated in tools running over Linux or Windows + * This module is part of ntfs-3g library * * Copyright (c) 2008-2009 Jean-Pierre Andre * @@ -89,12 +88,6 @@ struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */ char path_buffer[0]; /* above data assume this is char array */ } ; -struct INODE_STACK { - struct INODE_STACK *previous; - struct INODE_STACK *next; - ntfs_inode *ni; -} ; - struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */ INDEX_ENTRY_HEADER header; REPARSE_INDEX_KEY key; @@ -123,7 +116,7 @@ static const ntfschar vol_junction_head[] = { } ; static ntfschar reparse_index_name[] = { const_cpu_to_le16('$'), - const_cpu_to_le16('R') }; + const_cpu_to_le16('R') }; static const char mappingdir[] = ".NTFS-3G/"; @@ -194,10 +187,10 @@ static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, if (entry) { found = &entry->key.file_name; if (lkup - && !ntfs_names_collate(find.attr.file_name, + && ntfs_names_are_equal(find.attr.file_name, find.attr.file_name_length, found->file_name, found->file_name_length, - 1, IGNORE_CASE, + IGNORE_CASE, vol->upcase, vol->upcase_len)) lkup = 0; if (!lkup) { @@ -205,9 +198,7 @@ static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, * name found : * fix original name and return inode */ - lemref = *(le64*)((char*)found->file_name - - sizeof(INDEX_ENTRY_HEADER) - - sizeof(FILE_NAME_ATTR)); + lemref = entry->indexed_file; mref = le64_to_cpu(lemref); for (i=0; ifile_name_length; i++) uname[i] = found->file_name[i]; @@ -271,181 +262,90 @@ static char *search_absolute(ntfs_volume *vol, ntfschar *path, return (target); } -/* - * Stack the next inode in the path - * - * Returns the new top of stack - * or NULL (with stack unchanged) if there is a problem - */ - -static struct INODE_STACK *stack_inode(struct INODE_STACK *topni, - ntfschar *name, int len, BOOL fix) -{ - struct INODE_STACK *curni; - u64 inum; - - if (fix) - inum = ntfs_fix_file_name(topni->ni, name, len); - else - inum = ntfs_inode_lookup_by_name(topni->ni, name, len); - if (inum != (u64)-1) { - inum = MREF(inum); - curni = (struct INODE_STACK*)malloc(sizeof(struct INODE_STACK)); - if (curni) { - curni->ni = ntfs_inode_open(topni->ni->vol, inum); - topni->next = curni; - curni->previous = topni; - curni->next = (struct INODE_STACK*)NULL; - } - } else - curni = (struct INODE_STACK*)NULL; - return (curni); -} - -/* - * Destack and close the current inode in the path - * - * Returns the new top of stack - * or NULL (with stack unchanged) if there is a problem - */ - -static struct INODE_STACK *pop_inode(struct INODE_STACK *topni) -{ - struct INODE_STACK *curni; - - curni = (struct INODE_STACK*)NULL; - if (topni->previous) { - if (!ntfs_inode_close(topni->ni)) { - curni = topni->previous; - free(topni); - curni->next = (struct INODE_STACK*)NULL; - } - } else { - /* ".." reached the root of fs */ - errno = ENOENT; - } - return (curni); -} - /* * Search for a symbolic link along the target path, * with the target defined as a relative path * + * Note : the path used to access the current inode, may be + * different from the one implied in the target definition, + * when an inode has names in several directories. + * * Returns the path translated to a Linux path * or NULL if the path is not valid */ -static char *search_relative(ntfs_volume *vol, ntfschar *path, int count, - const char *base, BOOL isdir) +static char *search_relative(ntfs_inode *ni, ntfschar *path, int count) { - struct INODE_STACK *topni; - struct INODE_STACK *curni; - char *target; - ntfschar *unicode; - int unisz; - int start; - int len; + char *target = (char*)NULL; + ntfs_inode *curni; + ntfs_inode *newni; + u64 inum; + int pos; + int lth; + BOOL ok; + int max = 32; /* safety */ - target = (char*)NULL; /* default return */ - topni = (struct INODE_STACK*)malloc(sizeof(struct INODE_STACK)); - if (topni) { - topni->ni = ntfs_inode_open(vol, FILE_root); - topni->previous = (struct INODE_STACK*)NULL; - topni->next = (struct INODE_STACK*)NULL; - } - if (topni && topni->ni) { - /* - * Process the base path - */ - unicode = (ntfschar*)NULL; - unisz = ntfs_mbstoucs(base, &unicode); - if ((unisz > 0) && unicode) { - start = 1; - do { - len = 0; - while (((start + len) < unisz) - && (unicode[start + len] - != const_cpu_to_le16('/'))) - len++; - curni = (struct INODE_STACK*)NULL; - if ((start + len) < unisz) { - curni = stack_inode(topni, - &unicode[start], len, FALSE); - if (curni) - topni = curni; - } else - curni = topni; - start += len + 1; - } while (curni - && topni->ni - && (topni->ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY) - && (start < unisz)); - free(unicode); - if (curni - && topni->ni - && (topni->ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY)) { - start = 0; - do { - len = 0; - while (((start + len) < count) - && (path[start + len] - != const_cpu_to_le16('\\'))) - len++; - curni = (struct INODE_STACK*)NULL; - if ((path[start] - == const_cpu_to_le16('.')) - && ((len == 1) - || ((len == 2) - && (path[start+1] - == const_cpu_to_le16('.'))))) { - /* leave the .. or . in the path */ - curni = topni; - if (len == 2) { - curni = pop_inode(topni); - if (curni) - topni = curni; - } - } else { - curni = stack_inode(topni, - &path[start], len, TRUE); - if (curni) - topni = curni; - } - if (topni->ni) { - start += len; - if (start < count) - path[start++] - = const_cpu_to_le16('/'); - } - } while (curni - && topni->ni - && (topni->ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY) - && (start < count)); - if (curni - && topni->ni - && (topni->ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY - ? isdir : !isdir)) { - if (ntfs_ucstombs(path, count, - &target, 0) < 0) { - if (target) { - free(target); - target = (char*)NULL; + pos = 0; + ok = TRUE; + curni = ntfs_dir_parent_inode(ni); + while (curni && ok && (pos < (count - 1)) && --max) { + if ((count >= (pos + 2)) + && (path[pos] == const_cpu_to_le16('.')) + && (path[pos+1] == const_cpu_to_le16('\\'))) { + path[1] = const_cpu_to_le16('/'); + pos += 2; + } else { + if ((count >= (pos + 3)) + && (path[pos] == const_cpu_to_le16('.')) + &&(path[pos+1] == const_cpu_to_le16('.')) + && (path[pos+2] == const_cpu_to_le16('\\'))) { + path[2] = const_cpu_to_le16('/'); + pos += 3; + newni = ntfs_dir_parent_inode(curni); + if (curni != ni) + ntfs_inode_close(curni); + curni = newni; + if (!curni) + ok = FALSE; + } else { + lth = 0; + while (((pos + lth) < count) + && (path[pos + lth] != const_cpu_to_le16('\\'))) + lth++; + if (lth > 0) + inum = ntfs_fix_file_name(curni,&path[pos],lth); + else + inum = (u64)-1; + if (!lth + || ((curni != ni) + && ntfs_inode_close(curni)) + || (inum == (u64)-1)) + ok = FALSE; + else { + curni = ntfs_inode_open(ni->vol, MREF(inum)); + if (!curni) + ok = FALSE; + else { + if (ok && ((pos + lth) < count)) { + path[pos + lth] = const_cpu_to_le16('/'); + pos += lth + 1; + } else { + pos += lth; + if ((ni->mrec->flags ^ curni->mrec->flags) + & MFT_RECORD_IS_DIRECTORY) + ok = FALSE; + if (ntfs_inode_close(curni)) + ok = FALSE; } } } } } - do { - if (topni->ni) - ntfs_inode_close(topni->ni); - curni = topni; - topni = topni->previous; - free(curni); - } while (topni); + } + + if (ok && (ntfs_ucstombs(path, count, &target, 0) < 0)) { + free(target); // needed ? + target = (char*)NULL; } return (target); } @@ -492,6 +392,62 @@ static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) return (ret); } +/* + * Do some sanity checks on reparse data + * + * The only general check is about the size (at least the tag must + * be present) + * If the reparse data looks like a junction point or symbolic + * link, more checks can be done. + * + */ + +static BOOL valid_reparse_data(ntfs_inode *ni, + const REPARSE_POINT *reparse_attr, size_t size) +{ + BOOL ok; + unsigned int offs; + unsigned int lth; + const struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + const struct SYMLINK_REPARSE_DATA *symlink_data; + + ok = ni && reparse_attr + && (size >= sizeof(REPARSE_POINT)) + && (((size_t)le16_to_cpu(reparse_attr->reparse_data_length) + + sizeof(REPARSE_POINT)) == size); + if (ok) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* consistency checks */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + || ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct MOUNT_POINT_REPARSE_DATA) + + offs + lth)) > size)) + ok = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (const struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + if ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct SYMLINK_REPARSE_DATA) + + offs + lth)) > size) + ok = FALSE; + break; + default : + break; + } + } + if (!ok) + errno = EINVAL; + return (ok); +} + /* * Check and translate the target of a junction point or * a full absolute symbolic link. @@ -507,16 +463,12 @@ static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) * or NULL if there were some problem, as described by errno */ - static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, - int count, const char *path, BOOL isdir) + int count, const char *mnt_point, BOOL isdir) { char *target; char *fulltarget; - int i; int sz; - int level; - const char *p; char *q; enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind; @@ -554,19 +506,11 @@ static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, && !ntfs_drive_letter(vol, junction[4])) { target = search_absolute(vol,&junction[7],count - 7, isdir); if (target) { - level = 0; - for (p=path; *p; p++) - if (*p == '/') - level++; - fulltarget = (char*)ntfs_malloc(3*level - + strlen(target) + 1); + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); if (fulltarget) { - fulltarget[0] = 0; - if (level > 1) { - for (i=1; i= 'a') && (target[0] <= 'z')) target[0] += 'A' - 'a'; - level = 0; - for (p=path; *p; p++) - if (*p == '/') - level++; - fulltarget = (char*)ntfs_malloc(3*level - + sizeof(mappingdir) + strlen(target) - 3); + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); if (fulltarget) { - fulltarget[0] = 0; - if (level > 1) { - for (i=1; i 1) { - for (i=1; i= 'a') && (target[0] <= 'z')) target[0] += 'A' - 'a'; - level = 0; - for (p=path; *p; p++) - if (*p == '/') - level++; - fulltarget = (char*)ntfs_malloc(3*level - + sizeof(mappingdir) + strlen(target) - 3); + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); if (fulltarget) { - fulltarget[0] = 0; - if (level > 1) { - for (i=1; ivol; reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); - if (reparse_attr && attr_size) { + if (reparse_attr && attr_size + && valid_reparse_data(ni, reparse_attr, attr_size)) { switch (reparse_attr->reparse_tag) { case IO_REPARSE_TAG_MOUNT_POINT : mount_point_data = (struct MOUNT_POINT_REPARSE_DATA*) reparse_attr->reparse_data; offs = le16_to_cpu(mount_point_data->subst_name_offset); lth = le16_to_cpu(mount_point_data->subst_name_length); - /* consistency checks */ - if (isdir - && ((le16_to_cpu(reparse_attr->reparse_data_length) - + 8) == attr_size) - && ((int)((sizeof(REPARSE_POINT) - + sizeof(struct MOUNT_POINT_REPARSE_DATA) - + offs + lth)) <= attr_size)) { - target = ntfs_get_fulllink(vol, - (ntfschar*)&mount_point_data->path_buffer[offs], - lth/2, org_path, isdir); - if (target) - bad = FALSE; - } + /* reparse data consistency has been checked */ + target = ntfs_get_fulllink(vol, + (ntfschar*)&mount_point_data->path_buffer[offs], + lth/2, mnt_point, isdir); + if (target) + bad = FALSE; break; case IO_REPARSE_TAG_SYMLINK : symlink_data = (struct SYMLINK_REPARSE_DATA*) @@ -836,44 +746,37 @@ char *ntfs_make_symlink(const char *org_path, else kind = REL_TARGET; p--; - /* consistency checks */ - if (((le16_to_cpu(reparse_attr->reparse_data_length) - + 8) == attr_size) - && ((int)((sizeof(REPARSE_POINT) - + sizeof(struct SYMLINK_REPARSE_DATA) - + offs + lth)) <= attr_size)) { - switch (kind) { - case FULL_TARGET : - if (!(symlink_data->flags - & const_cpu_to_le32(1))) { - target = ntfs_get_fulllink(vol, - p, lth/2, - org_path, isdir); - if (target) - bad = FALSE; - } - break; - case ABS_TARGET : - if (symlink_data->flags - & const_cpu_to_le32(1)) { - target = ntfs_get_abslink(vol, - p, lth/2, - org_path, isdir); - if (target) - bad = FALSE; - } - break; - case REL_TARGET : - if (symlink_data->flags - & const_cpu_to_le32(1)) { - target = ntfs_get_rellink(vol, - p, lth/2, - org_path, isdir); - if (target) - bad = FALSE; - } - break; + /* reparse data consistency has been checked */ + switch (kind) { + case FULL_TARGET : + if (!(symlink_data->flags + & const_cpu_to_le32(1))) { + target = ntfs_get_fulllink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; } + break; + case ABS_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_abslink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case REL_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_rellink(ni, + p, lth/2); + if (target) + bad = FALSE; + } + break; } break; } @@ -984,8 +887,9 @@ static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr, key.reparse_tag = *preparse_tag; /* danger on processors which require proper alignment ! */ memcpy(&key.file_id, &file_id, 8); - if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr)) - ret = ntfs_index_rm(xr); + if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr) + && ntfs_index_rm(xr)) + ret = -1; } else { ret = -1; errno = ENODATA; @@ -1005,10 +909,20 @@ static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr, static ntfs_index_context *open_reparse_index(ntfs_volume *vol) { + u64 inum; ntfs_inode *ni; + ntfs_inode *dir_ni; ntfs_index_context *xr; - ni = ntfs_pathname_to_inode(vol, NULL, "$Extend/$Reparse"); + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$Reparse"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } if (ni) { xr = ntfs_index_ctx_get(ni, reparse_index_name, 2); if (!xr) { @@ -1133,8 +1047,7 @@ int ntfs_delete_reparse_index(ntfs_inode *ni) * and the buffer is updated if it is long enough */ -int ntfs_get_ntfs_reparse_data(const char *path __attribute__((unused)), - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size) { REPARSE_POINT *reparse_attr; s64 attr_size; @@ -1168,9 +1081,8 @@ int ntfs_get_ntfs_reparse_data(const char *path __attribute__((unused)), * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), - const char *value, size_t size, int flags, - ntfs_inode *ni) +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, + const char *value, size_t size, int flags) { int res; u8 dummy; @@ -1178,7 +1090,7 @@ int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), ntfs_index_context *xr; res = 0; - if (ni && value && (size >= 4)) { + if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { xr = open_reparse_index(ni->vol); if (xr) { if (!ntfs_attr_exist(ni,AT_REPARSE_POINT, @@ -1194,9 +1106,11 @@ int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), AT_REPARSE_POINT, AT_UNNAMED,0,&dummy, (s64)0); - if (!res) - ni->flags |= + if (!res) { + ni->flags |= FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } NInoSetDirty(ni); } else { errno = EOPNOTSUPP; @@ -1237,8 +1151,7 @@ int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), * Returns 0, or -1 if there is a problem */ -int ntfs_remove_ntfs_reparse_data(const char *path __attribute__((unused)), - ntfs_inode *ni) +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) { int res; int olderrno; @@ -1267,6 +1180,7 @@ int ntfs_remove_ntfs_reparse_data(const char *path __attribute__((unused)), if (!res) { ni->flags &= ~FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); } else { /* * If we could not remove the diff --git a/source/libntfs/reparse.h b/source/libntfs/reparse.h index 91ede03e..35f4aa45 100644 --- a/source/libntfs/reparse.h +++ b/source/libntfs/reparse.h @@ -24,15 +24,15 @@ #ifndef REPARSE_H #define REPARSE_H -char *ntfs_make_symlink(const char *org_path, - ntfs_inode *ni, int *pattr_size); +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size); BOOL ntfs_possible_symlink(ntfs_inode *ni); -int ntfs_get_ntfs_reparse_data(const char *path, - char *value, size_t size, ntfs_inode *ni); -int ntfs_set_ntfs_reparse_data(const char *path, const char *value, - size_t size, int flags, ntfs_inode *ni); -int ntfs_remove_ntfs_reparse_data(const char *path, ntfs_inode *ni); +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); int ntfs_delete_reparse_index(ntfs_inode *ni); diff --git a/source/libntfs/runlist.c b/source/libntfs/runlist.c index f81996bd..cea24672 100644 --- a/source/libntfs/runlist.c +++ b/source/libntfs/runlist.c @@ -117,18 +117,33 @@ static runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size, * * Returns the reallocated runlist * or NULL if reallocation was not possible (with errno set) + * the runlist is left unchanged if the reallocation fails */ -runlist_element *ntfs_rl_extend(runlist_element *rl, int more_entries) +runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries) { + runlist_element *newrl; int last; + int irl; - last = 0; - while (rl[last].length) - last++; - rl = ntfs_rl_realloc(rl,last+1,last+more_entries+1); - if (!rl) - errno = ENOMEM; + if (na->rl && rl) { + irl = (int)(rl - na->rl); + last = irl; + while (na->rl[last].length) + last++; + newrl = ntfs_rl_realloc(na->rl,last+1,last+more_entries+1); + if (!newrl) { + errno = ENOMEM; + rl = (runlist_element*)NULL; + } else + na->rl = newrl; + rl = &newrl[irl]; + } else { + ntfs_log_error("Cannot extend unmapped runlist"); + errno = EIO; + rl = (runlist_element*)NULL; + } return (rl); } @@ -764,7 +779,7 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl, * two into one, if that is possible (we check for overlap and discard the new * runlist if overlap present before returning NULL, with errno = ERANGE). */ -runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, +static runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, const ATTR_RECORD *attr, runlist_element *old_rl) { VCN vcn; /* Current vcn. */ @@ -1622,7 +1637,11 @@ int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) if (!arl || !*arl) { errno = EINVAL; - ntfs_log_perror("rl_truncate error: arl: %p *arl: %p", arl, *arl); + if (!arl) + ntfs_log_perror("rl_truncate error: arl: %p", arl); + else + ntfs_log_perror("rl_truncate error:" + " arl: %p *arl: %p", arl, *arl); return -1; } diff --git a/source/libntfs/runlist.h b/source/libntfs/runlist.h index 43de53cc..4b73af9f 100644 --- a/source/libntfs/runlist.h +++ b/source/libntfs/runlist.h @@ -49,7 +49,8 @@ struct _runlist_element {/* In memory vcn to lcn mapping structure element. */ s64 length; /* Run length in clusters. */ }; -extern runlist_element *ntfs_rl_extend(runlist_element *rl, int more_entries); +extern runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries); extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn); diff --git a/source/libntfs/security.c b/source/libntfs/security.c index 38acce40..ecf3ca07 100644 --- a/source/libntfs/security.c +++ b/source/libntfs/security.c @@ -41,17 +41,18 @@ #ifdef HAVE_FCNTL_H #include #endif -#ifdef HAVE_SYS_STAT_H -#include -#endif #ifdef HAVE_SETXATTR #include #endif +#ifdef HAVE_SYS_STAT_H +#include +#endif #include #include #include +#include "param.h" #include "types.h" #include "layout.h" #include "attrib.h" @@ -60,6 +61,7 @@ #include "bitmap.h" #include "security.h" #include "acls.h" +#include "cache.h" #include "misc.h" /* @@ -712,6 +714,7 @@ static le32 entersecurityattr(ntfs_volume *vol, INDEX_ENTRY *entry; INDEX_ENTRY *next; ntfs_index_context *xsii; + int retries; ntfs_attr *na; int olderrno; @@ -756,10 +759,14 @@ static le32 entersecurityattr(ntfs_volume *vol, * All index blocks should be at least half full * so there always is a last entry but one, * except when creating the first entry in index root. - * A simplified version of next(), limited to - * current index node, could be used + * This was however found not to be true : chkdsk + * sometimes deletes all the (unused) keys in the last + * index block without rebalancing the tree. + * When this happens, a new search is restarted from + * the smallest key. */ keyid = const_cpu_to_le32(0); + retries = 0; while (entry) { next = ntfs_index_next(entry,xsii); if (next) { @@ -775,6 +782,20 @@ static le32 entersecurityattr(ntfs_volume *vol, size = le32_to_cpu(psii->datasize); } entry = next; + if (!entry && !keyid && !retries) { + /* search failed, retry from smallest key */ + ntfs_index_ctx_reinit(xsii); + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Index $SII is broken"); + } else { + /* restore errno */ + errno = olderrno; + entry = xsii->entry; + } + retries++; + } } } if (!keyid) { @@ -1080,7 +1101,6 @@ static int update_secur_descr(ntfs_volume *vol, /* mark node as dirty */ NInoSetDirty(ni); - ntfs_inode_sync(ni); /* useful ? */ return (res); } @@ -1105,7 +1125,7 @@ static int update_secur_descr(ntfs_volume *vol, * -1 if there is a problem */ -static int upgrade_secur_desc(ntfs_volume *vol, const char *path, +static int upgrade_secur_desc(ntfs_volume *vol, const char *attr, ntfs_inode *ni) { int attrsz; @@ -1116,11 +1136,11 @@ static int upgrade_secur_desc(ntfs_volume *vol, const char *path, /* * upgrade requires NTFS format v3.x * also refuse upgrading for special files + * whose number is less than FILE_first_user */ if ((vol->major_ver >= 3) - && (path[0] == '/') - && (path[1] != '$') && (path[1] != '\0')) { + && (ni->mft_no >= FILE_first_user)) { attrsz = ntfs_attr_size(attr); securid = setsecurityattr(vol, (const SECURITY_DESCRIPTOR_RELATIVE*)attr, @@ -1149,9 +1169,8 @@ static int upgrade_secur_desc(ntfs_volume *vol, const char *path, } } else res = -1; - /* mark node as dirty */ - NInoSetDirty(ni); - ntfs_inode_sync(ni); /* useful ? */ + /* mark node as dirty */ + NInoSetDirty(ni); } else res = 1; @@ -1788,8 +1807,7 @@ static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id) * The returned descriptor is dynamically allocated and has to be freed */ -static char *getsecurityattr(ntfs_volume *vol, - const char *path, ntfs_inode *ni) +static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni) { SII_INDEX_KEY securid; char *securattr; @@ -1815,8 +1833,8 @@ static char *getsecurityattr(ntfs_volume *vol, securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, &readallsz); if (securattr && !ntfs_valid_descr(securattr, readallsz)) { - ntfs_log_error("Bad security descriptor for %s\n", - path); + ntfs_log_error("Bad security descriptor for inode %lld\n", + (long long)ni->mft_no); free(securattr); securattr = (char*)NULL; } @@ -1830,7 +1848,8 @@ static char *getsecurityattr(ntfs_volume *vol, * minimum rights, so that a real descriptor can * be created by chown or chmod */ - ntfs_log_error("No security descriptor found for %s\n",path); + ntfs_log_error("No security descriptor found for inode %lld\n", + (long long)ni->mft_no); securattr = ntfs_build_descr(0, 0, adminsid, adminsid); } return (securattr); @@ -1854,6 +1873,7 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, int groupperms; int mask; BOOL somegroup; + BOOL needgroups; mode_t perms; int i; @@ -1888,9 +1908,17 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, } else perms &= 07700; } else { - /* analyze designated users and get mask */ + /* + * analyze designated users, get mask + * and identify whether we need to check + * the group memberships. The groups are + * not needed when all groups have the + * same permissions as other for the + * requested modes. + */ userperms = -1; groupperms = -1; + needgroups = FALSE; mask = 7; for (i=pxdesc->acccnt-1; i>=0 ; i--) { pxace = &pxdesc->acl.ace[i]; @@ -1902,6 +1930,12 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, case POSIX_ACL_MASK : mask = pxace->perms & 7; break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_GROUP : + if (((pxace->perms & mask) ^ perms) + & (request >> 6) & 7) + needgroups = TRUE; + break; default : break; } @@ -1909,6 +1943,8 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, /* designated users */ if (userperms >= 0) perms = (perms & 07000) + (userperms & mask); + else if (!needgroups) + perms &= 07007; else { /* owning group */ if (!(~(perms >> 3) & request & mask) @@ -1951,7 +1987,7 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, */ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode * ni, mode_t request) + ntfs_inode * ni, mode_t request) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -1977,7 +2013,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, perm = 0; /* default to no permission */ isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*) securattr; @@ -2017,7 +2053,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, && (perm >= 0) && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, path, + upgrade_secur_desc(scx->vol, securattr, ni); /* * fetch owner and group for cacheing @@ -2051,9 +2087,8 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, * the caller is expected to issue a new call */ -int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, char *value, size_t size, - ntfs_inode *ni) +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size) { const SECURITY_DESCRIPTOR_RELATIVE *phead; struct POSIX_SECURITY *pxdesc; @@ -2076,7 +2111,7 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, if (cached) pxdesc = cached->pxdesc; else { - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); if (securattr) { @@ -2107,7 +2142,7 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { upgrade_secur_desc(scx->vol, - path, securattr, ni); + securattr, ni); } #if OWNERFROMACL uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); @@ -2185,13 +2220,10 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, * Do no use as mode of the file * * returns -1 if there is a problem - * - * This is only used for checking creation of DOS file names */ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, - mode_t request __attribute__((unused))) + ntfs_inode *ni, mode_t request) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -2203,7 +2235,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, gid_t gid; int perm; - if (!scx->mapping[MAPUSERS] || !scx->uid) + if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC))) perm = 07777; else { /* check whether available in cache */ @@ -2216,7 +2248,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, perm = 0; /* default to no permission */ isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*) securattr; @@ -2248,7 +2280,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, && (perm >= 0) && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, path, + upgrade_secur_desc(scx->vol, securattr, ni); /* * fetch owner and group for cacheing @@ -2267,14 +2299,28 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, } } if (perm >= 0) { - if (uid == scx->uid) - perm &= 07700; - else - if ((gid == scx->gid) - || groupmember(scx, scx->uid, gid)) - perm &= 07070; + if (!scx->uid) { + /* root access and execution */ + if (perm & 0111) + perm = 07777; else - perm &= 07007; + perm = 0; + } else + if (uid == scx->uid) + perm &= 07700; + else + /* + * avoid checking group membership + * when the requested perms for group + * are the same as perms for other + */ + if ((gid == scx->gid) + || ((((perm >> 3) ^ perm) + & (request >> 6) & 7) + && groupmember(scx, scx->uid, gid))) + perm &= 07070; + else + perm &= 07007; } } return (perm); @@ -2290,15 +2336,14 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, * the caller is expected to issue a new call */ -int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name __attribute__((unused)), - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size) { char *securattr; size_t outsize; outsize = 0; /* default to no data and no error */ - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { outsize = ntfs_attr_size(securattr); if (outsize <= size) { @@ -2315,8 +2360,7 @@ int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, */ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode * ni, - struct stat *stbuf) + ntfs_inode * ni, struct stat *stbuf) { const SECURITY_DESCRIPTOR_RELATIVE *phead; char *securattr; @@ -2343,7 +2387,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, perm = -1; /* default to error */ isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*) @@ -2379,7 +2423,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { upgrade_secur_desc(scx->vol, - path, securattr, ni); + securattr, ni); } #if OWNERFROMACL stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); @@ -2420,8 +2464,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, */ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, - const char *dir_path, ntfs_inode *dir_ni, - mode_t mode, BOOL isdir) + ntfs_inode *dir_ni, mode_t mode, BOOL isdir) { const struct CACHED_PERMISSIONS *cached; const SECURITY_DESCRIPTOR_RELATIVE *phead; @@ -2445,7 +2488,7 @@ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, scx->umask,isdir); } } else { - securattr = getsecurityattr(scx->vol, dir_path, dir_ni); + securattr = getsecurityattr(scx->vol, dir_ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*) securattr; @@ -2475,7 +2518,7 @@ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, if (!test_nino_flag(dir_ni, v3_Extensions) && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, dir_path, + upgrade_secur_desc(scx->vol, securattr, dir_ni); /* * fetch owner and group for cacheing @@ -2503,8 +2546,8 @@ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, */ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, const char *dir_path, - ntfs_inode *dir_ni, mode_t mode, BOOL isdir) + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir) { #if !FORCE_FORMAT_v1x const struct CACHED_SECURID *cached; @@ -2523,7 +2566,7 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, #if !FORCE_FORMAT_v1x - pxdesc = inherit_posix(scx, dir_path, dir_ni, mode, isdir); + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); if (pxdesc) { /* check whether target securid is known in cache */ @@ -2586,7 +2629,7 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, - const char *dir_path, ntfs_inode *dir_ni, mode_t mode) + ntfs_inode *dir_ni, mode_t mode) { struct POSIX_SECURITY *pxdesc; char *newattr; @@ -2599,7 +2642,7 @@ int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, res = -1; isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - pxdesc = inherit_posix(scx, dir_path, dir_ni, mode, isdir); + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); if (pxdesc) { usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); @@ -2613,7 +2656,7 @@ int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, if (newattr) { /* Adjust Windows read-only flag */ res = update_secur_descr(scx->vol, newattr, ni); - if (!res) { + if (!res && !isdir) { if (mode & S_IWUSR) ni->flags &= ~FILE_ATTR_READONLY; else @@ -2630,7 +2673,7 @@ int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); ntfs_invalidate_cache(scx->vol->legacy_cache, GENERIC(&legacy), - (cache_compare)leg_compare); + (cache_compare)leg_compare,0); } #endif free(newattr); @@ -2802,10 +2845,13 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, res = update_secur_descr(scx->vol, newattr, ni); if (!res) { /* adjust Windows read-only flag */ - if (mode & S_IWUSR) - ni->flags &= ~FILE_ATTR_READONLY; - else - ni->flags |= FILE_ATTR_READONLY; + if (!isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + NInoFileNameSetDirty(ni); + } /* update cache, for subsequent use */ if (test_nino_flag(ni, v3_Extensions)) { wanted.securid = ni->security_id; @@ -2828,7 +2874,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, #endif ntfs_invalidate_cache(scx->vol->legacy_cache, GENERIC(&legacy), - (cache_compare)leg_compare); + (cache_compare)leg_compare,0); } #endif } @@ -2851,8 +2897,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, * if not, errno tells why */ -BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni) +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) { const struct CACHED_PERMISSIONS *cached; char *oldattr; @@ -2862,36 +2907,48 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, BOOL gotowner; int allowed; - gotowner = FALSE; /* default */ processuid = scx->uid; - /* get the owner, either from cache or from old attribute */ - cached = fetch_cache(scx, ni); - if (cached) { - uid = cached->uid; - gotowner = TRUE; - } else { - oldattr = getsecurityattr(scx->vol,path, ni); - if (oldattr) { -#if OWNERFROMACL - usid = ntfs_acl_owner(oldattr); -#else - const SECURITY_DESCRIPTOR_RELATIVE *phead; - - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; - usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; -#endif - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); - gotowner = TRUE; - free(oldattr); - } - } - allowed = FALSE; - if (gotowner) { /* TODO : use CAP_FOWNER process capability */ - if (!processuid || (processuid == uid)) - allowed = TRUE; - else - errno = EPERM; + /* + * Always allow for root + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] || !processuid) + allowed = TRUE; + else { + gotowner = FALSE; /* default */ + /* get the owner, either from cache or from old attribute */ + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gotowner = TRUE; + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + const SECURITY_DESCRIPTOR_RELATIVE *phead; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + usid = (const SID*)&oldattr + [le32_to_cpu(phead->owner)]; +#endif + uid = ntfs_find_user(scx->mapping[MAPUSERS], + usid); + gotowner = TRUE; + free(oldattr); + } + } + allowed = FALSE; + if (gotowner) { +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (processuid == uid)) + allowed = TRUE; + else + errno = EPERM; + } } return (allowed); } @@ -2908,9 +2965,9 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, * Returns 0, or -1 if there is a problem which errno describes */ -int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, const char *name, const char *value, size_t size, - int flags, ntfs_inode *ni) + int flags) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -2950,7 +3007,7 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, (const struct POSIX_ACL*)value,count,deflt); } } else { - oldattr = getsecurityattr(scx->vol,path, ni); + oldattr = getsecurityattr(scx->vol, ni); if (oldattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; #if OWNERFROMACL @@ -3011,11 +3068,11 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, * Returns 0, or -1 if there is a problem which errno describes */ -int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, ntfs_inode *ni) +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name) { - return (ntfs_set_posix_acl(scx, path, name, - (const char*)NULL, 0, 0, ni)); + return (ntfs_set_posix_acl(scx, ni, name, + (const char*)NULL, 0, 0)); } #endif @@ -3026,11 +3083,8 @@ int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, - const char *path __attribute__((unused)), - const char *name __attribute__((unused)), - const char *value, size_t size, int flags, - ntfs_inode *ni) +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags) { char *attr; int res; @@ -3068,7 +3122,7 @@ int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, legacy.varsize = 0; ntfs_invalidate_cache(scx->vol->legacy_cache, GENERIC(&legacy), - (cache_compare)leg_compare); + (cache_compare)leg_compare,0); } #endif free(attr); @@ -3091,8 +3145,7 @@ int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, * -1 on failure, with errno = EIO */ -int ntfs_set_mode(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, mode_t mode) +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -3133,7 +3186,7 @@ int ntfs_set_mode(struct SECURITY_CONTEXT *scx, newpxdesc = (struct POSIX_SECURITY*)NULL; #endif } else { - oldattr = getsecurityattr(scx->vol,path, ni); + oldattr = getsecurityattr(scx->vol, ni); if (oldattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; #if OWNERFROMACL @@ -3280,7 +3333,7 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) */ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, + ntfs_inode *ni, int accesstype) /* access type required (S_Ixxx values) */ { int perm; @@ -3288,10 +3341,6 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, int allow; struct stat stbuf; -#if POSIXACLS - /* shortcut, use only if Posix ACLs in use */ - if (scx->vol->secure_flags & (1 << SECURITY_DEFAULT)) return (1); -#endif /* * Always allow for root unless execution is requested. * (was checked by fuse until kernel 2.6.29) @@ -3303,7 +3352,7 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)))) allow = 1; else { - perm = ntfs_get_perm(scx, path, ni, accesstype); + perm = ntfs_get_perm(scx, ni, accesstype); if (perm >= 0) { res = EACCES; switch (accesstype) { @@ -3330,7 +3379,7 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, break; case S_IWRITE + S_IEXEC + S_ISVTX: if (perm & S_ISVTX) { - if ((ntfs_get_owner_mode(scx,path,ni,&stbuf) >= 0) + if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) && (stbuf.st_uid == scx->uid)) allow = 1; else @@ -3339,6 +3388,11 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); break; + case S_IREAD + S_IWRITE + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; default : res = EINVAL; allow = 0; @@ -3352,6 +3406,8 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, return (allow); } +#if 0 /* not needed any more */ + /* * Check whether user can access the parent directory * of a file in a specific way @@ -3364,7 +3420,7 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, * This is used for Posix ACL and checking creation of DOS file names */ -BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, const char *path, int accesstype) { int allow; @@ -3374,10 +3430,6 @@ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, ntfs_inode *dir_ni; struct stat stbuf; -#if POSIXACLS - /* shortcut, use only if Posix ACLs in use */ - if (scx->vol->secure_flags & (1 << SECURITY_DEFAULT)) return (TRUE); -#endif allow = 0; dirpath = strdup(path); if (dirpath) { @@ -3387,7 +3439,7 @@ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, *name = 0; dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath); if (dir_ni) { - allow = ntfs_allowed_access(scx,dirpath, + allow = ntfs_allowed_access(scx, dir_ni, accesstype); ntfs_inode_close(dir_ni); /* @@ -3400,7 +3452,7 @@ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, path); allow = FALSE; if (ni) { - allow = (ntfs_get_owner_mode(scx,path,ni,&stbuf) >= 0) + allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) && (stbuf.st_uid == scx->uid); ntfs_inode_close(ni); } @@ -3411,14 +3463,16 @@ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, return (allow); /* errno is set if not allowed */ } +#endif + /* * Define a new owner/group to a file * * returns zero if successful */ -int ntfs_set_owner(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, uid_t uid, gid_t gid) +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -3453,7 +3507,7 @@ int ntfs_set_owner(struct SECURITY_CONTEXT *scx, fileuid = 0; filegid = 0; mode = 0; - oldattr = getsecurityattr(scx->vol, path, ni); + oldattr = getsecurityattr(scx->vol, ni); if (oldattr) { isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); @@ -3533,6 +3587,121 @@ int ntfs_set_owner(struct SECURITY_CONTEXT *scx, return (res ? -1 : 0); } +/* + * Define new owner/group and mode to a file + * + * returns zero if successful + */ + +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, const mode_t mode) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t fileuid; + uid_t filegid; + BOOL isdir; + int res; +#if POSIXACLS + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; + int pxsize; +#endif + + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; +#if POSIXACLS + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } +#endif + } else { + fileuid = 0; + filegid = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; +#endif +#if POSIXACLS + newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + else { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } +#endif + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; +#if POSIXACLS + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } +#if POSIXACLS + free(newpxdesc); +#endif + return (res ? -1 : 0); +} + /* * Build a security id for a descriptor inherited from * parent directory the Windows way @@ -3670,7 +3839,7 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, */ le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, - const char *dir_path, ntfs_inode *dir_ni, BOOL fordir) + ntfs_inode *dir_ni, BOOL fordir) { struct CACHED_PERMISSIONS *cached; char *parentattr; @@ -3693,7 +3862,7 @@ le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, * Note : if parent directory has no id, it is not cacheable */ if (!securid) { - parentattr = getsecurityattr(scx->vol, dir_path, dir_ni); + parentattr = getsecurityattr(scx->vol, dir_ni); if (parentattr) { securid = build_inherited_id(scx, parentattr, fordir); @@ -3799,14 +3968,13 @@ static int link_group_members(struct SECURITY_CONTEXT *scx) return (res); } - /* * Apply default single user mapping * returns zero if successful */ static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, - const SID *usid) + uid_t uid, gid_t gid, const SID *usid) { struct MAPPING *usermapping; struct MAPPING *groupmapping; @@ -3824,10 +3992,10 @@ static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); if (groupmapping) { usermapping->sid = sid; - usermapping->xid = scx->uid; + usermapping->xid = uid; usermapping->next = (struct MAPPING*)NULL; groupmapping->sid = sid; - groupmapping->xid = scx->uid; + groupmapping->xid = gid; groupmapping->next = (struct MAPPING*)NULL; scx->mapping[MAPUSERS] = usermapping; scx->mapping[MAPGROUPS] = groupmapping; @@ -3836,7 +4004,6 @@ static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, } } return (res); - } /* @@ -3878,6 +4045,8 @@ static BOOL check_mapping(const struct MAPPING *usermapping, #endif +#if 0 /* not used any more */ + /* * Try and apply default single user mapping * returns zero if successful @@ -3894,12 +4063,13 @@ static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx) res = -1; ni = ntfs_pathname_to_inode(scx->vol, NULL, "/."); if (ni) { - securattr = getsecurityattr(scx->vol,"/.",ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; usid = (SID*)&securattr[le32_to_cpu(phead->owner)]; if (ntfs_is_user_sid(usid)) - res = ntfs_do_default_mapping(scx,usid); + res = ntfs_do_default_mapping(scx, + scx->uid, scx->gid, usid); free(securattr); } ntfs_inode_close(ni); @@ -3907,6 +4077,8 @@ static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx) return (res); } +#endif + /* * Basic read from a user mapping file on another volume */ @@ -3939,7 +4111,8 @@ static int localread(void *fileid, char *buf, size_t size, off_t offs) * (failure should not be interpreted as an error) */ -int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path) +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef) { struct MAPLIST *item; struct MAPLIST *firstitem; @@ -3947,6 +4120,22 @@ int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path) struct MAPPING *groupmapping; ntfs_inode *ni; int fd; + static struct { + u8 revision; + u8 levels; + be16 highbase; + be32 lowbase; + le32 level1; + le32 level2; + le32 level3; + le32 level4; + le32 level5; + } defmap = { + 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5), + const_cpu_to_le32(21), + const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2), + const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE) + } ; /* be sure not to map anything until done */ scx->mapping[MAPUSERS] = (struct MAPPING*)NULL; @@ -3986,9 +4175,10 @@ int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path) firstitem = item; } } else { - /* no mapping file, try default mapping */ - if (scx->uid && scx->gid) { - if (!ntfs_default_mapping(scx)) + /* no mapping file, try a default mapping */ + if (allowdef) { + if (!ntfs_do_default_mapping(scx, + 0, 0, (const SID*)&defmap)) ntfs_log_info("Using default user mapping\n"); } } @@ -4002,8 +4192,7 @@ int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path) * The attribute is returned according to cpu endianness */ -int ntfs_get_ntfs_attrib(const char *path __attribute__((unused)), - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size) { u32 attrib; size_t outsize; @@ -4035,9 +4224,8 @@ int ntfs_get_ntfs_attrib(const char *path __attribute__((unused)), * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_attrib(const char *path __attribute__((unused)), - const char *value, size_t size, int flags, - ntfs_inode *ni) +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags) { u32 attrib; le32 settable; @@ -4073,6 +4261,7 @@ int ntfs_set_ntfs_attrib(const char *path __attribute__((unused)), if (!res) { ni->flags = (ni->flags & ~settable) | (cpu_to_le32(attrib) & settable); + NInoFileNameSetDirty(ni); NInoSetDirty(ni); } } else @@ -4490,7 +4679,7 @@ int ntfs_get_file_security(struct SECURITY_API *scapi, if (scapi && (scapi->magic == MAGIC_API)) { ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); if (ni) { - attr = getsecurityattr(scapi->security.vol, path, ni); + attr = getsecurityattr(scapi->security.vol, ni); if (attr) { if (feedsecurityattr(attr,selection, buf,buflen,psize)) { @@ -4559,7 +4748,7 @@ int ntfs_set_file_security(struct SECURITY_API *scapi, NULL, path); if (ni) { oldattr = getsecurityattr(scapi->security.vol, - path, ni); + ni); if (oldattr) { if (mergesecurityattr( scapi->security.vol, @@ -4950,7 +5139,7 @@ struct SECURITY_API *ntfs_initialize_file_security(const char *device, scx->pseccache = &scapi->seccache; scx->vol->secure_flags = 0; /* accept no mapping and no $Secure */ - ntfs_build_mapping(scx,(const char*)NULL); + ntfs_build_mapping(scx,(const char*)NULL,TRUE); ntfs_open_secure(vol); } else { if (scapi) diff --git a/source/libntfs/security.h b/source/libntfs/security.h index eaa67b41..4f3b5c54 100644 --- a/source/libntfs/security.h +++ b/source/libntfs/security.h @@ -34,6 +34,9 @@ #define POSIXACLS 0 #endif +typedef u16 be16; +typedef u32 be32; + #if __BYTE_ORDER == __LITTLE_ENDIAN #define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) #define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ @@ -79,6 +82,7 @@ struct CACHED_PERMISSIONS { struct CACHED_PERMISSIONS_LEGACY { struct CACHED_PERMISSIONS_LEGACY *next; + struct CACHED_PERMISSIONS_LEGACY *previous; void *variable; size_t varsize; /* above fields must match "struct CACHED_GENERIC" */ @@ -92,6 +96,7 @@ struct CACHED_PERMISSIONS_LEGACY { struct CACHED_SECURID { struct CACHED_SECURID *next; + struct CACHED_SECURID *previous; void *variable; size_t varsize; /* above fields must match "struct CACHED_GENERIC" */ @@ -238,28 +243,29 @@ extern int ntfs_sd_add_everyone(ntfs_inode *ni); extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len); -int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path); +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef); int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, struct stat*); -int ntfs_set_mode(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, mode_t mode); -BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni); -int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, const char *path, + ntfs_inode *ni, struct stat*); +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode); +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni); +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, int accesstype); -BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, const char *path, int accesstype); #if POSIXACLS le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, const char *dir_path, - ntfs_inode *dir_ni, mode_t mode, BOOL isdir); + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir); #else le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid, mode_t mode, BOOL isdir); #endif -int ntfs_set_owner(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, uid_t uid, gid_t gid); +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid); +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); #if POSIXACLS int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, @@ -269,7 +275,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); #endif le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, - const char *dir_path, ntfs_inode *dir_ni, BOOL fordir); + ntfs_inode *dir_ni, BOOL fordir); int ntfs_open_secure(ntfs_volume *vol); void ntfs_close_secure(struct SECURITY_CONTEXT *scx); @@ -277,28 +283,25 @@ void ntfs_close_secure(struct SECURITY_CONTEXT *scx); int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, - const char *dir_path, ntfs_inode *dir_ni, mode_t mode); -int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, char *value, size_t size, - ntfs_inode *ni); -int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, + ntfs_inode *dir_ni, mode_t mode); +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size); +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, const char *name, const char *value, size_t size, - int flags, ntfs_inode *ni); -int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, ntfs_inode *ni); + int flags); +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name); #endif -int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, char *value, size_t size, - ntfs_inode *ni); -int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, const char *value, size_t size, - int flags, ntfs_inode *ni); -int ntfs_get_ntfs_attrib(const char *path, - char *value, size_t size, ntfs_inode *ni); -int ntfs_set_ntfs_attrib(const char *path, - const char *value, size_t size, int flags, - ntfs_inode *ni); +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size); +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags); + +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size); +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags); + /* * Security API for direct access to security descriptors diff --git a/source/libntfs/types.h b/source/libntfs/types.h index 12e58f29..3fafe8a7 100644 --- a/source/libntfs/types.h +++ b/source/libntfs/types.h @@ -1,5 +1,5 @@ /* - * types.h - Misc type definitions not related to on-disk structure. + * types.h - Misc type definitions not related to on-disk structure. * Originated from the Linux-NTFS project. * * Copyright (c) 2000-2004 Anton Altaparmakov @@ -87,8 +87,8 @@ typedef sle64 leLSN; * Cygwin has a collision between our BOOL and 's * As long as this file will be included after were fine. */ -#ifndef _WINDEF_H #ifndef GEKKO +#ifndef _WINDEF_H /** * enum BOOL - These are just to make the code more readable... */ @@ -112,8 +112,8 @@ typedef enum { ONE = 1, #endif } BOOL; -#endif /* GEKKO */ #endif /* defined _WINDEF_H */ +#endif /* defined GECKO */ /** * enum IGNORE_CASE_BOOL - diff --git a/source/libntfs/unistr.c b/source/libntfs/unistr.c index 22eb2c61..cc3a1b8d 100644 --- a/source/libntfs/unistr.c +++ b/source/libntfs/unistr.c @@ -132,37 +132,30 @@ BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, TRUE; } -/** - * ntfs_names_collate - collate two Unicode names +/* + * ntfs_names_full_collate() fully collate two Unicode names + * * @name1: first Unicode name to compare * @name1_len: length of first Unicode name to compare * @name2: second Unicode name to compare * @name2_len: length of second Unicode name to compare - * @err_val: if @name1 contains an invalid character return this value * @ic: either CASE_SENSITIVE or IGNORE_CASE * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) * - * ntfs_names_collate() collates two Unicode names and returns: - * * -1 if the first name collates before the second one, * 0 if the names match, * 1 if the second name collates before the first one, or - * @err_val if an invalid character is found in @name1 during the comparison. * - * The following characters are considered invalid: '"', '*', '<', '>' and '?'. - * - * A few optimizations made by JPA */ - -int ntfs_names_collate(const ntfschar *name1, const u32 name1_len, +int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, const ntfschar *name2, const u32 name2_len, - const int err_val __attribute__((unused)), const IGNORE_CASE_BOOL ic, const ntfschar *upcase, const u32 upcase_len) { u32 cnt; - ntfschar c1, c2; + u16 c1, c2; + u16 u1, u2; #ifdef DEBUG if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { @@ -171,38 +164,70 @@ int ntfs_names_collate(const ntfschar *name1, const u32 name1_len, } #endif cnt = min(name1_len, name2_len); - /* JPA average loop count is 8 */ if (cnt > 0) { - if (ic) - /* JPA this loop in 76% cases */ + if (ic == CASE_SENSITIVE) { do { c1 = le16_to_cpu(*name1); name1++; c2 = le16_to_cpu(*name2); name2++; - if (c1 < upcase_len) - c1 = le16_to_cpu(upcase[c1]); - if (c2 < upcase_len) - c2 = le16_to_cpu(upcase[c2]); - } while ((c1 == c2) && --cnt); - else + } while (--cnt && (c1 == c2)); + u1 = c1; + u2 = c2; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + if ((u1 == u2) && cnt) + do { + u1 = le16_to_cpu(*name1); + name1++; + u2 = le16_to_cpu(*name2); + name2++; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } else { do { - /* JPA this loop in 24% cases */ - c1 = le16_to_cpu(*name1); + u1 = c1 = le16_to_cpu(*name1); name1++; - c2 = le16_to_cpu(*name2); + u2 = c2 = le16_to_cpu(*name2); name2++; - } while ((c1 == c2) && --cnt); - if (c1 < c2) + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + } else { + if (name1_len < name2_len) return -1; - if (c1 > c2) + if (name1_len > name2_len) return 1; } - if (name1_len < name2_len) - return -1; - if (name1_len == name2_len) - return 0; - return 1; + return 0; } /** @@ -264,7 +289,7 @@ int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, const ntfschar *upcase, const u32 upcase_size) { - ntfschar c1, c2; + u16 c1, c2; size_t i; #ifdef DEBUG @@ -357,13 +382,28 @@ void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase, const u32 upcase_len) { u32 i; - ntfschar u; + u16 u; for (i = 0; i < name_len; i++) if ((u = le16_to_cpu(name[i])) < upcase_len) name[i] = upcase[u]; } +/** + * ntfs_name_locase - Map a Unicode name to its lowercase equivalent + */ +void ntfs_name_locase(ntfschar *name, u32 name_len, const ntfschar *locase, + const u32 locase_len) +{ + u32 i; + u16 u; + + if (locase) + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < locase_len) + name[i] = locase[u]; +} + /** * ntfs_file_value_upcase - Convert a filename to upper case * @file_name_attr: @@ -381,31 +421,6 @@ void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, file_name_attr->file_name_length, upcase, upcase_len); } -/** - * ntfs_file_values_compare - Which of two filenames should be listed first - * @file_name_attr1: - * @file_name_attr2: - * @err_val: - * @ic: - * @upcase: - * @upcase_len: - * - * Description... - * - * Returns: - */ -int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, - const FILE_NAME_ATTR *file_name_attr2, - const int err_val, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_len) -{ - return ntfs_names_collate((ntfschar*)&file_name_attr1->file_name, - file_name_attr1->file_name_length, - (ntfschar*)&file_name_attr2->file_name, - file_name_attr2->file_name_length, - err_val, ic, upcase, upcase_len); -} - /* NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough for now]) for path names, but the Unicode code points need to be @@ -505,7 +520,7 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, char *t; int i, size, ret = -1; - ntfschar halfpair; + int halfpair; halfpair = 0; if (!*outs) @@ -614,24 +629,26 @@ static int utf8_to_utf16_size(const char *s) while ((byte = *((const unsigned char *)s++))) { if (++count >= PATH_MAX) goto fail; - if (byte >= 0xF5) { - errno = EILSEQ; - goto out; - } - if (!*s) - break; - if (byte >= 0xC0) - s++; - if (!*s) - break; - if (byte >= 0xE0) - s++; - if (!*s) - break; - if (byte >= 0xF0) { - s++; - if (++count >= PATH_MAX) - goto fail; + if (byte >= 0xc0) { + if (byte >= 0xF5) { + errno = EILSEQ; + goto out; + } + if (!*s) + break; + if (byte >= 0xC0) + s++; + if (!*s) + break; + if (byte >= 0xE0) + s++; + if (!*s) + break; + if (byte >= 0xF0) { + s++; + if (++count >= PATH_MAX) + goto fail; + } } } ret = count; @@ -663,8 +680,6 @@ static int utf8_to_unicode(u32 *wc, const char *s) } else if (byte < 0xc2) { goto fail; } else if (byte < 0xE0) { - if (strlen(s) < 2) - goto fail; if ((s[1] & 0xC0) == 0x80) { *wc = ((u32)(byte & 0x1F) << 6) | ((u32)(s[1] & 0x3F)); @@ -673,8 +688,6 @@ static int utf8_to_unicode(u32 *wc, const char *s) goto fail; /* three-byte */ } else if (byte < 0xF0) { - if (strlen(s) < 3) - goto fail; if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) { *wc = ((u32)(byte & 0x0F) << 12) | ((u32)(s[1] & 0x3F) << 6) @@ -693,8 +706,6 @@ static int utf8_to_unicode(u32 *wc, const char *s) goto fail; /* four-byte */ } else if (byte < 0xF5) { - if (strlen(s) < 4) - goto fail; if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80) && ((s[3] & 0xC0) == 0x80)) { *wc = ((u32)(byte & 0x07) << 18) @@ -737,6 +748,7 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) #endif /* defined(__APPLE__) || defined(__DARWIN__) */ const char *t = ins; u32 wc; + BOOL allocated; ntfschar *outpos; int shorts, ret = -1; @@ -744,18 +756,30 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) if (shorts < 0) goto fail; + allocated = FALSE; if (!*outs) { *outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar)); if (!*outs) goto fail; + allocated = TRUE; } outpos = *outs; while(1) { int m = utf8_to_unicode(&wc, t); - if (m < 0) - goto fail; + if (m <= 0) { + if (m < 0) { + /* do not leave space allocated if failed */ + if (allocated) { + free(*outs); + *outs = (ntfschar*)NULL; + } + goto fail; + } + *outpos++ = const_cpu_to_le16(0); + break; + } if (wc < 0x10000) *outpos++ = cpu_to_le16(wc); else { @@ -763,8 +787,6 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) *outpos++ = cpu_to_le16((wc >> 10) + 0xd800); *outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00); } - if (m == 0) - break; t += m; } @@ -1028,6 +1050,61 @@ err_out: return -1; } +/* + * Turn a UTF8 name uppercase + * + * Returns an allocated uppercase name which has to be freed by caller + * or NULL if there is an error (described by errno) + */ + +char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_size) +{ + int size; + char *upp; + u32 wc; + int n; + const char *s; + char *t; + + size = strlen(low); + upp = (char*)ntfs_malloc(3*size + 1); + if (upp) { + s = low; + t = upp; + do { + n = utf8_to_unicode(&wc, s); + if (n > 0) { + if (wc < upcase_size) + wc = le16_to_cpu(upcase[wc]); + if (wc < 0x80) + *t++ = wc; + else if (wc < 0x800) { + *t++ = (0xc0 | ((wc >> 6) & 0x3f)); + *t++ = 0x80 | (wc & 0x3f); + } else if (wc < 0x10000) { + *t++ = 0xe0 | (wc >> 12); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } else { + *t++ = 0xf0 | ((wc >> 18) & 7); + *t++ = 0x80 | ((wc >> 12) & 63); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } + s += n; + } + } while (n > 0); + if (n < 0) { + free(upp); + upp = (char*)NULL; + errno = EILSEQ; + } + *t = 0; + } + return (upp); +} + /** * ntfs_upcase_table_build - build the default upcase table for NTFS * @uc: destination buffer where to store the built table @@ -1099,6 +1176,38 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) } } +/* + * Build a table for converting to lower case + * + * This is only meaningful when there is a single lower case + * character leading to an upper case one, and currently the + * only exception is the greek letter sigma which has a single + * upper case glyph (code U+03A3), but two lower case glyphs + * (code U+03C3 and U+03C2, the latter to be used at the end + * of a word). In the following implementation the upper case + * sigma will be lowercased as U+03C3. + */ + +ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt) +{ + ntfschar *lc; + u32 upp; + u32 i; + + lc = (ntfschar*)ntfs_malloc(uc_cnt*sizeof(ntfschar)); + if (lc) { + for (i=0; i' - 0x20)) | (1L << ('?' - 0x20)); - forbidden = (len == 0) || (le16_to_cpu(name[len-1]) == ' '); + forbidden = (len == 0) + || (le16_to_cpu(name[len-1]) == ' ') + || (le16_to_cpu(name[len-1]) == '.'); for (i=0; ivol_name); free(v->upcase); + if (v->locase) free(v->locase); free(v->attrdef); free(v); @@ -486,7 +489,14 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) ntfs_upcase_table_build(vol->upcase, vol->upcase_len * sizeof(ntfschar)); + /* Default with no locase table and case sensitive file names */ + vol->locase = (ntfschar*)NULL; + NVolSetCaseSensitive(vol); + /* by default, all files are shown and not marked hidden */ + NVolSetShowSysFiles(vol); + NVolSetShowHidFiles(vol); + NVolClearHideDotFiles(vol); if (flags & MS_RDONLY) NVolSetReadOnly(vol); @@ -760,6 +770,70 @@ out: return errno ? -1 : 0; } +/* + * Make sure a LOGGED_UTILITY_STREAM attribute named "$TXF_DATA" + * on the root directory is resident. + * When it is non-resident, the partition cannot be mounted on Vista + * (see http://support.microsoft.com/kb/974729) + * + * We take care to avoid this situation, however this can be a + * consequence of having used an older version (including older + * Windows version), so we had better fix it. + * + * Returns 0 if unneeded or successful + * -1 if there was an error, explained by errno + */ + +static int fix_txf_data(ntfs_volume *vol) +{ + void *txf_data; + s64 txf_data_size; + ntfs_inode *ni; + ntfs_attr *na; + int res; + + res = 0; + ntfs_log_debug("Loading root directory\n"); + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_perror("Failed to open root directory"); + res = -1; + } else { + /* Get the $TXF_DATA attribute */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, TXF_DATA, 9); + if (na) { + if (NAttrNonResident(na)) { + /* + * Fix the attribute by truncating, then + * rewriting it. + */ + ntfs_log_debug("Making $TXF_DATA resident\n"); + txf_data = ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM, + TXF_DATA, 9, &txf_data_size); + if (txf_data) { + if (ntfs_attr_truncate(na, 0) + || (ntfs_attr_pwrite(na, 0, + txf_data_size, txf_data) + != txf_data_size)) + res = -1; + free(txf_data); + } + if (res) + ntfs_log_error("Failed to make $TXF_DATA resident\n"); + else + ntfs_log_error("$TXF_DATA made resident\n"); + } + ntfs_attr_close(na); + } + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close root"); + res = -1; + } + } + return (res); +} + /** * ntfs_device_mount - open ntfs volume * @dev: device to open @@ -1045,9 +1119,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) goto error_exit; for (j = 0; j < (s32)u; j++) { - ntfschar uc = le16_to_cpu(vname[j]); + u16 uc = le16_to_cpu(vname[j]); if (uc > 0xff) - uc = (ntfschar)'_'; + uc = (u16)'_'; vol->vol_name[j] = (char)uc; } vol->vol_name[u] = '\0'; @@ -1111,6 +1185,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) goto error_exit; } } + /* make $TXF_DATA resident if present on the root directory */ + if (!NVolReadOnly(vol) && fix_txf_data(vol)) + goto error_exit; return vol; io_error_exit: @@ -1126,6 +1203,58 @@ error_exit: return NULL; } +/* + * Set appropriate flags for showing NTFS metafiles + * or files marked as hidden. + * Not set in ntfs_mount() to avoid breaking existing tools. + */ + +int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, + BOOL hide_dot_files) +{ + int res; + + res = -1; + if (vol) { + NVolClearShowSysFiles(vol); + NVolClearShowHidFiles(vol); + NVolClearHideDotFiles(vol); + if (show_sys_files) + NVolSetShowSysFiles(vol); + if (show_hid_files) + NVolSetShowHidFiles(vol); + if (hide_dot_files) + NVolSetHideDotFiles(vol); + res = 0; + } + if (res) + ntfs_log_error("Failed to set file visibility\n"); + return (res); +} + +/* + * Set ignore case mode + */ + +int ntfs_set_ignore_case(ntfs_volume *vol) +{ + int res; + + res = -1; + if (vol && vol->upcase) { + vol->locase = ntfs_locase_table_build(vol->upcase, + vol->upcase_len); + if (vol->locase) { + NVolClearCaseSensitive(vol); + res = 0; + } + } + if (res) + ntfs_log_error("Failed to set ignore_case mode\n"); + return (res); +} + /** * ntfs_mount - open ntfs volume * @name: name of device/file to open @@ -1432,7 +1561,7 @@ error_exit: * * Return 0 if successful and -1 if not with errno set to the error code. */ -int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags) +int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags) { ATTR_RECORD *a; VOLUME_INFORMATION *c; diff --git a/source/libntfs/volume.h b/source/libntfs/volume.h index a0c71bdf..79193c53 100644 --- a/source/libntfs/volume.h +++ b/source/libntfs/volume.h @@ -42,10 +42,6 @@ #include #endif -#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */ -#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */ -#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */ - /* * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY, * so we define them ourselves. @@ -65,6 +61,7 @@ /* Forward declaration */ typedef struct _ntfs_volume ntfs_volume; +#include "param.h" #include "types.h" #include "support.h" #include "device.h" @@ -110,6 +107,10 @@ typedef enum { NV_ReadOnly, /* 1: Volume is read-only. */ NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ NV_LogFileEmpty, /* 1: $logFile journal is empty. */ + NV_ShowSysFiles, /* 1: Show NTFS metafiles. */ + NV_ShowHidFiles, /* 1: Show files marked hidden. */ + NV_HideDotFiles, /* 1: Set hidden flag on dot files */ + NV_Compression, /* 1: allow compression */ } ntfs_volume_state_bits; #define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) @@ -128,6 +129,22 @@ typedef enum { #define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) #define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) +#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles) +#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles) +#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles) + +#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles) +#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles) +#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles) + +#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles) +#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles) +#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles) + +#define NVolCompression(nv) test_nvol_flag(nv, Compression) +#define NVolSetCompression(nv) set_nvol_flag(nv, Compression) +#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression) + /* * NTFS version 1.1 and 1.2 are used by Windows NT4. * NTFS version 2.x is used by Windows 2000 Beta @@ -159,7 +176,7 @@ struct _ntfs_volume { ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */ u8 major_ver; /* Ntfs major version of volume. */ u8 minor_ver; /* Ntfs minor version of volume. */ - u16 flags; /* Bit array of VOLUME_* flags. */ + le16 flags; /* Bit array of VOLUME_* flags. */ u16 sector_size; /* Byte size of a sector. */ u8 sector_size_bits; /* Log(2) of the byte size of a sector. */ @@ -220,6 +237,9 @@ struct _ntfs_volume { FILE_UpCase. */ u32 upcase_len; /* Length in Unicode characters of the upcase table. */ + ntfschar *locase; /* Lower case equivalents of all 65536 2-byte + Unicode characters. Only if option + case_ignore is set. */ ATTR_DEF *attrdef; /* Attribute definitions. Obtained from FILE_AttrDef. */ @@ -235,6 +255,12 @@ struct _ntfs_volume { #if CACHE_INODE_SIZE struct CACHE_HEADER *xinode_cache; #endif +#if CACHE_NIDATA_SIZE + struct CACHE_HEADER *nidata_cache; +#endif +#if CACHE_LOOKUP_SIZE + struct CACHE_HEADER *lookup_cache; +#endif #if CACHE_SECURID_SIZE struct CACHE_HEADER *securid_cache; #endif @@ -261,14 +287,17 @@ extern int ntfs_version_is_supported(ntfs_volume *vol); extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose); extern int ntfs_logfile_reset(ntfs_volume *vol); -extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags); +extern int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags); extern int ntfs_volume_error(int err); extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); extern int ntfs_volume_get_free_space(ntfs_volume *vol); +extern int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files); extern int ntfs_set_locale(void); +extern int ntfs_set_ignore_case(ntfs_volume *vol); #endif /* defined _NTFS_VOLUME_H */ diff --git a/source/main.cpp b/source/main.cpp index c79c7117..9ed977a5 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -51,6 +51,7 @@ extern "C" #include "usbloader/usbstorage2.h" #include "memory/mem2.h" #include "lstub.h" +#include "usbloader/usbstorage2.h" extern bool geckoinit; extern bool textVideoInit; @@ -65,172 +66,19 @@ extern char headlessID[8]; FreeTypeGX *fontSystem=0; FreeTypeGX *fontClock=0; PartList partitions; - u8 dbvideo =0; -static void BootUpProblems() -{ - s32 ret2; - - // load main font from file, or default to built-in font - fontSystem = new FreeTypeGX(); - fontSystem->loadFont(NULL, font_ttf, font_ttf_size, 0); - fontSystem->setCompatibilityMode(FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_PASSCLR | FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_NONE); - - GuiImageData bootimageData(gxlogo_png); - GuiImage bootimage(&bootimageData); - GuiText boottext(NULL, 20, (GXColor) {255, 255, 255, 255} - ); - boottext.SetPosition(200, 240-1.2*bootimage.GetHeight()/2+250); - bootimage.SetPosition(320-1.2*bootimage.GetWidth()/2, 240-1.2*bootimage.GetHeight()/2); - bootimage.SetScale(1.2); - - GuiImageData usbimageData(usbport_png); - GuiImage usbimage(&usbimageData); - usbimage.SetPosition(400,300); - usbimage.SetScale(.7); - usbimage.SetAlpha(200); - - time_t curtime; - time_t endtime = time(0) + 30; - do - { - /*ret2 = IOS_ReloadIOSsafe(249); - if (ret2 < 0) { - ret2 = IOS_ReloadIOSsafe(222); - SDCard_Init(); - load_ehc_module(); - SDCard_deInit(); - if(ret2 <0) { - boottext.SetText("ERROR: cIOS could not be loaded!"); - bootimage.Draw(); - boottext.Draw(); - Menu_Render(); - sleep(5); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - }*/ - USBDevice_deInit(); - USBDevice_Init(); - ret2 = WBFS_Init(WBFS_DEVICE_USB); - if (ret2 >= 0) - { - boottext.SetText("Loading..."); - bootimage.Draw(); - boottext.Draw(); - Menu_Render(); - break; - } - curtime = time(0); - boottext.SetTextf("Waiting for your slow USB Device: %i secs...", int(endtime-curtime)); - while(curtime == time(0)) - { - boottext.Draw(); - bootimage.Draw(); - if (endtime-curtime<15)usbimage.Draw(); - Menu_Render(); - } - } while((endtime-time(0)) > 0); - - /*if(ret2 < 0) { - boottext.SetText("ERROR: USB device could not be loaded!"); - usbimage.Draw(); - bootimage.Draw(); - boottext.Draw(); - Menu_Render(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - }*/ - - ///delete font to load up custom if set - if(fontSystem) - { - delete fontSystem; - fontSystem = NULL; - } -} - - -unsigned int *xfb = NULL; - -void InitTextVideo () -{ - gprintf("\nInitTextVideo ()"); - if (textVideoInit) - { - gprintf("...0"); - return; - } - dbvideo=1; - VIDEO_Init(); - // get default video mode - GXRModeObj *vmode = VIDEO_GetPreferredMode(NULL); - - // widescreen fix - VIDEO_Configure (vmode); - - // Allocate the video buffers - xfb = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); - - // A console is always useful while debugging - console_init (xfb, 20, 64, vmode->fbWidth, vmode->xfbHeight, vmode->fbWidth * 2); - - // Clear framebuffers etc. - VIDEO_ClearFrameBuffer (vmode, xfb, COLOR_BLACK); - VIDEO_SetNextFramebuffer (xfb); - - VIDEO_SetBlack (FALSE); - VIDEO_Flush (); - VIDEO_WaitVSync (); - if (vmode->viTVMode & VI_NON_INTERLACE) - VIDEO_WaitVSync (); - - //send console output to the gecko - if (geckoinit)CON_EnableGecko(1, true); - textVideoInit = true; - gprintf("...1"); - -} - - -int -main(int argc, char *argv[]) +int main(int argc, char *argv[]) { + MEM2_init(48); + PAD_Init(); + InitVideo(); setlocale(LC_ALL, "en.UTF-8"); geckoinit = InitGecko(); - - if (hbcStubAvailable() || geckoinit) - { - InitTextVideo(); - } - - // DEBUG_Init(GDBSTUB_DEVICE_USB, 1); - //_break(); - - __exception_setreload(5); //auto reset code dump nobody gives us codedump info anyways. - - gprintf("\n\n------------------"); - gprintf("\nUSB Loader GX rev%s",GetRev()); - gprintf("\nmain(%d", argc); - for (int i=0;i"); - gprintf(")"); - - // This part is added, because we need a identify patched ios -// printf("\n\tReloading into ios 236"); - if (IOS_ReloadIOSsafe(236) < 0) - { -// printf("\n\tIOS 236 not found, reloading into 36"); - IOS_ReloadIOSsafe(36); - } + __exception_setreload(5); printf("\n\tStarting up"); - MEM2_init(36); // Initialize 36 MB - MEM2_takeBigOnes(true); - - s32 ret; - bool startupproblem = false; - bool bootDevice_found=false; if (argc >= 1) { @@ -242,144 +90,15 @@ main(int argc, char *argv[]) bootDevice_found = true; } - printf("\n\tInitializing controllers"); - - /** PAD_Init has to be before InitVideo don't move that **/ - PAD_Init(); // initialize PAD/WPAD - - printf("\n\tInitialize USB (wake up)"); - - USBDevice_Init(); // seems enough to wake up some HDDs if they are in sleep mode when the loader starts (tested with WD MyPassport Essential 2.5") - - gprintf("\n\tChecking for stub IOS"); - ios222rev = getIOSrev(0x00000001000000dell); - ios249rev = getIOSrev(0x00000001000000f9ll); - - //if we don't like either of the cIOS then scram - if (!(ios222rev==4 || ios222rev==5 || (ios249rev>=9 && ios249rev<65280))) - { - InitTextVideo(); - printf("\x1b[2J"); - if ((ios222rev < 0 && ios222rev != WII_EINSTALL) && (ios249rev < 0 && ios249rev != WII_EINSTALL)) - { - printf("\n\n\n\tWARNING!"); - printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); - printf("\n\n\tWe cannot determine the versions on your system,\n\tsince you have no patched ios 36 or 236 installed."); - printf("\n\tTherefor, if loading of USB Loader GX fails, you\n\tprobably have installed the 4.2 update,"); - printf("\n\tand you should go figure out how to get some cios action going on\n\tin your Wii."); - printf("\n\n\tThis message will show every time."); - sleep(5); - } - else - { - printf("\n\n\n\tERROR!"); - printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); - printf("\n\n\tI found \n\t\t222 = %d%s",ios222rev,ios222rev==65280?" (Stubbed by 4.2 update)":""); - printf("\n\t\t249 = %d%s",ios249rev,ios249rev==65280?" (Stubbed by 4.2 update)":""); - printf("\n\n\tGo figure out how to get some cIOS action going on\n\tin your Wii and come back and see me."); - - sleep(15); - printf("\n\n\tBye"); - - USBDevice_deInit(); - exit(0); - } - } - - printf("\n\tReloading ios 249..."); - ret = IOS_ReloadIOSsafe(249); - - printf("%d", ret); - - if (ret < 0) - { - printf("\n\tIOS 249 failed, reloading ios 222..."); - ret = IOS_ReloadIOSsafe(222); - printf("%d", ret); - - if (ret < 0) - { - printf("\n\tIOS 222 failed, reloading ios 250..."); - ret = IOS_ReloadIOSsafe(250); - printf("%d", ret); - - if(ret < 0) - { - printf("\n\tIOS 250 failed, reloading ios 223..."); - ret = IOS_ReloadIOSsafe(223); - printf("%d", ret); - - if (ret < 0) - { - printf("\n\tERROR: cIOS could not be loaded!\n"); - sleep(5); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - } - } - printf("\n\tInitialize sd card"); - SDCard_Init(); - printf("\n\tLoad ehc module"); - load_ehc_module(); - printf("\n\tdeinit sd card"); - SDCard_deInit(); - } - - printf("\n\tInit wbfs..."); - ret = WBFS_Init(WBFS_DEVICE_USB); - printf("%d", ret); - - if (ret < 0) - { - printf("\n\tYou have issues with a slow disc, or a difficult disc\n\tReloading 222..."); - ret = IOS_ReloadIOSsafe(222); - printf("%d", ret); - /*if(ret < 0) { - // printf("\n\tSleeping for 4 seconds"); - // sleep(4); - - InitVideo(); // Initialise video - Menu_Render(); - BootUpProblems(); - startupproblem = true; - ret = 1; - }*/ - printf("\n\tInitialize sd card"); - SDCard_Init(); - printf("\n\tLoad ehc module"); - load_ehc_module(); - printf("\n\tdeinit sd card"); - SDCard_deInit(); - - printf("\n\tInitialize wbfs..."); - USBDevice_deInit(); - USBDevice_Init(); - ret = WBFS_Init(WBFS_DEVICE_USB); - printf("%d", ret); - - if(ret < 0) - { - // printf("\n\tSleeping for 4 seconds"); - // sleep(4); - InitVideo(); // Initialise video - Menu_Render(); - BootUpProblems(); - startupproblem = true; - ret = 1; - } - } - + //Let's use libogc sd/usb for config loading printf("\n\tInitialize sd card"); - SDCard_Init(); // mount SD for loading cfg's - - //this should have already been done by now in order to WBFS_Init(). + SDCard_Init(); printf("\n\tInitialize usb device"); - USBDevice_Init(); // and mount USB:/ + USBDevice_Init(); if (!bootDevice_found) { printf("\n\tSearch for configuration file"); - //try USB //left in all the dol and elf files in this check in case this is the first time running the app and they dont have the config if (checkfile((char*) "USB:/config/GXglobal.cfg") || (checkfile((char*) "USB:/apps/usbloader_gx/boot.elf")) @@ -390,82 +109,39 @@ main(int argc, char *argv[]) printf("\n\tConfiguration file is on %s", bootDevice); } - // Try opening and closing the configuration file here - // to prevent a crash dump later on - giantpune - char GXGlobal_cfg[26]; - sprintf(GXGlobal_cfg, "%s/config/GXGlobal.cfg", bootDevice); - FILE *fp = fopen(GXGlobal_cfg, "r"); - if (fp) - { - fclose(fp); - } - gettextCleanUp(); printf("\n\tLoading configuration..."); CFG_Load(); printf("done"); - // gprintf("\n\tbootDevice = %s",bootDevice); - /* Load Custom IOS */ - if ((Settings.cios == ios222 && IOS_GetVersion() != 222) || - (Settings.cios == ios223 && IOS_GetVersion() != 223)) + SDCard_deInit();// unmount SD for reloading IOS + USBDevice_deInit();// unmount USB for reloading IOS + USBStorage2_Deinit(); + + // This part is added, because we need a identify patched ios + //! pune please replace this with your magic patch functions - Dimok + if (IOS_ReloadIOSsafe(236) < 0) + IOS_ReloadIOSsafe(36); + + printf("\n\tCheck for an existing cIOS"); + CheckForCIOS(); + + // Let's load the cIOS now + if(LoadAppCIOS() < 0) { - printf("\n\tReloading IOS to config setting (%d)...", Settings.cios == ios222 ? 222 : 223); - SDCard_deInit(); // unmount SD for reloading IOS - USBDevice_deInit(); // unmount USB for reloading IOS - USBStorage2_Deinit(); - ret = IOS_ReloadIOSsafe(Settings.cios == ios222 ? 222 : 223); - printf("%d", ret); - SDCard_Init(); - load_ehc_module(); - if (ret < 0) - { - SDCard_deInit(); - Settings.cios = ios249; - ret = IOS_ReloadIOSsafe(249); - // now mount SD:/ //no need to keep mindlessly mounting and unmounting SD card - SDCard_Init(); - } - - USBDevice_Init(); // and mount USB:/ - WBFS_Init(WBFS_DEVICE_USB); - } else if ((Settings.cios == ios249 && IOS_GetVersion() != 249) || - (Settings.cios == ios250 && IOS_GetVersion() != 250)) - { - - printf("\n\tReloading IOS to config setting (%d)...", ios249 ? 249 : 250); - SDCard_deInit(); // unmount SD for reloading IOS - USBDevice_deInit(); // unmount USB for reloading IOS - USBStorage2_Deinit(); - ret = IOS_ReloadIOSsafe(ios249 ? 249 : 250); - printf("%d", ret); - if (ret < 0) - { - Settings.cios = ios222; - ret = IOS_ReloadIOSsafe(222); - SDCard_Init(); - load_ehc_module(); - } - - else SDCard_Init(); // now mount SD:/ //no need to keep mindlessly mounting and unmounting SD card - USBDevice_Init(); // and mount USB:/ - WBFS_Init(WBFS_DEVICE_USB); - } - - // Partition_GetList(&partitions); - - if (ret < 0) - { - printf("\nERROR: cIOS could not be loaded!"); + printf("\n\tERROR: No cIOS could be loaded. Exiting...."); sleep(5); - exit(0); - //SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + Sys_BackToLoader(); } - //gprintf("\n\tcIOS = %u (Rev %u)",IOS_GetVersion(), IOS_GetRevision());//don't need gprintf if sending console shit to gecko, too + printf("\n\tcIOS = %u (Rev %u)",IOS_GetVersion(), IOS_GetRevision()); - // printf("Sleeping for 5 seconds\n"); - // sleep(5); + // Init WBFS + int ret = WBFS_Init(WBFS_DEVICE_USB); + if (ret < 0) + { + printf("\nERROR: No WBFS drive mounted."); + } //if a ID was passed via args copy it and try to boot it after the partition is mounted //its not really a headless mode. more like hairless. @@ -478,9 +154,7 @@ main(int argc, char *argv[]) //! Init the rest of the System Sys_Init(); Wpad_Init(); - if(!startupproblem) - InitVideo(); - InitAudio(); // Initialize audio + InitAudio(); WPAD_SetDataFormat(WPAD_CHAN_ALL,WPAD_FMT_BTNS_ACC_IR); WPAD_SetVRes(WPAD_CHAN_ALL, screenwidth, screenheight); diff --git a/source/memory/mem2.cpp b/source/memory/mem2.cpp index 01630927..7f7361c7 100644 --- a/source/memory/mem2.cpp +++ b/source/memory/mem2.cpp @@ -1,17 +1,27 @@ #include "mem2.h" -#include "mem2alloc.h" +#include "mem2alloc.hpp" #include #include -#define MEM2_PRIORITY_SIZE 0x40 +#define MEM2_PRIORITY_SIZE 2097152 //2MB // Forbid the use of MEM2 through malloc u32 MALLOC_MEM2 = 0; static CMEM2Alloc g_mem2gp; +static bool g_bigGoesToMem2 = false; + +extern "C" +{ + +void MEM2_takeBigOnes(bool b) +{ + g_bigGoesToMem2 = b; +} + void MEM2_init(unsigned int mem2Size) { g_mem2gp.init(mem2Size); @@ -22,41 +32,31 @@ void MEM2_cleanup(void) g_mem2gp.cleanup(); } -extern "C" void *MEM2_alloc(unsigned int s) +void *MEM2_alloc(unsigned int s) { return g_mem2gp.allocate(s); } -extern "C" void MEM2_free(void *p) +void MEM2_free(void *p) { g_mem2gp.release(p); } -extern "C" void *MEM2_realloc(void *p, unsigned int s) +void *MEM2_realloc(void *p, unsigned int s) { return g_mem2gp.reallocate(p, s); } -extern "C" unsigned int MEM2_usableSize(void *p) +unsigned int MEM2_usableSize(void *p) { return CMEM2Alloc::usableSize(p); } -// Give priority to MEM2 for big allocations -// Used for saving some space in malloc, which is required for 2 reasons : -// - decent speed on small and frequent allocations -// - newlib uses its malloc internally (for *printf for example) so it should always have some memory left -bool g_bigGoesToMem2 = false; - -void MEM2_takeBigOnes(bool b) +unsigned int MEM2_freesize() { - g_bigGoesToMem2 = b; + return g_mem2gp.FreeSize(); } - -extern "C" -{ - extern __typeof(malloc) __real_malloc; extern __typeof(calloc) __real_calloc; extern __typeof(realloc) __real_realloc; @@ -133,9 +133,12 @@ void __wrap_free(void *p) if(!p) return; - if (((u32)p & 0x10000000) != 0) { + if (((u32)p & 0x10000000) != 0) + { MEM2_free(p); - } else { + } + else + { __real_free(p); } } @@ -185,4 +188,4 @@ size_t __wrap_malloc_usable_size(void *p) return __real_malloc_usable_size(p); } -} +} ///extern "C" diff --git a/source/memory/mem2.h b/source/memory/mem2.h index 410705f6..b8659631 100644 --- a/source/memory/mem2.h +++ b/source/memory/mem2.h @@ -1,26 +1,24 @@ -// 2 MEM2 allocators, one for general purpose, one for covers -// Aligned and padded to 32 bytes, as required by many functions - -#ifndef __MEM2_HPP -#define __MEM2_HPP - -#ifdef __cplusplus -extern "C" -{ -#endif - -void *MEM2_alloc(unsigned int s); -void *MEM2_realloc(void *p, unsigned int s); -void MEM2_free(void *p); -unsigned int MEM2_usableSize(void *p); - -#ifdef __cplusplus -} - -void MEM2_init(unsigned int mem2Size); -void MEM2_cleanup(void); -void MEM2_takeBigOnes(bool b); - -#endif - -#endif // !defined(__MEM2_HPP) +// 2 MEM2 allocators, one for general purpose, one for covers +// Aligned and padded to 32 bytes, as required by many functions + +#ifndef __MEM2_H_ +#define __MEM2_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void MEM2_init(unsigned int mem2Size); +void MEM2_cleanup(void); +void MEM2_takeBigOnes(bool b); +void *MEM2_alloc(unsigned int s); +void *MEM2_realloc(void *p, unsigned int s); +void MEM2_free(void *p); +unsigned int MEM2_usableSize(void *p); +unsigned int MEM2_freesize(); + +#ifdef __cplusplus +} +#endif + +#endif // !defined(__MEM2_H_) diff --git a/source/memory/mem2alloc.cpp b/source/memory/mem2alloc.cpp index c6898b4a..50209ee2 100644 --- a/source/memory/mem2alloc.cpp +++ b/source/memory/mem2alloc.cpp @@ -1,201 +1,238 @@ - -#include "mem2alloc.h" - -#include -#include -#include - - -class LockMutex -{ - mutex_t &m_mutex; -public: - LockMutex(mutex_t &m) : m_mutex(m) { LWP_MutexLock(m_mutex); } - ~LockMutex(void) { LWP_MutexUnlock(m_mutex); } -}; - -void CMEM2Alloc::init(unsigned int size) -{ - m_baseAddress = (SBlock *)(((u32)SYS_GetArena2Lo() + 31) & ~31); - m_endAddress = (SBlock *)((char *)m_baseAddress + std::min(size * 0x100000, SYS_GetArena2Size() & ~31)); - if (m_endAddress > (SBlock *)0x93000000) // See loader/disc.c - m_endAddress = (SBlock *)0x93000000; - SYS_SetArena2Lo(m_endAddress); - LWP_MutexInit(&m_mutex, 0); -} - -void CMEM2Alloc::init(void *addr, void *end) -{ - m_baseAddress = (SBlock *)(((u32)addr + 31) & ~31); - m_endAddress = (SBlock *)((u32)end & ~31); - LWP_MutexInit(&m_mutex, 0); -} - -void CMEM2Alloc::cleanup(void) -{ - LWP_MutexDestroy(m_mutex); - m_mutex = 0; - m_first = 0; -// // Try to release the range we took through SYS functions -// if (SYS_GetArena2Lo() == m_endAdress) -// SYS_SetArena2Lo(m_baseAddress); - m_baseAddress = 0; - m_endAddress = 0; -} - -void CMEM2Alloc::clear(void) -{ - m_first = 0; - memset(m_baseAddress, 0, (u8 *)m_endAddress - (u8 *)m_endAddress); -} - -unsigned int CMEM2Alloc::usableSize(void *p) -{ - return p == 0 ? 0 : ((SBlock *)p - 1)->s * sizeof (SBlock); -} - -void *CMEM2Alloc::allocate(unsigned int s) -{ - if (s == 0) - s = 1; - // - LockMutex lock(m_mutex); - // - s = (s - 1) / sizeof (SBlock) + 1; - // First block - if (m_first == 0) - { - if (m_baseAddress + s + 1 >= m_endAddress) - return 0; - m_first = m_baseAddress; - m_first->next = 0; - m_first->prev = 0; - m_first->s = s; - m_first->f = false; - return (void *)(m_first + 1); - } - // Search for a free block - SBlock *i; - SBlock *j; - for (i = m_first; i != 0; i = i->next) - { - if (i->f && i->s >= s) - break; - j = i; - } - // Create a new block - if (i == 0) - { - i = j + j->s + 1; - if (i + s + 1 >= m_endAddress) - return 0; - j->next = i; - i->prev = j; - i->next = 0; - i->s = s; - i->f = false; - return (void *)(i + 1); - } - // Reuse a free block - i->f = false; - // Split it - if (i->s > s + 1) - { - j = i + s + 1; - j->f = true; - j->s = i->s - s - 1; - i->s = s; - j->next = i->next; - j->prev = i; - i->next = j; - if (j->next != 0) - j->next->prev = j; - } - return (void *)(i + 1); -} - -void CMEM2Alloc::release(void *p) -{ - if (p == 0) - return; - LockMutex lock(m_mutex); - SBlock *i = (SBlock *)p - 1; - i->f = true; - // Merge with previous block - if (i->prev != 0 && i->prev->f) - { - i = i->prev; - i->s += i->next->s + 1; - i->next = i->next->next; - if (i->next != 0) - i->next->prev = i; - } - // Merge with next block - if (i->next != 0 && i->next->f) - { - i->s += i->next->s + 1; - i->next = i->next->next; - if (i->next != 0) - i->next->prev = i; - } -} - -void *CMEM2Alloc::reallocate(void *p, unsigned int s) -{ - SBlock *i; - SBlock *j; - void *n; - - if (s == 0) - s = 1; - if (p == 0) - return allocate(s); - i = (SBlock *)p - 1; - s = (s - 1) / sizeof (SBlock) + 1; - { - LockMutex lock(m_mutex); - if (i + s + 1 >= m_endAddress) - return 0; - - // Last block - if (i->next == 0 && i + s + 1 < m_endAddress) - { - i->s = s; - return p; - } - // Size <= current size + next block - if (i->s < s && i->next->f && i->s + i->next->s + 1 >= s) - { - // Merge - i->s += i->next->s + 1; - i->next = i->next->next; - if (i->next != 0) - i->next->prev = i; - } - // Size <= current size - if (i->s >= s) - { - // Split - if (i->s > s + 1) - { - j = i + s + 1; - j->f = true; - j->s = i->s - s - 1; - i->s = s; - j->next = i->next; - j->prev = i; - i->next = j; - if (j->next != 0) - j->next->prev = j; - } - return p; - } - } - // Size > current size - n = allocate(s * sizeof (SBlock)); - if (n == 0) - return 0; - memcpy(n, p, i->s * sizeof (SBlock)); - release(p); - return n; -} + +#include "mem2alloc.hpp" + +#include +#include +#include + + +class LockMutex +{ + mutex_t &m_mutex; +public: + LockMutex(mutex_t &m) : m_mutex(m) { LWP_MutexLock(m_mutex); } + ~LockMutex(void) { LWP_MutexUnlock(m_mutex); } +}; + +void CMEM2Alloc::init(unsigned int size) +{ + m_baseAddress = (SBlock *) (((u32)SYS_GetArena2Lo() + 31) & ~31); + m_endAddress = (SBlock *) ((char *)m_baseAddress + std::min(size * 0x100000, SYS_GetArena2Size() & ~31)); + if (m_endAddress > (SBlock *) 0x93300000) //rest is reserved for usb/usb2/network and other stuff... (0xE0000 bytes) + m_endAddress = (SBlock *) 0x93300000; + SYS_SetArena2Lo(m_endAddress); + LWP_MutexInit(&m_mutex, 0); +} + +void CMEM2Alloc::init(void *addr, void *end) +{ + m_baseAddress = (SBlock *)(((u32)addr + 31) & ~31); + m_endAddress = (SBlock *)((u32)end & ~31); + LWP_MutexInit(&m_mutex, 0); +} + +void CMEM2Alloc::cleanup(void) +{ + LWP_MutexDestroy(m_mutex); + m_mutex = 0; + m_first = 0; + // Try to release the range we took through SYS functions + if (SYS_GetArena2Lo() == m_endAddress) + SYS_SetArena2Lo(m_baseAddress); + m_baseAddress = 0; + m_endAddress = 0; +} + +void CMEM2Alloc::clear(void) +{ + m_first = 0; + memset(m_baseAddress, 0, (u8 *)m_endAddress - (u8 *)m_endAddress); +} + +unsigned int CMEM2Alloc::usableSize(void *p) +{ + return p == 0 ? 0 : ((SBlock *)p - 1)->s * sizeof (SBlock); +} + +void *CMEM2Alloc::allocate(unsigned int s) +{ + if (s == 0) + s = 1; + // + LockMutex lock(m_mutex); + // + s = (s - 1) / sizeof (SBlock) + 1; + // First block + if (m_first == 0) + { + if (m_baseAddress + s + 1 >= m_endAddress) + return 0; + m_first = m_baseAddress; + m_first->next = 0; + m_first->prev = 0; + m_first->s = s; + m_first->f = false; + return (void *)(m_first + 1); + } + // Search for a free block + SBlock *i; + SBlock *j; + for (i = m_first; i != 0; i = i->next) + { + if (i->f && i->s >= s) + break; + j = i; + } + // Create a new block + if (i == 0) + { + i = j + j->s + 1; + if (i + s + 1 >= m_endAddress) + return 0; + j->next = i; + i->prev = j; + i->next = 0; + i->s = s; + i->f = false; + return (void *)(i + 1); + } + // Reuse a free block + i->f = false; + // Split it + if (i->s > s + 1) + { + j = i + s + 1; + j->f = true; + j->s = i->s - s - 1; + i->s = s; + j->next = i->next; + j->prev = i; + i->next = j; + if (j->next != 0) + j->next->prev = j; + } + return (void *)(i + 1); +} + +void CMEM2Alloc::release(void *p) +{ + if (p == 0) + return; + + LockMutex lock(m_mutex); + SBlock *i = (SBlock *)p - 1; + i->f = true; + + // If there are no other blocks following yet, + // set the remaining size to free size. - Dimok + if(i->next == 0) + i->s = m_endAddress - i - 1; + + // Merge with previous block + if (i->prev != 0 && i->prev->f) + { + i = i->prev; + i->s += i->next->s + 1; + i->next = i->next->next; + if (i->next != 0) + i->next->prev = i; + } + // Merge with next block + if (i->next != 0 && i->next->f) + { + i->s += i->next->s + 1; + i->next = i->next->next; + if (i->next != 0) + i->next->prev = i; + } +} + +void *CMEM2Alloc::reallocate(void *p, unsigned int s) +{ + SBlock *i; + SBlock *j; + void *n; + + if (s == 0) + s = 1; + if (p == 0) + return allocate(s); + + i = (SBlock *)p - 1; + s = (s - 1) / sizeof (SBlock) + 1; + { + LockMutex lock(m_mutex); + + //out of memory /* Dimok */ + if (i + s + 1 >= m_endAddress) + { + return 0; + } + + // Last block + if (i->next == 0 && i + s + 1 < m_endAddress) + { + i->s = s; + return p; + } + // Size <= current size + next block + if (i->next != 0 && i->s < s && i->next->f && i->s + i->next->s + 1 >= s) + { + // Merge + i->s += i->next->s + 1; + i->next = i->next->next; + if (i->next != 0) + i->next->prev = i; + } + // Size <= current size + if (i->s >= s) + { + // Split + if (i->s > s + 1) + { + j = i + s + 1; + j->f = true; + j->s = i->s - s - 1; + i->s = s; + j->next = i->next; + j->prev = i; + i->next = j; + if (j->next != 0) + j->next->prev = j; + } + return p; + } + } + // Size > current size + n = allocate(s * sizeof (SBlock)); + if (n == 0) + return 0; + memcpy(n, p, i->s * sizeof (SBlock)); + release(p); + return n; +} + +unsigned int CMEM2Alloc::FreeSize() +{ + LockMutex lock(m_mutex); + + if (m_first == 0) + return (const char *) m_endAddress - (const char *) m_baseAddress; + + SBlock *i; + unsigned int size = 0; + + for(i = m_first; i != 0; i = i->next) + { + if(i->f && i->next != 0) + size += i->s; + + else if(i->f && i->next == 0) + size += m_endAddress - i - 1; + + else if(!i->f && i->next == 0) + size += m_endAddress - i - i->s - 1; + } + + return size*sizeof(SBlock); +} diff --git a/source/memory/mem2alloc.h b/source/memory/mem2alloc.hpp similarity index 95% rename from source/memory/mem2alloc.h rename to source/memory/mem2alloc.hpp index 6d681b09..e66f524a 100644 --- a/source/memory/mem2alloc.h +++ b/source/memory/mem2alloc.hpp @@ -1,41 +1,42 @@ -// MEM2 allocator -// Made as a class so i can have 2 sections, one being dedicated to the covers - -#ifndef __MEM2ALLOC_HPP -#define __MEM2ALLOC_HPP - -#include - -class CMEM2Alloc -{ -public: - void *allocate(unsigned int s); - void release(void *p); - void *reallocate(void *p, unsigned int s); - void init(unsigned int size); - void init(void *addr, void *end); - void cleanup(void); - void clear(void); - static unsigned int usableSize(void *p); - void forceEndAddress(void *newAddr) { m_endAddress = (SBlock *)newAddr; } - void *getEndAddress(void) const { return m_endAddress; } - void info(void *&address, unsigned int &size) const { address = m_baseAddress; size = (const char *)m_endAddress - (const char *)m_baseAddress; } - // - CMEM2Alloc(void) : m_baseAddress(0), m_endAddress(0), m_first(0), m_mutex(0) { } -private: - struct SBlock - { - unsigned int s; - SBlock *next; - SBlock *prev; - bool f; - } __attribute__((aligned(32))); - SBlock *m_baseAddress; - SBlock *m_endAddress; - SBlock *m_first; - mutex_t m_mutex; -private: - CMEM2Alloc(const CMEM2Alloc &); -}; - -#endif // !defined(__MEM2ALLOC_HPP) +// MEM2 allocator +// Made as a class so i can have 2 sections, one being dedicated to the covers + +#ifndef __MEM2ALLOC_HPP +#define __MEM2ALLOC_HPP + +#include + +class CMEM2Alloc +{ +public: + void *allocate(unsigned int s); + void release(void *p); + void *reallocate(void *p, unsigned int s); + void init(unsigned int size); + void init(void *addr, void *end); + void cleanup(void); + void clear(void); + static unsigned int usableSize(void *p); + void forceEndAddress(void *newAddr) { m_endAddress = (SBlock *)newAddr; } + void *getEndAddress(void) const { return m_endAddress; } + void info(void *&address, unsigned int &size) const { address = m_baseAddress; size = (const char *)m_endAddress - (const char *)m_baseAddress; } + unsigned int FreeSize(); + // + CMEM2Alloc(void) : m_baseAddress(0), m_endAddress(0), m_first(0), m_mutex(0) { } +private: + struct SBlock + { + unsigned int s; + SBlock *next; + SBlock *prev; + bool f; + } __attribute__((aligned(32))); + SBlock *m_baseAddress; + SBlock *m_endAddress; + SBlock *m_first; + mutex_t m_mutex; +private: + CMEM2Alloc(const CMEM2Alloc &); +}; + +#endif // !defined(__MEM2ALLOC_HPP) diff --git a/source/menu.cpp b/source/menu.cpp index ee849e29..242b2b53 100644 --- a/source/menu.cpp +++ b/source/menu.cpp @@ -350,10 +350,8 @@ int MainMenu(int menu) { gettextCleanUp(); if(dbvideo) - { - InitVideodebug (); - //printf("\n\n\n\n\n"); - } + InitVideodebug (); + if (mountMethod==3) { struct discHdr *header = &gameList[gameSelected]; @@ -372,13 +370,13 @@ int MainMenu(int menu) { WII_LaunchTitle(0x0000000100000100ULL); } - else if (boothomebrew == 1) { + if (boothomebrew == 1) { gprintf("\nBootHomebrew"); BootHomebrew(Settings.selected_homebrew); } else if (boothomebrew == 2) { - gprintf("\nBootHomebrewFromMenu"); - BootHomebrewFromMem(); + gprintf("\nBootHomebrew from Menu"); + BootHomebrew(); } else { gprintf("\n\tSettings.partition:%d",Settings.partition); diff --git a/source/menu/menu_disclist.cpp b/source/menu/menu_disclist.cpp index 563c5af7..1cdecf26 100644 --- a/source/menu/menu_disclist.cpp +++ b/source/menu/menu_disclist.cpp @@ -577,7 +577,7 @@ int MenuDiscList() { w.Append(&sdcardBtn); w.Append(&poweroffBtn); w.Append(&gameInfo); - if (Settings.godmode && load_from_fs != PART_FS_NTFS) + if (Settings.godmode) w.Append(&installBtn); w.Append(&homeBtn); w.Append(&settingsBtn); @@ -800,18 +800,10 @@ int MenuDiscList() { gameCarousel->SetFocus(1); } } else if (installBtn.GetState() == STATE_CLICKED) { - gprintf("\n\tinstallBtn clicked"); - if (load_from_fs == PART_FS_NTFS) - { - choice = 0; - WindowPrompt(tr("Install not possible"), tr("You are using NTFS filesystem. Due to possible write errors to a NTFS partition, installing a game is not possible."), tr("OK")); - } - else - choice = WindowPrompt(tr("Install a game"),0,tr("Yes"),tr("No")); + choice = WindowPrompt(tr("Install a game"),0,tr("Yes"),tr("No")); if (choice == 1) { - if (load_from_fs == PART_FS_NTFS) - menu = MENU_INSTALL; + menu = MENU_INSTALL; break; } else @@ -825,7 +817,7 @@ int MenuDiscList() { gameCarousel->SetFocus(1); } } - } else if ((covert & 0x2)&&(covert!=covertOld) && load_from_fs != PART_FS_NTFS) { + } else if ((covert & 0x2)&&(covert!=covertOld)) { gprintf("\n\tNew Disc Detected"); choice = WindowPrompt(tr("New Disc Detected"),0,tr("Install"),tr("Mount DVD drive"),tr("Cancel")); if (choice == 1) { diff --git a/source/sys.cpp b/source/sys.cpp index 68abdfc8..7ddfd742 100644 --- a/source/sys.cpp +++ b/source/sys.cpp @@ -12,8 +12,10 @@ #include "audio.h" #include "menu.h" #include "fatmounter.h" +#include "gecko.h" #include "sys.h" #include "wpad.h" +#include "lstub.h" extern char game_partition[6]; extern u8 load_from_fs; @@ -197,7 +199,8 @@ void Sys_LoadMenu(void) { } void Sys_BackToLoader(void) { - if (*((u32*) 0x80001800)) { + + if (hbcStubAvailable()) { _ExitApp(); exit(0); } @@ -264,6 +267,85 @@ s32 IOS_ReloadIOSsafe(int ios) return r; } +s32 CheckForCIOS() +{ + gprintf("\n\tChecking for stub IOS"); + s32 ret = 1; + ios222rev = getIOSrev(0x00000001000000dell); + ios249rev = getIOSrev(0x00000001000000f9ll); + + //if we don't like either of the cIOS then scram + if (!((ios222rev >= 4 && ios222rev < 65280) || (ios249rev >=9 && ios249rev < 65280))) + { + printf("\x1b[2J"); + if ((ios222rev < 0 && ios222rev != WII_EINSTALL) && (ios249rev < 0 && ios249rev != WII_EINSTALL)) { + printf("\n\n\n\tWARNING!"); + printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); + printf("\n\n\tWe cannot determine the versions on your system,\n\tsince you have no patched ios 36 or 236 installed."); + printf("\n\tTherefor, if loading of USB Loader GX fails, you\n\tprobably have installed the 4.2 update,"); + printf("\n\tand you should go figure out how to get some cios action going on\n\tin your Wii."); + printf("\n\n\tThis message will show every time."); + sleep(5); + } else { + printf("\n\n\n\tERROR!"); + printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); + printf("\n\n\tI found \n\t\t222 = %d%s",ios222rev,ios222rev==65280?" (Stubbed by 4.2 update)":""); + printf("\n\t\t249 = %d%s",ios249rev,ios249rev==65280?" (Stubbed by 4.2 update)":""); + printf("\n\n\tGo figure out how to get some cIOS action going on\n\tin your Wii and come back and see me."); + + sleep(15); + printf("\n\n\tBye"); + + USBDevice_deInit(); + exit(0); + } + } + + return ret; +} + +int LoadAppCIOS() +{ + s32 ret = 1; + /* Load Custom IOS */ + SDCard_deInit();// unmount SD for reloading IOS + USBDevice_deInit();// unmount USB for reloading IOS + USBStorage2_Deinit(); + + if (Settings.cios == ios222 && IOS_GetVersion() != 222) + { + printf("\n\tReloading IOS to config setting (222)..."); + ret = IOS_ReloadIOSsafe(222); + printf("%d", ret); + if (ret < 0) + { + Settings.cios = ios249; + IOS_ReloadIOSsafe(249); + } + } + + if ((Settings.cios == ios249 && IOS_GetVersion() != 249) + || (Settings.cios == ios250 && IOS_GetVersion() != 250)) + { + printf("\n\tReloading IOS to config setting (%d)...", (Settings.cios == ios249) ? 249 : 250); + ret = IOS_ReloadIOSsafe((Settings.cios == ios249) ? 249 : 250); + printf("%d", ret); + if (ret < 0) { + Settings.cios = ios222; + ret = IOS_ReloadIOSsafe(222); + } + } + + SDCard_Init(); + if(IOS_GetVersion() == 222) + load_ehc_module(); + + USBDevice_Init(); + + return ret; +} + + #include void ScreenShot() diff --git a/source/sys.h b/source/sys.h index 2ee89d1b..9fcd55ec 100644 --- a/source/sys.h +++ b/source/sys.h @@ -16,7 +16,8 @@ int Sys_IosReload(int IOS); bool Sys_IsHermes(); s32 IOS_ReloadIOSsafe(int ios); void ScreenShot(); - +s32 CheckForCIOS(); +int LoadAppCIOS(); void ShowMemInfo(); extern s32 ios222rev; extern s32 ios223rev; diff --git a/source/video.cpp b/source/video.cpp index c82226cc..2d9f2a50 100644 --- a/source/video.cpp +++ b/source/video.cpp @@ -177,9 +177,6 @@ InitVideo () { xfb[0] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); xfb[1] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); - // A console is always useful while debugging - console_init (xfb[0], 20, 64, vmode->fbWidth, vmode->xfbHeight, vmode->fbWidth * 2); - // Clear framebuffers etc. VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK); VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK); @@ -197,6 +194,9 @@ InitVideo () { StartGX(); ResetVideo_Menu(); // Finally, the video is up and ready for use :) + + // A console is always useful while debugging + console_init (xfb[0], 80, 100, 500, 350, vmode->fbWidth * 2); } static unsigned int *xfbDB = NULL;